Java_SE(二)IO流

目录

一. IO概述

1.1 什么是IO

1.2 IO分类

1.2.1 按照数据的流向分

1.2.2 按照读写单位划分

1.2.3 按照格式划分

1.3 IO作用

1.4 IO的顶级父类

二. 字节流

2.1 字节输出流【OutputStream】

2.1.1 FileOutputStream类

2.2 字节输入流【InputStream】

2.2.1 FileInputStream类

三. 字符流

3.1 字符输出流【Writer】

3.1.1 FileWriter类

3.2 字符输入流【Reader】

3.2.1 FileReader类

四. 缓冲流

4.1 缓冲流概述

4.2 字节缓冲流

4.2.1 构造方法

4.2.2 效率测试演示

4.3 字符缓冲流

4.3.1 构造方法

4.3.2 特有方法

五. 对象流(序列化)

5.1 序列化概述

5.2 ObjectOutputStream类

5.2.1 构造方法

5.2.2 序列化操作

5.3 ObjectInputStream类

5.3.1 构造方法

5.3.2 反序列化操作1

5.3.3 反序列化操作2

六. 转换流 

6.1 InputStreamReader类

6.1.1 构造方法

6.1.2 指定编码读取 

6.2 OutputStreamWriter类

6.2.1 构造方法

6.2.2 指定编码写出


一. IO概述

1.1 什么是IO

java将IO比喻为"流",即:stream。 就像生活中的"电流"、"水流"一样。它是以同一个方向顺序移动的过程,只不过这里流动的是字节(2进制数据)。所以在IO中有输入流和输出流之分,我们理解他们是连接程序与另一端的"管道",用于获取或发送数据到另一端。

流示意图
流示意图

1.2 IO分类

1.2.1 按照数据的流向分

  1. 输入流:用来读取数据的,是从外界到程序的方向,用于获取数据。

  2. 输出流:用来写出数据的,是从程序到外界的方向,用于发送数据。

1.2.2 按照读写单位划分

  1. 字节流:以字节为单位,读写数据的流。

  2. 字符流:以字符为单位,读写数据的流。 

1.2.3 按照格式划分

  1. 节点流(低级流):节点流的另一端是明确的,是实际读写数据的流,读写一定是建立在节点流基础上进行的。

  2. 处理流(高级流):处理流不能独立存在必须连接在其他流上,目的是当数据流经当前流时对数据进行加工处理来简化我们对数据的操作。

1.3 IO作用

java io可以让我们用标准的读写操作来完成对不同设备的读写数据工作。

1.4 IO的顶级父类

  1. java.io.OutputStream
  2. java.io.InputStream

二. 字节流

2.1 字节输出流【OutputStream】

java.io.OutputStream:表示字节输出流的所有类的超类,将指定的字节信息写出到目的地。它定义了字节输出流的基本共性功能方法。

  • public void close():关闭此输出流并释放与此流相关联的任何系统资源。  
  • public void flush() :刷新此输出流并强制任何缓冲的输出字节被写出。  
  • public void write(byte[] b):将 b.length字节从指定的字节数组写入此输出流。  
  • public void write(byte[] b, int off, int len):从指定的字节数组写入 len字节,从偏移量 off开始输出到此输出流。  
  • public abstract void write(int b) :将指定的字节输出流。

2.1.1 FileOutputStream类

java.io.FileOutputStream:是文件输出流,用于将数据写出到文件。

  1. 构造方法
    1. public FileOutputStream(File file):创建文件输出流以写入由指定的 File对象表示的文件
    2. public FileOutputStream(String name): 创建文件输出流以指定的名称写入文件

      注:当你创建一个流对象时,必须传入一个文件路径。该路径下,如果没有这个文件,会创建该文件。如果有这个文件,会清空这个文件的数据。

      public class FileOutputStreamConstructor throws IOException {
          public static void main(String[] args) {
         	 	// 使用File对象创建流对象
              File file = new File("a.txt");
              FileOutputStream fos = new FileOutputStream(file);
            
              // 使用文件名称创建流对象
              FileOutputStream fos = new FileOutputStream("b.txt");
          }
      }
  2. 写出字节数据
    1. 写出字节:write(int b) 方法,每次可以写出一个字节数据,代码使用演示:
      public class FOSWrite {
          public static void main(String[] args) throws IOException {
              // 使用文件名称创建流对象
              FileOutputStream fos = new FileOutputStream("fos.txt");     
            	// 写出数据
            	fos.write(97); // 写出第1个字节
            	fos.write(98); // 写出第2个字节
            	fos.write(99); // 写出第3个字节
            	// 关闭资源
              fos.close();
          }
      }
      输出结果:
      abc
    2. 写出字节数组:write(byte[] b),每次可以写出数组中的数据,代码使用演示:

      public class FOSWrite {
          public static void main(String[] args) throws IOException {
              // 使用文件名称创建流对象
              FileOutputStream fos = new FileOutputStream("fos.txt");     
            	// 字符串转换为字节数组
            	byte[] b = "我是程序员".getBytes();
            	// 写出字节数组数据
            	fos.write(b);
            	// 关闭资源
              fos.close();
          }
      }
      输出结果:
      我是程序员
    3. 写出指定长度字节数组:write(byte[] b, int off, int len) ,每次写出从off索引开始,len个字节,代码使用演示:

      public class FOSWrite {
          public static void main(String[] args) throws IOException {
              // 使用文件名称创建流对象
              FileOutputStream fos = new FileOutputStream("fos.txt");     
            	// 字符串转换为字节数组
            	byte[] b = "abcde".getBytes();
      		// 写出从索引2开始,2个字节。索引2是c,两个字节,也就是cd。
              fos.write(b,2,2);
            	// 关闭资源
              fos.close();
          }
      }
      输出结果:
      cd
  3. 数据追加续写

    1. public FileOutputStream(File file, boolean append): 创建文件输出流以写入由指定的 File对象表示的文件。

    2. public FileOutputStream(String name, boolean append): 创建文件输出流以指定的名称写入文件。

      public class FOSWrite {
          public static void main(String[] args) throws IOException {
              // 使用文件名称创建流对象
              // 构造方法,需要传入一个boolean值,true表示追加数据,false表示清空原有数据
              FileOutputStream fos = new FileOutputStream("fos.txt",true);     
            	// 字符串转换为字节数组
            	byte[] b = "abcde".getBytes();
      		// 写出从索引2开始,2个字节。索引2是c,两个字节,也就是cd。
              fos.write(b);
            	// 关闭资源
              fos.close();
          }
      }
      文件操作前:cd
      文件操作后:cdabcde

2.2 字节输入流【InputStream】

java.io.InputStream:表示字节输入流的所有类的超类,可以读取字节信息到内存中。它定义了字节输入流的基本共性功能方法。

  1. public void close():关闭此输入流并释放与此流相关联的任何系统资源。    
  2. public abstract int read(): 从输入流读取数据的下一个字节。 
  3. public int read(byte[] b): 从输入流中读取一些字节数,并将它们存储到字节数组 b中 。

2.2.1 FileInputStream类

java.io.FileInputStream: 是文件输入流,从文件中读取字节。

  1. 构造方法
    1. FileInputStream(File file): 通过打开与实际文件的连接来创建一个 FileInputStream ,该文件由文件系统中的 File对象 file命名。 
    2. FileInputStream(String name): 通过打开与实际文件的连接来创建一个 FileInputStream ,该文件由文件系统中的路径名 name命名。
      public class FileInputStreamConstructor throws IOException{
          public static void main(String[] args) {
         	 	// 使用File对象创建流对象
              // 创建流对象时,必须传入一个文件路径。如果没有该文件,会抛出FileNotFoundException
              File file = new File("a.txt");
              FileInputStream fos = new FileInputStream(file);
            
              // 使用文件名称创建流对象
              FileInputStream fos = new FileInputStream("b.txt");
          }
      }
  2. 读取字节数据
    1. 读取字节:read方法,每次可以读取一个字节的数据,提升为int类型,读取到文件末尾,返回-1,代码使用演示:
      public class FISRead {
          public static void main(String[] args) throws IOException{
            	// 使用文件名称创建流对象
             	FileInputStream fis = new FileInputStream("read.txt");
            	// 定义变量,保存数据
              int b ;
              // 循环读取
              while ((b = fis.read())!=-1) {
                  System.out.println((char)b);
              }
      		// 关闭资源
              fis.close();
          }
      }
      输出结果:
      a
      b
      c
      d
      e
    2. 使用字节数组读取:read(byte[] b),每次读取b的长度个字节到数组中,返回读取到的有效字节个数,读取到末尾时,返回-1 ,代码使用演示: 
      public class FISRead {
          public static void main(String[] args) throws IOException{
            	// 使用文件名称创建流对象.
             	FileInputStream fis = new FileInputStream("read.txt"); // 文件中为abcde
            	// 定义变量,作为有效个数
              int len ;
              // 定义字节数组,作为装字节数据的容器   
              byte[] b = new byte[2];
              // 循环读取
              while (( len= fis.read(b))!=-1) {
                 	// 每次读取后,把数组的有效字节部分,变成字符串打印
                  System.out.println(new String(b,0,len));//  len 每次读取的有效字节个数
              }
      		// 关闭资源
              fis.close();
          }
      }
      
      输出结果:
      ab
      cd
      e

三. 字符流

3.1 字符输出流【Writer】

java.io.Writer:表示用于写出字符流的所有类的超类,将指定的字符信息写出到目的地。它定义了字节输出流的基本共性功能方法。

  • void write(int c) 写入单个字符。
  • void write(char[] cbuf) 写入字符数组。 
  • abstract  void write(char[] cbuf, int off, int len) 写入字符数组的某一部分,off数组的开始索引,len写的字符个数。 
  • void write(String str) 写入字符串。 
  • void write(String str, int off, int len) 写入字符串的某一部分,off字符串的开始索引,len写的字符个数。
  • void flush() 刷新该流的缓冲。  
  • void close() 关闭此流,但要先刷新它。 

3.1.1 FileWriter类

java.io.FileWriter:是写出字符到文件的便利类。构造时使用系统默认的字符编码和默认字节缓冲区。

  1. 构造方法
    1. FileWriter(File file): 创建一个新的 FileWriter,给定要读取的File对象。
    2. FileWriter(String fileName): 创建一个新的 FileWriter,给定要读取的文件的名称。
      public class FileWriterConstructor {
          public static void main(String[] args) throws IOException {
         	 	// 使用File对象创建流对象,必须要给到一个文件路径
              File file = new File("a.txt");
              FileWriter fw = new FileWriter(file);
            
              // 使用文件名称创建流对象
              FileWriter fw = new FileWriter("b.txt");
          }
      }
  2. 基本写出数据
    1. 写出字符:write(int b) 方法,每次可以写出一个字符数据,代码使用演示:
      public class FWWrite {
          public static void main(String[] args) throws IOException {
              // 使用文件名称创建流对象
              FileWriter fw = new FileWriter("fw.txt");     
            	// 写出数据
            	fw.write(97); // 写出第1个字符
            	fw.write('b'); // 写出第2个字符
            	fw.write('C'); // 写出第3个字符
            	fw.write(30000); // 写出第4个字符,中文编码表中30000对应一个汉字。
      
              //【注意】关闭资源时,与FileOutputStream不同。如果不关闭,数据只是保存到缓冲区,并未保存到文件。
              fw.close();
          }
      }
      输出结果:
      abC田
  3. 关闭和刷新
    1. 因为内置缓冲区的原因,如果不关闭输出流,无法写出字符到文件中。但是关闭的流对象,是无法继续写出数据的。如果我们既想写出数据,又想继续使用流,就需要flush方法了。
      1. flush:刷新缓冲区,流对象可以继续使用。
      2. close:先刷新缓冲区,然后通知系统释放资源。流对象不可以再被使用了。
        public class FWWrite {
            public static void main(String[] args) throws IOException {
                // 使用文件名称创建流对象
                FileWriter fw = new FileWriter("fw.txt");
                // 写出数据,通过flush
                fw.write('刷'); // 写出第1个字符
                fw.flush();
                fw.write('新'); // 继续写出第2个字符,写出成功
                fw.flush();
              
              	// 写出数据,通过close
                fw.write('关'); // 写出第1个字符
                fw.close();
                fw.write('闭'); // 继续写出第2个字符,【报错】java.io.IOException: Stream closed
                fw.close();
        
                // 即便是flush方法写出了数据,操作的最后还是要调用close方法,释放系统资源。
            }
        }
  4. 写出其他数据
    1. 写出字符数组:write(char[] cbuf) 和 write(char[] cbuf, int off, int len) ,每次可以写出字符数组中的数据,用法类似FileOutputStream,代码使用演示:
      public class FWWrite {
          public static void main(String[] args) throws IOException {
              // 使用文件名称创建流对象
              FileWriter fw = new FileWriter("fw.txt");     
            	// 字符串转换为字节数组
            	char[] chars = "我是程序员".toCharArray();
            
            	// 写出字符数组
            	fw.write(chars); // 我是程序员
              
      		// 写出从索引2开始,2个字节。索引2是'程',两个字节,也就是'程序'。
              fw.write(b,2,2); // 程序
            
            	// 关闭资源
              fos.close();
          }
      }
    2. 写出字符串:write(String str) 和 write(String str, int off, int len) ,每次可以写出字符串中的数据,更为方便,代码使用演示:
      public class FWWrite {
          public static void main(String[] args) throws IOException {
              // 使用文件名称创建流对象
              FileWriter fw = new FileWriter("fw.txt");     
            	// 字符串
            	String msg = "我是程序员";
            
            	// 写出字符数组
            	fw.write(msg); //我是程序员
            
      		// 写出从索引2开始,2个字节。索引2是'程',两个字节,也就是'程序'。
              fw.write(msg, 2, 2);	// 程序
            	
              // 关闭资源
              fos.close();
          }
      }
    3. 续写和换行:操作类似于FileOutputStream。
      public class FWWrite {
          public static void main(String[] args) throws IOException {
              // 使用文件名称创建流对象,可以续写数据
              FileWriter fw = new FileWriter("fw.txt",true);     
            	// 写出字符串
              fw.write("我是");
            	// 写出换行
            	fw.write("\r\n");
            	// 写出字符串
        		fw.write("程序员");
            	// 关闭资源
              fw.close();
          }
      }
      输出结果:
      我是
      程序员

3.2 字符输入流【Reader】

java.io.Reader:是表示用于读取字符流的所有类的超类,可以读取字符信息到内存中。它定义了字符输入流的基本共性功能方法。

  • public void close() :关闭此流并释放与此流相关联的任何系统资源。    
  • public int read() :从输入流读取一个字符。 
  • public int read(char[] cbuf) :从输入流中读取一些字符,并将它们存储到字符数组 cbuf中 。

3.2.1 FileReader类

java.io.FileReader:是读取字符文件的便利类。构造时使用系统默认的字符编码和默认字节缓冲区。

  1. 构造方法
    1. FileReader(File file): 创建一个新的 FileReader ,给定要读取的File对象。
    2. FileReader(String fileName): 创建一个新的 FileReader ,给定要读取的文件的名称。
      public class FileReaderConstructor throws IOException{
          public static void main(String[] args) {
         	 	// 使用File对象创建流对象
              File file = new File("a.txt");
              FileReader fr = new FileReader(file);
            
              // 使用文件名称创建流对象
              FileReader fr = new FileReader("b.txt");
          }
      }
  2. 读取字符数据
    1. 读取字符:read方法,每次可以读取一个字符的数据,提升为int类型,读取到文件末尾,返回-1,循环读取,代码使用演示:
      public class FRRead {
          public static void main(String[] args) throws IOException {
            	// 使用文件名称创建流对象
             	FileReader fr = new FileReader("read.txt");
            	// 定义变量,保存数据
              int b ;
              // 循环读取
              while ((b = fr.read())!=-1) {
                  System.out.println((char)b);
              }
      		// 关闭资源
              fr.close();
          }
      }
      输出结果:
      我
      是
      程
      序
      员
    2. 使用字符数组读取:read(char[] cbuf),每次读取b的长度个字符到数组中,返回读取到的有效字符个数,读取到末尾时,返回-1 ,代码使用演示:
      public class FISRead {
          public static void main(String[] args) throws IOException {
            	// 使用文件名称创建流对象
             	FileReader fr = new FileReader("read.txt");
            	// 定义变量,保存有效字符个数
              int len ;
              // 定义字符数组,作为装字符数据的容器
              char[] cbuf = new char[2];
              // 循环读取
              while ((len = fr.read(cbuf))!=-1) {
                  System.out.println(new String(cbuf,0,len));
              }
          	// 关闭资源
              fr.close();
          }
      }
      
      输出结果:
      我是
      程序
      员

四. 缓冲流

4.1 缓冲流概述

缓冲流高级流,作用是提高读写数据的效率。内部有一个字节数组,默认长度是8K,缓冲流读写数据时一定是将数据的读写方式转换为块读写来保证读写效率。

  • 字节缓冲流:BufferedInputStream,BufferedOutputStream 
  • 字符缓冲流:BufferedReader,BufferedWriter,PrintWriter

4.2 字节缓冲流

4.2.1 构造方法

  • public BufferedInputStream(InputStream in) :创建一个 新的缓冲输入流。 
  • public BufferedOutputStream(OutputStream out): 创建一个新的缓冲输出流。
    // 创建字节缓冲输入流
    BufferedInputStream bis = new BufferedInputStream(new FileInputStream("bis.txt"));
    // 创建字节缓冲输出流
    BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("bos.txt"));

4.2.2 效率测试演示

缓冲流读写方法与基本的流是一致的,我们通过复制大文件(375MB),测试它的效率。我们分三种方式来测试,大家注意观察测试三个代码块的执行效率~

// 基本流测试如下

public class BufferedDemo {
    public static void main(String[] args) throws FileNotFoundException {
        // 记录开始时间
      	long start = System.currentTimeMillis();
		// 创建流对象
        try (
        	FileInputStream fis = new FileInputStream("jdk9.exe");
        	FileOutputStream fos = new FileOutputStream("copy.exe")
        ){
        	// 读写数据
            int b;
            while ((b = fis.read()) != -1) {
                fos.write(b);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
		// 记录结束时间
        long end = System.currentTimeMillis();
        System.out.println("普通流复制时间:"+(end - start)+" 毫秒");
    }
}

Long time passes away....
// 缓冲流,代码如下

public class BufferedDemo {
    public static void main(String[] args) throws FileNotFoundException {
        // 记录开始时间
      	long start = System.currentTimeMillis();
		// 创建流对象
        try (
        	BufferedInputStream bis = new BufferedInputStream(new FileInputStream("jdk9.exe"));
	     BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("copy.exe"));
        ){
        // 读写数据
            int b;
            while ((b = bis.read()) != -1) {
                bos.write(b);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
		// 记录结束时间
        long end = System.currentTimeMillis();
        System.out.println("缓冲流复制时间:"+(end - start)+" 毫秒");
    }
}

缓冲流复制时间:8016 毫秒
// 使用数组的方式

public class BufferedDemo {
    public static void main(String[] args) throws FileNotFoundException {
      	// 记录开始时间
        long start = System.currentTimeMillis();
		// 创建流对象
        try (
			BufferedInputStream bis = new BufferedInputStream(new FileInputStream("jdk9.exe"));
		 BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("copy.exe"));
        ){
          	// 读写数据
            int len;
            byte[] bytes = new byte[8*1024];
            while ((len = bis.read(bytes)) != -1) {
                bos.write(bytes, 0 , len);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
		// 记录结束时间
        long end = System.currentTimeMillis();
        System.out.println("缓冲流使用数组复制时间:"+(end - start)+" 毫秒");
    }
}
缓冲流使用数组复制时间:666 毫秒

4.3 字符缓冲流

4.3.1 构造方法

  • public BufferedReader(Reader in) :创建一个 新的缓冲输入流。 
  • public BufferedWriter(Writer out) : 创建一个新的缓冲输出流。

构造方法代码演示:

// 创建字符缓冲输入流
BufferedReader br = new BufferedReader(new FileReader("br.txt"));
// 创建字符缓冲输出流
BufferedWriter bw = new BufferedWriter(new FileWriter("bw.txt"));

4.3.2 特有方法

  • BufferedReader:public String readLine(): 读一行文字。 
  • BufferedWriter:public void newLine(): 写一行行分隔符,由系统属性定义符号。
// readLine方法演示

public class BufferedReaderDemo {
    public static void main(String[] args) throws IOException {
      	 // 创建流对象
        BufferedReader br = new BufferedReader(new FileReader("in.txt"));
		// 定义字符串,保存读取的一行文字
        String line  = null;
      	// 循环读取,读取到最后返回null
        while ((line = br.readLine())!=null) {
            System.out.print(line);
            System.out.println("------");
        }
		// 释放资源
        br.close();
    }
}
// newLine方法演示

public class BufferedWriterDemo throws IOException {
    public static void main(String[] args) throws IOException  {
      	// 创建流对象
		BufferedWriter bw = new BufferedWriter(new FileWriter("out.txt"));
      	// 写出数据
        bw.write("我是");
      	// 写出换行
        bw.newLine();
        bw.write("程序");
        bw.newLine();
        bw.write("员");
        bw.newLine();
		// 释放资源
        bw.close();
    }
}
输出效果:
我是
程序
员

五. 对象流(序列化)

5.1 序列化概述

Java 提供了一种对象序列化的机制。用一个字节序列可以表示一个对象,该字节序列包含该对象的数据、对象的类型和对象中存储的属性等信息。字节序列写出到文件之后,相当于文件中持久保存了一个对象的信息。 

反之,该字节序列还可以从文件中读取回来,重构对象,对它进行反序列化。对象的数据、对象的类型和对象中存储的数据信息,都可以用来在内存中创建对象。看图理解序列化: 

5.2 ObjectOutputStream类

java.io.ObjectOutputStream:将Java对象的原始数据类型写出到文件,实现对象的持久存储。

5.2.1 构造方法

  • public ObjectOutputStream(OutputStream out) : 创建一个指定OutputStream的ObjectOutputStream。

    FileOutputStream fileOut = new FileOutputStream("employee.txt");
    ObjectOutputStream out = new ObjectOutputStream(fileOut);

5.2.2 序列化操作

  1. 一个对象要想序列化,必须满足两个条件:
    1. 必须实现java.io.Serializable接口,Serializable是一个标记接口,不实现此接口的类将不会使任何状态序列化或反序列化,会抛出NotSerializableException
    2. 该类的所有属性必须是可序列化的。如果有一个属性不需要可序列化的,则该属性必须注明是瞬态的,使用transient关键字修饰。
      public class Employee implements java.io.Serializable {
          public String name;
          public String address;
          public transient int age; // transient瞬态修饰成员,不会被序列化
          public void addressCheck() {
            	System.out.println("Address  check : " + name + " -- " + address);
          }
      }
  2. 写出对象方法
    1. public final void writeObject (Object obj) : 将指定的对象写出。

      public class SerializeDemo{
         	public static void main(String [] args)   {
          	Employee e = new Employee();
          	e.name = "zhangsan";
          	e.address = "北京海淀";
          	e.age = 20; 
          	try {
            		// 创建序列化流对象
                ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream("employee.txt"));
              	// 写出对象
              	out.writeObject(e);
              	// 释放资源
              	out.close();
              	fileOut.close();
              	System.out.println("Serialized data is saved"); // 姓名,地址被序列化,年龄没有被序列化。
              } catch(IOException i)   {
                  i.printStackTrace();
              }
         	}
      }
      输出结果:
      Serialized data is saved

5.3 ObjectInputStream类

ObjectInputStream:反序列化流,将之前使用ObjectOutputStream序列化的原始数据恢复为对象。

5.3.1 构造方法

  • public ObjectInputStream(InputStream in) : 创建一个指定InputStream的ObjectInputStream。 

5.3.2 反序列化操作1

如果能找到一个对象的class文件,我们可以进行反序列化操作,调用ObjectInputStream读取对象的方法:

  • public final Object readObject (): 读取一个对象。
public class DeserializeDemo {
   public static void main(String [] args)   {
        //对于JVM可以反序列化对象,它必须是能够找到class文件的类。
        //如果找不到该类的class文件,则抛出一个 ClassNotFoundException异常。

        Employee e = null;
        try {		
             // 创建反序列化流
             FileInputStream fileIn = new FileInputStream("employee.txt");
             ObjectInputStream in = new ObjectInputStream(fileIn);
             // 读取一个对象
             e = (Employee) in.readObject();
             // 释放资源
             in.close();
             fileIn.close();
        }catch(IOException i) {
             // 捕获其他异常
             i.printStackTrace();
             return;
        }catch(ClassNotFoundException c)  {
        	// 捕获类找不到异常
             System.out.println("Employee class not found");
             c.printStackTrace();
             return;
        }
        // 无异常,直接打印输出
        System.out.println("Name: " + e.name);	// zhangsan
        System.out.println("Address: " + e.address); // 北京海淀
        System.out.println("age: " + e.age); // 0
    }
}

对于JVM可以反序列化对象,它必须是能够找到class文件的类。如果找不到该类的class文件,则抛出一个 ClassNotFoundException异常。

5.3.3 反序列化操作2

当JVM反序列化对象时,能找到class文件,但是class文件在序列化对象之后发生了修改,那么反序列化操作也会失败,抛出一个InvalidClassException异常。发生这个异常的原因如下:

  • 该类的序列版本号与从流中读取的类描述符的版本号不匹配 
  • 该类包含未知数据类型 
  • 该类没有可访问的无参数构造方法 

Serializable接口给需要序列化的类,提供了一个序列版本号 serialVersionUID ,该版本号的目的在于验证序列化的对象和对应类是否版本匹配。

public class Employee implements java.io.Serializable {
     // 加入序列版本号
     private static final long serialVersionUID = 1L;
     public String name;
     public String address;
     // 添加新的属性 ,重新编译, 可以反序列化,该属性赋为默认值.
     public int eid; 

     public void addressCheck() {
         System.out.println("Address  check : " + name + " -- " + address);
     }
}

​

六. 转换流 

java.io.InputStreamReader 和 java.io.OutputStreamWriter

它们是字符流非常常用的一对实现类,同时也是一对高级流,实际开发中我们不直接操作它们,但是它们在流连接中是非常重要的一环。核心意义在于:

  • 衔接其它字节与字符流
  • 将字符与字节进行转换

6.1 InputStreamReader类

转换流java.io.InputStreamReader,是Reader的子类,是从字节流到字符流的桥梁。它读取字节,并使用指定的字符集将其解码为字符。它的字符集可以由名称指定,也可以接受平台的默认字符集。 

6.1.1 构造方法

  • InputStreamReader(InputStream in): 创建一个使用默认字符集的字符流。 
  • InputStreamReader(InputStream in, String charsetName): 创建一个指定字符集的字符流。

构造代码示例如下:

InputStreamReader isr = new InputStreamReader(new FileInputStream("in.txt"));
InputStreamReader isr2 = new InputStreamReader(new FileInputStream("in.txt") , "GBK");

6.1.2 指定编码读取 

public class ReaderDemo2 {
    public static void main(String[] args) throws IOException {
      	// 定义文件路径,文件为gbk编码
        String FileName = "E:\\file_gbk.txt";
      	// 创建流对象,默认UTF8编码
        InputStreamReader isr = new InputStreamReader(new FileInputStream(FileName));
      	// 创建流对象,指定GBK编码
        InputStreamReader isr2 = new InputStreamReader(new FileInputStream(FileName) , "GBK");
		// 定义变量,保存字符
        int read;
      	// 使用默认编码字符流读取,乱码
        while ((read = isr.read()) != -1) {
            System.out.print((char)read); // ��Һ�
        }
        isr.close();
      
      	// 使用指定编码字符流读取,正常解析
        while ((read = isr2.read()) != -1) {
            System.out.print((char)read);// 大家好
        }
        isr2.close();
    }
}

6.2 OutputStreamWriter类

转换流 java.io.OutputStreamWriter ,是Writer的子类,是从字符流到字节流的桥梁。使用指定的字符集将字符编码为字节。它的字符集可以由名称指定,也可以接受平台的默认字符集。 

6.2.1 构造方法

  • OutputStreamWriter(OutputStream in): 创建一个使用默认字符集的字符流。 
  • OutputStreamWriter(OutputStream in, String charsetName): 创建一个指定字符集的字符流。

构造代码示例如下:

OutputStreamWriter isr = new OutputStreamWriter(new FileOutputStream("out.txt"));
OutputStreamWriter isr2 = new OutputStreamWriter(new FileOutputStream("out.txt") , "GBK");

6.2.2 指定编码写出

public class OutputDemo {
    public static void main(String[] args) throws IOException {
      	// 定义文件路径
        String FileName = "E:\\out.txt";
      	// 创建流对象,默认UTF8编码
        OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream(FileName));
        // 写出数据
      	osw.write("你好"); // 保存为6个字节
        osw.close();
      	
		// 定义文件路径
		String FileName2 = "E:\\out2.txt";
     	// 创建流对象,指定GBK编码
        OutputStreamWriter osw2 = new OutputStreamWriter(new FileOutputStream(FileName2),"GBK");
        // 写出数据
      	osw2.write("你好");// 保存为4个字节
        osw2.close();
    }
}

写麻了已经,总感觉缺了点什么...大家一起补充吧~附上一张找到的图,可以参考...


本文完!

写在结尾:

2022 年 9 月 26日 一个java菜鸟,发布于北京海淀。

好记性不如烂笔头,持续学习,坚持输出~今天是持续写作的第7天。可以点赞、评论、收藏啦。


评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

码云说

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值