文章目录
- 序列化和反序列化
- IO流
- IO流的简介和分类
- Java IO流四大家族
- 要掌握的16个流
- 文件专属
- java.io.FileInputStream,字节输入流
- java.io.FileOutputStream,字节输出流,
- java.io.FileReader(文件字符输入流)
- java.io.FileWriter(文件字符输入流)
- 转换流(将字节流转换成字符流)
- 缓冲流专属
- java.io.BufferedReader,带有缓冲区的字符输入流
- java.io.BufferedWriter,带有缓冲去的字符输入流
- java.io.BufferedInputStream
- java.io.BufferedOutputSteam
- 数据流专属
- java.io.DataInputStream,数据字节写入流
- java.io.DataOutputStream,数据字节输出流
- 标准输出流
- java.io.PrintWriter,标准字节输出流
- java.io.PrintStream
- 对象流
- java.io.ObjectInputStream
- java.io.ObjectOutputStream
序列化和反序列化
- 序列化:Serialize ,将内存中的Java对象存储到文件中的过程
Java实现序列化有两种方式:
- 实现serializable接口:直接实现Serializable接口的类是JDK自动把这个类的对象序列化
- 实现externaliazble接口:如果实现public interface Externalizable extends Serializable的类则可以自己控制对象的序列化,建议能让JDK自己控制序列化的就不要让自己去控制
transient关键字修饰的属性是透明的,序列化时候就不需要考虑这个属性了
-
反序列化:DeSerialize,将文件中的数据重新读取到内存中,恢复成Java对象的过程
-
Java是通过什么机制来确定是否是一个类的?
第一,首先通过类名来确定,类名不一样,肯定不是一个类。
第二,类名一样,通过序列化版本号进行区分。
IO流
IO流的简介和分类
通过IO可以完成硬盘文件的读和写,IO流又称输入输出流,输入和输出均是以内存作为参照。IO流按照流的方向可以分为:输入(读),输出(写)。按照读取数据方式不同进行分类:
-
字节流: 按照字节的方式读取数据,一次读写一个字节(Byte),等同于一次读取8个二进制位,这种流是万能的,什么文件都可以读取,包括:文本文件,图片,声音文件,视频文件等。
例如: a中国bo账单fe
第一次读取,一个字节,刚好读取‘a’,
第二次读取,一个字节,读取”中“字符的一半,
第三次读取,一个字节,读取”中“字符的另一半 -
字符流: 按照字符的方式读取数据,一次读取一个字符,这种流是为了方便普通文本文件而存在的,字符流不能读取图片、声音、视频文件。只能读取存文本文件。
例如:a中国bo账单fe
第一次读取,‘a’字符,
第二次读取,‘中’字符 -
节点流、包装流(处理流):
节点流:当一个流的构造方法需要一个流的时候,这个被传入的流就叫做节点流。
包装流(处理流):外部负责包装的流就叫做包装流,也称为处理流。
包装流关闭资源时候,只需要关闭外部包装流即可,内部节点流会自动关闭。
Java IO流四大家族
Java IO有四大家族,均是抽象类,分别是:
java.io.InputStream :字节输入流
java.io.OutputStream: 字节输出流
java.io.Reader:字符输入流
java.io.Writer:字符输出流
所有流均实现了java.io.Closeable接口,都是可关闭的,都有close()方法。
所有输出流也实现了java.io.Flushable接口,都是可刷新的, 都有flush()方法,输入流输出之后,一定要进行flush()操作,保证将管道、通道中剩余未输出的数据输出完成,清空管道。
Java中类名以”Stream“结尾的都是字节流,以”Reader、Weiter“结尾的都是字符流。
要掌握的16个流
文件专属
java.io.FileInputStream,字节输入流
任何形式文件都能读取,但是一个字节一个字节的读取,内存和硬盘的交互太频繁了,所以一般采取一次读取一个字节数组(byte[])的方式进行读取。
int read():一次读取一个字节,读取长度为-1时候,就是最后一次读取
int read(byte[] b): 一次读取b.length个字节,当读取长度小于b.length时候,就是最后一次读取
int available(): 返回六种剩余没有读取的字节数量
long skip(long n):跳过几个字节不读
FileInputStream fileInputStream = null;try{fileInputStream = new FileInputStream("C:\\Users\\Administrator\\Desktop\\temp.txt");// 准备读取byte数组,一次读取4个字节byte[] bytes = new byte[1024];// 定义读取长度int readCount;while((readCount = fileInputStream.read(bytes))!=-1) {System.out.println(new String(bytes,0,readCount));}}catch (Exception e) {e.printStackTrace();}finally {// 关闭流if(fileInputStream != null) {try {fileInputStream.close();} catch (IOException e) {e.printStackTrace();}}}
java.io.FileOutputStream,字节输出流,
负责写,从内存到硬盘,为了减少内存和硬盘的交互,一般也是采用一次写入一个字节数据(byte[])方式进行写入。以不清空源文件的方式写入,要选择追加的方式写入们需要在创建对象时候设置参数true,用来表示追加写入。
FileOutputStream fileOutputStream = null;try{// 设置true 表示追加fileOutputStream = new FileOutputStream("C:\\Users\\Administrator\\Desktop\\temp.txt",true);// 定义写入数组//byte[] bytes = new byte[1024];String s = "FileOutputStreamDemo" + Calendar.getInstance();fileOutputStream.write(s.getBytes());}catch (Exception e) {e.printStackTrace();}finally {try {fileOutputStream.flush();fileOutputStream.close();} catch (IOException e) {e.printStackTrace();}}
java.io.FileReader(文件字符输入流)
FileReader reader = null;
try{// 创建文件字符输入流reader = new FileReader("C:\\Users\\Administrator\\Desktop\\temp.txt");// 开始读char[] chars = new char[10];int readCounts = 0;while((readCounts = reader.read(chars)) != -1){System.out.print(new String(chars,0,readCounts));}}catch (Exception e){e.printStackTrace();}finally {try {reader.close();} catch (IOException e) {e.printStackTrace();}}
java.io.FileWriter(文件字符输入流)
FileWriter out = null;try {// 初始化一个文件字符输出流对象out = new FileWriter("C:\\Users\\Administrator\\Desktop\\temp03.txt",true); // 不想清空一直累加的话 可以在此处加true// 开始写char[] chars = {'我','是','中','国','人'};// 刷新out.write(chars);out.write(chars,0,2);out.write("我是一名java工程师!");}catch (Exception e) {e.printStackTrace();}finally {try {out.flush();out.close();} catch (IOException e) {e.printStackTrace();}}
转换流(将字节流转换成字符流)
-
java.io.InputStreamReader
-
java.io.OutputStreamWriter
缓冲流专属
java.io.BufferedReader,带有缓冲区的字符输入流
使用的时候不需要创建数组了,自带缓冲区
FileReader fileReader = null;BufferedReader bufferedReader = null;try{fileReader = new FileReader("C:\\Users\\Administrator\\Desktop\\temp01.txt");// 当一个流的构造方法中需要一个流的时候,这个被传进来的流叫做: 节点流.// 外部负责包装的这个流,叫做:包装流,还有一个名字叫做 : 处理流// 像当前这个程序来说: FileReader就是一个节点流。BufferedReader就是包装流/处理流。bufferedReader = new BufferedReader(fileReader);String s ;while((s = bufferedReader.readLine())!=null){System.out.println(s);}}catch (Exception e ) {e.printStackTrace();} finally {// 关闭流// 对于包装流来说,只需要关闭最外层流就行,里面的节点流会自动关闭!try {bufferedReader.close();} catch (IOException e) {e.printStackTrace();}}
java.io.BufferedWriter,带有缓冲去的字符输入流
BufferedWriter bw = null;
try{bw = new BufferedWriter(new OutputStreamWriter(new FileOutputStream("C:\\Users\\Administrator\\Desktop\\temp04.txt",true)));bw.write("hello world");bw.write("\n");bw.write("xxliao");
}catch (Exception e){e.printStackTrace();
}finally {try {bw.flush();// 关闭最外层bw.close();}catch (Exception e) {e.printStackTrace();}
}
java.io.BufferedInputStream
java.io.BufferedOutputSteam
数据流专属
java.io.DataInputStream,数据字节写入流
对于DataOutputStream写入的文件,只能使用DataInputStream去读取,并且读的时候需要提前知道写入的顺序,只有写和读取的顺序一致,才能正确取出数据。
DataInputStream dis = new DataInputStream(new FileInputStream("data"));// 开始读Byte b = dis.readByte();Short s = dis.readShort();int i = dis.readInt();Float f = dis.readFloat();Boolean sex = dis.readBoolean();Double d = dis.readDouble();System.out.println(b);System.out.println(s);System.out.println(i);System.out.println(f);System.out.println(sex);System.out.println(d);
java.io.DataOutputStream,数据字节输出流
这个流可以将数据、以及该数据的数据类型一同写入文件,但是这个文件不是普通的文本文件。
// 创建数据专属的字节输出流DataOutputStream dos = new DataOutputStream(new FileOutputStream("data"));// 写数据byte b =100;short s =200;int i =300;long l = 400L;float f =3.0f;double d = 3.14;boolean sex = false;char c = 'a';// 写dos.writeByte(b);dos.writeShort(s);dos.writeInt(i);dos.writeFloat(f);dos.writeBoolean(sex);dos.writeDouble(d);dos.flush();dos.close();
标准输出流
java.io.PrintWriter,标准字节输出流
默认输出到控制台上,也就是System.out.println()方法
PrintStream ps = null;
try {// 指向一个日志文件ps = new PrintStream(new FileOutputStream("C:\\Users\\Administrator\\Desktop\\log.txt",true));// 改变流的输出方向System.setOut(ps);// 获取系统当前时间Date nowTime = new Date();// 日期格式化SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss SSS");String strTime = sdf.format(nowTime);System.out.println(strTime+": "+msg);} catch (FileNotFoundException e) {e.printStackTrace();}finally {ps.close();}
java.io.PrintStream
对象流
java.io.ObjectInputStream
ObjectInputStream ois = new ObjectInputStream(new FileInputStream("students"));// 开始反序列化 从硬盘中读到内存当中Object obj = ois.readObject();// 反序列化回来是一个学生对象 所以会调用学生对象的toString方法System.out.println(obj);ois.close();
ObjectInputStream oos = new ObjectInputStream(new FileInputStream("users"));// 自己测试的时候 用 instanceOf 判断过返回的对象是 list集合List<User> userList = (List<User>)oos.readObject();for(User user : userList){System.out.println(user);}oos.close();
java.io.ObjectOutputStream
// 实现可序列化的接口 接口中无任何内容 只是一个标志性接口
public class Student implements Serializable {/*** Java虚拟机看到Serializable接口后,会自动生成一个序列化版本号* 这里没有手动写出来,java虚拟机会吗,默认提供这个序列化版本号* 建议将序列化版本号写出来,不建议自动生成*/private static final long serialVersionUID = 1L; // 手动写出序列化版本号private int no;private String name;// 过了很久,Student这个类源代码改动了// 源代码改动之后,需要重新编译,编译之后生成了全新的字节码文件。// 并且class文件再次运行的时候,java虚拟机生成的序列化版本号也会发生相应的改变。private int age;
}
// 创建java对象Student s = new Student(111, "张三");// 序列化ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("students"));// 序列化对象oos.writeObject(s);// 刷新oos.flush();// 关闭oos.close();