本文共 5106 字,大约阅读时间需要 17 分钟。
一、IO基本概念
1、什么是java中的流
它是IO(输入输出)包中相关api的处理对象,特指数据流。 2、流的分类 从大的方面来看有两种分类方式: 第一,依据数据流的流动方向来划分,分为输入流和输出流。 输入流的方向:数据从外部向内存(jvm的内存,程序的执行内存)中流动, 一般包括读文件,下载文件,键盘输入。 输出流的方向:数据从内存向外部流动,上传文件,保存文件等。 第二、依据程序所处理的数据的单位来划分,分为字节流和字符流。 字节流,计算机存储数据的最小单位就是字节,所有的文件都以字节为单位来存储,网络通讯也是以字节为单位来进行传输。一个字节就是八位二进制数,表示0-255。对应于基本类型的byte。 字符流,它是字节的倍数,一个汉字就是一个字符,比如一个汉字在gbk的编码集中用两个字节表示,在utf-8编码集中用三个字节表示,字符就对应于基本类型中的char。 把以上的两种分类再进行两两结合就可以分成四种流类,字节输入流,字节输出流,字符输入流,字符输出流。 3、java.io包中流类的层次结构 首先,java提供了四个抽象类,分别对应以下四种流类. 字节输入流 InputStream,是字节输入流的所有类的超类 字节输出流 OutputStream 是字节输出流的所有类的超类 字符输入流 Reader 是字符输入流的所有类的超类 字符输出流 Witer 是字符输出流的所有类的超类 在设计中,以上四个抽象类不会直接去使用,都是使用它们的子类. 流类中方法的识别规律: 输入流,读数据的方法都是read()方法,有多种重载形式,具有不同的参数或参数组合,依据参数的不同可分为一次读一份数据,或一次读多份数据,或一次读多份数据中的一部分。 输出流,写数据的方法都是write() (个别用print()),有多种重载形式,具有不同的参数和参数组合,可一次写一份数据,一次写多份数据,一次写多份中的一部分数据。 输出流需要刷新。 无论什么流,用完后都要关闭,关闭就是释放资源,不释放就不安全。二、文件字节流
存放在磁盘中的所有文件都是以字节为单位存储的,无论是图片,文本文件,音视频,可执行程序都是以字节为单位的。所有的文件都可以用字节流来进行输入输出操作。
1、文件字节输入流 FileInputStream ,用来读取磁盘中的文件数据到内存中。FileInputStream fis = new FileInputStream("D:\\image\\tv1.png");//完整地读取文件,注意套路,进一步的实现文件的复制//字节流,所以要定义一个字节数组,一次读取多个字节,它就象水勺 一次一勺byte[] b = new byte[1024];int len = -1; //读数据时,read方法会返回当前读取的总字节数,该值要保存到变量中//文件的大小一般都会比我们定义的字节数组要大,会读多次,要用循环的方式来读//len变量的值如果为-1,表示本次的读取已到文件末尾,没有读到任何数据,就不用再读了int count = 0;while((len = fis.read(b))!=-1){ count++; System.out.println("第"+count+"次读"); for (int i = 0; i < b.length; i++) { System.out.print(b[i]+" "); } System.out.println("\r\n");}fis.close();
2、文件字节输出流,用来把内存中的数据写到磁盘文件中。
FileOutputStream 实现文件的复制,以下代码可以实现对任何文件的复制FileInputStream fis = new FileInputStream("D:\\image\\tv1.png");FileOutputStream fos = new FileOutputStream("D:\\image\\other.png");//完整地读取文件,注意套路,进一步的实现文件的复制//字节流,所以要定义一个字节数组,一次读取多个字节,它就象水勺 一次一勺byte[] b = new byte[1024];int len = -1; //读数据时,read方法会返回当前读取的总字节数,该值要保存到变量中//文件的大小一般都会比我们定义的字节数组要大,会读多次,要用循环的方式来读//len变量的值如果为-1,表示本次的读取已到文件末尾,没有读到任何数据,就不用再读了int count = 0;while((len = fis.read(b))!=-1){ count++; if(count != 100){ fos.write(b); } System.out.println("第"+count+"次读"); //利用输出流对象把数据写到文件中去}fis.close();fos.flush();fos.close();
三、文件字符流
虽然字节流可以读写任何文件,但是文本文件用字节流来读写效率并不高,因为文本文件是以字符作为单位的,用字节效率很低。所以读写文件文件尽量使用字符流。
1、文件字符输入流
FileReader,读取的最小的单位是字符。2、文件字符输出流
FileWriter四、Properties 类
该类在工具包中,不是io中的类,这表示一个键值对的集合,它具有特殊的用途,在以后的开发中,一般都用它的对象来封装配置文件中的属性文件的属性值。
Properties类表示一组持久的属性以上的这些文件相关的流类是基础流,直接继承于流的抽象类。
五、包装流
在java的io体系中,很多api的设计模式都采用了装饰器设计模式;这种设计模式主要特点是一个类中功能的实现要利用另个一个类的功能实现。目的在于增强原有类的功能或改变同种功能的实现方式。
包装流都采用了装饰器模式,这些流的内部利用了其它基础流的功能,因此在创建包装流的时候一定要给它传入一个其它流的对象。 1、缓冲流 缓冲流共有四个,分别是字节输入和输出缓冲流,字符输入和输出缓冲流。字节流缓冲流传入的就是字节的基础流,字符传入的是字符基础流。 这种流带有缓冲区(内存中的一块数据区域),缓冲区可以在数据的来源和目的地间起到一个缓冲的作用,在读取或写入数据时,读写操作针对的是缓冲区而不是直接面对磁盘、网络等外部设备。因此利用缓冲流可以大大地提高io操作的速度。BufferedInputStream bis = new BufferedInputStream(fis);BufferedOutputStream bos = new BufferedOutputStream(fos);
使用缓冲流比基础流的速度要增加将近一百倍,原因在于缓冲流在创建时内部默认生成了一个大小为8k的缓冲区,在读数据时,先把8k的数据从磁盘一次性读到缓冲区,然后再从缓冲区中取数(内存操作)。
private static int DEFAULT_BUFFER_SIZE = 8192;public synchronized int read() throws IOException { if (pos >= count) { fill();//把外部的数据一次性读取到内存中 if (pos >= count) return -1; } return getBufIfOpen()[pos++] & 0xff;}
该流并未使用什么新技术,所有的读写还是利用基础流。
2、转换流
它可以把字节流转换为字符流,包括输入和输出两个方向。 一般情况下,如果采用的是字节流来读写数据,可是这些数据又都是文本数据,比如接收网络传来的数据(只能字节),如微信或QQ的一句话,或上传下载的文本文件。 使用时也要给它一份基础流。InputStreamReader isr = new InputStreamReader( new FileInputStream("g:/mypage/table.html"),"utf-8");//OutputStreamWriter
3、对象序列化
把存在于内存中的对象数据,以二进制流的方式写到外部的文件中去,或者通过网络向对方进行传送。因此保存在文件中的对象的数据就是一份二进制的数据(字节为单位),如果在网络上传送对象,网络只能接收二进制的数据(字节),所以必须要进行序列化。 4、对象反序列化 把文件中的对象的数据或通过网络接收到的对象的数据写入到内存中。 因此序列化与反序列化是同时存在的,互相依存。 对象序列化要使用ObjectOutputStream,反序列化要使用ObjectInputStream。它们称为对象输入输出流,也是包装流。 被序列化的对象必须实现序列化接口,该接口没有任何抽象方法,是空接口,主要作用就是标识。 如果一个对象已被序列化,在反序列化之前对象的字节码不能发生改变,否则反序列化不成功。//对象序列化Person person = new Person("aaaa", 23);ObjectOutputStream oos = new ObjectOutputStream( new FileOutputStream("d:/person.bin"));//针对对象执行了持久化操作,永久的保存下来oos.writeObject(person);oos.flush();oos.close();//对象反序列化ObjectInputStream ois = new ObjectInputStream( new FileInputStream("d:/person.bin"));//把文件中的对象数据读到内存中Person person = (Person)ois.readObject();System.out.println(person);
5、打印流
打印流的主要作用可以按照数据的类型实现不同格式的输出。 PrintStream 字节打印流,它的典型应用就是System.out System类的源码public final static PrintStream out = null;public void print(long l) { write(String.valueOf(l));}
实现格式化打印的方法仍然调用的是底层的write()方法,在输出前把当前类型转换为字符串再输出。
主要例子就是用得到最多的System.out.println(); PrintWriter 字符打印流,它的典型应用是javaWeb中,用来向客户端输出。以上所讲的流都在io包中,这些流都是java中使用的传统流。
在后面的版本中,为了更好的实现数据的传输和读写,出现了nio,是新的io流。 在nio中的主要改变: 1、添加了专门的缓冲区类,用来作为数据传输的缓冲区使用,也提供了缓冲区的标记,同时提供了对缓冲区数据的清理,翻转和倒带操作。 2、添加了专门的通道对象来对数据进行io操作。3、传统流中提供了一些转换方法,比如
public FileChannel getChannel(),要以在传统流的基础上得到通道对象,有了通道对象就可以基于nio的buffer来进行更高效率的流操作。现代的通讯对速度的要求,对并发的要求都很高,原来传统的io不适应新的要求,所以出现了nio,nio出现过两次,第一次叫nio,第二次nio2,比如现代流行的netty(实现通讯功能的框架软件就是基于nio打造的),很多有名框架软件都基于netty来打造。
用nio1写程序,它要求采用多线程的方式来处理每个数据请求,会使用到线程池,速度比较快,但是代码写起来较复杂。它是阻塞的异步的。
用nio2写程序,它提供用单线程,用单线程来接收请求,利用事件机制去处理各种请求,它是非阻塞的,而且是异步的,它的实现效率比第一种更快。一般程序员看不懂代码。通俗的说法,称为React(火箭发射)模式。转载地址:https://blog.csdn.net/qq_45442483/article/details/109726275 如侵犯您的版权,请留言回复原文章的地址,我们会给您删除此文章,给您带来不便请您谅解!