Java【IO】

Java【I/O】


一、IO概述

1.1 什么是IO

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

在这里插入图片描述

1.2顶级父类们:
输入流输出流
字节流字节输入流
InputStream
字节输出流
OutputStream
字符流字符输入流
Reader
字符输出流
Writer
二、字节流

2.1 一切皆为字节

一切文件数据(文本、图片、视频等)在存储时,都是以二进制数字的形式保存的,都是一个一个的字节。

2.2 字节输出流【OutputStream】

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

  • void close():Closes this output stream and releases any system resources associated with this stream.
  • void flush() :Flushes this output stream and forces any buffered output bytes to be written out.
  • void write(byte[] b): Writes b.length bytes from the specified byte array to this output stream.
  • void write(byte[] b, int off, int len):Writes len bytes from the specified byte array starting at offset off to this output stream.
  • abstract void write(int b):Writes the specified byte to this output stream.
2.3 [FileOutputStream]

java.io.FileOutputStream类是文件输出流,把内存中的数据写入到文件中。

构造方法

  • FileOutputStream(String name):创建一个向具有指定名称的文件中写入数据的输出文件流。
  • FileOutputStream(File file):创建一个向指定File对象表示的文件中写入数据的文件输出流。

构造方法的作用:创建FileOutputStream对象,根据构造方法中传递的文件/文件路径,创建一个空的文件,把FileOutputStream对象指向创建好的文件。

// 1、创建一个FileOutputStream对象,构造方法中传递写入数据的目的地
FileOutputStream fos = new FileOutputStream("day20-advanced/a.txt");
// 2、调用FileOutputStream对象中的方法write,把数据写入到文件中
// abstract void write(int b):将指定的字节写入
fos.write(97);
// 3、释放资源
fos.close();

追加写/续写

  • FileOutputStream(String name, boolean append): 创建一个向具有指定 name 的文件中写入数据的输出文件流。

  • FileOutputStream(File file, boolean append) :创建一个向指定file 对象表示的文件中写入数据的文件输出Luis。

    boolean append:追加写开关。 true:创建对象不会覆盖源文件,继续在文件的末尾追加写数据; false:创建一个新文件,覆盖原文件。

    FileOutputStream fos = new FileOutputStream("day20-advanced/c.txt",true);
    fos.write("您好".getBytes()); // 将字符串转换为字节数组
    fos.close();
    
2.3字节输入流【InputStream】

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

  • int read() : 从输入流中读取数据的下一个字节。
  • int read(byte[] b):从输入流汇总读取一定数量的字节,并将其存储在缓冲区数组b中。
  • void close() :关闭此输入流并释放与该流关联的所有系统资源。
2.4 [FileInputStream]

java.io.FileInputStream类是文件输入流,把硬盘文件中的数据,读取到内存中使用。

构造方法

  • FileInputStream(String name)
  • FileInputStream(File file)
FileInputStream fis = new FileInputStream("day20-advanced/a.txt"); // abc
/*
    读取文件是一个重复的过程,可以使用循环优化
    不知道文件有多少个字节,使用while循环
    结束条件是读到-1结束
*/
int len = 0; // 记录读到的字节
while((len = fis.read() )!=-1 ) {
  System.out.print((char)len); // abc
}
fis.close();

一次读取多个字节的方法

  • int read(byte[] b):从输入流中读取一定数量的字节,并将其存储在缓冲区数组b中。

    参数:byte[] b起到缓冲作用,存储每次读取到的多个字节。数组的长度一般定义为1024(1kb)或者1024的整数倍。

    返回值:每次读取的有效字节个数。

    // 创建FileInputStream 对象,构造方法中绑定要读取的数据源
    FileInputStream fis = new FileInputStream("day20-advanced/a.txt");
    /*
    发现以上读取是一个重复的过程,可以使用循环优化
    不知道文件汇总有多少字节,所以使用while循环
    while循环结束的条件,读取到-1结束
    */
    byte[] bytes = new byte[1024];// 存储读取到的多个字节
    int len = 0;// 记录每次读取的有效字节个数
    while((len = fis.read(bytes)) != -1) {
    	System.out.println(len);// 3
    	//System.out.println(new String(bytes));// abc
    	//String(byte[] bytes, int offset, int length) 把字节数组的一部分转换为字符串 offset:数组的开始索引 length:转换的字节个数
    	System.out.println(new String(bytes,0 ,len)); //abc
    }
    fis.close();
    
  • tips:String类的构造方法

    String(byte[] bytes):把字节数组转换为字符串
    String(byte[] bytes, int offset, int length): 把字节数组的一部分转换为字符串, offset:数组的开始索引, length:转换的字节个数。

2.5 练习

使用FileOutputStreamFileInputStream完成文件复制操作。

long s = System.currentTimeMillis();
// 1、创建字节输入流,读取数据源
FileInputStream fis = new FileInputStream("/Users/yanzhuang/Pictures/pap.er/1cD6Dm7VcgA.jpg");
//2、 创建字节输出流,写入读取的数据
FileOutputStream fos = new FileOutputStream("/Users/yanzhuang/Desktop/1.jpg");
3、 读取数据,可一次性读取1024个字节,也可尝试一个字节一个字节读read()方法
//byte[] bytes = new byte[1024];
//int len = 0;
//while((len = fis.read(bytes)) != -1) {
//    // 4、将读取的数据写入到输出流中
//    fos.write(bytes,0,len);
//}
int len = 0;
while ((len = fis.read()) != -1) {
    fos.write(len);
}
// 5、释放资源(先关写的,如果写完了肯定读完了)
fos.close();
fis.close();
long e = System.currentTimeMillis();
System.out.println("字节输入输出流复制文件共耗时:" + (e-s) + "毫秒");
三、字符流

使用字节流读取文本文件时,遇到中文字符,可能不会显示完整的字符,原因是一个中文字符可能只能有多个字节(1个中文,GBK:占用两个字节,UTF-8:占用3个字节),Java提供了字符流类,以字符为单位读写数据,用于处理文本文件。

3.1 字符输入流【Reader】

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

  • int read():读取单个字符并返回
  • int read(char[] cbuf): 一次读取多个字符,将字符读入数组
  • void close(): 关闭该流并释放与之关联的所有资源。
3.2 [FileReader]

java.io.FileReader文件字符输入流,把硬盘文件中的数据以字符的方式读取到内存中,构造时按照系统默认的字符编码(Idea中默认编码是utf-8)和默认字节缓冲区。

public static void main(String[] args) throws IOException {
    // 1、创建FileReader对象,构造方法中绑定要读取的数据源
    FileReader fr = new FileReader("day20-advanced/a.txt");
    // 2、使用FileReader对象中的read方法
//        int len = 0;
//        while ((len = fr.read()) != -1) {
//            System.out.println(len);
//        }
    char[] cs = new char[1024];// 一次读取多个字符,将字符读入数组
    int len = 0;// 记录的是每次去的的有效字符个数
    while((len = fr.read(cs)) != -1 ) {
        /*
            String类的构造方法
            String(char[] value) 把字符数组转换为字符串
            String(char[] value, int offset, int count) 把字符数组的一部分转换为字符串 offset数组的开始索引, count转换的个数
         */
        System.out.println(new String(cs));
    }
    // 3、释放资源
    fr.close();
}
3.3 字符输出流【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.4 [FileWriter]

java.io.FileWriter文件字符输出流,可以把内存中的字符数据写入到文件中。构造时使用系统默认字符编码和默认的字节缓冲区。

构造方法

  • FileWriter(File file)根据给定的File对象构造一个FileWriter对象。
  • FileWriter(String fileName)根据给定的文件名构造一个FileWriter对象
// 1、创建FileWriter对象,构造方法中绑定要写入数据的目的地
FileWriter fw = new FileWriter("day20-advanced/d.txt");
// 2、使用FileWriter中的方法write,把数据写入到内存缓冲区中(字符转换为字节的过程)
fw.write(97);
// 3、使用FileWriter中的方法flush,把内存缓冲区的数据,刷新到文件中
fw.flush();
// 4、释放资源(会先把内存缓冲区中的数据刷新到文件中)
fw.close();

flush和close的区别

  • flush: 刷新缓冲区,流对象可以继续使用
  • close: 先刷新缓冲区,然后通知系统释放资源。 流对象不可以再被使用了。

字符输出流写数据的其他方法

  • void write(char[] cbuf)写入字符数组。
  • abstract void write(char[] cbuf, int offset, int len)写入字符数组的某一部分,off数组的开始索引,len写的字符个数
  • void write(String str)写入字符串
  • void write(String str, int off, int len)写入字符串的某一部分,off字符串的开始索引,len写的字符个数
FileWriter fw = new FileWriter("day20-advanced/e.txt");
char[] cs = {'a','b','c','d','e'};
// void write(char[] cbuf)写入字符数组
fw.write(cs);// abcde
//void write(char[] cbuf, int offset, int len) 写入字符数组的某一部分,off数组的开始索引,len写的字符个数
fw.write(cs, 1, 3);// bcd
fw.flush();
fw.close();

续写换行

  • FileWriter(String fileName, boolean append)
  • FileWriter(File file, boolean append)

换行:换行符号win:\r\n; linux:/n; mac:/r

FileWriter fw = new FileWriter("day20-advanced/f.txt",true);
for (int i = 0; i < 10; i++) {
    fw.write("HelloZX" + i + "\r");
}
fw.close();
四、IO异常处理

4.1 在jdk1.7之前使用try-catch-finally处理流中的异常
// 提高变量的作用域,让finally可以使用
// 变量在定义的时候,可以没有值,但是使用的时候必须有值
FileWriter fw = null;
try {
    fw = new FileWriter("day20-advanced/g.txt",true);
    for (int i = 0; i < 10; i++) {
        fw.write("HelloZX" + i + "\r");
    }
} catch (IOException e) {
    // 异常的处理逻辑
    System.out.println(e);
} finally {
    // 一定会执行的代码
    // 创建对象失败了,fw的默认值就是null,null是不能调用方法的,会抛出NullPointerException,需要增加一个判断
   if(fw!=null){
       try {
           fw.close();
       } catch (IOException e) {
           e.printStackTrace();
       }
   }
}
4.2 jdk1.7处理流异常

jdk1.7的新特性,在try后面可以增加一个(),在括号中可以定义流对象,那么这个流对象的作用域就在try中有效。try中的代码执行完毕,会自动将流对象释放,不用写finally

try(// 1、创建字节输入流,读取数据源
    FileInputStream fis = new FileInputStream("/Users/yanzhuang/Pictures/pap.er/1cD6Dm7VcgA.jpg");
    //2、 创建字节输出流,写入读取的数据
    FileOutputStream fos = new FileOutputStream("/Users/yanzhuang/Desktop/1.jpg");){

    //3、 读取数据,可一次性读取1024个字节,也可尝试一个字节一个字节读read()方法
    byte[] bytes = new byte[1024];
    int len = 0;
    while((len = fis.read(bytes)) != -1) {
        // 4、将读取的数据写入到输出流中
        fos.write(bytes,0,len);
    }
} catch (IOException e) {
    // 异常的处理逻辑
    System.out.println(e);
}
4.3 jdk9处理流异常

jdk9新特性,try的前边可以定义流对象,在try后面的()中可以直接引入流对象的名称(变量名),在try代码执行完毕之后,流对象亦可以释放掉,不用写finally

// 1、创建字节输入流,读取数据源
FileInputStream fis = new FileInputStream("/Users/yanzhuang/Pictures/pap.er/1cD6Dm7VcgA.jpg");
//2、 创建字节输出流,写入读取的数据
FileOutputStream fos = new FileOutputStream("/Users/yanzhuang/Desktop/1.jpg");

try(fis;fos) {
    //3、 读取数据,可一次性读取1024个字节,也可尝试一个字节一个字节读read()方法
    byte[] bytes = new byte[1024];
    int len = 0;
    while((len = fis.read(bytes)) != -1) {
        // 4、将读取的数据写入到输出流中
        fos.write(bytes,0,len);
    }
} catch (IOException e) {
    System.out.println(e);
}
五、属性集
5.1 概述

java.util.Properties继承与Hashtable,来表示一个持久的属性集。它使用键值结构存储数据,每个键及其对应值都是一个字符串。Properties 集合是一个唯一和IO流结合的集合。

可以使用Properties集合中的方法store,把集合中的临时数据,持久化写入到硬盘中存储;可以使用Properties集合中的方法load,把硬盘中保存的文件(键值对),读取到集合中使用。属性列表中每个键及其对应值是一个字符串。Properties 集合是一个双列集合,keyvalue默认都是字符串。

5.2 Properties类

构造方法

  • public Properties():创建一个空的属性列表

基本的存储方法

  • String getProperty(String key)Searches for the property with the specified key in this property list.
  • Object setProperty(String key, String value) Calls the Hashtable method put.
  • Set<String> stringPropertyNames() 返回此属性列表中的键集,其中改键及其对应值是字符串,此方法相当于Map集合中的keySet方法

Properties集合中load方法

  • void load(InputStream inStream)
  • void load(Reader reader)
// 1、创建Properties对象
Properties prop = new Properties();
// 2、使用Properties集合对象中的方法load读取保存键值的文件
//prop.load(new FileReader("day20-advanced/prop.txt"));
prop.load(new FileInputStream("day20-advanced/prop.txt"));
// 3、遍历Properties集合
Set<String> set = prop.stringPropertyNames();
for (String key : set) {
    String value = prop.getProperty(key);
    System.out.println(key + "=" + value);
}

Properties集合中的方法store

  • void store(OutputStream out, String comments)
  • void store(Writer writer, String comments)

参数:
OutputStream out : 字节输出流,不能写入中文
Writer writer: 字符输出流,可以写中文
String comments:注释,用来解释说明保存的文件是做什么用的,不能使用中文,会产生乱码,默认是Unicode编码, 一般使用""空字符串。

// 1、创建Properties集合对象,添加数据
Properties prop = new Properties();
prop.setProperty("赵丽颖","168");
prop.setProperty("古力娜扎","178");
prop.setProperty("赵晓","165");
 2、创建字节输出流/字符输出流对象,构造方法中绑定要输出的目的地
//FileWriter fw = new FileWriter("day20-advanced/prop.txt");
//
 3、使用Properties集合中的方法store,把集合中的临时数据,持久化写入到硬盘中存储
//prop.store(fw,"save data");
//
 4、释放资源
//fw.close();
prop.store(new FileOutputStream("day20-advanced/prop.txt"), "");// 用字节输入流写中文会出现乱码
六、缓冲流

6.1 概述

缓冲流,是对四个基本的FileXxx流的增强,所以数量也是四个流,按照类型分为

输入流输出流
字节缓冲流BufferedInputStream
字节缓冲输入流
BufferedOutputStream
字节缓冲输出流
字符缓冲流BufferedReader
字符缓冲输入流
BufferedWriter
字符缓冲输出流

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

6.2 字节缓冲输出流【BufferedOutputStream】

java.io.BufferedOutputStream 继承自OutputStream

构造方法

  • BufferedOutputStream(OutputStream out)创建一个新的缓冲输出流,以将数据写入指定的底层输出流
  • BufferedOutputStream(OutpurStream out, int size)创建一个新的缓冲输出流,以将具有指定缓冲区大小的数据写入指定的底层

参数:
OutputStream out :字节输出流。我们可以传递FileOutputStream, 缓冲流会给FileOutputStream 增加一个缓冲区,提高FileOutputStream的写入效率。
int size:指定缓冲流内部缓冲区的大小,不指定默认

继承自父类的共性成员方法

  • 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): 将指定的字节写入此输出流。
//1、创建FileOutputStream 对象,构造方法中绑定要输出的目的地
FileOutputStream fos = new FileOutputStream("day20-advanced/g.txt");
//2、创建BufferedOutputStream 对象,构造方法中传递FileOutputStream 对象,提高FileOutputStream对象的效率
BufferedOutputStream bos = new BufferedOutputStream(fos);
//3、使用BufferedOutputStream 对象中的write方法,把数据写入到内部缓冲区中
bos.write("你这个年纪还睡得着觉".getBytes());
//4、使用BufferedOutputStream对象中的flush方法,把内部缓冲区中的数据,刷新到文件中
bos.flush();
//5、释放资源(会先调用flush方法刷新数据,第4步可以省略)
bos.close();
6.3 字节缓冲输入流【BufferedInputStream】

java.io.BufferedInputStream 继承自InputStream

构造方法

  • BufferedInputStream(InputStream in)创建一个BufferedInputStream对象并保存其参数,即输入流in,以便将来使用。
  • BufferedInputStream(InputStream in, int size) 创建具有指定缓冲区大小的 BufferedInputStream 并保存器参数,即输入流in。

参数:
InputStream :字节输入流,我们可以传递FileInputStream,缓冲流会给FileInputStream增加一个缓冲区,提高FileInputStream的读取效率
int size :指定缓冲流内部缓冲区的大小,不指定默认

继承自父类的成员方法

  • int read()从输入流中读取数据的下一个字节。
  • int read(byte[] b)从输入流中读取一定数量的字节,并将其存储在缓冲区数组b中。
  • void close()关闭此输入流并释放与该流关联的所有系统资源
//1、创建FileInputStream对象,构造方法中绑定要读取的数据
FileInputStream fis = new FileInputStream("day20-advanced/g.txt");
//2、创建BufferedInputStream对象,构造方法中传递FileInputStream对象,提高FileInputStream对象的读取效率
BufferedInputStream bis = new BufferedInputStream(fis);
//3、使用BufferedInputStream对象中的read方法读取文件
// int read() 是一个字节一个字节的读
//int len = 0;
//while ((len = bis.read()) != -1){
//    System.out.println(len);
//}
// int read(byte[] b) 字节数组来读
byte[] bytes = new byte[1024];
int len = 0;
while ((len = bis.read(bytes)) != -1) {
    System.out.println(new String(bytes,0,len));
}
//4、释放资源
bis.close();
6.4 练习【文件复制】

文件复制练习:一读一写 ,和之前的字节输入输出流复制文件的效率进行对比。

long s = System.currentTimeMillis();
//1、创建字节缓冲输入流对象,构造方法中传递字节输入流
BufferedInputStream bis = new BufferedInputStream(
        new FileInputStream("/Users/yanzhuang/Pictures/pap.er/1cD6Dm7VcgA.jpg"));
//2、创建字节缓冲输出流对象,构造方法中传递字节输出流
BufferedOutputStream bos = new BufferedOutputStream(
        new FileOutputStream("/Users/yanzhuang/Desktop/2.jpg")
);
//3、使用字节缓冲输入流中的方法read,读取文件
//int len = 0;
//while ((len = bis.read()) != -1 ) {
//    //4、使用字节缓冲输出流中的方法write,把读取的数据写入到内部缓冲区
//    bos.write(len);
//}
byte[] bytes = new byte[1024];
int len = 0;
while((len = bis.read(bytes)) != -1) {
    bos.write(bytes,0, len);
}
//5、释放资源(会先把缓冲区中的数据,刷新到文件中)
bos.close();
bis.close();

long e = System.currentTimeMillis();
System.out.println("字节缓冲输入输出流复制文件共耗时:" + (e-s) + "毫秒");
6.5 字符缓冲输出流【BufferedWriter】

java.io.BufferedWriter继承自Writer

构造方法

  • BufferedWriter(Writer out)创建一个使用默认大小输出缓冲区的缓冲字符输出流。
  • BufferedWriter(Writer out, int sz)创建一个使用给定大小输出缓冲区的新缓冲字符输出流。

参数:
Writer out: 字符输出流,我们可以传递FileWriter,缓冲流会给FileWriter增加一个缓冲区,提高FileWriter的写入效率
int sz:指定缓冲区的大小,不写默认大小

继承自父类的共性成员方法

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

特有的成员方法

  • void newLine()写入一个行分隔符。会根据不同的操作系统,获取不同的行分隔符
//1、创建字符缓冲输出流对象,构造方法中传递字符输出流
BufferedWriter bw = new BufferedWriter(
        new FileWriter("day20-advanced/h.txt"));
//2、调用字符缓冲输出流中的方法write,把数据写入到内存缓冲区中
for (int i = 0; i < 10; i++) {
    //bw.write("helloZX" + i + "\r");
    bw.write("helloZX" + i);
    bw.newLine();
}
//3、调用字符缓冲输出流中的方法flush,把内存缓冲区中的数据,刷新到文件中
bw.flush();
//4、释放资源
bw.close();
6.6 字符缓冲输入流【BufferedReader】

java.io.BufferedReader继承自 Reader

构造方法

  • BufferedReader(Reader in) 创建一个使用默认大小输入缓冲区的缓冲字符输入流
  • BufferedReader(Reader in, int sz)创建一个使用指定大小输入缓冲区的缓冲字符输入流

继承自父类的共性成员方法

  • int read()读取单个字符并返回。
  • int read(char[] cbuf)一次读取多个字符,将字符读入数组
  • void close() 关闭改流并释放与之相关的所有资源

特有的成员方法

  • String readLine()读取一个文本行。读取一行数据,行的终止符号:通过下列字符之一即可认为某行终止:换行、回车、或回车后跟着换行(\r\n)。返回值:包含该行内容的字符串,不包含任何行终止符,如果已到达流末尾,则返回null
//1、创建字符缓冲输入流对象,构造方法中传递字符输入流
BufferedReader br = new BufferedReader(new FileReader("day20-advanced/h.txt"));
//2、使用字符缓冲输入流对象中的read/readLine方法读取文本
//String line = br.readLine();
//System.out.println(line);
/*
    读取是一个重复的过程,使用循环优化,不知道有多少行数据,所以使用while循环
    结束条件为读到null结束
 */
String line;
while((line = br.readLine()) != null){
    System.out.println(line);
}
//System.out.println(br.readLine());// null
//3、释放资源
br.close();
6.7 练习

要求:对文本的内容进行排序in.txt,输出到文件out.txt中。

in.txt文件中的内容:

5.烹羊宰牛且为乐,会须一饮三百杯。
2.君不见高堂明镜悲白发,朝如青丝暮成雪。
10.陈王昔时宴平乐,斗酒十千恣欢谑。
8.钟鼓馔玉不足贵,但愿长醉不愿醒。
7.与君歌一曲,请君为我倾耳听。
12.五花马、千金裘,呼儿将出换美酒,与尔同销万古愁。
9.古来圣贤皆寂寞,惟有饮者留其名。
11.主人何为言少钱,径须沽取对君酌。
3.人生得意须尽欢,莫使金樽空对月。
1.君不见黄河之水天上来,奔流到海不复回。
6.岑夫子,丹丘生,将进酒,杯莫停。
4.天生我材必有用,千金散尽还复来。
//1、创建字符输入缓冲流对象,读取文件in.txt
BufferedReader br = new BufferedReader(new FileReader("day20-advanced/in.txt"));
//2、创建字符缓冲输入流对象,写入文件out.txt
BufferedWriter bw = new BufferedWriter(new FileWriter("day20-advanced/out.txt"));
//3、创建map集合,用来存储序号和内容
HashMap<Integer,String> map = new HashMap<>();
//4、创建list集合,用来存储序号并进行排序
ArrayList<Integer> list = new ArrayList<>();
//5、使用字符缓冲输入流按行读取字符串,并进行分割,将序号单独存储在list集合中,序号-内容存储在map集合中
String line;
while((line = br.readLine()) != null){
    System.out.println(line);
    // 转义字符要加上
    String[] str = line.split("\\.");
    list.add(Integer.parseInt(str[0]));
    map.put(Integer.parseInt(str[0]),str[1]);
}
//6、使用Collections.sort方法进行排序
Collections.sort(list);
//7、遍历list集合,通过list集合的内容查找map集合中的值并拼接成字符串
for (Integer i : list) {
    String val = map.get(i);
    line = i + "." + val;
    System.out.println(i+"." + val);// 需要写入的信息
    //8、将拼接的字符串写入到out.txt中
    bw.write(line);
    bw.newLine();
}
//9、释放资源
bw.close();
br.close();

注意

HashMap是可以自动按照key值排序的,不需要单独将key值存储到list集合中进行排序了。

七、转换流

7.1 字符编码和字符集

在这里插入图片描述

字符编码:自然语言的字符与二进制数之间的对应规则

按照某种规则,将字符存储到计算机中,成为编码。反之,将存储在计算机中的二进制数按照某种规则解析出来,成为解码

字符集(Charset):也叫编码表。是一个系统支持的所有字符的集合。

7.2 编码引出的问题

在IDEA中,使用FileReader读取文本文件,使用默认的UTF-8编码,如果文件是Windows系统创建的,默认中文的编码是gbk,这个时候使用IDEA读取就会显示为乱码。

7.3 [OutputStreamWriter]

java.io.OutputStreamWriter继承自Writer,是字符流通向字节流的桥梁;,可使用指定的 charset 将要写入流中的字符编码成字节。

构造方法

  • OutputStreamWriter(OutputStream out)创建使用默认字符编码的OutputStreamWriter
  • OutputStreamWriter(OutputStream out, String charsetName)创建使用指定字符集d的OutputStreamWriter

继承自父类的共性成员方法

  • void write(int c)写入单个字符
  • void write(char[] cbuf)写入字符数组
  • abstract void write(char[] cbuf, int off, int len)写入字符串的某一部分,off字符串的开始索引,len写的字符个数
  • void write(String str, int off, int len) 写入字符串的某一部分,off 字符串的开始索引,len写的字符个数。
  • void flush() 刷新该流的缓冲。
  • void close()关闭此流,但要先刷新它。
/*
    使用转换流OutputStreamWriter写gbk格式的文件
 */
private static void write_gbk() throws IOException {
    // 1、创建OutputStreamWriter对象,构造方法中传递字节输出流和指定的编码表名称
    OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream("day20-advanced/gbk.txt"),"gbk");
    // 2、使用OutputStreamWriter对象的方法write,把字符转换为字节存储缓冲区中(编码)
    osw.write("你好");
    // 3、使用OutputStreamWriter对象中的方法flush,把内存缓冲区中的字节刷新到文件中(使用字节流写字节的过程)
    osw.flush();
    // 4、释放资源
    osw.close();
}

/*
    使用转换流OutputStreamWriter写UTF-8格式的文件
 */
private static void write_utf_8() throws IOException {
    // 1、创建OutputStreamWriter对象,构造方法中传递字节输出流和指定的编码表名称
    OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream("day20-advanced/utf_8.txt"),"utf-8");
    // 2、使用OutputStreamWriter对象的方法write,把字符转换为字节存储缓冲区中(编码)
    osw.write("你好");
    // 3、使用OutputStreamWriter对象中的方法flush,把内存缓冲区中的字节刷新到文件中(使用字节流写字节的过程)
    osw.flush();
    // 4、释放资源
    osw.close();
}
7.4 [InputStreamReader]

java.io.InputStreamReader 继承自Reader,是字节通向字符流的桥梁,它使用指定的charset读取字节并将其解码为字符。

构造方法

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

参数:
InputStream in: 字节输入流,用来读取文件中保存的字节
String charsetName: 指定的编码表名称,不区分大小写,可以是utf-8/UTF-8,gbk/GBK,…不指定默认使用utf-8。

注意事项构造方法中指定的编码表名称要和文件的编码相同,否则会发现乱码

继承自父类的共性成员方法

  • int read()读取单个字符并返回。
  • int read(char[] cbuf)一次读取多个字符,将字符读入数组
  • void close()关闭改流并释放与之相关的所有资源
private static void read_gbk() throws IOException {
    //1、创建InputStreamReader对象,构造方法中传递字节输入流和指定的编码表名称
    //InputStreamReader isr = new InputStreamReader(new FileInputStream("day20-advanced/gbk.txt"),"gbk");// 你好
    InputStreamReader isr = new InputStreamReader(new FileInputStream("day20-advanced/gbk.txt"));//���
    //2、使用InputStreamReader对象中的方法reader读取文件
    int len = 0;
    while((len = isr.read()) != -1 ){
        System.out.print((char) len);
    }
    //3、释放资源
    isr.close();
}

private static void read_utf_8() throws IOException{
    //1、创建InputStreamReader对象,构造方法中传递字节输入流和指定的编码表名称
    //InputStreamReader isr = new InputStreamReader(new FileInputStream("day20-advanced/utf-8.txt"),"utf-8");
    InputStreamReader isr = new InputStreamReader(new FileInputStream("day20-advanced/utf-8.txt"));
    //2、使用InputStreamReader对象中的方法reader读取文件
    int len = 0;
    while((len = isr.read()) != -1 ){
        System.out.print((char) len);
    }
    //3、释放资源
    isr.close();
}
7.5 练习【转换文件编码】

转换文件编码:将GBK编码的文本文件,转换成UTF-8编码的文本文件。

//1、创建转换输入流对象 charsetName 是gbk
InputStreamReader isr = new InputStreamReader(
        new FileInputStream("day20-advanced/gbk.txt"),"gbk"
);
//2、创建转换输出流对象,默认charsetName是utf8
OutputStreamWriter osw = new OutputStreamWriter(
        new FileOutputStream("day20-advanced/gbk_2_utf8.txt")
);
//3、使用转换输入流对象通过gbk编码读取gbk格式的文件中数据
int len = 0;
while((len = isr.read()) != -1) {
    //4、使用转换输出流对象通过utf编码写入到文件中
    osw.write(len);
}
//5、释放资源
osw.close();
isr.close();
八、对象序列化与反序列化

8.1 概述

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

反之,可以将改字节序列从文件中读取出来,重构对象,称之为反序列化

8.2 [ObjectOutputStream]

java.io.ObjectOutputStream继承自OutputStream,对象的序列化流。

构造方法

  • ObjectOutputStream(OutputStream out)创建写入指定OutputStreamObjectOutputStream

特有的成员方法

  • void writeObject(Object obj)将指定的对象写入ObjectOutputStream
//1、创建ObjectOutputStream对象,构造方法中传递字节输出流
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("day20-advanced/person.txt"));
//2、使用ObjectOutputStream对象中的方法writeObject,把对象写入到文件中
oos.writeObject(new Person("赵晓",18));
//3、释放资源
oos.close();
8.3 [ObjectInputStream]

java.io.ObjectInputStream继承自OutputStream,对象的反序列化流。

构造方法

  • ObjectInputStream(InputStream in) 创建从指定InputStream 读取的ObjectInputStream

特有的成员方法

  • Object readObject()ObjectInputStream中读取对象。

注意readObject方法声明抛出了ClassNotFoundException(class文件找不到异常),当不存在对象的class文件时抛出此异常。

反序列化的前提:1.类必须实现Serisalizable; 2.必须存在类对应的class文件

//1、创建ObjectInputStream对象,构造方法中传递字节输入流
ObjectInputStream ois = new ObjectInputStream(new FileInputStream("day20-advanced/person.txt"));
//2、使用ObjectInputStream对象中的方法readObject读取保存对象的文件
Object o = ois.readObject();// 多态
//3、释放资源
ois.close();
//4、使用读取出来的对象(打印)
System.out.println(o);
8.4 练习【序列化集合】

要求

  • 将存有多个自定义对象的集合序列化操作,保存到list.txt文件中。
  • 反序列化list.txt,并遍历集合,打印对象信息
/*
    练习:序列化集合
    分析:
    1、把若干个学生对象,保存到集合中
    2、序列化集合
    3、反序列化堆区,再转换为集合对象
    4、遍历集合,打印学生信息
 */
public class Demo03Test {
    public static void main(String[] args) throws IOException, ClassNotFoundException {
        ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("day20-advanced/list.txt"));
        List<Student> list = new ArrayList<>();
        Student stu1 = new Student("张三",22);
        Student stu2 = new Student("李四",22);
        Student stu3 = new Student("赵晓",18);
        Student stu4 = new Student("徐超",39);
        list.add(stu1);
        list.add(stu2);
        list.add(stu3);
        list.add(stu4);
        oos.writeObject(list);
        ObjectInputStream ois = new ObjectInputStream(new FileInputStream("day20-advanced/list.txt"));
        Object o = ois.readObject();
        ArrayList<Student> list2 = (ArrayList<Student>) o;
        for (Student student : list2) {
            System.out.println(student);
        }
        oos.close();
        ois.close();
    }
}
九、打印流

9.1 概述

平时我们在控制台打印输出,是调用print方法和println方法完成的,这两个方法都来自java.io.PrintStream类,该类能够方便打印各种数据类型的值,是一种快捷的输出方式。

9.2 [PrintStream]

PrintStream继承自OutputStream只负责数据的输出,不负责数据的读取,与其他输出流不同,PrintStream永远不会抛出 IOException,有特有的方法。

构造方法

  • PrintStream(File file): 输出的目的地是一个文件
  • PrintStream(OutputStream out): 输出的 目的地是一个字节输出流
  • PrintStream(String fileName): 输出的目的地是一个文件路径

继承自父类的共性成员方法

  • void close():Closes this output stream and releases any system resources associated with this stream.
  • void flush():Flushes this output stream and forces any buffered output bytes to be written out.
  • void write(byte[] b): Writes b.length bytes from the specified byte array to this output stream.
  • void write(byte[] b, int off, int len):Writes len bytes from the specified byte array starting at offset off to this output stream.
  • abstract void write(int b):Writes the specified byte to this output stream.

特有的成员方法

  • void print(任意类型的值)
  • void println(任意类型的值并换行)

注意

如果使用继承自父类的write方法写数据,那么查看数据时候就会查询编码表 97->a;如果使用自己特有的方法print/println方法写数据,写的时候原样输出 97-> 97。

// 创建打印流对象,构造方法中绑定要输出的目的地
PrintStream ps = new PrintStream("day20-advanced/print.txt");

// 如果使用继承自父类的write方法写数据,那么查看数据的时候会查询编码表
ps.write(97); // a
//如果使用自己特有的方法print/println方法写数据,写的时候原样输出 97-> 97
ps.println(97);

ps.println("hello");
ps.println(823.23423);

//释放资源
ps.close();
9.3 System.setOut方法

使用System.setOut方法改变输出语句的目的地为参数中传递的打印流的目的地。

  • static void setOut(PrintStream out):重新分配"标准"输出流。
System.out.println("我是在控制台输出的");
PrintStream ps = new PrintStream("day20-advanced/目的地是打印流.txt");
System.setOut(ps);// 把输出语句的目的地改为打印流的目的地
System.out.println("我是在打印流的目的地中输出");
ps.close()
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值