【Java】一篇文章让你会用Java的io流

第一时间阅读最新文章

一、什么是io流

JavaIO流,是一种计算机用语。主要是用于处理数据的传输。
——百度百科

流是指数据的流动,io流就是输入输出的流动。
在java中对数据的操作是用流的方式来实现的,数据不可能一瞬间就全部地从一个设备传到另一个设备,所以只能采用一点一点流动的方式。
输入流就是将其他地方的数据读取到程序中输出流就是将程序中的数据写入到其他地方

二、io流的分类

1.四个抽象类

在java.io中提供了最重要的四个抽象类,InputStream(字节输入流),OutputStream(字节输出流),Reader(字符输入流),Writer(字符输出流)

一字符等于两字节
根据输出的数据单位,可以分为两类,字节流字符流

graph LR
A((字节流))--输入-->B(InputStream)
A--输出-->C(OutputStream)
D((字符流))--输入-->E(Reader)
D--输出-->F(Writer)

还可以根据输入或输出,分为输入流输出流
根据实现功能的不同,还可以分为节点流处理流
节点流就是,从一个特定的节点读取或者写入数据。一看到节点就感觉好深奥,其实说白了就是从一个地方读取或写入数据,而这个地方可以是数据库,控制台,文件等。
处理流就是,代理节点流,实现一些其他的功能。像BufferedWriter,如果是普通的FileWriter,在执行写操作时就就只能一个字符一个字符来写,如果再套上一个BufferedWriter,就会有缓存的功能,写的操作就会更加的快速了。

2.各种实现类

graph LR
A((InputStream))-->AN[节点流]
AN-->A1(FileOutputStream)
AN-->A2(PipedOutputStream)
AN-->A3(ByteArrayOutputStream)
A-->AH[处理流]
AH-->A4(BufferedOutputStream)
AH-->A5(PrintStream)
AH-->A6(DataOutputStream)
AH-->A7(ObjectOutputStream)

B((OutputStream))-->BN[节点流]
BN-->B1(FileInputStream)
BN-->B2(PipedInputStream)
BN-->B3(ByteArrayInputStream)
B-->BH[处理流]
BH-->B4(BufferedInputStream)
BH-->B5(SequenceInputStream)
BH-->B6(DataInputStream)
BH-->B7(ObjectInputStream)
BH-->B8(PrintStream)

D((Reader))-->DN[节点流]
DN-->D1(FileReader)
DN-->D2(PipedReader)
DN-->D3(CharArrayReader)
D-->DH[处理流]
DH-->D4(BufferedReader)
DH-->D5(InputStreamReader)

C((Writer))-->CN[节点流]
CN-->C1(FileWriter)
CN-->C2(PipedWriter)
CN-->C3(CharArrayWriter)
C-->CH[处理流]
CH-->C4(BufferedWriter)
CH-->C5(InputStreamWriter)
CH-->C6(PrintWriter)

可能会有所遗漏,更加详细的可以查看java官方的文档。

三、如何使用

以FileInputStream和FileOutputStream为例

1.代码

package test;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;

/**
 * @author xxj
 * io流测试
 */
public class IOTest {
    public static void main(String[] args) {
       //先打开一个文件
        File file= new File("C:\\Users\\Administrator\\Desktop\\test.txt");
        try{
            //先写入数据
            FileOutputStream fileOutputStream=new FileOutputStream(file);
            fileOutputStream.write("ABCD".getBytes());
            fileOutputStream.close();
            //读取数据
            FileInputStream fileInputStream=new FileInputStream(file);
            int i= 0;
            while ((i=fileInputStream.read())!=-1){
                System.out.println("通过fileInputStream读取的数据:"+i);
                System.out.println("通过fileInputStream读取的数据:"+(char)i);
                System.out.println("------");
            }
            fileInputStream.close();
        }catch (Exception e){}
    }
}

2.输出结果

在这里插入图片描述

因为我加了(char)所以自动做了ASCII码表的转换
我的桌面也生成了test.txt文件,有兴趣的拿代码去跑一下吧。

3.解释

可能有人会问,你这不对啊,你不是FileInputStream和FileOutputStream吗,怎么刚好每次读取都是一个字母呢,不是一个字节吗?
一个字节是八位,再看一下ASCII码表,里面已经有上百个符号或字母了,肯定不能只用4bit来表示啦,最少也要用8bit,8bit最高可以表示256,所以这里直接读取就是单个字母来读取的。

再以FileReader和FileWriter为例

4.代码

public class IOTest {
    public static void main(String[] args) {
        try{
            //先写入
            File file=new File("C:\\Users\\Administrator\\Desktop\\writer.txt");
            FileWriter fileWriter=new FileWriter(file);
            fileWriter.write("ABC一二三");
            fileWriter.close();
            //读取
            FileReader fileReader=new FileReader(file);
            int i=0;
            while ((i=fileReader.read())!=-1){
                System.out.println("通过reader读取的数据:"+i);
                System.out.println("通过reader读取的数据:"+(char)i);
                System.out.println("------");
            }
            fileReader.close();
        }catch (Exception e){}
    }
}

5. 输出结果

在这里插入图片描述

6.解释

我有个疑问,FileReader为什么可以读字母,跟FileInputStream一样,那为什么它会刚好可以只读一个中文又只读到一个字母呢?
19968=0x4e00
writer.txt的16进制文件为

4142 43 e4b8 80e4 ba8c e4b8 89

根本没有4e00,那么我们再转化成二进制
(一)19968=100111000000000
(二)20108=100111010001100
(三)19977=100111000001001
0xe4b880e4ba8ce4b889=111001001011100010000000111001001011101010001100111001001011100010001001
我使用了String.replace()方法,发现e4b8 80e4 ba8c e4b8 89中并没有包含一二三的二进制。

这里就涉及到编码了,常用的支持中文的编码表是UTF-8,于是我将一二三拉进去查看进制转换

在这里插入图片描述

这。真的是一套一套的。
到了这里,也不是很清楚究竟是怎么确认只读一个字节还是读一个字符

四、各种io流的特点

不同的io流使用起来是差不多的,只是细节上有些不同,具体的可以查查百度或者直接看每个实现类的源码,毕竟它们都是分别继承了InputStream(字节输入流),OutputStream(字节输出流),Reader(字符输入流),Writer(字符输出流),其中处理流是根据Filter···stream间接继承的。

我稍微看了一下,想FileOutputStream最后的写操作是带有native修饰符的,又要查漏补缺了。

private native void writeBytes(byte b[], int off, int len, boolean append)
        throws IOException;

字节流和字符流的区别

  1. 字节流一次只能操作一个字节字符流可以操作两个字节
  2. 字节流能够处理任何文件类型,而字符流只能处理文本类型的文件

1.节点流

File流

字节:FileInputStream,FileOutputStream
字符:FileReader,FileWriter

特点

这些都是需要先创建一个File对象,然后才能使用的,而读取或写入操作都是在创建的File对象上做的。
只有一个区别,前两个是字节流后两个是字符流,如果需要读取或写入中文,就需要使用后两个

Piped流

字节:PipedInputStream,PipedOutStream,
字符:PipedReader,PipedWriter
一般是多线程之间进行通信时使用的

特点

在使用这些流时,相比与File流,就不用先创建一个File对象,只需要跟普通的流一样创建即可,但是在管道(Piped)相互通信前,需要使用connect方法将两个管道连接起来

Piped流不需要使用文件作为数据的载体。

字节/字符数组流

字节:ByteArrayInputStream,ByteArrayOutputStream
字符:CharArrayReader,CharArrayWriter

这四个流内部都会有一个数组,使用时是先向数组中存入数据,然后在从数组中读取将数组的数据写入到其他地方。

特点

Byte开头的,就是只能读取或写入字节。ByteArrayInputStream在创建时需要向构造方法传递一个参数(字节数组)ByteArrayOutputStream则在使用时,需要调用writeTo(某个流)这两个流可以帮助我们先将数据存储到缓存区,然后读取写入更加快速,开辟的缓存区是用来存储字节数组的
CharArrayReader,CharArrayWriter和ByteArrayInputStream,ByteArrayOutputStream类似,只不过Char开头的是读写字符,而Byte是读写字节,用法都一样的。

2.处理流

处理流在使用的时候,是需要套上一个节点流,然后使用方法跟使用节点流差不多。

Buffered缓冲流

字节:BufferedInputStream,BufferedOutputStream
字符:BufferedReader,BufferedWriter

这四个流跟字节/字符流类似,它们内部也有一个数组作为缓存区存在。但是,字节/字符数组流是将数据都缓存到数组中,并不会去刷新这个数组,想读取或写入都是从这个数组出发的;而Buffered缓冲流,这会有一个不会二次改变的数组(可以在构造方法中传入size)一旦这个数组存满了,就会将数组的数据全部读出或写入到其他地方,然后刷新数组

字节/字符数组流建议一次性用完就关闭

特点

在套用了其他节点流之后,使用的方法和节点流的使用方式差不多,但是在Buffered缓冲流写入时还需要使用一个flush()方法,将缓存内部的数据全部写入并且刷新缓存

转化流

字符:InputStreamReader,OutputStreamWriter

特点

就是能够将字节流转换成字符流,在创建时,需要向构造方法传递一个字节流和字符集(默认为GBK)使用的方法跟字符流的使用方法差不多。

数据流

字节:DataInputStream,DataOutputStream

特点

数据流是能够将数据按照不同的类型写入读取也要按照相应的类型读取。类似于写入时,将数据装入一个个不同类型的盒子了,读取时要按照盒子的类型并且按照写入的顺序来读取。

打印流

字节:PrintStream
字符:PrintWriter

特点

这两个流并没有对应的输入流,使用这两个的方法跟其他处理流差不多。在套上一个节点流后,就可以使用print()或println()直接写入数据
有没有觉得这两个方法很熟悉,平时我们使用System.out.println()这个方法的内部其实也是使用PrintStream来实现的。

对象流

字节:ObjectInputStream,ObjectOutputStream

特点

这两个流可以做一件事,就是我们要写入或读取的数据序列化,序列化就是将对象转化成二进制数据。要使用这两个流,第一步也是要套上一个节点流第二步就是在要序列化的类上实现Serializable接口,剩下的使用方法就跟普通的节点流差不多了。

五、总结

这些流真的是多,总结一下使用场景吧。
写入或读取都是文本类型有中文,就用字符流
写入或读取其他杂七杂八的文件类型没有中文,就用字节流
要考虑线程通信,就用Piped流;要操作文件,就用File流;要将数据存在内存中,就用字节/字符数组流,没有性能和功能要求,用这三个就够了。
想要提高读写性能,就套个Buffered缓冲流;想要对规定数据格式,就套个数据流;想要使用方便或换行,就套个打印流;想要对对象序列化,就套个对象流

——————————————————————————————

你知道的越多,不知道的就越多。

如果本文章内容有问题,请直接评论或者私信我。如果觉得我写得还不错的话,点个赞也是对我的支持哦

未经允许,不得转载!

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 205,386评论 6 479
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,939评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,851评论 0 341
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,953评论 1 278
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,971评论 5 369
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,784评论 1 283
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,126评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,765评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,148评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,744评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,858评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,479评论 4 322
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,080评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,053评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,278评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,245评论 2 352
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,590评论 2 343

推荐阅读更多精彩内容