Java的IO流

 

在Java中,把不同的输入/输出源(键盘、文件、网络连接等)中的有序数据抽象为流(stream)。

stream(流)是从起源(source)到接收(sink)的有序数据。

通过流的方式,Java可以使用相同的方式来访问、操作不同类型的输入/输出源,不管输入、输出节点是磁盘文件、网络连接,还是其他的IO设备,只要将这些节点包装成流,我们就可以使用相同的方式来进行输入、输出操作。

原本进行文件读写要用文件读写的一套方法,进行网络读写要用网络读写的一套方法.....不同类型的IO节点,用的方法体系不同,要单独写代码。

java统一了不同类型节点的IO操作,都把输入、输出节点包装成流,使用一套方法就可以进行文件读写、网络读写等不同类型节点的读写,不必单独写代码,从而简化了IO操作。

 

 

 

 

流的分类:

1、按流的方向分:

  • 输入流:从磁盘、网络等读取到内存,只能读,不能写。
  • 输出流:从内存写出到磁盘、网络,只能写,不能读。

 

2、按操作的数据单元分:

  • 字节流:以字节为基本操作单位
  • 字符流:以字符为基本操作单位

字节流、字符流的用法基本一样,只是操作的数据单元不同。

 

3、按照流的角色来分:

  • 节点流:向某个IO设备/节点(磁盘文件、网络等)直接读写数据,也称为低级流。
  • 处理流:用于包装一个已存在的流,通过这个已存在的流来进行读写操作,并不直接操作IO节点,也称为高级流、包装流。

处理流是一种典型的装饰器设计模式,通过使用处理流来包装不同的节点流,可以消除不同节点流的实现差异,访问不同的数据源。

 

 

 

 

 

InputStream是字节输入流的顶级父类,常用方法:

  • int  read()     //读取一个字节,返回该字节数据的Unicode码值
  • int  read(byte[]  buff)    //最多读取buff.length个字节,将读取的数据放在buff数组中,返回实际读取的字节数
  • int  read(byte[]  buff, int off, int length)    //最多读取length个字节,放在buff数组中,从数组的off位置开始放置数据,返回实际读取的字节数。off一般设置为0,length一般设置为buff的长度(这样其实和第二个函数的功能完全一样)。

 

Reader时字符输入流的顶级父类,常用方法:

  • int  read()     //读取一个字符,返回该字符的Unicode码值,注意并不是返回该字符。
  • int  read(char[]  buff)   //最多读取buff.length个字符,放在buff数组中,返回实际读取的字符数
  • int  read(char[]  buff, int  off, int  length) //最多读取length个字节,放在buff数组中,从数组的off位置开始放置数据,返回实际读取的字符数。off一般设置为0,length一般设置为buff的长度(这样其实和第二个函数的功能完全一样)。

 

字节流和字符流的用法差不多,只是操作的数据单元不同。

如果已读完(没有数据可读),以上方法均返回 -1 。

InputStream、Reader都是抽象类,不能直接创建示例,要用已实现的子类创建实例,比如FileInputStream类、FileReader类。

 

示例:

 1 FileInputStream fis=new FileInputStream("./1.txt");
 2         /*
 3         因为是以字节为单位操作的,如果要将byte[]转换为String,中文、byte[]长度设置不对,就可能读取到中文字符的半个字节,最终得到的String会乱码。
 4         有2种解决中文乱码的方式:
 5         1、将数组长度设置为较大的值(大于等于输入流的内容长度)
 6         2、如果输入节点以GBK、GB2312编码(2字节码),将数组长度设置为2的倍数;如果输入节点以UTF-8编码(3字节码),将数组长度设置为3的倍数
 7         注意:在Windows的资源管理器中新建文本文件,默认字符集是ASCII,保存时注意修改编码方式。
 8         */
 9        byte[] buff=new byte[1024];
10        int length=0;
11         while ((length=fis.read(buff))!=-1){
12             //使用String的构造函数将byte[]转化为String。此处用new String(buff)也是一样的
13             System.out.println(new String(buff,0,length));     
14         }
15         //使用完,要关闭流
16         fis.close();

 

1 FileReader fr=new FileReader("./1.txt");
2         //因为操作单位是一个字符,所以数组大小设置为多少都行,不会乱码。
3         char[] buff=new char[1024];
4         while (fr.read(buff)!=-1){
5             System.out.println(new String(buff));
6         }
7         fr.close();

 

 

InputStream、Reader均提供了移动记录指针的方法:

  • long  skip(long  n)     //指针向前移动n个字节/字符
  • void  mark(int maxReadLimit)    //在当前指针处作一个标记。参数指定此标记的有效范围,mark()做标记后,再往后读取maxReadLimte字节,此标记就失效。

实际上,并不完全由maxReadLimte参数决定,也和BufferedInputStream类的缓冲区大小有关。只要缓冲区够大,mark()后读取的数据没有超出缓冲区的大小,mark标记就不会失效。如果不够大,mark后又读取了大量的数据,导致缓冲区更新,原来标记的位置自然找不到了。

  • void  reset()   将指针重置到上一个mark()标记的位置
  • boolean  markSupported()     //检查这个流是否支持mark()操作

以上方法只能用于输入流,不能用于输出流。

 

 

 

 

 

 

OutputStream是字节输出流的顶级父类,常用方法:

  • void  write(int  i)    \\输出一个字节,i是码值,即read()得到的码值,输出i对应的字节
  • void  write(byte[]  buff)   //输出整个字节数组的内容
  • void  write(byte[]  buff, int  off, int  length)    //把字节数组从off位置开始,输出长度为length字节的内容

 

Writer是字符输出流的顶级父类,常用方法:

  • void  write(int  i)    //输出一个字符,i是码值,输出的是i对应的字符
  • void  write(char[]  buff)    //输出整个char[]的内容
  • void  write(char[]  buff,  int  off,  int  length)      //把char[]从off位置开始,输出长度为length字符的内容

可以用String代替char[],所以Writer还具有以下2个方法:

  • void  write(String str)
  • void  write(String str, int off, int length)

 

OutputStream、Writer是抽象类,不能实例化,要创建对象,需使用已实现的子类,比如FileOutputStream、FileWriter。

 

示例:

1  //默认是覆盖
2         FileOutputStream fos=new FileOutputStream("./2.txt");
3         fos.write("你好!\n".getBytes());
4         fos.close();
5 
6         //可指定是否是追加模式,true——是追加模式,false——不是追加模式(即覆盖)
7         fos=new FileOutputStream("./2.txt",true);
8         fos.write("我是chenhongyong。".getBytes());
9         fos.close();

 

1 //默认是覆盖
2         FileWriter fw=new FileWriter("./2.txt");
3         fw.write("hello\n");
4         fw.close();
5 
6         //追加
7         fw=new FileWriter("./2.txt",true);
8         fw.write("world!");
9         fw.close();

 

 1 //如果输出流的文件对象不存在,会报错
 2         FileInputStream fis=new FileInputStream("./1.txt");
 3         //如果输出流的文件不存在,会自动创建
 4         FileOutputStream fos=new FileOutputStream("./2.txt");
 5         byte[] buff=new byte[1024];
 6         //文件复制
 7         while (fis.read(buff)!=-1)
 8             fos.write(buff);
 9         fis.close();
10         fos.close();

 

Windows的换行符是\r\n,Linux、Unix的换行符是\n。最好用\n,\n是所有OS通用的换行符。

 

 

 

 

计算机中的文件可以分为二进制文件、文本文件,能用记事本打开、并可以看到字符内容的文件就是文本文件,反之称为二进制文件。

实际上,从底层来看,计算机上的所有文件都是基于二进制存储、读写的。计算机上的文件都是二进制文件,文本文件是一种特殊的二进制文件。

 

字节流的功能更强大,因为字节流可以操作所有的二进制文件(计算机上的所有文件)。

但如果用字节流处理文本文件,要进行字符、字节之间的转换,要麻烦一些。

所以一般情况下,我们用字符流处理文本文件,用字节流处理二进制文件。

 

 

 

 

上面使用的FileInputStream、FileWriter、FileOutputStream、FileWriter都是直接操作IO节点(磁盘文件),构造函数里是IO节点,它们都是节点流(低级流)。

处理流用来包装一个已存在的流,示例:

 1  FileOutputStream fos=new FileOutputStream("./1.txt");
 2         //以一个已存在的流对象作为参数
 3         PrintStream ps=new PrintStream(fos);
 4 
 5         //可以字节为单位进行IO操作
 6         ps.write("你好!".getBytes());
 7         //可以字符为单位进行IO操作
 8         ps.print("很高兴认识你!");
 9         //println()输出后直接换行,不用我们在写\n,更加简便
10         ps.println("我是chenhongyong。");
11         //可输出各种类型的数据
12         ps.write(12);
13         ps.print(12);
14         ps.print(12.34);
15 
16         //关闭处理流时,会自动关闭其包装的节点流,代码更简洁。
17         ps.close();
18 
19         //处理流提供了更多的输入/输出方法,更加简单、强大

PrintStream类的输出功能很强大,可以把输出的低级流包装成PrintStream使用。

 

处理流的优点:

  • 更简单、更强大
  • 执行效率更高

 

 

 

 

处理流之缓冲流:

前面提到可以用byte[]、char[]做缓冲区,提高读写效率。Java提供了缓冲流,来实现相同的效果,使用方式基本相同。

 1  //以相应的节点流对象作为参数
 2         BufferedInputStream bis = new BufferedInputStream(new FileInputStream("./1.txt"));
 3         BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("./2.txt"));
 4         //文件复制
 5         int i = 0;
 6         while ((i = bis.read()) != -1){   //length是读取到的内容的码值,不是读取到的内容的长度。一次只读一个字节。
 7             System.out.println(length);
 8             bos.write(length);   //将读取的数据写到输出流
 9     }
10         //关闭处理流即可,会自动关闭对应的节点流
11         bis.close();
12         bos.close();

 

 1  BufferedInputStream bis=new BufferedInputStream(new FileInputStream("./1.txt"));
 2        BufferedOutputStream bos=new BufferedOutputStream(new FileOutputStream("./2.txt"));
 3        byte[] buff=new byte[1024];
 4        int length=0;
 5        while ((length=bis.read(buff))!=-1){   //length是读取到的内容的长度
 6            System.out.println(length);
 7             bos.write(buff);
 8        }
 9        bis.close();
10        bos.close();

 

缓冲区:

计算机访问外部设备或文件,要比直接访问内存慢的多。如果我们每次调用read()方法或者writer()方法访问外部的设备或文件,CPU就要花上最多的时间是在等外部设备响应,而不是数据处理。
为此,我们开辟一个内存缓冲区的内存区域,程序每次调用read()方法或writer()方法都是读写在这个缓冲区中。当这个缓冲区被装满后,系统才将这个缓冲区的内容一次集中写到外部设备或读取进来给CPU。使用缓冲区可以有效的提高CPU的使用率,能提高读写效率。

缓冲流:

读写数据时,让数据在缓缓冲区能减少系统实际对原始数据来源的存取次数,因为一次能做多个数据单位的操作,相较而言,对于从文件读取数据或将数据写入文件,比起缓冲区的读写要慢多了。所以使用缓冲区的 流,一般都会比没有缓冲区的流效率更高,拥有缓冲区的流别称为缓冲流。缓冲流本身没有输入、输出功能,它只是在普通IO流上加一个缓冲区。

 

缓冲流是和4级顶级父类对应的:加前缀Buffered

InputStream   BufferedInputStream   字节输入缓冲流,可作为所有字节输入流类的缓冲流

OutputStream   BufferedOutputStream    字节输出缓冲流

Reader     BufferedReader    字符输入缓冲流

Writer  BufferedWriter    字符输出缓冲流

 

 执行效率:

普通IO流,逐个字节/字符操作,效率极低。缓冲流自带缓冲区,就算是逐个字节/字符操作,效率也是很高的。

普通IO流,用数组作为缓冲区,效率极高。缓冲流使用数组,效率极高。二者实现方式是相同的。

大致上:使用数组的缓冲流 ≈ 使用数组的普通流 >>  逐个字节/字符操作的缓冲流 >>  逐个字节/字符操作的普通IO流。

尽量不要逐个字节/字符操作,太慢了。

 

由于使用了缓冲区,写的数据不会立刻写到物理节点,而是会写到缓冲区,缓冲区满了才会把缓冲区的数据一次性写到物理节点。

可以使用流对象的flush()方法,强制刷出缓冲区的数据到物理节点。

当然,调用close()关闭流对象时,会先自动调用flush()刷出缓冲区的数据到物理节点。

 

 

 

 

 

处理流之转换流:Java提供了2个转化流,用于将字节流转换为字符流。

1、InputStreamReader类,顾名思义,是从InputStream到Reader。InputStreamReader是Reader的子类,操作的数据单元是字符。

InputStreamReader  isr=new  InputStreamReader(InputStream is);     //参数是InputStream类的对象

 

2、OutputStreamWriter类,顾名思义,是从OutputStream到Writer。OutputStreamWriter是Writer的子类,操作的数据单元是字符。

OutputStreamWriter  isr=new  OutputStreamWriter(OutputStream os);     //参数是OutputStream类的对象

 

因为字符流本就比较方便,所以没有将字符流转换为字节流的类。

 

 

 

 

 

 

预定义的标准流对象:

  • System.in     标准输入流对象(一般指键盘输入),是InputStream的一个对象,可使用InputStream的一切方法。
  • System.out    标准输出流对象(一般指显示器),是OutputStream的一个对象
  • System.err    标准错误输出流。
1  byte[] buff=new byte[100];
2         //将键盘输入(从控制台输入)读取到byte[]中
3         System.in.read(buff);
4         //转换为String输出
5         System.out.println(new String(buff));

 

 

 重定向标准流对象:

  • System.setIn(InputStream  is);     //将标准输入流切换到指定的InputStream对象
  • System.setOut(PrintStream   ps);   
  • System.setErr(PrintStream  ps);
1 PrintStream ps=new PrintStream("./err.txt");
2         //重定向标准错误输出流,错误信息不再输出到控制台,而是输出到err.txt文件
3         System.setErr(ps);
4         int a=4/0;

 

1  PrintStream ps=new PrintStream("./out.txt");
2         //重定向标准输出流,标准输出不再输出到屏幕(控制台),而是输出到out.txt文件
3         System.setOut(ps);
4         //1会输出到out.txt文件
5         System.out.println(1);

 

 

 

 

 

 

Java常用的流:

 

分类 字节输入流 字节输出流字符输入流字符输出流
抽象基类  InputStream  OutputStreamReaderWriter
操作文件FileInputStream   FileOutputStream  FileReader  FileWriter  
操作数组  ByteArrayInputStream  ByteArrayOutputStream  CharArrayReader  CharArrayWriter  
操作字符串    StringReaderStringWriter
缓冲流      BufferedInputStreamBufferedOutputStreamBufferedReaderBufferedWriter
转换流  InputStreamReader    OutputStreamWriter
对象流(用于序列化)ObjectInputStreamObjectOutputStream  
抽象基类(用于过滤)    FilterInputStreamFilterOutputStreamFilterReaderFilterWriter
打印流(输出功能极其大) PrintStream(实际上也可用于输出字符) PrintWriter(不能输出byte)  

 

部分流可以在构造函数中以参数String charset或Charset  charset的形式指定字符集。

 

此外,还有其他的IO流,比如:

  • AudioInputStream、AudioOutputStream     音频输入、输出
  • CipherInputStream、CipherOutputStream      加密、解密
  • DeflaterInputStream、ZipInputStream、ZipOutputStream    文件压缩、解压

 

转载于:https://www.cnblogs.com/chy18883701161/p/10915576.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值