Java IO流与对象IO序列化(转)

来源:互联网 发布:淘宝上能买手机号码 编辑:程序博客网 时间:2024/06/01 08:45
什么是IO流?
 byte序列的读写,Java中的IO流是实现输入/输出的基础.

Java将数据从源(文件、内存、键盘、网络)读入到内存 中,形成了流,然后将这些流还可以写到另外的目的地(文件、内存、控制台、网络),之所以称为流,是因为这个数据序列在不同时刻所操作的是源的不同部分。

2、分类

按照不同的分类标准,IO流分为不同类型。主要有以下几种方式:按照数据流方向、数据处理的单位和功能。还有一些分类比如:对象流、缓冲流、压缩流、文件流等等。其实都是节点流和处理流的子分类。不管流的分类是多么的丰富和复杂,其根源来自于四个基本的类。这个四个类的关系如下:

 

 

字节流

字符流

输入流

InputStream

Reader

输出流

OutputStream

Writer

2.1输入流与输出流。

数据从内存到硬盘,通常认为是输出流,即写操作;相反,从硬盘到内存,通常认为是输入流,即读操作;这里的输入、输出是从内存的角度划分的。

2.2字节流和字符流

字节流和字符流区别非常简单,它们的用法几乎一样。区别在于字节流和字符流所处理的最小数据单元不同。

 

处理最小数据单元

基类

字节流

8

In/OutStream

字符流

16

Reader/writer

3.3节点流和处理流

节点流是可以从或向一个特定的地方(节点)读写数据,也叫 低级流。如FileReader。

处理流是在对节点流封装的基础上的一种流,通过封装后来实现数据的读写功能,也叫高级流。

 

 

节点流

未经封装,low level stream

处理流

封装过,  high level stream

      常用节点流

父 类 InputStream OutputStream Reader Writer
文 件 FileInputStream FileOutputStrean FileReader FileWriter 文件进行处理的节点流
数 组 ByteArrayInputStream   ByteArrayOutputStream   CharArrayReader   CharArrayWriter 对数组进行处理的节点流(对应的不再是文件,而是内存中的一个数组)
字符串 StringReader StringWriter    对字符串进行处理的节点流
管 道 PipedInputStream PipedOutputStream PipedReader PipedWriter 对管道进行处理的节点流

    常用处理流(关闭处理流使用关闭里面的节点流)

父 类   InputStream OutputStream Reader Writer
缓冲流 BufferedImputStrean BufferedOutputStream BufferedReader BufferedWriter

----需要父类作为参数构造,增加缓冲功能,避免频繁读写硬盘,可以初始化缓冲数据的大小,由于带了缓冲功能,所以就写数据的时候需要使用flush 方法咯 转换流 *InputStreamReader   OutputStreamWriter- 要inputStream 或OutputStream 作为

参数,实现从字节流到字符流的转换数据流 *DataInputStream DataOutputStream -提供将基础数据类型写入到文件中,或者

读取出来,为什么要有这个流呢?看这样的分析,如果没有这种流的话,有一个long,本身只占8 个字节,如果我要写入到文件,需要转成字符串,然后在转成字符数组,那空间会占用很多,但是有了这种流之后就很方便了,直接将这8 个字节写到文件就完了。。是不是既节约了内存空间有让程序写起来更加方便简单了呐。写倒是很简单,但是读取的时候就注意了,根据读取的数据类型,指针会往下移,所以你写的顺序必须要和读的顺序一致才能完成正确的需求.

3.4 Java输入输出流

总结。

分类

字节输入流

字节输出流

字符输入流

字符输出流

抽象基类

InputStream

OutputStream

Reader

Writer

访问文件

FileInputStream

FileOutputStream

FileReader

FileWriter

访问数组

ByteArrayInputStream

ByteArrayOutputStream

CharArrayReader

CharArrayWriter

访问管道

PipedInputStream

PipedOutputStream

PipedReader

PipedWriter

访问字符串

 

 

StringReader

StringWriter

缓冲流

BufferedInputStream

BufferedOutputStream

BufferedReader

BufferedWriter

转换流

 

 

InputStreamReader

OutputStreamWriter

对象流

ObjectInputStream

ObjectOutputStream

 

 

抽象基类

FilterInputStream

FilterOutputStream

FilterReader

FilterWriter

打印流

 

PrintStream

 

PrintWriter

推回输入流

PushbackInputStream

 

PushbackReader

 

特殊流

DataInputStream

DataOutputStream

 

 

 


1)
InputStream : 抽象类读取数据的过程  包含读取方法read();
  in 模仿了读取小说的过程

 简单说  :  in是读取文件的

OutputStream:抽象了写出数据的过程  包含写出方法write();
 out模仿了写笔记记录的过程

 简单说  : out是写入文件的

 

基本的byte流
InputStream(抽象方法read())
 |--- FileInputStream(read()在文件上读取)   节点流
 |
 |--- FilterInputStream 过滤器流,输入流功能的扩展
 |   |--- DataInputStream(readInt())  基本类型数据的读取
 |   |--- BufferedInputStream 提供读取缓冲区管理
 | --- ObjectInputStream   过滤器流,依赖基本byte流,扩展对象的反序列化

OutputStream(抽象方法write()) 
 |--- FileOutputStream(write()在文件上写实现写入)   节点流
 |
 |--- FilterOutputStream 过滤器流,输出流功能的扩
 |    |--- DataOutputStream(writerInt())  基本类型数据的写出
 |    |--- BufferedOutputStream 提供了输出缓冲区管理
 | --- ObjectOutputStream 过滤器流,依赖基本byte流,扩展对象的序列化

注意:除节点流外都是过滤器流

字符流,可以处理字符编码,底层依赖于byte流
Reader 读取文本
     | --- InputStreamReader  过滤去,依赖基本byte输入流
     |      实现文本编码的解析
     |
     | --- BufferedReader 过滤器, 需要依赖Reader 实例
     |    提供了readLine()方法, 可以在文本文件中读取一行
     |    是常用的文本读取方法   
Writer
     | --- OutputStreamWriter  过滤器,,依赖基本byte输出流
     |        实现文本编码
     | --- PrintWriter  过滤器,依赖于Writer 流
     |                       提供了输出文本常有方法println()

 

2)  EOF =  End of File = -1 (文件读到末尾会返回-1)

3)  输入流的基本方法  
 InputStream in = new InputStream(file) / /file是文件名
 int b  = in.read();      读取一个byte无符号填充到int底八位,-1是EOF
 int.read(byte[] buf)    读取数据填充到buf中
 int.read(byte[] buf,int start,int size)  读取数据填充到buf中
 in.close      关闭输入流

4)输出流的基本方法:
 OutputStream out = new OutputStream(file) / /file是文件名
 out.write(int b)     写出一个byte 到流 b 的底八位写出
 out.write(byte[] buf)    将buf的一部分写入流中
 out.write(byte[] buf, int start, int size)  将buf的一部分写入流中
 out.flush()      清理缓存
 out.close

1.FileInputStream (read()在文件上读取)   节点流
方法:  read()         从输入流中读取数据的下一个字节
     read(byte[] buf)  从输入流中读取一定数量的字节,并将其存储在缓冲区数组 buf中 
     read(byte[] b, int off, int len) 将输入流中最多 len 个数据字节读入 byte 数组。

复制代码
 1 import java.io.FileInputStream; 2 import java.io.IOException; 3 import java.io.InputStream; 4  5 public class InputStreamDemo { 6  public static void main(String[] args)  7   throws IOException { 8   String file = "out.txt"; 9   InputStream in = new FileInputStream(file);10   int b;11   while((b=in.read())!=-1){//read()方法12    System.out.print(Integer.toHexString(b) + " ");13   }14   in.close();15   16   in = new FileInputStream(file);17   //in.available() 可以读取的数据个数,小文件一般是文件长度18   byte[] buf = new byte[in.available()];19   in.read(buf);//read(byte[] buf)方法重载20   in.close();21   for (byte c : buf) {22    System.out.print(Integer.toHexString(c & 0xff) + " ");23    // c & 0xff --->将16进制写成0xff的格式24    //ffffffd6---> d625    //11111111 11111111 11111111 11010110  &对应相乘26    //00000000 00000000 00000000 11111111  0xff27    //00000000 00000000 00000000 1101011028   }29  }30 }
复制代码

 


2  FileOutputStream(write()在文件上写实现写入)   节点流
方法 :write(int b)  将指定的字节写入此输出流。
 write(byte[] buf)   将 b.length 个字节从指定的 byte 数组写入此输出流。
 write(byte[] b, int off, int len) 将指定 byte 数组中从偏移量 off 开始的 len 个字节写入此输出流。
例子

复制代码
 1 import java.io.*; 2  3 public class OutputStreamDemo { 4  public static void main(String[] args)  5  throws IOException{ 6   String file = "out.txt"; 7   OutputStream out = new FileOutputStream(file); 8   out.write(65);//在文件中是以16进制存储的,对应0x41 9   out.write(66);//0x4210   byte[] buf = {(byte)0xd6,(byte)0xd0};11   out.write(buf);12   out.flush();//刷出缓存,清理缓冲区,保证可靠写 13   out.close();14  }15 }
复制代码

 

3.BufferedInputStream和BufferedOutputStream 的 用法

BufferedInputStream(FileInputStream in)
BufferedOutputStream(FileOutputStream out)
可以提高性能
例子

复制代码
 1 import java.io.BufferedInputStream; 2 import java.io.BufferedOutputStream; 3 import java.io.FileInputStream; 4 import java.io.FileOutputStream; 5 import java.io.IOException; 6 import java.io.InputStream; 7  8 public class BufferedStreamDemo { 9  public static void main(String[] args) throws IOException {10   //BufferedInputStream普通写法11   String file = "out.txt";12   InputStream ins = new FileInputStream(file);13   BufferedInputStream bufin= new BufferedInputStream(ins);14   int b;15   while((b=bufin.read())!=-1){16    System.out.println(Integer.toHexString(b));17   }18   //常用写法,只要用到FileInputStream的地方都可以套一个BufferedInputStream用来提升性能19   BufferedInputStream in = new BufferedInputStream(20     new FileInputStream("out.txt"));21   22   //BufferedOutputStream23   BufferedOutputStream out = new BufferedOutputStream(24     new FileOutputStream("out.txt"));25   out.write(65);26  }27 }
复制代码

 

4.基本类型数据的写出和读入
DataOutputStream  方法:readInt()  readLong()  readBoolean()等
写出(写)

复制代码
 1 例子 2 import java.io.*; 3 public class DataOutDemo { 4  public static void main(String[] args) 5   throws IOException{  6   String file = "data.dat";//项目文件夹 7   OutputStream out = new FileOutputStream(file); 8   //DataOutputStream 实现基本类型数据的序列化 9   //将基本类型数据拆开为byte序列,写入到out流中10   DataOutputStream dos = new DataOutputStream(out);11   dos.write(-2);12   dos.writeInt(-2);13   dos.writeLong(-2);14   dos.writeByte(-2);15   dos.writeDouble(-2);16   dos.writeShort(-2);17   dos.writeFloat(-2);18   dos.writeBoolean(true);19   dos.writeChar('中');20   dos.close();21 22  }23 }
复制代码

 

DataInputStream   方法:  writeInt()  writeChar() 等8种
读入(读)

复制代码
 1 import java.io.DataInputStream; 2 import java.io.FileInputStream; 3 import java.io.IOException; 4 import java.io.InputStream; 5  6 public class DataInDemo { 7  public static void main(String[] args)  8   throws IOException{ 9   10   String file = "data.dat";11 12   InputStream in = new FileInputStream(file);13   //DataInputStream 从基本流中读取基本类型数据,实现基本14   //类型数据的反序列化15   DataInputStream dis = new DataInputStream(in);16   int b = dis.read();17   int i = dis.readInt();18   long l= dis.readLong();19   byte bx = dis.readByte();20   double d = dis.readDouble();21   short s = dis.readShort();22   float f = dis.readFloat();23   boolean bol = dis.readBoolean();24   char c = dis.readChar();25   dis.close();26   System.out.print( b +" ");//254  fe27   System.out.print(i+" ");28   System.out.print(l+" ");29   System.out.print(bx+" ");30   System.out.print(d+" ");31   System.out.print(s+" ");32   System.out.print(f+" ");33   System.out.print(bol+" ");34   System.out.print(c+" ");35   36  }37 }
复制代码

 

 

5 字符串的序列化(文字的编码方案)
 从char序列到byte序列 的转换,叫"编码"
 1) String 字符串本质上是Char
 
 2)utf-16be 编码-----将16位char从中间切开为2个byte
     utf -16be是将 unicode char[] 序列化为byte[]的编码方案
     能够支持65535个字符编码,英文浪费空间

 如:
 char[] = ['A',    'B',    '中']
  对应     0041,0042,4e2d

 utf-8:国际标准,是将unicode编码为byte序列的方案,采用变长编码 1-N方案,其中英文1个byte,中文3个byte
   unicoded的" 中": 4e 2d = 01001110 00101101
    utf-8的"中":e4 b8 ad =11100100 10111000 10101101
        1110xxxx 10xxxxxx 10xxxxxx

 以0开头的是英文(0-127)
 110表示连续2字节表示一个字符
 1110表示连续3字节表示一个字符
 每个数据字节以10开头

GBK: 中国标准,支持20000+中日韩文,英文编码1byte,中文2byte
 与unicode不兼容,中文windows默认gbk

ISO8859-1:只支持255个英文字符,不支持中文(Sun服务器默认编码,如tomcat等)

例子

复制代码
 1 import java.io.FileOutputStream; 2 import java.io.IOException; 3 import java.io.OutputStream; 4  5 public class CharIODemo { 6  public static void main(String[] args)  7   throws IOException{ 8   String str = "ABCD中国"; 9   System.out.println(str);10   //Java 的字符是16位 Unicode值,而文件是8位Byte序列11   12   //GBK13   System.out.println("GBK编码方案,对字符编码");14   String file = "gbk.txt";15   OutputStream out = new FileOutputStream(file);//默认GBK编码方案16   byte[] gbk = str.getBytes("GBK");17   out.write(gbk);18   out.close();19   IOUtils.print(file);20   //UTF-16BE,每个编码是2个字节21   System.out.println("UTF-16BE编码方案,对字符编码");22   file = "utf-16be.txt";23   out = new FileOutputStream(file);24   byte[] utf16be = str.getBytes("UTF-16BE");25   out.write(utf16be);26   out.close();27   IOUtils.print(file);28   29   //UTF-8,英文是1个字节,中文是3个字节30   System.out.println("UTF-8编码方案,对字符编码");31   file = "utf-8.txt";32   out = new FileOutputStream(file);33   byte[] utf8 = str.getBytes("UTF-8");//编码string -> byte[]34   out.write(utf8);35   out.close();36   IOUtils.print(file);37   38   byte[] buf = IOUtils.read("utf-8.txt");39   //new String(buf,"UTF-8"),构造器可以将 byte编码序列40   //解码为 char序列(字符串)41   String s = new String(buf,"UTF-8");//解码byte-> String42   System.out.println(s);43  }44 }
复制代码

 

6 字符流IO(Reader Writer)
1) 字符的处理,一次处理一个字符(unicode编码)
2) 字符的底层仍然是基本的字节流
3) 字符流的基本实现
 InputStreamReader 完成byte流解析为char流,按照编码解析
 OutputStreamWriter 提供char流到byte流,按照编码处理
4) 字符流的过滤器
    是字符读写的功能扩展,极大的方便了文本的读写操作
    BufferedReader : readLine()   一次读取一行
    PrintWriter : println()  一次打印一行
5)读取一个文本文件
 InputStream is = new FileInputStream("test.txt");
 Reader in = new InputStreamReader(is);
 BufferedReader reader = new BufferedReader(in);
 或者
 BufferedReader in = new BufferedReader(new FileReader(filename));

例子:

复制代码
 1 import java.io.BufferedInputStream; 2 import java.io.BufferedReader; 3 import java.io.FileInputStream; 4 import java.io.IOException; 5 import java.io.InputStreamReader; 6  7 public class TestReaderDemo { 8  public static void main(String[] args)  9   throws IOException{10   //Scanner BufferedReader都是流的功能扩展,是过滤器11   // 不能单独使用,最终需要依赖于基本byte流(in)12   //Scanner 提供了nextLine()方法//Java5以后13   //BufferedReader 提供了 readLine()方法,读取一行14   //readLine()读取到文件末尾返回null15   16   //逐行读取文本文件,显示到系统控制台17   //工作中常用18   String file = "in.txt"; //为当前工作区workspace/项目名/in.txt19   BufferedReader in = new BufferedReader(20     new InputStreamReader(21       new BufferedInputStream(22         new FileInputStream(file)),"gbk"));23   String str;24   while((str = in.readLine()) != null){25    System.out.println(str);26   }27   in.close();28  }
复制代码

 


6)写出一个文本文件
 PrintWriter out = new PrintWriter(new FileWriter(new FileOutputStream(filename)));
 或者
 PrintWriter out = new PrintWriter(
     new OutputStreamWriter(
      new FileOutputStream(filename)))
例子

复制代码
 1 import java.io.IOException; 2 import java.io.PrintWriter; 3 import java.util.Scanner; 4  5 public class SyncWriteDemo { 6  public static void main(String[] args)  7   throws IOException{ 8   Scanner in = new Scanner(System.in); 9   String file = "sync.txt";10   PrintWriter out = new PrintWriter(file,"UTF-8");11   while(true){12    String str = in.nextLine();13    out.println(str);14    if("q".equalsIgnoreCase(str)){15     break;16    }17   }18   out.close();19  }20 }
复制代码

 

7)系统的默认编码,中文一般是GBK
如何查看默认编码?

String encoding = System.getProperty("file.encoding");

 


7 对象的IO序列化和深层复制

什么是对象序列化:
 将对象Object转换为byte序列,反之叫做对象的反序列华
1)序列化流,是过滤流
 ObjectOutputStream   方法 writeObject()  对象的序列化
 ObjectInputStream     方法readObject()  对象的反序列化
2)序列化接口(Serializable)


   对象必须实现"序列化接口Serializable"才能进行序列化,否则将出现不能序列化的异常
 Serializable是一个空的接口,没有任何方法 ,仅作为序列化的一个标识


3)JavaBean 规范规定,Java类必须实现Serializable接口


   Java API中的类大多是符合Java Bean规范的,基本都实现了Serializable


4) 对象的序列化可以变相实现对象的深层复制
例子

复制代码
 1 import java.io.BufferedInputStream; 2 import java.io.BufferedOutputStream; 3 import java.io.FileInputStream; 4 import java.io.FileOutputStream; 5 import java.io.ObjectInputStream; 6 import java.io.ObjectOutputStream; 7 import java.io.Serializable; 8  9 public class ObjectIODemo {10  public static void main(String[] args) 11   throws Exception{12   String file = "obj.dat";13   ObjectOutputStream out = new ObjectOutputStream(14     new BufferedOutputStream(15       new FileOutputStream(file)));16   Foo foo =new Foo();17   out.writeObject(foo);//将foo引用的对象,序列化到文件中18   out.close();19   20   //读出21   ObjectInputStream in = new ObjectInputStream(22     new BufferedInputStream(23       new FileInputStream(file)));24   Foo foo1 = (Foo)in.readObject();//对象反序列化25   in.close();26   System.out.println(foo1.name);27 28   System.out.println("深层复制:对象被复制,对象属性也被复制");29   System.out.println(foo==foo1);//false 对象复制了(一层)30   System.out.println(foo.name == foo1.name);//false ,属性被复制了(二层)31   //利用序列化 和 反序列化  可以简洁的实现 对象的深层复制32  }33 34 }35 class Foo implements Serializable{//Serializable没有声明方法36  String name = "Tom";37 }
复制代码

 


浅层复制与深层复制
1)java的默认规则是浅层复制,性能好,但隔离性差,如(clone(),Arrays.copyOf)
 浅层复制 : 对象的引用不同,但对象中属性的引用相同
2)利用序列化可以实现深层复制
 深层复制: 对象的引用不同,但对象中的属性的引用也不相同

复制代码
 1 import java.io.ByteArrayInputStream; 2 import java.io.ByteArrayOutputStream; 3 import java.io.ObjectInputStream; 4 import java.io.ObjectOutputStream; 5  6 public class DeepcopyDemo { 7  public static Object deepCope(Object obj){ 8   try{ 9    //1. 对象序列化10    // 缓冲流: 字节数组输出流11    ByteArrayOutputStream buf =12     new ByteArrayOutputStream();13    //对象输出流14    ObjectOutputStream out = 15     new ObjectOutputStream(16       new ByteArrayOutputStream());17    18    out.writeObject(obj);//序列化对象到buf中19    out.close();20    21    //2 .对象的反序列化22    byte[] ary = buf.toByteArray();23    ByteArrayInputStream bais = 24     new ByteArrayInputStream(ary);25    ObjectInputStream in = 26     new ObjectInputStream(bais);27    Object o = in.readObject();//从ary反序列化28    in.close();29    return o;30    31   }catch(Exception e){32    e.printStackTrace();33    throw new RuntimeException(e);34   }35  }36 }
复制代码

 

以上用到的ByteArrayInputStreamByteArrayOutputStream

下面有一个ByteArrayInputStream和ByteArrayOutputStream的例子

例子

复制代码
 1 import java.io.ByteArrayInputStream; 2 import java.io.ByteArrayOutputStream; 3 import java.io.IOException; 4 import java.util.Arrays; 5  6 import com.tarena.day18.IOUtils; 7  8 public class ByteArrayIODemo { 9  public static void main(String[] args) 10   throws IOException{11   byte[] ary = {1,-1,127,-128};12   //   {00000001, 11111111, 01111111, 10000000}13   ByteArrayInputStream in = new ByteArrayInputStream(ary);14   int b = in.read();//1 00000000 00000000 00000000 0000000115   System.out.println(b);16   b = in.read();17   System.out.println(b);//255   00000000 00000000 00000000 1111111118   b = in.read();   19   System.out.println(b);//127   00000000 00000000 00000000  0111111120   b = in.read();21   System.out.println(b);//128   00000000 00000000 00000000  1000000022   b = in.read();23   System.out.println(b);//-1    11111111  11111111  11111111  1111111124   in.close();25   26   ByteArrayOutputStream out = new ByteArrayOutputStream();//默认开辟32byte的数组作为输出目标27   //如果满了就自动扩容28   //out:[0,0,0,0,0,0,0,.....]29   //30   out.write(1);//[1,0,0,0,0,0,....]31   out.write(-2);//[1,fe,0,0,0,0,0,....]32   out.write(-1);//[1,fe,ff,0,0,0,0,....]33   out.close();34   byte[] buf = out.toByteArray();//复制有效部分35   IOUtils.print(buf);//[01, fe, ff ]36  }37 38 }39 40 41  
复制代码