• Java常用IO流操作详解


    IO流基本概念

    按照流动的方向,以内存为基准,分为输入input输出output ,即流向内存是输入流,流出内存的输出流。

    Java中I/O操作主要是指使用java.io包下的内容,进行输入、输出操作。输入也叫做读取数据,输出也叫做作写出数据。

    IO的分类

    根据数据的流向分为:输入流输出流

    • 输入流 :把数据从其他设备上读取到内存中的流。
    • 输出流 :把数据从内存 中写出到其他设备上的流。

    格局数据的类型分为:字节流字符流

    • 字节流 :以字节为单位,读写数据的流。
    • 字符流 :以字符为单位,读写数据的流。

    基本输入输出流

    输入流 输出流
    字节流 字节输入流
    InputStream
    字节输出流
    OutputStream
    字符流 字符输入流
    Reader
    字符输出流
    Writer

    字节流

    一切文件数据(文本、图片、视频等)在存储时,都是以二进制数字的形式保存,都一个一个的字节,那么传输时一样如此。所以,字节流可以传输任意文件数据。在操作流的时候,我们要时刻明确,无论使用什么样的流对象,底层传输的始终为二进制数据。

    字节输出流【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) :将指定的字节输出流。
    

    FileOutputStream类

    OutputStream有很多子类,我们从最简单的一个子类开始。

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

    构造方法

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

    • public FileOutputStream(String name): 创建文件输出流以指定的名称写入文件。

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

    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();
        }
    }
    输出结果:
    字节输出流
    

    字节输入流【InputStream】

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

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

    FileInputStream类

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

    构造方法

    • FileInputStream(File file): 通过打开与实际文件的连接来创建一个 FileInputStream ,该文件由文件系统中的 File对象 file命名。
    • FileInputStream(String name): 通过打开与实际文件的连接来创建一个 FileInputStream ,该文件由文件系统中的路径名 name命名。

    当你创建一个流对象时,必须传入一个文件路径。该路径下,如果没有该文件,会抛出FileNotFoundException

    读取字节read方法,每次可以读取一个字节的数据,提升为int类型,读取到文件末尾,返回-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
    

    字符流

    当使用字节流读取文本文件时,可能会有一个小问题。就是遇到中文字符时,可能不会显示完整的字符,那是因为一个中文字符可能占用多个字节存储。所以Java提供一些字符流类,以字符为单位读写数据,专门用于处理文本文件。

    字符输入流【Reader】

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

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

    FileReader类

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

    1. 字符编码:字节与字符的对应规则。Windows系统的中文编码默认是GBK编码表。

      idea中UTF-8

    2. 字节缓冲区:一个字节数组,用来临时存储字节数据。

    构造方法

    • FileReader(File file): 创建一个新的 FileReader ,给定要读取的File对象。
    • FileReader(String fileName): 创建一个新的 FileReader ,给定要读取的文件的名称。

    读取字符数据

    读取字符: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();
        }
    }
    

    字符输出流【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() 关闭此流,但要先刷新它。

    FileWriter类

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

    构造方法

    • FileWriter(File file): 创建一个新的 FileWriter,给定要读取的File对象。
    • FileWriter(String fileName): 创建一个新的 FileWriter,给定要读取的文件的名称。

    写出字符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田
    

    缓冲流

    概述

    缓冲流,也叫高效流,是对4个基本的FileXxx 流的增强,所以也是4个流,按照数据类型分类:

    • 字节缓冲流BufferedInputStreamBufferedOutputStream
    • 字符缓冲流BufferedReaderBufferedWriter

    缓冲流的基本原理,是在创建流对象时,会创建一个内置的默认大小的缓冲区数组,通过缓冲区读写,减少系统IO次数,从而提高读写的效率。

    字节缓冲流

    构造方法

    • public BufferedInputStream(InputStream in) :创建一个 新的缓冲输入流。
    • public BufferedOutputStream(OutputStream out): 创建一个新的缓冲输出流。

    构造举例,代码如下:

    // 创建字节缓冲输入流
    BufferedInputStream bis = new BufferedInputStream(new FileInputStream("bis.txt"));
    // 创建字节缓冲输出流
    BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("bos.txt"));
    

    字节缓冲流,代码如下:

    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 BufferedReader(Reader in) :创建一个 新的缓冲输入流。
    • public BufferedWriter(Writer out): 创建一个新的缓冲输出流。

    构造举例,代码如下:

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

    特有方法

    字符缓冲流的基本方法与普通字符流调用方式一致,不再阐述,我们来看它们具备的特有方法。

    • 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();
        }
    }
    

    转换流

    InputStreamReader类

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

    构造方法

    • 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");
    

    指定编码读取

    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();
        }
    }
    

    OutputStreamWriter类

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

    构造方法

    • 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");
    

    指定编码写出

    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();
        }
    }
    

    转换流理解图解

    转换流是字节与字符间的桥梁!

    序列化

    概述

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

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

    ObjectOutputStream类

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

    构造方法

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

    构造举例,代码如下:

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

    序列化操作

    1. 一个对象要想序列化,必须满足两个条件:
    • 该类必须实现java.io.Serializable 接口,Serializable 是一个标记接口,不实现此接口的类将不会使任何状态序列化或反序列化,会抛出NotSerializableException
    • 该类的所有属性必须是可序列化的。如果有一个属性不需要可序列化的,则该属性必须注明是瞬态的,使用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.写出对象方法

    • public final void writeObject (Object obj) : 将指定的对象写出。
    public class SerializeDemo{
       	public static void main(String [] args)   {
        	Employee e = new Employee();
        	e.name = "zhangsan";
        	e.address = "beiqinglu";
        	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
    

    ObjectInputStream类

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

    构造方法

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

    反序列化操作1

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

    • public final Object readObject () : 读取一个对象。
    public class DeserializeDemo {
       public static void main(String [] args)   {
            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); // beiqinglu
            System.out.println("age: " + e.age); // 0
        }
    }
    

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

    反序列化操作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);
         }
    }
    
  • 相关阅读:
    MYSQL导入,导出命令。
    MySQL修改,表结构大幅修改
    Ajax
    js和jQuery的日常
    freemarker 分页取值
    Timer定时任务
    汉字相似度比较
    读取Properties键值对
    Python+requests+unittest+excel实现接口自动化测试框架
    Android App 压力测试方法(Monkey)
  • 原文地址:https://www.cnblogs.com/lalalaxiaoyuren/p/15955994.html
Copyright © 2020-2023  润新知