`

Java Io NIo (一) IO基础

阅读更多

一   I/O缓冲区

 

 

 用户空间:常规进程所在区域,JVM就是常规进程,该区域执行的代码不能直接访问硬件

                     设备

 

 内核空间:操作系统所在区域。内核代码它能与设备控制器通讯,控制着用户区域进程的运

                     行状态,等等。最重要的是,所有的I/O 都直接或间接通过内核空间

 

 数据交互:当用户(java)进程进行I/O操作的时候,它会执行一个系统调用将控制权移交给

                     内核,内核代码负责找到请求的数据,并将数据传送到用户空间内的指定缓冲区

 

 内核空间缓冲区:内核代码读写数据要通过磁盘的I/O操作,由于磁盘I/O操作的速度比直接访

                                 问内存慢了好几个数量级,所以内核代码会对数据进行高速缓存或预读取

                                 到内核空间的缓冲区(减少磁盘I/O,提高性能)

 

 用户空间缓冲区:同上,java I/O进程通过系统调用读写数据的速度,要比直接访问虚拟机内

                                 存慢好几个数量级,所以可以执行一次系统调用时,预读取大量数据,缓

                                 存在虚拟机内存中。

 

 

 

二  输入流InputStream、输出流OutputStream

  

      a) FileInputStream类实现了InputStream的读取单个字节read()、读取字节数组

             read(byte b[], int off, int len)的方法

             FileOutputStream类实现了OutputStream的写入单个字节write(int b)、写入字节数组

             write(byte b[], int off, int len)的方法(后面对Out介绍略过,参考In)            

		/**
		 *  文件输出流构造器
		 *  name 路径名
		 *  append 参数为true,数据将被添加到文件末尾,
		 *  而具有相同名字的已有文件不会被删除,否则这个方法删除所有具有相同名字的文件
		 */
	    public FileOutputStream(String name, boolean append)
	            throws FileNotFoundException
	        {
	            this(name != null ? new File(name) : null, append);
	        }

 

      b) 因为InputStream的实现类,只能读取字节,而且每次read都会执行一次系统调用,所

             以对其实现类进行扩展是必需的,过滤器FilterInputStream继承自InputStream,是一些

             扩展类(DataInputStream、BufferedInputStream)的基类。            

		public class FilterInputStream extends InputStream {
			protected volatile InputStream in;
			/**
			 * 构造方法受保护的,只能通过子类调用(初始化)
			 * 参数是InputStream的实现类,例:FileInputStream
			 */
		    protected FilterInputStream(InputStream in) {
		        this.in = in;
		    }

  

      c)   read()方法每次都会发起一次系统调用,而系统调用的代价是非常高的,所以为了

               减少系统调用的次数,就需要通过装饰器BufferedInputStream一次read()大量字节缓

               存在字节数组中。这里的缓存并不是为了减少磁盘IO操作次数,因为这个操作系统

               已经帮我们做了

		// 默认缓存8192字节=8kb,可以通过参数进行设置
		InputStream in = new BufferedInputStream(new FileInputStream("路径"), 8192);
		// 每次write()的字节,都会缓存到长度8912的字节数组中,直到写满才进行系统调用一次性写入
		OutputStream out = new BufferedOutputStream(new FileOutputStream("路径"), 8192);
		// 同理BufferedReader、BufferedWriter也是一样

 

       d)  数据在读取和写入时都是字节状态,适配器DataInputStream 实现了DataInput接口

                可以将读取的字节,在后台转换成java的基本类型然后进行读取,

                 例:readInt每次读取4个字节

	/**
     * DataInputStream类方法
	 * 转换成int类型,int4字节组成,一个字节8位
	 * 高位在前,进行数值还原
	 */
    public final int readInt() throws IOException {
        int ch1 = in.read();
        int ch2 = in.read();
        int ch3 = in.read();
        int ch4 = in.read();
        if ((ch1 | ch2 | ch3 | ch4) < 0)
            throw new EOFException();
        return ((ch1 << 24) + (ch2 << 16) + (ch3 << 8) + (ch4 << 0));
    }

 

      e) DataOutputStream、DataInputStream两者一般结合使用,如果DataOutputStream 写

             入数据,java保证我们可以使用DataInputStream准确的读取数据。还有writeUTF()和

             readUTF()方法,可以对字符串进行写入、读取。 用到这两个类的来读写文件的数据格

             式一般比较固

		DataInputStream buff = new DataInputStream(
				               new BufferedInputStream(
				               new FileInputStream("路径")));

 

        f)read和write方法在执行时将阻塞,直到字节被读入或者写出。不过通常是因为网络

             繁忙。available()方法“在没有阻塞的情况下所能读取的字节数”,即当前可读取

             入的字节数量,那么下面这样就不会 阻塞

		// 对于文件,这意味着整个文件都可读取
		int bytesAvailable = in.available();
		if (bytesAvailable > 0) {
			byte[] bytes = new byte[bytesAvailable];
			in.read(bytes);
		}

        

       g)输入流InputStream、输出流OutputStram的几个方法        

	public static void main(String[] args) throws IOException {

		// ByteArrayInputStream 可以将一个字节数组转成输入流
		InputStream in = new ByteArrayInputStream("abcdefghi".getBytes());
		// 判断是否支持标记功能
		in.markSupported(); // true
		// 跳过2个字节
		in.skip(2);
		/**
		 * 在当前位置打标记,非所有流都支持(这个流支持) 
		 * 如果从输入流中已经读的字节数大于5个,则这个流允许忽略这个标记(这个流不支持)
		 */
		in.mark(5);
		// 跳过2个字节
		in.skip(2);
		System.out.println((char) in.read());// 输出:e
		// 返回到最后一个标记,随后read将重新读入这些字节。如果没有标记不被重置
		in.reset();
		System.out.println((char) in.read());// 输出:c
		// 超过5个字节被读取
		in.skip(4);
		// mark不失效
		in.reset();
		System.out.println((char) in.read());// 输出:c

		byte[] b = new byte[10];
		// 向b中塞4个字节,从b[5]开始
		in.read(b, 5, 4);
		// 关闭流
		in.close();

		// OutputStram
		OutputStream out = new ByteArrayOutputStream();
		// 冲刷输出流,也就是将所有缓冲的数据送到目的地
		out.flush();
		// 冲刷并关闭流
		out.close();

	}

   

 

三  字符输入流Reader、字符输出流Writer

   

      a) 不管磁盘还是网络传输,最小的存储单位都是字节,所以抽象类InputStream和

              OutputStream构成了输入/输出(I/O)类层次的基础。

 

      b) 基础的输出输入流仅支持8位的字节流,并且不能很好的处理16为的Unicode字符,

              由于Unicode用于字符国际化(java本身的char也是16的Unicode,即包含两个字

              节), 所以适配器InputStreamReader、OutputStreamWriter出现了,用于字节和

              字符之间的 转换。

		// ByteArrayInputStream 可以将一个字节数组转成输入流
		InputStream in = new ByteArrayInputStream("你好".getBytes());
		// 先转换成字符,然后用BufferedReader进行缓存
		BufferedReader br = new BufferedReader(new InputStreamReader(in));

      

      c) 字符就是大家都能看懂的文字(包括其它国家文字)、符号之类的,比如txt存储的

              都是字符。无法转换成字符的如图片、MP3

 

      d)  以字符格式写出数据,可以使用PrintWriter,还可以设置是否每次写入都冲刷

               输出流。print/println方法可以写入字符和java的基本类型

	/**
	 * 其中的一个私有构造器方法
	 * @param charset 以选定的字符编码,将Unicode字符转换成字节进行存储
	 * @param file  文件路径
	 * @throws FileNotFoundException
	 */
    private PrintWriter(Charset charset, File file)
            throws FileNotFoundException
        {
            this(new BufferedWriter(//对字符缓存
            	 new OutputStreamWriter( 
            	 new FileOutputStream(file), charset)),false);
        }

 

      e) 以字符格式读取数据,可以使用BufferedReader,有一个readLine()方法,每次读取一

              行。没有按java基本类型读取数据功能。

             下面是将字节按照GBK字符集格式转换成字符。

		BufferedReader buff = new BufferedReader(
				              new InputStreamReader(
				              new FileInputStream(path), "GBK")); 

 

      f) java.util.Scanner类可以按行读取数据,也可以按java基本类型读取数据。

 

      g) RandomAccessFile类可以在文件中任何位置查找和写入数据,适合大小已知的记录

              组成的文件。类中有一个表示下一个读入或写出的字节所处的位置,用seek()方法,

              可以设置指针在文件中的任意位置。其实现了DataOutput, DataInput接口,可以对

              java的基本类型进行读写。在使用该类时,一般要知道文件的排版。还有其大多

              功能都可以用nio取代

		//只能进行读入访问
		RandomAccessFile in = new RandomAccessFile(path, "r");
		//可以进行读入、写出访问
		RandomAccessFile inOut = new RandomAccessFile(path, "rw");

 

 

 

四  ZipInputStream和ZipOutputStream

  

	//压缩:写出到ZIP文件
	ZipOutputStream zipOut = new ZipOutputStream(new FileOutputStream("压缩文件路径.zip"));
	//压缩文件中的每一项都是一个ZipEntry对象
	ZipEntry entry = new ZipEntry("文件名字");
	//将ZipEntry添加到压缩文件中
	zipOut.putNextEntry(entry);
	//可以通过缓存流写入数据
	BufferedOutputStream outbuf = new BufferedOutputStream(zipOut);
	//还可以继续嵌套
	DataOutputStream dou = new DataOutputStream(outbuf);
	
	
	//解压:将文件从压缩文件中读出
	ZipInputStream zipIn = new ZipInputStream(new FileInputStream("压缩文件路径.zip"));
	//返回下一个ZipEntry对象,没有更多项返回null
	zipIn.getNextEntry();
	//可以通过缓存流读取数据
	BufferedInputStream inbuf = new BufferedInputStream(zipIn);
	//还可以继续嵌套
	DataInputStream din = new DataInputStream(inbuf);

       a) ZipInputStream不能一次解压整个压缩文件,只能一次一个ZipEntry 的解压。

              ZipOutputStream不能一次压缩一个文件夹,只能一次一个ZipEntry 的压缩。

 

      b) ZipInputStream的read方法再碰到当前ZipEntry结尾时返回-1,然后必须调用

              closeEntry来读入下一个ZipEntry 。写入时同理。

 

 

五 对象序列化

 

  a)java的对象序列化将那些实现了Serializable接口的对象转换成一个字节序列,并可以将

        转换的字节序列恢复为原来的对象,这种机制可以弥补不同操作系统之间的差异,可以在

        windows上序列化传到unix恢复


  b)java的远程方法调用RMI(Remote Mthod Invocation)就是通过序列化实现的,当远程对象

        发送信息时,需要通过对象序列化来传输参数和返回值

 

  c)对象序列化不紧可以保存对象的全景图,还能追踪对象内所包含的全部引用,并保存那

        些对象

 

   d)序列化时每个对象的引用都会关联一个序列号,相同对象的重复序列化,将被存储为对

         这个对象序列号的引用。反序列化时过程相反。

 

   e)在反序列化一个对象时,会拿其序列号与它所属类的当前序列号进行对比,不匹配,说明

         这个类在序列化后发生过变化,这涉及“版本管理”就不概述了

	public static void main(String[] args) throws IOException, ClassNotFoundException {

		// 序列化
		ByteArrayOutputStream out = new ByteArrayOutputStream();
		// 创一个ObjectOutputStream 写出到指定的OutputStream
		ObjectOutputStream oos = new ObjectOutputStream(out);
		// 这个方法将存储指定对象的类、类的签名,以及这个类及其超类中所有非静态和非transient(瞬时)修饰的域的值
		oos.writeObject("实现Serializable接口的对象");
		oos.writeObject("可以序列化多个对象");

		// 反序列化
		byte[] bs = out.toByteArray();
		InputStream is = new ByteArrayInputStream(bs);
		// 创建一个ObjectInputStream 用于从指定的InputStream读回对象信息
		ObjectInputStream ois = new ObjectInputStream(is);
		// 返回对象的类、签名、以及这个类及其超类中所有非静态和非瞬时域的值
		String str1 = (String) ois.readObject();
		String str2 = (String) ois.readObject();
		System.out.println(str1 + str2);
	}

 

    f) 实现Externalizable接口,它继承自Serializable。在其序列化时会调用writeExternal方法,

          反序列化时通过调用无参构造器,然后调用readExternal进行对象的初始化。

public class Test implements Externalizable {

	private String str;
	private int i;

	// 反序列化时调用
	public Test() {
		System.out.println("无参构造器");
	}

	public Test(String str, int i) {
		System.out.println("构造器");
		this.str = str;
		this.i = i;
	}

	@Override
	public String toString() {
		return str + i;
	}

	// 序列化时调用
	@Override
	public void writeExternal(ObjectOutput out) throws IOException {
		out.writeObject("writeExternal-");
		out.writeInt(1);
	}

	// 反序列化时调用
	@Override
	public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
		this.str = (String) in.readObject();
		this.i = in.readInt();
	}

	public static void main(String[] args) throws IOException, ClassNotFoundException {
		// 初始化
		Test test = new Test("test", 0);
		// 序列化
		ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
		ObjectOutputStream oos = new ObjectOutputStream(outputStream);
		oos.writeObject(test);
		oos.close();
		// 反序列化
		ByteArrayInputStream inputStream = new ByteArrayInputStream(outputStream.toByteArray());
		ObjectInputStream ois = new ObjectInputStream(inputStream);
		Test te = (Test) ois.readObject();
		System.out.println(te);
		inputStream.close();
		ois.close();

	}
}

 

9
7
分享到:
评论

相关推荐

    Java IO NIO and NIO 2 无水印pdf

    Java IO NIO and NIO 2 英文无水印pdf pdf所有页面使用FoxitReader和PDF-XChangeViewer测试都可以打开 本资源转载自网络,如有侵权,请联系上传者或csdn删除 本资源转载自网络,如有侵权,请联系上传者或csdn...

    java IO NIO AIO.xmind

    涉及到java io, nio, aio相关知识点,学习过程中的一些总结,持续更新中,xmind 格式

    Java IO NIO and NIO 2 epub

    Java IO NIO and NIO 2 英文epub 本资源转载自网络,如有侵权,请联系上传者或csdn删除 本资源转载自网络,如有侵权,请联系上传者或csdn删除

    Java IO_NIO

    1、Java IO_NIO 2、Java+IO.pdf

    Java IO,NIO and NIO.2 mobi

    java io nio nio2 java io的百科全书 mobi格式 需要kindle 软件

    java学习笔记1(java io/nio)

    java学习笔记1(java io/nio)设计模式

    java io 与java nio区别

    java频道\java io 与java nio区别.txt

    Java IO, NIO and NIO.2

    这是一本介绍java io以及nio相关知识的书,书中对知识的讲解通俗易懂,是学习java nio以及复习java io相关知识的必备书籍。注意:本书为英文版!!!

    Java IO与NIO文档

    Java IO与NIO开发文档

    JavaIO和NIO练习

    JavaIO和NIO练习,主要是依据新旧JavaIO所写的一个输出输入流的Demo

    JAVA IO-NIO 详解

    在Java中,IO(输入/输出)是程序与外部世界进行交互的重要桥梁,而NIO(非阻塞IO)则是Java IO的一个重要扩展,它提供了更为高效、灵活的数据处理方式。 传统的Java IO是阻塞式的,即当程序进行读写操作时,如果...

    JAVA IO and NIO

    JAVA兩種方式IO 和NIO 實現客戶端和服務器通訊,可以參考一下。

    Ioserver java Nio socket 框架

    Ioserver java Nio socket 框架 是个不错的NIO 通讯框架,本来想学习mina框架,看了看mina的源码太头痛,本人觉得看懂了Ioserver 再看mina的框架,想多的学习 java NIO 的也可以下载 看看,很值得学习啊!!!

    JavaNIO chm帮助文档

    Java NIO系列教程(一) Java NIO 概述 Java NIO系列教程(二) Channel Java NIO系列教程(三) Buffer Java NIO系列教程(四) Scatter/Gather Java NIO系列教程(五) 通道之间的数据传输 Java NIO系列教程(六)...

    Java_NIO与IO的区别和比较.doc

    Java NIO IO 区别 比较 doc

    java NIO 视频教程

    Java NIO(New IO)是一个可以替代标准Java IO API的IO API(从Java 1.4开始),Java NIO提供了与标准IO不同的IO工作方式。 Java NIO: Channels and Buffers(通道和缓冲区) 标准的IO基于字节流和字符流进行操作的,...

    Java.nio 与Java.io比较

    Java.nio 与Java.io比较

    Java IO, NIO and NIO.2 原版pdf by Friesen

    not introduced with the other NIO types in Java 1.4 because they depend on the variable arguments capability that was introduced in Java 5.) NIO is missing several features, which were subsequently ...

    java NIO 中文版

    讲解了 JavaIO 与 JAVA NIO区别,JAVA NIO设计理念,以及JDK中java NIO中语法的使用

Global site tag (gtag.js) - Google Analytics