JAVA IO流

122 阅读8分钟

一、FILE

1.1 创建对象

image.png

注意:

  • File 对象既可以代表文件、也可以代表文件夹
  • File 封装的对象仅仅是一个路径名,这个路径可以是存在的,也允许不存在的。
//绝对路径--从盘符开始
File file = new File("D:\\sample\\a.txt");

//相对路径--不带盘符,默认直接到当前工程下的目录寻找文件
File file = new File("模块名\\a.txt");

//反斜杠写法
File file = new File("D:/sample/a.txt");

1.2 常用方法--判断文件类型、获取文件信息

image.png

1.3 常用方法--创建文件、删除文件

image.png

注意delete 方法默认只能删除文件和空文件夹,删除后的文件不会进入回收站

1.4 常用方法--遍历文件夹

image.png

image.png

二、IO流

image.png

  • 字节输入流:以内存为基准,来自磁盘文件 / 网络中的数据以字节的形式读入到内存中去的流
  • 字节输出流:以内存为基准,把内存中的数据以字节写出到磁盘文件或者网络中去的流。
  • 字符输入流:以内存为基准,来自磁盘文件 / 网络中的数据以字符的形式读入到内存中去的流。
  • 字符输出流:以内存为基准,把内存中的数据以字符写出到磁盘文件或者网络介质中去的流。

image.png

2.1 前置知识--字符集

1. 标准 ASCLL 字符集

使用1 个字节存储一个字符,首尾是 0 ,总共可表示 128 个字符。

2. GBK (汉字内码扩展规范,国标)

  • 汉字编码字符集,包含了 2 万多个汉字等字符, GBK 中一个中文字符编码成两个字节的形式存储。
  • 注意: GBK兼容了 ASCII字符集。

3. Unicode 字符集(统一码,也叫万国码)

UTF-8Unicode 字符集的一种编码方案,采取可变长编码方案,共分四个长度区: 1 个字节, 2 个字节, 3 个字节, 4 个字节。

汉字占 3 个字节,英文、数字占 1 个字节

image.png

  • ASCLL字符集:只有英文、数字、符号等,占一个字节。
  • GBK字符集汉字两个字节,英文、数字占一个字节。
  • UTF-8字符集汉字三个字节,英文、数字占一个字节。

注意:

  1. 解码和编码时使用的字符集必须一致,否则会出现乱码
  2. 英文、数字一般不会乱码,因为很多字符集都兼容了ASCLL编码

4. Java编码、解码

image.png

2.2 释放资源的方式

1. try-catch-finally

finally 代码区的特点:无论 try 中的程序是正常执行了,还是出现了异常,最后都一定会执行 finally 区除非JVM 终止

作用:一般用于在程序执行完成后进行资源的释放操作(专业级做法)。

import java.io.FileInputStream;  
import java.io.IOException;  
  
public class Main {  
    public static void main(String[] args){  
        FileInputStream is = null;  
        try {  
            is = new FileInputStream("module1\\src\\sing.txt");  

            byte[] buffer = new byte[3];  

            int len;  
            while((len = is.read(buffer)) != -1){  
                System.out.print(new String(buffer, 0, len));  
            }  
        } catch (IOException e) {  
            e.printStackTrace();  
        } finally {  
            try{  
                if(is != null) is.close();  
            }catch (Exception e){  
                e.printStackTrace();  
            }  
        }  
    }  
}

2. try-with-resource

JDK 7 开始提供了更简单的资源释放方案,资源使用完毕后,会自动调用其 close() 方法,完成对资源的释放

image.png

注意:()中只能放置资源,资源即实现了AutoCloseable 接口的类。

import java.io.FileInputStream;  
  
public class Main {  
    public static void main(String[] args){  
        try (  
                FileInputStream is = new FileInputStream("module1\\src\\sing.txt");  
                ){  
            byte[] buffer = new byte[3];  

            int len;  
            while((len = is.read(buffer)) != -1){  
                System.out.print(new String(buffer, 0, len));  
            }  
        } catch (Exception e) {  
            e.printStackTrace();  
        }  
    }  
}
//手写资源
class Demo implements AutoCloseable{  
    @Override  
    public void close() throws Exception {  
        System.out.println("临界资源释放。。。");  
    }  
}
public class Main {  
    public static void main(String[] args){  
        try (  
                Demo d = new Demo();  
                ){  
        } catch (Exception e) {  
            e.printStackTrace();  
        }  
    }  
}

输出:

image.png

2.3 IO流--字节流

1. 字节输入流--FileInputStream

抽象类InputStream的一个实现类

image.png

1.1 每次读取一个字节
//代码示例
import java.io.FileInputStream;  
  
public class Main {  
    public static void main(String[] args) throws Exception{  
        FileInputStream is = new FileInputStream("module1\\src\\sing.txt");  

        int b;  

        while((b = is.read()) != -1){  
            System.out.print((char) b);  
        }  

        is.close();  
    }  
}

使用 FileInputStream 每次读取一个字节,读取性能较差,并且读取汉字输出会乱码。

1.2 每次读取多个字节
//代码示例
import java.io.FileInputStream;  
  
public class Main {  
    public static void main(String[] args) throws Exception{  
        FileInputStream is = new FileInputStream("module1\\src\\sing.txt");  

        byte[] buffer = new byte[3];  

        int len;  
        while((len = is.read(buffer)) != -1){  
            System.out.print(new String(buffer,0,len));  
        }  

        is.close();  
    }  
}

使用 FileInputStream 每次读取多个字节,读取性能得到了提升,但读取汉字输出还是会乱码。

1.3 一次读取完全部字节

image.png

//方式一
import java.io.File;  
import java.io.FileInputStream;  
  
public class Main {  
    public static void main(String[] args) throws Exception{  
        File file = new File("module1\\src\\sing.txt");  
        FileInputStream is = new FileInputStream(file);  

        byte[] buffer = new byte[(int) file.length()];  

        is.read(buffer); 
        
        //GBK为sing.txt文件编码的字符集
        System.out.println(new String(buffer,"GBK"));  

        is.close();  
    }  
}
//方式二
import java.io.File;  
import java.io.FileInputStream;  
  
public class Main {  
    public static void main(String[] args) throws Exception{  
        FileInputStream is = new FileInputStream("module1\\src\\sing.txt");  
        byte[] bytes = is.readAllBytes();  

        System.out.println(new String(bytes, "GBK"));  

        is.close(); 
    }  
}

可以解决字节流读取中文输出乱码的问题。

如果文件过大,创建的字节数组也会过大,可能引起内存溢出。

2. 字节输出流--FileOutputStream

抽象类OutputStream的一个实现类

image.png

//覆盖文件  
import java.io.FileOutputStream;  
  
public class Main {  
    public static void main(String[] args) throws Exception{  
        //覆盖文件  
        FileOutputStream os = new FileOutputStream("module1\\src\\test.txt");  

        os.write(76);  
        os.write(new byte[]{77,78,79});  
        os.write(new byte[]{77,78,79},1,2);  

        os.close();  
    }  
}

image.png

//在文件后追加 
import java.io.FileOutputStream;  
  
public class Main {  
    public static void main(String[] args) throws Exception{  
        //在文件后追加  
        FileOutputStream os = new FileOutputStream("module1\\src\\test.txt",true);  

        os.write(76);  
        os.write(new byte[]{77,78,79});  
        os.write(new byte[]{77,78,79},1,2);  

        os.close();  
    }  
}

image.png

2.4 IO流--字符流

字节流适合复制文件,不适合读写文本。

字符流适合读写文本。

1. 字符输入流--FileReader

抽象类Reader的一个实现类

image.png

import java.io.FileReader;  
  
public class Main {  
    public static void main(String[] args){  
        try (  
                FileReader fr = new FileReader("module1\\src\\sing.txt");  
                ){  
            int c;  
            while((c = fr.read()) != -1){  
                System.out.print((char) c);  
            }  
        } catch (Exception e) {  
            e.printStackTrace();  
        }  
    }  
}

2. 字符输出流--FileWriter

抽象类Writer的一个实现类

image.png

import java.io.FileWriter;  
  
public class Main {  
    public static void main(String[] args){  
        try (  
                FileWriter fr = new FileWriter("module1\\src\\test.txt");  
                ){  
            fr.write('你');  
            fr.write('好');  
        } catch (Exception e) {  
            e.printStackTrace();  
        }  
    }  
}

注意:字符输出流写出数据后,必须刷新流,或者关闭流,写出去的数据才能生效

image.png

2.5 IO流--缓冲流

image.png

1. 引入

缓冲流是对原始流进行包装,以提高原始流读写数据的性能。

作用:提高字节/符流读写数据的性能。

原理:字节/符缓冲流自带了 8KB 缓冲池

以字符缓冲输入流为例:在内存中申请了8KB的缓冲池。每次从数据源(外存)中一次性读入8KB的数据,之后原始流再在缓冲池读入数据(内存->内存)减少了系统调用 的次数,提高性能。

image.png

2. 字节缓冲流

image.png

3. 字符缓冲流

3.1 字符缓冲输入流--BufferedReader

image.png

import java.io.BufferedReader;  
import java.io.FileReader;  
  
public class Main {  
    public static void main(String[] args){  
        try (  
                BufferedReader br = new BufferedReader(new FileReader("module1\\src\\sing.txt"));  
                ){  
            String str;  
            while((str = br.readLine()) != null){  
                System.out.println(str);  
            }  
        } catch (Exception e) {  
            e.printStackTrace();  
        }  
    }  
}
3.2 字符缓冲输出流--BufferedWriter

image.png

2.6 IO流--转换流

1. 引入

不同编码读取出现乱码问题:

  1. 如果代码编码和被读取的文本文件的编码是一致的,使用字符流读取文本文件时不会出现乱码
  2. 如果代码编码和被读取的文本文件的编码是不一致的,使用字符流读取文本文件时就会出现乱码

2. 字符输入转换流--InputStreamReader

抽象类Reader的一个实现类

image.png

解决思路:先获取文件的原始字节流,再将其按真实的字符集编码转成字符输入流,这样字符输入流中的字符就不乱码了。

import java.io.FileInputStream;  
import java.io.InputStreamReader;  
  
public class Main {  
    public static void main(String[] args){  
        try (  
                //test.txt按GBK编码
                FileInputStream is = new FileInputStream("module1\\src\\test.txt");  
                InputStreamReader isr = new InputStreamReader(is,"GBK");  
                ){  
            int c;  
            while((c = isr.read()) != -1){  
            System.out.print((char) c);  
            }  
        } catch (Exception e) {  
            e.printStackTrace();  
        }  
    }  
}

2. 字符输出转换流--OutputStreamWriter

抽象类Writer的一个实现类

可以控制写出去的字符使用什么字符集编码。

image.png

解决思路获取字节输出流,再按照指定的字符集编码将其转换成字符输出流,以后写出去的字符就会用该字符集编码了。

import java.io.*;  
  
public class Main {  
    public static void main(String[] args){  
        try (  
                FileOutputStream os = new FileOutputStream("module1\\src\\test.txt");  
                OutputStreamWriter osw = new OutputStreamWriter(os,"GBK");  
                ){  
            osw.write('你');  
            osw.write(97);  
        } catch (Exception e) {  
            e.printStackTrace();  
        }  
    }  
}

2.7 IO流--打印流

作用:打印流可以实现更方便、更高效的打印数据出去,能实现打印啥出去就是啥出去。

1. 字节输出打印流--PrintStream

image.png

1. 字符输出打印流--PrintWriter

image.png

PrintStream 和 PrintWriter 的区别

  • 打印数据的功能上是一模一样的:都是使用方便,性能高效(核心优势)
  • PrintStream 继承自字节输出流 OutputStream ,因此支持写字节数据的方法。
  • PrintWriter 继承自字符输出流 Writer ,因此支持写字符数据出去。
//因为功能是相同的,所以代码只展示一个
import java.io.PrintStream;  
  
public class Main {  
    public static void main(String[] args){  
        try (  
                PrintStream ps = new PrintStream("module1\\src\\test.txt");  
                ){  
            ps.println(123);  
            ps.println("你好");  
        } catch (Exception e) {  
            e.printStackTrace();  
        }  
    }  
}

2.8 IO流--数据流

1. 数据输出流--DataOutputStream

抽象类OutputStream的一个实现类 允许把数据和其类型一并写出去

image.png

import java.io.DataOutputStream;  
import java.io.FileOutputStream;  
  
public class Main {  
    public static void main(String[] args){  
        try (  
                DataOutputStream dos = new DataOutputStream(new FileOutputStream("module1\\src\\test.txt"));  
                ){  
            dos.writeByte(97);  
            dos.writeInt(123);  
            dos.writeBoolean(true);  
            dos.writeUTF("Hello World");  
        } catch (Exception e) {  
            e.printStackTrace();  
        }  
    }  
}

2. 数据输入流--DataInputStream

抽象类InputStream的一个实现类

用于读取数据输出流写出去的数据

数据输出流写出的数据:

image.png

需要用数据输入流来解析

import java.io.DataInputStream;  
import java.io.FileInputStream;  
  
public class Main {  
    public static void main(String[] args){  
        try (  
                DataInputStream dis = new DataInputStream(new FileInputStream("module1\\src\\test.txt"));  
                ){  
            System.out.println(dis.readByte());  
            System.out.println(dis.readInt());  
            System.out.println(dis.readBoolean());  
            System.out.println(dis.readUTF());  
        } catch (Exception e) {  
            e.printStackTrace();  
        }  
    }  
}

注意:读取数据的顺序应和写入的数据保持一致

2.9 IO流--序列化流

1. 对象字节输出流--ObjectOutputStream

可以把 Java 对象进行序列化:把 Java 对象存入到文件中去。

image.png

import java.io.FileOutputStream;  
import java.io.ObjectOutputStream;  
  
public class Main {  
    public static void main(String[] args){  
        try (  
                ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("module1\\src\\test.txt"));  
                ){  
            oos.writeObject(new Student("张三","男",18,96));  
            oos.writeObject(new Student("李四","男",20,86));  
            oos.writeObject(new Student("王五","男",20,72));  
            System.out.println("序列化成功...");  
        } catch (Exception e) {  
            e.printStackTrace();  
        }  
    }  
}

注意:对象如果要参与序列化,必须实现序列化接口 import.io.Serializable

注意:如果某个成员变量不想参与序列化,在前加transient关键字

2. 对象字节输入流--ObjectInputStream

可以把 Java 对象进行反序列化:把存储在文件中的 Java 对象读入到内存中来。

image.png