当不少人学到IO的时候都特别懵,这也难怪,毕竟关于IO有各类流,记都要记糊涂了。其实只要换一个思惟角度来看待IO流,仍是不难的,甚至是很是容易和方便的,至少日常的应用不难。更深层次、更底层或者更高级的咱暂且不谈,这篇文章只介绍最基本的运用,让新手能熟悉得将IO流用到本身的项目中(其实不讲高级的缘由是我不会(●′ω`●))java
贴上代码以前我们先捋一下IO的使用思路,为啥新手懵,由于流对象太多,开发时不知道用哪一个对象合适,只要理清思路便可知道该使用哪些对象数组
IO,分为Input和Output,输入和输出。将硬盘上的文件读取到内存里来,为输入;将内存中的数据存储到硬盘上的文件里去,为输出。(这里可以处理的数据不光只有硬盘,还有其余设备,好比键盘或者网络流,可是我们最常处理的就是硬盘上的文件,硬盘上的文件会操做以后,其余设备天然就会了)
不管输入仍是输出,流程线是彻底一致的,只是顺序不一样:
**输入:拿到文件中的数据 >>> 开始读取数据 >>> 将数据“转移”到内存中去 >>> 在内存中操做数据
输出:拿到内存中的数据 >>> 开始读取数据 >>> 将数据“转移”到文件中去 >>> 文件中保存了数据**缓存
因此,在使用IO流前,你先得确认第一个问题:明确数据源和目的地,即明确数据流的方向网络
明确数据流方向以后,我们就得确认第二个问题:我要处理的是什么数据。 我们处理的数据能够分为两大类:文本和非文本。 文本须要处理的数据为字符,非文本须要处理的数据为字节。性能
要处理非文本数据(字节)就用:
(输入)InputStream,(输出)OutputStream
要处理文本数据(字符)就用:
(输入)Reader,(输出)Writer优化
OK,这两个问题确认好后,基本上就知道要用哪一个对象了。以前也说了,数据不光只有硬盘上的文件,其实还有其余设备,可是为了方便你们理解我们就以硬盘上的文件来操做。 既然要操做文件,那就确定要用到File,流也要用处理File设备的流,即:
(输入)FileInputStream,(输出)FileOutputStream
(输入)FileReader,(输出)FileWriter编码
不论是什么流,其中的方法基本都是一致的,输入数据就用 read(),输出数据就用 write(),必定要记住这一点哦,为啥java这么流行,就是由于无论你操做的是啥数据、啥设备、啥流,处理方式都是同样的:code
注意哈,为了方便演示,代码中就没有写上 异常的捕捉和声明,正常使用的过程当中是须要进行异常处理的!对象
好了我们如今来实战,我要读取一个文本文件里的文字到个人内存中,该怎么操做?
文本文件,那就是FileReader或者FileWriter呗,读到内存里,那就是FileReader呗,看到没,该使用什么对象立马就肯定好了。内存
/*假设如今硬盘有一个文件为1.txt 内容为:哈哈哈哈哈哈*/ // 首先我们开始得建立一个FileReader对象 // 流建立的同时你确定也要指定操做哪一个东西嘛,因此 // 文件流的对象参数里天然就是放的File对象,或者文件路径,这里放的是文件路径 FileReader fr = new FileReader("src/1.txt"); // 还记得以前说的嘛,输入就用read()方法,因此这里咱就要调用read方法来开始读数据了 // 由于是文本文件,因此处理的是字符,read()方法则每次读取都是读的一个字符 // read()方法返回值是字符的字符编码,即int类型,因此声明一个int变量来接受 int len; // 既然要读文本,天然就建立一个字符串来接受文件中的文本 String str = ""; // 开始循环读取数据,若是read()的返回值是-1,就表明没有内容可读取了,因此循环的判断条件即不为-1 while((len = fr.read()) != -1) { // 每读一次,就将读取到的字符编码转换成字符存到咱们刚才的字符串里 str += (char)len; } // 流都操做完了,就不要留着占资源了嘛,记得每次用完关掉流哦 fr.close(); // 循环完毕了,就表明全部文本已经读取完毕了,打印便可 System.out.println(str);
刚才的代码咋一看很复杂,其实内容很是简单,能够回顾一下以前说的流程线:
拿到数据 >>> 读取数据 >>> 操做数据,即
建立流对象 >>> 用read()方法读取数据 >>> 打印字符串
输入流过了一遍,咱再过一下输出流。我要将一个字符串输出到一个文本文件里,该怎么操做?
文本文件,那就是FileReader或者FileWriter呗,输出到文件里,那就是FileWriter呗:
// 老套路,建立一个流对象,流对象参数里放上文件路径 FileWriter fw = new FileWriter("src/1.txt"); // 记得以前说的嘛,输出用write()方法 fw.write("嘿嘿嘿嘿"); /*为啥这里不用循环呢,由于直接将要输出的全部数据都一次性给流了,read()是一个字符一个字符读,天然要用循环*/ // 输出完了,记得关闭流 fw.close(); /*这时候文件里的文本内容就变成了“嘿嘿嘿嘿”,要注意哦,这里输出是会覆盖原文件的文本的*/
看到没,三句话搞定,彻底对应了流程线,是否是简单的一批?
拿到数据 >>> 输出数据 >>> 保存数据,即
建立流对象 >>> 用write()方法输出数据 >>> 文件内容已被覆盖
注意哈,上面我演示的是很是简单的输入和输出方法,运行性能也并非特别好,可是先掌握这个,我们慢慢来加难度。
刚才我们处理的是文本文件,那么如何处理非文本文件呢? 非文本文件,我们就从文件的复制来开始入手。复制这个功能,确定要将文件A的数据,转移到文件B(这个文件B是要本身建立),这表明既要输入又要输出,因此(输入)FileInputStream,(输出)FileOutputStream两个对象都要建立。
// 先建立一个文件读取流,流对象参数里放上须要复制的文件的路径 FileInputStream fis = new FileInputStream("src/1.gif"); // 再建立一个文件输出流,流对象参数里放上目标文件路径 // 文件输出的时候,若是没有该文件,则会自动建立文件(注意,读取的时候可不行,输入的源文件必须存在,不然报错) FileOutputStream fos = new FileOutputStream("src/2.gif"); // 以前说过,字符流处理的数据是字符,字节流是字节,以前字符流的read()方法读取的是一个字符,那字节流的read()方法天然就是字节了 // 字节流read()方法返回的字节数据,即int类型,因此建立一个变量来接收 int len; // 开始循环读取数据,操做方式和流程和字符类是同样的 while((len = fis.read()) != -1) { // 每读取到一个字节,就写到目标文件中去 fos.write(len); } // 关闭流 fis.close(); fos.close();
就算建立了两个流对象,可是操做流程仍是同样地简单:
拿到数据 >>> 输出数据 >>> 保存数据,即
建立流对象 >>> 读数据 >>> 写数据
在这里基本上就能印证以前的思路了:无论IO你要处理啥,怎样处理,本质的操做都是同样的!就算业务复杂的一批,无非就是 先读数据,再写(操做)数据
必定要记住这个基本的思路,思路解决后,我们再来进行优化和进步!
上面复制文件的代码,虽然功能是能够完成,可是性能太慢太慢了!它是一个字节字节读取而后写入的,你们都知道,内存的读写速度要比硬盘的速度快得多!上面代码操做呢,彻底就是在硬盘里进行读写,就算复制一个1MB的文件,只怕也要十几秒。因此上面的方式,只是为了让你们了解基本的操做,可是在实际运用中是不会这么用的。如今就介绍一下比较经常使用的方法来优化,下面代码要仔细看一下注释:
// 这个确定是不变的,建立输入和输出流 FileInputStream fis = new FileInputStream("src/1.gif"); FileOutputStream fos = new FileOutputStream("src/2.gif"); // read()的返回值一直是int,因此得建立一个变量来接受,这个也不会变 int len; // 这里是重点,为啥要建立一个字节数组呢,由于read()方法里面其实能够放参数,就能够放字节数组 // read()参数里放了字节数组后,就表明着将读取的数据所有先存放到数组里 // 说白了就是建立数组用来存读取的数据,也就是常常说的缓存,数组的初始化大小有多大,就表明能存多少数据 byte[] buf = new byte[1024]; // 开始循环读取数据到缓存数组里(这里就和以前有一点不一样,多了一个参数) // 这里返回值是仍是int类型,以前返回的是一个字节数据,加了参数后,返回的就是输入的数据长度 while((len = fis.read(buf)) != -1) { // 这里也是重点!write也能够放参数,以前放的是字节数据,固然也能够放字节数组 // 参数第一个表明要写的数据数组,第二个和第三个参数表明要写的长度,从0开始写,写到结尾 fos.write(buf,0,len); } // 关闭流 fis.close(); fos.close();
这种代码是比较经常使用的,运行速度比以前的代码快了不少不少,最重要的就是加入了一个缓存数组。以前代码是一个字节一个字节往硬盘里写,如今代码就是,先将内存里的缓存存满,而后再将缓存里的数据一次性给存入到硬盘里,说白了,就是读写硬盘的次数变少了,读写内存的次数变多了,天然而然速度就快了。
你们不要懵,一开始我就是在这里挺懵的,为啥好端端加个数组我开始彻底弄不明白,在这里我举个例子你们就会清楚为何了:
就好像在超市里购物,若是你看中同样东西,就立马得把那个东西拿到收银台先放着,而后再继续购物,又看中一个东西,又得跑到收银台放着,循环往复,最后再结帐,这样是否是慢的一批。这个收银台就至关于硬盘,超市里的物品就至关于内存中的数据。而缓存是啥呢,就是购物车!有了购物车以后,你就能在超市里购物时,看中一个东西了,先放到购物车里而后再继续选购,直到你选购完毕再推着购物车里去收银台结帐,这样效率就高多了!
这就是为何要用到缓存机制了!在代码里,那个字节数组buf就至关因而购物车,先存够必定的数据,再跑到“硬盘”那里去“结帐”。
谈到Java,就确定要谈到面向对象,那么问题就来了,对象这种东西,又不是文本我该怎样去保存对象数据到文件里呢? Java固然贴心的为你提供了解决的方案:那就是对象的序列化。在这里,我只讲怎样用IO实现序列化,至于序列化的一些细节等我之后单独写一篇文章再说。
首先,我们弄清楚一下序列化的定义,我看到有些同窗在网上查询序列化相关的知识,越查越懵。其实懵是由于 在没有掌握基本的使用方法,却去了解使用原理,这样是绝对会懵的。
序列化,说白了就是将对象保存到本地文件上,反序列化,说白了就是将本地文件上的对象数据,读取出来到内存里:
序列化: 对象数据 >>> 本地文件
反序列化:本地文件 >>> 对象数据
是否是和IO的操做没啥区别,事实也确实如此,就是没啥本质的区别,只是由于要处理的是对象数据,全部就要用到序列化相关的流。在介绍序列化流前呢,我们仍是按照以前的思路来走一遍:
如今我要将对象数据存到本地文件里,对象数据是文本数据吗? 那确定不是,因此就要用FileInputStream或者FileOutputStream呗。这里我们要的是输出,那就是FileOutputStream嘛
// 老套路,建立一个输出流,设置好文件路径名(序列化不必定要这个文件后缀,其余的也能够,没有特别规定) FileOutputStream fos = new FileOutputStream("src/obj.data");
假设我们要存(序列化)的是数组,咋存呢,直接用write()吗?那确定不行,字节流write()里只能放字节数组或者int类型的字节数据,放不了其余的玩意。这里只要额外加一个东西就行了,就是对象序列化流
// 须要保存(序列化)的数据 int[] array = {1,2,3}; // 老套路,建立一个输出流,设置好文件路径名 FileOutputStream fos = new FileOutputStream("src/obj.data"); /*注意,这里是重点了*/ // 建立一个对象输出流,构造方法里放的是一个输出流对象 // 这就表明着,我要处理的是对象,可是呢,我本身只能处理对象还处理不了文件 // 因此就得链接一个能处理文件的文件输出流 ObjectOutputStream oos = new ObjectOutputStream(fos); // 调用序列化流对象的方法,参数里面就是你要序列化的对象数据,一句话序列化完毕了! oos.writeObject(array); // 关闭流 oos.close(); fos.close();
是否是处理对象的操做流程也是同样的?甚至比操做普通的文件还简单吧!
拿到数据 >>> 输出数据 ,即
建立序列化流对象 >>> 写数据
演示了序列化,那反序列化呢,很简单嘛,将流程线反过来就行了:
得到文件 >>> 拿到数据 >>> 读取数据,即
建立反序列化流对象 >>> 读数据
// 老套路,要读数据嘛,建立一个文件输入流,设置好文件路径名 FileInputStream fis = new FileInputStream("src/obj.data"); // 建立一个反序列化流,就是把Output改为Input就能够了,记得链接输出流 ObjectInputStream oos = new ObjectInputStream(fis); // 将对象数据读回来,注意哦,反序列化拿到的对象都是Object对象,因此要强制转换类型 int[] arrays = (int[])oos.readObject(); // 正常使用数据 System.out.println(arrays[1]);
是否是也特别简单?
我们再次回顾一下思路:
源:是纯文本:Reader
目的:是纯文本 Writer
是否是以为很简单了?为啥不少人学到这就懵了呢,由于 在没有掌握基本的使用方法,却去了解使用原理,其实你只要先掌握基本的使用方法,而后慢慢了解就行了!