JAVA-IO流


韩顺平IO专题

思维导图:在这里插入图片描述

1、前置知识

  1. 输入流:数据从外部(磁盘等)到程序(内存)的路径。
  2. 输出流:数据从程序(内存)到外部(磁盘等)的路径。

1.1、创建文件对象相关构造器和方法

在这里插入图片描述
注:在new File(path)之后,一定要使用createNewFile()方法,否则文件不会被创建,只存在内存创建了一个File对象。

1.2、获取文件相关信息

1.2.1、常用的文件操作:

在这里插入图片描述

1.2.2、目录操作和文件删除

在这里插入图片描述
注:在Java编程中,目录也被当做文件

2、IO流原理和分类

2.1、原理

  1. I/O技术是用于处理数据传输。例如读写文件,网络通讯等。
  2. Java.io包下提供了各种“流”类和接口,用以获得不同种类的数据,并通过方法输入或者输出数据。
  3. 对于数据的输入/输出操作是以“流”的方式进行的。

2.2、分类

在这里插入图片描述

注:

  1. 一个字符占多少个字节和编码有关系,不能明确。
  2. 上面四个类都是抽象类,要使用他们的子类。

2.2.1、FileInputStream

  1. 结构图:
    在这里插入图片描述

  2. 创造文件输入流构造方法:
    在这里插入图片描述
    一旦有了文件流对象,就使用文件流对象对文件进行操作

  3. 读取操作:

//直接读
public int read() throws IOException
//指定每次读byte数组中的内容,不够就是实际读取的个数(推荐)
public int read(byte b[]) throws IOException
//指定在byte数组中读多少 
public int read(byte b[], int off, int len) throws IOException 

示例:

public void readFile02() {
        String filePath = "e:\\hello.txt";
        //字节数组
        byte[] buf = new byte[8]; //一次读取8个字节.
        int readLen = 0;
        FileInputStream fileInputStream = null;
        try {
            //创建 FileInputStream 对象,用于读取 文件
            fileInputStream = new FileInputStream(filePath);
            //从该输入流读取最多b.length字节的数据到字节数组。 此方法将阻塞,直到某些输入可用。
            //如果返回-1 , 表示读取完毕
            //如果读取正常, 返回实际读取的字节数
            while ((readLen = fileInputStream.read(buf)) != -1) {
                System.out.print(new String(buf, 0, readLen));//显示
            }

        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            //关闭文件流,释放资源.
            try {
                fileInputStream.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }

    }

2.2.2、FileOutputStream

  1. 构造方法:在这里插入图片描述

注:

  1. 如果使用该类向文件中写入,文件不存在会自动创建。
  2. 如果想追加到原文件末尾,那么要写第二个参数(true),默认是对源文件内容覆盖
  1. 相关方法:
public void write(int b) throws IOException
public void write(byte b[]) throws IOException
public void write(byte b[], int off, int len) throws IOException

如果要写入字符串,但是write方法只有写入byte数组的,所以将字符串转换为数组即可(getBytes())。

2.2.3、FileReader

在这里插入图片描述

文件字符输入流

  1. 构造器:
public FileReader(File file) throws FileNotFoundException
public FileReader(String fileName) throws FileNotFoundException
  1. 相关操作(使用父类定义的操作)
public int read() throws IOException
public int read(char cbuf[], int offset, int length) throws IOException

在这里插入图片描述
3. 如果在读取的时候,使用read(char[]),那么在写出的时候,使用new String(char[],off, len)写出。

2.2.4、FileWriter

在这里插入图片描述

文件字符输出流

  1. 构造器:
    在这里插入图片描述
  2. 相关方法:
    在这里插入图片描述

注:FileWriter使用之后,必须要关闭或者刷新,否则不会写入到指定文件。

2.2.5、文件的拷贝

  • 思路:
  1. 创建文件输入流,读入到内存。
  2. 创建文件输出流,从内存读取到磁盘。
  3. 在读取的时候,是读一部分就写入到指定文件中(write(byte[], off, len))。
  4. 最后记得关闭流。

2.3、节点流、处理流

在这里插入图片描述

2.3.1、节点流

  1. 上面提到了都是节点流,直接对数据操作的流就是节点流。

2.3.2、处理流

  1. 在已经存在的流之上,为程序提供更强大的读写功能。不会直接与数据源相连。
  2. 功能:
  1. 性能提高,主要以增加缓冲的方式来提高输入输出的效率。
  2. 操作的边界:处理流可能提供一系列边界方法来一次输入输出大量的数据,使用更加灵活方便。
  1. BufferedReader为例:
    在这里插入图片描述
    将Reader作为自己的一个属性,根据多态的思想,只要是Reader的子类,那么BufferedReader都可以进行处理。
  2. BufferedWriter同样是将Writer作为自己的属性,只要是Writer的实现子类都可以进行处理。
  3. 有处理流的时候,只需要关闭处理流即可,因为处理流里面包含了节点流(真正对数据进行操作的流)。
2.3.2.1、处理流的设计模式
  1. 这里韩老师自己手写模拟了一下。在这里插入图片描述

步骤:

  1. 创建一个抽象类(Reader_),只用来定义方法。两个子类(FileReader_StringReader_)继承并实现方法。这两个子类是模拟直接对数据源进行操作的
  2. 此时写一个处理流(BufferedReader_),对功能进行扩展,因为扩展的是Read_下的子类方法,但并没有指定是哪一个子类,所以直接使用Read_作为自己的属性,通过注入可以确定到底是哪个子类。
    • 装饰器模式。
2.3.2.2、BufferedReader

说明

  1. bufferedReader.readLine() 是按行读取文件
  2. 当返回null 时,表示文件读取完毕
  3. 在关闭的时候,追源码,可以看见in.close(),这个in就是处理流包装的节点流。
2.3.2.3、BufferedWriter

说明:

  1. new FileWriter(filePath, true) 表示以追加的方式写入。
  2. new FileWriter(filePath) , 表示以覆盖的方式写入。
  3. 换行是newLine()
2.3.2.4、BufferedInputStream
  1. 里面将IutputStream作为自己的属性,只要是IutputStream的实现子类,都可以传入作为参数,构建BufferedIutputStream对象。
  2. 这是用来对字节流进行加强的,可以处理图片,视频等二进制文件。同样也可以操作文本文件,但是效率没有字符流快。
  3. 读取还是使用字节数组进行读取,每次先设置一个字节数组,只要读取长度不等于-1,就接着读。
2.3.2.5、BufferedOutputStream
  1. 里面将outputStream作为自己的属性,只要实现了这个抽象类的子类,都可以传入作为参数,构建BufferedOutputStream对象。
  2. 同样是对二进制文件进行操作的。
  3. 写出的时候同样是write(byte[], off, len),按照每次读取的实际长度进行写出。
2.3.2.6、ObjectInputStream
  1. 对象写出,实现序列化。
2.3.2.7、ObjectOutputStream
  1. 对象写入,实现反序列化。

注意点:

  1. 读写顺序要一致。
  2. 要求序列化或者反序列化对象,需要实现Serialiable接口。
  3. 序列化的类中建议添加private static final long serialVersionUID = 1L; ,可以提高版本兼容性。
  4. 序列化对象的时候,默认将里面的所有属性都进行序列化,除了static或者transient修饰的成员。
    序列化对象的时候,要求里面属性的类型也需要实现序列化接口。
  5. 序列化具备可继承性,即:如果某类已经实现了序列化,那么它的所有子类也已经默认实现了序列化。
  6. 序列化对象如果是自定义的类初始化的,那么这个类最好单独写。

2.3.3、序列化和反序列化

  1. 序列化就是在保存数据的时候,保存数据的值类型

    序列化每次新增加了方法之后,需要重新执行,否则在反序列化的时候回报错。

  2. 反序列化就是在回复数据的时候,回复数据的值和数据类型。

2.3.4、BufferedWriter/BufferedReader拷贝

  1. 使用Reader和Writer是去操作字符文件,并不能去操作二进制的文件,否则就会产生文件损坏。
  2. 操作的时候,根据字符或者字节,创建对应的输入输出流,然后,然后每次只需要readLine(),只要不为空就继续读,每读一行就写一行,中间注意记得加上换行(newLine()),否则就会写成一行。
  3. 最后只需要关闭外层的流即可,内部流会自动关闭。

2.4、标准输入输出流

2.4.1、标准输入流

  1. System.in,编译类型:InputStream,运行类型:BufferedInputStream。
  2. 表示的是:标准输入 键盘。

2.4.2、标准输出流

  1. System.out,编译类型:PrintStream,运行类型:PrintStream。
  2. 表示的是:标准输出 显示器。

2.5、转换流

问题引出:字符集不一样,导致读取(例如:文本文件)的时候就会出现乱码。
解决:没有指定读取文件的编码方式,转换流就可以把字节流转为字符流,字节流是可以指定编码方式的。

2.5.1、InputStreamReader

类图:在这里插入图片描述

  1. Reader的子类,是字符流
  2. 在构造器中,传入字节流以及指定编码方式,就可以将字节流转换为字符流。

2.5.2、OutputStreamWriter

类图:在这里插入图片描述

  1. Writer的子类,是字符流
  2. 在构造器中,传入字节流以及指定编码方式,就可以将字节流转换为字符流。

3、Properties

  1. 专门用来读写配置文件的集合类,是Hashtable的子类。
  2. 配置文件的格式:key=value
  3. 键值两边不需要空格,值也不要用引号。默认类型是String。
  4. 常见方法:
    在这里插入图片描述

3.1、properties读文件

public class Properties02 {
    public static void main(String[] args) throws IOException {
        //使用Properties 类来读取mysql.properties 文件

        //1. 创建Properties 对象
        Properties properties = new Properties();
        //2. 加载指定配置文件
        properties.load(new FileReader("src\\mysql.properties"));
        //3. 把k-v显示控制台
        properties.list(System.out);
        //4. 根据key 获取对应的值
        String user = properties.getProperty("user");
        String pwd = properties.getProperty("pwd");
        System.out.println("用户名=" + user);
        System.out.println("密码是=" + pwd);
    }
}

3.2、properties修改文件

public class Properties03 {
    public static void main(String[] args) throws IOException {
        //使用Properties 类来创建 配置文件, 修改配置文件内容

        Properties properties = new Properties();
        //创建
        //1.如果该文件没有key 就是创建
        //2.如果该文件有key ,就是修改
        /*
            Properties 父类是 Hashtable , 底层就是Hashtable 核心方法
            public synchronized V put(K key, V value) {
                // Make sure the value is not null
                if (value == null) {
                    throw new NullPointerException();
                }

                // Makes sure the key is not already in the hashtable.
                Entry<?,?> tab[] = table;
                int hash = key.hashCode();
                int index = (hash & 0x7FFFFFFF) % tab.length;
                @SuppressWarnings("unchecked")
                Entry<K,V> entry = (Entry<K,V>)tab[index];
                for(; entry != null ; entry = entry.next) {
                    if ((entry.hash == hash) && entry.key.equals(key)) {
                        V old = entry.value;
                        entry.value = value;//如果key 存在,就替换
                        return old;
                    }
                }

                addEntry(hash, key, value, index);//如果是新k, 就addEntry
                return null;
            }

         */
        properties.setProperty("charset", "utf8");
        properties.setProperty("user", "汤姆");//注意保存时,是中文的 unicode码值
        properties.setProperty("pwd", "888888");

        //将k-v 存储文件中即可,第二个参数是注释,通常设置为null
        properties.store(new FileOutputStream("src\\mysql2.properties"), null);
        System.out.println("保存配置文件成功~");

    }
}

保存是输出流(不管是字节还是字符)。

4、总结

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值