常用的IO流总结

常用的IO流总结

jAVAIO:IO:Input,Output 即:输入与输出

  • JAVA IO用于我们程序可以和外界交换数据。用于与外界的数据进行读写操作的。
  • java中将输入与输出比喻为"流":stream,如何理解流:讲流想象为一个连接我们程序和另一端的"管道",在其中按照同一方向顺序移动的数据,有点像"水管"中向着统一方向流动的水。
  • 输入流:从外界向我们的程序中移动的方向,因此是用来获取数据的流,作用就是:读取操作,输出流:写出操作
  • 注意:流是单向的,输入永远用来读,输出永远用来写。将来我们在实际开发中希望与程序交互的另一端,互相发送数据时,我们只需要创建一个可以连接另一端的"流",进行读写操作完成。
Java定义了两个超类(抽象类):
  • java.io.InputStream:所有字节输入流的超类,其中定义了读取数据的方法.因此将来不管读取的是什么设备(连接该设备的流)都有这些读取的方法,因此我们可以用相同的方法读取不同设备中的数据
  • java.io.OutputStream:所有字节输出流的超类,其中定义了写出数据的方法.
java将流分为两类:节点流与处理流:
  • 节点流:也称为低级流.节点流的另一端是明确的,是实际读写数据的流,读写一定是建立在节点流基础上进行的.
  • 处理流:也称为高级流.处理流不能独立存在,必须连接在其他流上,目的是当数据流经当前流时对数据进行加工处理来简化我们对数据的该操作.

实际应用中,我们可以通过串联一组高级流到某个低级流上以流水线式的加工处理对某设备的数据进行读写,这个过程也成为流的连接,这也是IO的精髓所在.

文件流

文件流是一对低级流,用于读写文件数据的流.用于连接程序与文件(硬盘)的"管道".负责读写文件数据.

文件输出流:FileOutputStream
FileOutputStream fos = new FileOutputStream("./fos.dat");
/*write(int d) 该方法的作用是写出一个字节,写出的是int对应二进制的低八位
* 1个int值占4个字节,每个字节是一个8为2进制
int 1的2进制样子:
00000000 00000000 00000000 00000001
                           ^^^^^^^^
                          写出的字节*/
fos.write(3);
System.out.println("写出完毕");
fos.close();//用完后及时关闭流
文件输入流:FileInputStream
FileInputStream fis = new FileInputStream("fos.dat");
/*int read()
            读取1个字节并以int型整数返回读取到的字节内容,返回的int值中对应的2进制的"低八位"
            就是读取到的数据。如果返回的int值为整数-1(这是一个特殊值,32位2进制全都是1)表达的
            是流读取到了末尾了*/
int d = fis.read();//进行读取操作
System.out.println(d);//3
d = fis.read();//读取完成后再次进行
System.out.println(d);//-1
fis.close();
文件复制:
//用文件输入流读取待复制的文件
FileInputStream fis = new FileInputStream("image.jpg");
//用文件输出流向复制文件中写入复制的数据
FileOutputStream fos = new FileOutputStream("image_cp.jpg");
int d = 0;
while((d= fis.read()) != -1){//在读取的同时进行赋值,这样可以简化代码
     fos.write(d);//对文件进行写出
}
     System.out.println("复制完毕");
     fis.close();
     fos.close();
以上的操作由于受到读写字节的限制,效率并不高,所以可以使用块读写来提升效率

int read(byte[] data) 一次性从文件中读取给定的字节数组总长度的字节量,并存入到该数组中。 返回值为实际读取到的字节量。若返回值为-1则表示读取到了文件末尾。

块写操作 void write(byte[] data) 一次性将给定的字节数组所有字节写入到文件中

void write(byte[] data,int offset,int len) 一次性将给定的字节数组从下标offset处开始的连续len个字节写入文件

//用文件输入流读取待复制的文件
FileInputStream fis = new FileInputStream("heike.pdf");
//用文件输出流向复制文件中写入复制的数据
FileOutputStream fos = new FileOutputStream("heike_cp.pdf");
byte[] buf = new byte[1024 * 10];//10kb
int len;//记录每次实际读取到的字节数
long start = System.currentTimeMillis();//计时器开始
while ((len = fis.read(buf)) != -1) {
      fos.write(buf, 0, len);
}
long end = System.currentTimeMillis();//计时器结束
System.out.println("复制完毕,耗时:" + (end - start) + "ms");
fis.close();
fos.close();
向文本中写入数据:

String提供方法: byte[] getBytes(String charsetName) 将当前字符串转换为一组字节

参数为字符集的名字,常用的是UTF-8。 其中中文字3字节表示1个,英文1字节表示1个。

FileOutputStream fos = new FileOutputStream("fos.txt");
String line = "这是一个测试的例子";
byte[] data = line.getBytes(StandardCharsets.UTF_8);//指定字符集,避免出现乱码
fos.write(data);
System.out.println("写出完毕");
fos.close();
文件流有两种创建方式:
  • 覆盖模式,对应的构造器:

FileOutputStream(String filename)

FileOutputStream(File file)

所谓覆盖模式:文件流在创建是若发现该文件已存在,则会将该文件原内容全部删除。然后

在陆续将通过该流写出的内容保存到文件中。

FileOutputStream fos = new FileOutputStream("fos.txt");
String line = "这是二个测试的例子";
byte[] data = line.getBytes(StandardCharsets.UTF_8);//指定字符集,避免出现乱码
fos.write(data);
System.out.println("写出完毕");//这是二个测试的例子,输出后会覆盖掉原来的内容
fos.close();
  • 追加模式,对应的构造器
    FileOutputStream(String filename,boolean append)
    FileOutputStream(File file,boolean append)
    当第二个参数为true时,那么就是追加模式。
    所谓追加模式:文件流在创建时若发现该文件已存在,则原内容都保留。通过当前流陆续写出
    的内容都会被陆续追加到文件末尾。

    FileOutputStream fos = new FileOutputStream("fos.txt",true);
    //加上true之后会显示为追加模式,会保留之前的内容,并在后面添加新的内容
    String line = "第三次测试";
    byte[] data = line.getBytes(StandardCharsets.UTF_8);//指定字符集,避免出现乱码
    fos.write(data);
    System.out.println("输出完毕");//结果:这是二个测试的例子第三次测试
    fos.close();
    
读取文本数据
  • String提供了构造方法可以将一个字节数组还原为字符串

String(byte[] data,Charset charset) 将给定的字节数组data中所有字节按照给定的字符集转换为字符串。

​ String(byte[] data,int offset,int len,Charset charset)
​ 将给定的字节数组data从下标offset处开始的连续len个字节按照指定的字字符集转换为字符串

//因为是要读取文件,所以用文件流更好操作一些
File file = new File("./text.txt");
FileInputStream fis = new FileInputStream(file);
long len = file.length();//读取文件的长度
System.out.println("文件大小为"+len);
//根据文件大小创建等长的字节数组
byte[] data = new byte[(int)len];//注意这里有小问题,如果超出int结果可能会不准
fis.read(data);
String line = new String(data, StandardCharsets.UTF_8);
System.out.println(line);
fis.close();

高级流

  • 实际开发中我们经常会串联一组高级流最终连接到低级流上,在读写操作时以流水线式的加工完成复杂IO操作。这个过程也称为"流的连接"。
java.io.BufferedOutputStream和BufferedInputStream.

缓冲流是一对高级流,作用是提高读写数据的效率.

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

 //用文件输入流读取待复制的文件
FileInputStream fis = new FileInputStream("heike.pdf");
BufferedInputStream bis = new BufferedInputStream(fis);//缓冲输入流
//缓冲流是可以自己定义大小的,一般默认是一个8kb的byte数组,自己定义直接在后面添加即可
//BufferedInputStream bis = new BufferedInputStream(fis,1024*10);
//用文件输出流向复制文件中写入复制的数据
FileOutputStream fos = new FileOutputStream("heike_cp2.pdf");
BufferedOutputStream bos =new BufferedOutputStream(fos);//缓冲输出流
int d;
long start = System.currentTimeMillis();//计时器开始
while ((d = bis.read()) != -1) {
      bos.write(d);
}
long end = System.currentTimeMillis();//计时器结束
System.out.println("复制完毕,耗时:" + (end - start) + "ms");
bis.close();
bos.close();
缓冲输出流写出数据时的缓冲区问题

通过缓冲流写出的数据会被临时存入缓冲流内部的字节数组,直到数组存满数据才会真实写出一次

缓冲流的flush方法用于强制将缓冲区中已经缓存的数据一次性写出。
注:该方法实际上实在字节输出流的超类OutputStream上定义的,并非只有缓冲
输出流有这个方法。但是实际上只有缓冲输出流的该方法有实际意义,其他的流实现
该方法的目的仅仅是为了在流连接过程中传递flush动作给缓冲输出流。

FileOutputStream fos = new FileOutputStream("bos.txt");
BufferedOutputStream bos = new BufferedOutputStream(fos);
String line = "将东西一次全部冲出去!";
byte[] data = line.getBytes(StandardCharsets.UTF_8);
bos.write(data);
System.out.println("写出完毕!");
bos.flush();//如果没有的话,读写时不够则会存在缓存区里,不会读取或写出,加上此方法后则无论够不够在最后都会进行冲刷,将数据读取或写出。
bos.close();
对象流

java.io.ObjectOutputStream和ObjectInputSteam

对象流是一对高级流,在流连接中的作用是进行对象的序列化与反序列化。

对象序列化:将一个java对象按照其结构转换为一组字节的过程

对象反序列化:将一组字节还原为java对象(前提是这组字节是一个对象序列化得到的字节)

序列化时可能出现异常:
java.io.NotSerializableException: io.Person
注:冒号后面的io.Person是指序列化的就是这个类的实例出现的错误
原因:
对象输出流在进行序列化对象时,要求该对象所属的类必须实现接口:java.io.Serializable接口
并且该类中所有引用类型属性也必须实现该接口,否则会抛出上述异常。

//实验时需要先建一个对象类,并且对象类需要进行序列化,implements Serializable 
String name = "张三";
int age = 33;
String gender = "男";
String[] Other = {"法外狂徒","真男人"};
Person p = new Person(name,age,gender,Other);
FileOutputStream fos = new FileOutputStream("./person.obj");
ObjectOutputStream oos = new ObjectOutputStream(fos);
oos.writeObject(p);
System.out.println("写出完毕");
oos.close();
对象反序列化

Object readObject()
该方法会进行对象的反序列化,如果对象流通过其连接的流读取的字节分析并非
是一个java对象时,会抛出异常:ClassNotFoundException

需要进行序列化的类必须实现接口:java.io.Serializable 实现序列化接口后最好主动定义序列化版本号这个常量。 这样一来对象序列化时就不会根据类的结构生成一个版本号,而是使用该固定值。 那么反序列化时,只要还原的对象和当前类的版本号一致就可以进行还原。(可以将鼠标放在Serializable,出现弹窗后在下面有版本号)

transient关键字可以修饰属性,用于在进行对象序列化时忽略不必要的属性,达到对象瘦身的目的

FileInputStream fis = new FileInputStream("./person.obj");
ObjectInputStream ois = new ObjectInputStream(fis);
Person p = (Person)ois.readObject();
System.out.println(p);
ois.close();

字符流

  • java将流按照读写单位划分为字节流与字符流.
  • java.io.InputStream和OutputStream是所有字节流的超类
  • 而java.io.Reader和Writer则是所有字符流的超类,它们和字节流的超类是平级关系.
  • Reader和Writer是两个抽象类,里面规定了所有字符流都必须具备的读写字符的相关方法.
  • 字符流最小读写单位为字符(char),但是底层实际还是读写字节,只是字符与字节的转换工作由字符流完成.
转换流

java.io.InputStreamReader和OutputStreamWriter

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

使用转换输出流向文件中写入文本数据
FileOutputStream fos = new FileOutputStream("osw.txt");
OutputStreamWriter osw = new OutputStreamWriter(fos, StandardCharsets.UTF_8);
String line = "测试一";
osw.write(line);//转换流的write(String str)会将写出的字符串转换为字节然后写出
osw.write("测试二");
System.out.println("写出完毕!");
osw.close();
使用转换输入流读取文本文件
 //将osw.txt文件中的所有文字读取回来.
FileInputStream fis = new FileInputStream("osw.txt");
InputStreamReader isr = new InputStreamReader(fis,"UTF-8");
int d;
while((d = isr.read()) != -1){
      System.out.print((char)d);
}
isr.close();
转换流的意义:

实际开发中我们还有功能更好用的字符高级流.但是其他的字符高级流都有一个共通点:不能直接连接在字节流上.而实际操作设备的流都是低级流同时也都是字节流.因此不能直接在流连接中串联起来.转换流是一对可以连接在字节流上的字符流,其他的高级字符流可以连接在转换流上.在流连接中起到"转换器"的作用(负责字符与字节的实际转换)

缓冲字符流
缓冲字符输出流:java.io.PrintWriter

java.io.BufferedWriter和BufferedReader

缓冲字符流内部也有一个缓冲区,读写文本数据以块读写形式加快效率.并且缓冲流有一个特别的功能:可以按行读写文本数据,缓冲流内部维护一个char数组,默认长度8k.以块读写方式读写字符数据保证效率

java.io.PrintWriter具有自动行刷新的缓冲字符输出流,实际开发中更常用.它内部总是会自动连接BufferedWriter作为块写加速使用.

/*PrintWriter继承自Writer.
      它提供很多构造方法,其中就有可以直接对文件进行写操作的构造器
      PrintWriter(File file)
      PrintWriter(String filename)*/
    public static void main(String[] args) throws FileNotFoundException {
         //向文件中写入文本数据
        PrintWriter pw = new PrintWriter("./pw.txt","UTF-8");
        pw.println("第一次测试");
        pw.println("第二次测试");
        System.out.println("写出完毕");
        pw.close();
  //需要注意的是pw不支持追加模式,如果想要追加模式,需要将pw流分开写     
//pw是高级字符流,所以不能单独使用,但是其内部是有相关的流连接的,也就是其内部已经将创建的工作做好了。也就是自动为我们创建好了以下相关的操作
//文件输出流(低级流,字节流) 作用:向文件中写出字节
FileOutputStream fos = new FileOutputStream("pw2.txt");
//转换输出流(高级流,字符流) 作用:1衔接字符与字节流的 2:将写出的字符转换为字节
OutputStreamWriter osw = new OutputStreamWriter(fos, StandardCharsets.UTF_8);
//缓冲字符输出流(高级流,字符流) 作用:块写文本数据加速的(内部有一个8k的char数组)
BufferedWriter bw = new BufferedWriter(osw);
//具有自动行刷新功能(高级流,字符流) 作用:1按行写出字符串(println) 2:自动行刷新
PrintWriter pw = new PrintWriter(bw,true);//加true后可开启自动冲刷功能

总结:

输入流:

字节输入流:

  • FileInputStream:文件输入流,实际连接程序与文件的管道,负责从文件中读取字节
  • BufferedInputStream:缓冲字节输入流,块读字节数据加速
  • ObjectInputSteam:对象输入流,进行对象反序列化

字符输入流:

  • InputStreamReader:转换输入流,衔接字节流与字符流,将块读的字节转字符
  • BufferedReader:缓冲字符输入流,块读文本数据加速,按行读取字符串
输出流:

字节输出流:

  • FileOutputStream:文件输出流,实际连接程序与文件的管道,负责将字节写入文件
  • BufferedOutputStream:缓冲字节输出流,块写字节数据加速
  • ObjectOutputSteam:对象输出流,进行对象序列化

字符输出流:

  • OutputStreamWriter:转换输出流,衔接字节流与字符流,将写出的字符转换为字节
  • PrintWriter:缓冲字符输出流,块写文本数据加速,按行写出字符串,自动行数新
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值