流按操作类型分为两种,字节流,字符流,按流向分为输入流,输出流,输入流的抽象父类InputStream,输出流抽象父类OutputStream,字符流的抽象父类是Reader和Writer
一般用字节流copy文件可以用小数组的方式分批拷进去,实例代码如下:
FileInputStream fis = new FileInputStream("111.png"); FileOutputStream fos = new FileOutputStream("222.png"); byte[] a = new byte[1024 * 8]; int length; // 返回值length是读出的字节个数, fis.read(a)是读取最多a.length大小的字节数据,存入a里面 while ((length = fis.read(a)) != -1) { // 把数据从a中写出,数组偏移量为0,即从a[0]开始,写出length字节大小的数据 fos.write(a, 0, length); } fis.close(); fos.close();
还有一种方式,用输入输出缓冲区包装类,缓冲区类一次从文件中取出8192个字节,存储在缓冲区数组里,然后一个一个存入输出缓冲区类的缓冲区数组里,满了之后一次性存到硬盘文件中大大减少IO时间:
BufferedInputStream bis = new BufferedInputStream(new FileInputStream("部署.png")); BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("222.png")); int a; while ((a = bis.read()) != -1) { bos.write(a); } bis.close(); bos.close();
flush()方法可以刷新缓冲区,把缓冲区的数据写入文件中.而close()方法在关闭输出之前,也会把缓冲区的数据写入文件中,因为缓冲区的数据是存满8192字节才写入文件中,如果最后还剩下不到8192字节的数据在缓冲区中,不刷新缓冲区的话,里面的数据是不会写入文件中去的,会造成数据丢失,所以输出流一定要关闭.
如果不想自己调用close()方法的话,可以把可关闭的流的声明和赋值放到try()代码里面(小括号里面叫ARM块):
try (BufferedInputStream bis = new BufferedInputStream(new FileInputStream("部署.png")); BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("222.png"))) { int a; while ((a = bis.read()) != -1) { bos.write(a); } }
---------------------------------------------------------------------------------------------
字符流,读的时候把字节转换为字符,写的时候把字符转换为字节写入,可以通过项目默认的编码一次读取一个字符(如果是中文,就一次读取两个字节)
字符流 基础父类Reader-InputStreamReader-FileReader,一般用FileReader,Writer类也一样.
字符流Reader和Writer类默认都带2k的缓冲区,BufferedReader和BufferedWriter是带缓冲区(16k)的字符流类,直接父类Reader和Writer,BufferedReader的readLine()方法可以读一行(没有数据返回null),BufferedWriter的newLine()方法可以写出一个换行符(根据系统写出不同的换行符).
LineNumberReader是BufferedReader的子类,多了可以记住当前行号和设置当前行号的功能.
InputStreamReader可以指定用什么码表读取字符流,创建对象需要包装一个InputStream对象,OutputStreamWriter也一样,一般搭配BufferedReader或者BufferedWriter来高效的读取与写入,如:
new BufferedReader(new InputStreamReader(new FileInputStream("xxx.txt"),"utf-8"))
new String("bytes", "UTF-8")是按UTF-8码表把字节解码成字符,如果解码时有字节找不到对应码表中的字符,那么会以?代替. (解码:字节数据 --> 字符串)
getBytes("UTF-8")的意思是将(字符)数据按照UTF-8码表来编码成字节数组(此时字节数组的编码格式为UTF-8,如果没有指定那么会采用系统默认编码格式). (编码:字符串 --> 字节数据)
关于解码和编码: https://blog.csdn.net/mdreamlove/article/details/78468864
平时推荐使用 InputStreamReader 和 OutputStreamWriter 因为这两个可以显示指定解码编码字符集.
----------------------------------------------------------------------------------------------
其他流:
SequenceInputStream : 序列化输入流,可以吧多个InputStream放到里面,依次接收流数据.它从一个有序的输入流集合开始,从第一个读取到文件的结尾,然后从第二个文件读取,依此类推,直到最后一个输入流达到文件的结尾。 构造可以传入一个Enumeration对象
ByteArrayOutputStream : 内存中会创建一个缓冲区.输出的数据会写入这个缓冲区.缓冲区自动增长,可以用toByteArray和toString获取里面的数据.这个流不会造成IO异常,而且close方法没有任何效果
RandomAccessFile: 读取和写入随机访问文件,有一种游标,或索引到隐含的数组,称为文件指针 ; 输入操作读取从文件指针开始的字节,并使文件指针超过读取的字节。 如果在读/写模式下创建随机访问文件,则输出操作也可用; 输出操作从文件指针开始写入字节,并将文件指针提前到写入的字节。 (适合用来多线程大文件拷贝或下载)
DataInputStream DataOutputStream: 数据输入输出流,可以直接写出或读入比如int long类型的数据.
ObjectInputStream : 对象输入流,.使用readObject可以读取一个被序列化存储的对象.
ObjectOutputStream : 对象输出流,可以把一个实现了Serializable接口的对象序列化存储到文件中或者是进行传输.使用
writeObject方法.
提示:如果有个对象需要序列化存储,那么最好加上一个版本id: private static final long serialVersionUID = 1L;
如果这个类修改过了,那么反序列化的时候会报异常,可以根据异常的版本号来判断是哪次修改出了问题.
PrintStream : 打印字节流,可以将对象的toString方法的结果输出,System.out返回的就是一个PrintStream
print类的方法会先用toString转换为字符串并打印,而write方法会直接根据码表查找字符打印.
PrintWriter : 打印字符流,具体使用方法与一般输出字符流差不多.
标准输入输出流: System.in System.out 默认标准输入流是键盘,标准输出流为控制台. 可以用System.setIn setOut的方法改变标准流
Properties 类是Hashtable的子类,可以用load方法读取一个配置文件,如新建一个a.properties文件里面写入name=xxx,那么可以用
properties.load(new FileInputStream("a.properties")); 读取文件,然后用properties.getProperty("name", "yyy") 获取值.第二个参数是默认值,如果没有这个属性那么就会使用默认值.同样也可以用properties.setProperty("name2", "22222222");添加新属性,用
properties.store(new FileOutputStream("a.properties"), "描述");保存配置文件