Java io 和OKio的概念详解

io是什么

io是输入输出流,他的作用就是对外部进行数据交互使用的,内部和外部分别表示的是内存以及内存以外的,外部包括手机文件,电脑文件和网络, 服务器等都称为外部 ,外部统称为文件和网络

什么叫输入输出

输入就是从文件或者网络上的数据读取到内存里面,输出就是把内存的数据写入到文件或者发送到网络上。
java io的api作用就是为了和外部进行读写操作
先上一个简单例子

public class Main {
    public static void main(String[] args[]){
        try {
            OutputStream outputStream = new FileOutputStream("./app/text.txt");
            outputStream.write('a');
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

文件输出流的方式在文件中写入a,我们运行这段代码,就能够在app文件目录下找到text文件,打开文件能够看到文件内容 "a"


image.png

new FileOutputStream 通过文件存放的路径,就能够在文件路径下新建txt文件,通过outputStream.write "a"这个字符就写到文件中

再上一个输入流的例子

private static void io2(){
        try {
            InputStream inputStream = new FileInputStream("./app/text.txt");
            System.out.println((char) inputStream.read());
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }

    }

这样一个简单的输入流就算写好了,简单讲解下,刚才我们定义了输出流也就是新建一个text的文件并在文件上写入一个a字符,通过输入流把文件存放到内存中,这里我们通过打印的方式把文件的内容打印到控制台 截图如下:


image.png

这样一个简单的文件输入输出流就算完成了
但我们在实际的开发中面对的问题要比这个复杂的多,并且在实际开发中输入输出流读写完数据后需要关闭掉输入输出流。
java7有一个新特性可以不用关闭流,也能实现关闭流的效果也就是在try块中写相关代码,代码如下:

@RequiresApi(api = Build.VERSION_CODES.KITKAT)
    private static void io1(){
        try (OutputStream outputStream = new FileOutputStream("./app/text.txt")){
            outputStream.write('a');
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

字符 Reader 和Writer
这两者可以配合输入输出流一起使用,Reader和Writer又是什么呢,他们可以自动的把InputStream和OutputStream中的字节数据转换成字符数据
一般情况下InputStream和Reader,OutputStream和Writer结合使用
我们还可以在上面的基础上再套一层BufferReader或者BufferWriter,这又是什么呢?这个就是相当于给你的字符加一个buffer(缓冲),这样能够让我们读取或者写入的内容更加的稳定高效,主要的功能就是提高读写性能。

try(InputStream inputStream = new FileInputStream("./app/text.txt");
        Reader reader = new InputStreamReader(inputStream);
        BufferedReader bufferedReader = new BufferedReader(reader)){
            System.out.println(bufferedReader.readLine());
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }

这里我在text文件中外加了一些字符组合成子长串的字符再次运行,这样就可以不用使用单个的字符读取就能很方便的读取数据了


image.png

outputStream.flush()

这又是什么呢,flush使用的主要场景就是在输出流中用的比较常见,作用就是把没有到文件管道的数据flush到文件中,也就不会因为管道中一直存在一个比较难处理的文件数据而一直运行,这样就避免程序出问题的情况,从而提高性能。

文件复制原理

文件复制就集合了输入输出流相关的知识,就是通过输入流的数据通过输出流把数据copy到另外一个文件的过程,这里要注意的就是这里是使用的字节的方式输入输出的,如果文件比较大的话可以给他外加一个buffer,还有一点就是判断readCount不为-1,这是什么呢,我这里是使用了一个循环,其实任何的读取或者写入文件都可以使用这种方式来操作,这也是比较通用的。如果不等于-1也就是管道的数据还有那么就继续读写数据直到为-1管道数据没有,也就是读写操作完成。

private static void io4(){
        try(InputStream inputStream = new FileInputStream("./app/text.txt");
        OutputStream outputStream = new FileOutputStream("./app/text_copy.txt")){
            byte[] bytes = new byte[1024];
            int readCount;
            while ((readCount = inputStream.read(bytes))!=-1){
                outputStream.write(bytes,0,readCount);
            }
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

执行结果:


image.png

nio的简单使用

nio使用的是RandomAccessFile来创建文件对象,这里需要注意的是他有两个关键参数,这个也比io流要简洁一些。根据上面的讲解,我们知道io流有输入和输出流。而nio的输入输出流通过参数就可以配置。第一个参数是文件路径的配置,第二个参数就是配置你的这个文件是输入的还是输出的"r"读,"w"写这个其实也很好理解。
ByteBuffer的原理
ByteBuffer的读写操作,中学我们了解了游标卡尺,ByteBuffer的原理就有点类似于游标卡尺,
position和capacitylimit,他们分别表示的是读或写的位置以及我们自己设置的容器大小(这个参数是在new 构造器的一个参数),通过游标position读或写一个字符,游标position向右移动一个字符长度的位置,读或写的思路如下:


image.png

注意:为了更好的理解NIO中的Bytebuffer,可以把ByteBuffer看着是内存,如果相对于ByteBuffer自身而言是读的操作,反之是写的操作。

NIO在网络中实现通信

 private static void nio7(){
        try {
            ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
            serverSocketChannel.bind(new InetSocketAddress(8080));
            SocketChannel socketChannel = serverSocketChannel.accept();
            ByteBuffer byteBuffer = ByteBuffer.allocate(1024);
            while (socketChannel.read(byteBuffer)!=-1){
                byteBuffer.flip();
                socketChannel.write(byteBuffer);
                byteBuffer.clear();
            }
        } catch (IOException e) {
            e.printStackTrace();
        }

    }

注意:大部分的网络io操作都是阻塞的方式进行网络通信的,阻塞处serverSocketChannel.accept()
什么意思,也就是如果没有网络通信这段代码是阻塞的,当进行网络通信这段代码就开始正常工作,实现正常网络通信操作。
有些读者会问了,为什么要用阻塞的不用阻塞可以吗?NIO中也有这样的api,可以通过socketChannel.configureBlocking(false);
但是我不建议大家使用这个api,为什么。如果没有阻塞,那么在没有通信的情况下也能正常执行代码,就会出现我们拿不到socketChannel,导致阻塞后面的代码因为socketChannel为null而使我们的程序crash ,异常也就是空指针异常,大家可以自行测试下

Okio

由于okio配置不是androidstudio自带的所以我们这里需要添加依赖
okio依赖链接
okio也是通过输入输出流的方式对文件进行io操作,只不过okio的输入输出名称不一样,我们可以对比理解下,通过上面的讲解,io的输入使用的InputStream 可以对比okio的source ,io的输出使用的是OutputStream可以对比与okio的sink,这样我们就可以更快速的了解到okio了,okio也是支持buffer功能的。
还是上面的例子,在控制台通过okio读取text的内容
一种方式是通过实例 buffer也就是通过new Buffer() ,然后通过source.read(buffer) 这样我们就可以把数据缓冲到buffer中了
当然okio提供了带有buffer的BufferedSource,完成对文件的读操作

private static void okio9(){
        try(BufferedSource source = Okio.buffer(Okio.source(new File("./app/text.txt")))){
            System.out.println(source.readUtf8());
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

针对buffer,值得注意的是在buffer中写入数据使用的是输出而不是使用的输入buffer.outputStream()

传统io和okio进行交互

private static void okio10(){
        Buffer buffer = new Buffer();
        try(BufferedWriter bufferedWriter = new BufferedWriter(new OutputStreamWriter(buffer.outputStream()));) {
            bufferedWriter.write("hahahhaha");
            bufferedWriter.flush();

            System.out.println("read:"+buffer.readUtf8());
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

通过一个简单的例子来了解下io和okio的交互,如上,在我们实际的开发中也会遇到这样的情况,就是两者结合起来用有可能会更好些
上面我们也讲到从buffer缓冲中写入数据使用的是outputstream输出的方式,从这个例子也能够感受到 然后通过buffer.readUtf8()把数据读取出来,这里也可以把buffer理解为外部对象
这里我把结果打印出来,满足下我小小小的虚荣心,这也能让我们感受到对原理的理解通过实践来验证这些理论知识其实也能让我们更好的理解这些理论知识。

image.png

讲到这里我们就把io和okio相关的知识点讲完了,如果有疑问的地方或者比我理解的更深入或好的可以在评论区留言。如有写错误地地方欢迎指正。
代码传送门

如果你觉得写的还行,你的鼓励就是对我的最大的写作动力 ,欢迎点赞欢迎star。写的不对的地方还请指正 👌

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