收藏 分享(赏)

Java IO.doc

上传人:hwpkd79526 文档编号:8090937 上传时间:2019-06-08 格式:DOC 页数:24 大小:649KB
下载 相关 举报
Java IO.doc_第1页
第1页 / 共24页
Java IO.doc_第2页
第2页 / 共24页
Java IO.doc_第3页
第3页 / 共24页
Java IO.doc_第4页
第4页 / 共24页
Java IO.doc_第5页
第5页 / 共24页
点击查看更多>>
资源描述

1、前提概念了解IO主要是完成从数据源中获取数据,放到数据目的地的过程。IO分为文件 IO,网络IO。字节,字符的概念:1个字节是8位,字符是由字节组成,但是不同的编码情况下字符的字节数是不一定的。分类流的分类,Java的流分类比较丰富,刚接触的人看了后会感觉很晕。流分类的方式很多:1、按照输入的方向分,输入流和输出流,输入输出的参照对象是Java 程序。( InputStream OutPutStream)2、按照处理数据的单位不同分,字节流和字符流,字节流读取的最小单位是一个字节(1byte=8bit),而字符流一次可以读取一个字符(1char = 2byte = 16bit)。(InputS

2、tream Reader)3、按照功能的不同分,分节点流和处理流,节点流是直接从一个源读写数据的流(这个流没有经过包装和修饰),处理流是在对节 点流封装的基础上的 一种流,FileInputStream是一个接点流,可以直接从文件读取数据,但是BufferedInputStream可以包装 FileInputStream,使得其有缓冲功能。(FileInputStream BufferedInputStream)其实除了以上三种分类外,还 有一些常常听到的一些分类 比如:对象流、 缓冲流、压缩流、文件流等等。其实都是节点流和处 理流的子分类。当然你也可以创建新的流类型,只要你需要。字 节 流与

3、字符流之 间 的 转换1、从字节流到字符流:InputStreamReader 、OutputStreamWriter类可以实现。2、从字符流到字节流:可以从字符流中 获取char 数组,转换为 String,然后调用String的API函数getBytes() 获取到byte,然后就可以通过ByteArrayInputStream、ByteArrayOutputStream来实现到字节流的转换。设计模式适配器模式装 饰 模式InputStreamInputStream提供的最重要的方法是:用于从输入流中读取字节。Java代码 Int read(); 从输入流中读取一个8位的字节,把它 转换为

4、0255之间的整数,并返回这个整数。到输入流的结尾就返回-1; Int read(byte b) ; 从输入流中读取若干字节,把他 们保存到参数b指定的字节 数组中。返回整数表示 读取的字节数。输入流的结尾返回-1。 Int read(byte b, int off, int len) ; 从off开始读取的len长度放到byte Void Close():关闭输入流。释放和流有关的系统资源 Int available():返回可以从输入流中读取的字节数目 Skip(long n):从输入流中跳 过参数n指定数目的字节 Boolean markSubbported():判断流是否支持重复读入数

5、据,是返回true; Mark(int readLimit):从流的当前位置进行标记。 Reset():使输入流重新定位到刚才做了标记的起始位置。输入流中跟数据源直接接触的类有:FileInputStream从文件字 节 数 组读 入数据到 输 入流FileInputStream(File file)FileInputStream(String name)ByteArrayInputStream,实现 了从内存中的字 节 数 组读 入数据到 输 入流。ByteArrayInputStream(byte buf)ByteArrayInputStream(Byte buf,int offset,i

6、nt length)适配器模式:字节数组类型转换为输入流类型PipedInputStream: 允 许 以管道的方式来 处 理流。当 连 接到一个 PipedOutputStream后,它会 读 取后者 输 出到管道的数据。一般用于线程间的通信。管道输入流从管道输出流中读取数据。 线程A向PipedOutputStream写数据。线程B 通过PipedInputStream的read读取数据,若无数据,线程B就会阻塞。/Sender.java 发送者, 这个线程主要用于向另一个线程发送数据import java.io.*;public class Sender extends Thread /

7、表明是个线程类private PipedOutputStream out = new PipedOutputStream(); /发送者是要向外面写数据,所以发送者内部创建PipedOutputStream对象用于向外写数据public PipedOutputStream getOutputStream() /返回管道输出流对象return out;/这是一个线程类,所以它应该覆盖Thread 的run 方法,run方法在线程类启动时自动运行public void run() /这里的run方法用于向PipedOutputStream中写入一串数据String strInfo = new St

8、ring(“hello,receiver!“);try /write和close方法都可能有异常出现out.write(StrInfo.getBytes();out.close();catch(Exception e)e.printStackTrace();/Receiver.java /接收者,也是一个 线程类import java.io.*;public class Receiver extends Threadprivate PipedInputStream in = new PipedInputStream(); /接受者是要读取外面的数据,所以接受者内部创建PipedInputStr

9、eam对象用于读取外面的数据public PipedInputStream getInputStream() /返回管道输入流对象return in;/这是一个线程类,所以它应该覆盖Thread 的run 方法,run方法在线程类启动时自动运行public void run() /这里的run方法用于接收数据 byte buf = new byte1024; /字节数组try /read和close方法都可能有异常出现int len = in.read(buf); /读取数据, len表示实际读取到的内容(长度)System.out.println(“the following message

10、 comes from sender:n“ = new String(buf ,o,len) );in.close();catch(Exception e)e.printStackTrace();/PipedStreamTest.java /启动类import java.io.*;public calss PipedStreamTestpublic static void main(String args) throws Exception Sender t1 = new Sender(); /创建发送者对象;Receiver t2 = new Receiver(); /创建接收者对象;Pip

11、edOutputStream out = t1.getOutputStream();PipedInputStream in = t2.getIntStream();out.connect(in); /用于连接到PipedInputStream上,与in.connect(out)等价,connect方法有异常出现的可能t1.start(); /启动线程t2.start(); /启动线程 PipedWriter和PipedReader类,它用来 处理字符文本的管道通信. 使用管道流类,可以实现 名个程序模块之间的松耦合通信 ,这样我们在程序中就可以灵活的将多个这样的模块的输入流与输出流相联,然后以

12、拼 装成满足各种应用的程序,而不用对模块内部进行修改.使用管道连接的模块具有强内聚弱耦合的特点,若模块被替换或被拆卸不会影响到其他的模块.StringBufferInputStream:(已 经废 除)将字符串 转换为输 入流适配器模式,现在要用StringReaderSequenceInputStream: 能 对 多个 inputstream进 行 顺 序 处 理。SequenceInputStream(Enumeration e)SequenceInputStream(InputStream1 s1,InputSteam s2)ObjectInputStream:对 象 输 入流他的输入

13、流处理类都是装饰类(Decorator 模式),下面对他们进行一下简单介绍:BufferedInputStream: 提供了缓冲功能。DataInputStream: 允许应用程序以与机器无关方式从底层输入流中读取基本 Java 数据类型。应用程序可以使用数据 输出流写入稍后由数据 输入流读取的数据。PushbackInputStream: 允许放回已经读取的数据。OutputStreamOutputStream提供的最重要的方法是:用于将字节写入输出流。Java代码 write(int b);向输出流中写入一个字节 write(byte b); 把参数b指定的字节数组中的所有字节写到输出流

14、write(byte b, int off, int len) :将off开始的len长度的写到输出流 Close:关闭输出流,释放和流有关的资源 Flush:带缓 冲区的输出流写数据 时,数据先保存在缓冲区中,积累到一定程度才会真正写入到输出流,flush强制将缓 冲区中的数据写到输出流。 基本上每个输入流类都有一个相应的输出流类,提供相 应 的输出流处理。同样,跟数据目的地直接接触的 类有:FileOutputStream:数据流写入文件的功能ByteArrayOutputStream,,实现了一个输出流,其中的数据被写入一个 byte 数组。缓冲区会随着数据的不断写入而自 动增长。可使用

15、 toByteArray() 和 toString() 获取数据。PipedOutputStream: 允许以管道的方式来处理流。可以将管道 输出流连接到管道 输入流来创建通信管道。管道 输出流是管道的发送端。通常,数据由某个线程写入 PipedOutputStream 对象,并由其他线程从连接的 PipedInputStream 读取。ObjectOutputStream:对象输出流:序列化和反序列化下面对其它的装饰类做一下简单介绍:BufferedOutputStream: 提供了缓冲功能的输出流,在写出完成之前要 调用flush来保 证数据的输出。DataOutputStream: 数据

16、输出流允许应用程序以适当方式将基本 Java 数据类型写入输出流中。然后,应用程序可以使用数据输入流将数据读入。PrintStream: 为其他输出流添加了功能,使它 们能够方便地打印各种数据 值表示形式。我 们经常用到的System.out或者System.err 都是PrintStream 。Reader所有的字符流操作类都继承自Reader或者Writer这两个抽象类。Java中默 认用的字符编码是Unicode。Reader类 就是将输入流采用本地平台字符编码类型- Unicode字符Writer:Unicode-本地平台字符编码编码类型Reader提供的重要方法有:Java代码 re

17、ad(char cbuf); read(char cbuf, int off, int len); read(CharBuffer target); 他们提供了从流中读取数据到字符数组或者CharBuffer的功能。跟数据源直接接触的类:CharArrayReader: 从内存中的字符数组中读入数据,以对数据进行流式读取。StringReader:从内存中的字符串 读入数据,以对数据进行流式读取。FileReader:从文件中读入数据。注意这里读入数据时会根据JVM的默认编码对数据进行内转换,而不能指定使用的编码。所以当文件使用的编码不是JVM默认编码时,不要使用 这种方式。要正确地转码,使用

18、InputStreamReader 。装饰类:BufferedReader:提供缓冲功能,可以读取行:readLine();LineNumberReader: 提供读取行的控制:getLineNumber() 等方法。InputStreamReader: 字节流通向字符流的桥梁:它使用指定的 charset 读取字节并将其解码为字符。 InputStreamReader(InputStream in):按照本地平台的字符编码读 取输入流中的字符 InputStreamReader(InputStream in,String charsetName):按照参数charsetName指定的字符编码

19、读取输入流中的字符WriterWriter提供的重要方法有:Java代码 write(char cbuf); write(char cbuf, int off, int len); write(int c); write(String str); write(String str, int off, int len); 他们提供了把字符、字符数组 或者字符串写入流中的功能。输出流:根数据目的相关的类:CharArrayWriter:把内存中的字符数组写入输出流,输出流的缓冲区会自动增加大小。输出流的数据可以通过一些方法重新获取。StringWriter: 一个字符流,可以用其回收在字符串 缓冲

20、区中的 输出来构造字符串。FileWriter:把数据写入文件。装饰类:BufferedWriter:提供缓冲功能。OutputStreamWriter:字符流通向字节流的桥梁:可使用指定的 charset 将要写入流中的字符编码成字节。 OutputStreamWriter(OutputStream out)按照本地平台的字符编码向输入流写入字符 OutputStreamWriter(OutputStream out,String charsetName)按照参数 charsetName指定的字符编码向输入流写入字符。PrintWriter: 向文本输出流打印对象的格式化表示形式。和 Pri

21、ntStream一样。PrintWriter和BufferedWriter都有缓冲区,后者只有缓冲区满的时候才执行物理操作,前者可以由用户决定。流处理中的其它方法:mark和 reset用于重复 读取某段的数据,如下代码:Java代码 is = new BufferedInputStream(new FileInputStream(“res/input.data“); assertTrue(is.available() 0); assertTrue(is.markSupported(); / The read limit has no effect. is.mark(0); int first

22、 = is.read(); int second = is.read(); is.reset(); int firstAgain = is.read(); int secondAgain = is.read(); assertEquals(first, firstAgain); assertEquals(second, secondAgain); Writer或者 OutputStream中的flush(): 刷新该流的缓冲,用于确保数据的输出。close(): 关闭流并释放与之关联的所有系统资源。NIO四个关 键类 型 Buffer缓冲区,临时存放输入或输出数据 Charset字符编码转换功

23、能 Channel数据传输通道,数据源与Buffer之间的。 Selector异步IO操作。缓 冲区 Buffer提高效率:1 减少实际物理读写次数2 缓冲区在创建时被分配内存,内存区域一直被重用,减少 动态分配和回收内存区域的次数Buffer(抽象类)-ByteBuffer-MappedByteBufferCharBufferDoubleBufferFoatBufferIntBufferLongBufferShortBuffer缓冲区具有以下属性: 容量(capacity ):缓冲区可以保存多少数据 极限(limit):表示缓冲区当前终点,、 位置(position ):表示缓冲区下一个读写

24、单元的位置Clear():极限设为容量,位置=0Flip():把极限设为位置,再把位置设为0Rewind():不改变极限,把位置 设为0构造方法各种缓冲器均没有提供构造函数,想要构造一个Buffer ,必须通过他们提供出来的静态方法,以ByteBuffer为例,有三种方法可以 实现构造一个ByteBuffer的功能。1、allocate(int iCapacity):察看allocate方法源代 码,发现它是在调用HeapByteBuffer的构造函数,马上打开HeapByteBuffer的相关构造函数,MD, 它又转 回到ByteBuffer方法里面了(-_-晃点我),再杀回来无语中。原来a

25、llocate方法创建了一个HeapByteBuffer对象 的实例,在HeapByteBuffer中,根据提供的iCapacity创建了一个Byte Array,然后将此ByteArray赋值给ByteBuffer的内部变量final byte hb中。由于hb是final的,因此Buffer创建后其Capacity就不能再改变了。2、wrap(byte bytes):与其名称所述一样,wrap方法包装一个已存在的byte数组 ,然后生成一个 ByteBuffer,察看allocate方法源代码,我 晕,又得杀回 HeapByteBuffer中, MD,刚关上。这它又调用了其父类ByteBu

26、ffer的构造函数。-_-_-。弄明白了, wrap方法是将已存 在的bytes直接赋值给hb。但是,由于 Java传递的是引用,所以,对ByteBuffer 的任何操作,都会影响bytes,反之亦然。又查看了一下JDK的Src,发现,以前HeapByteArray 内部是有 hb这个变量的,后来由于避免 调用虚拟方法表的巨大开销,所以将它移 动ByteArray 中去了。思索一下,确实应该如此。3、allocateDirect(int iCapacity):第三种构造方法与第一种类似,区 别的地方在于,第一种 allocate方法使用的是JVM中的内存,而allocateDirect方法使用

27、操作系 统的内 存,这样做可以极大地改善程序性能。通常,Java程序在处理数据时,数据如此流动:ApplicationJVM Memory(即allocate分配的那个ByteBuffer)System MemorySystemExternal Memory(包括硬盘、网络什么的)按常规的内存分配机制JVM和System Memory之间肯定存在一个Copy 的过程,要知道,拷贝Array是相当消耗资源的,因次,JDK 开发者就提供了allocateDirect方法, 直接使用操作系统的Memory生成Buffer。在使用 allocateDirect方法时,数据如此流动:Applicatio

28、nSystemExternal Memory。将文件 映射 到内存了解内存映射的最好方法是使用例子。在下面的例子中,我们要将一个 FileChannel (它的全部或者部分)映射到内存中。 为此我们将使用 FileChannel.map() 方法。下面代码行将文件的前 1024 个字节映射到内存中:MappedByteBuffer mbb = fc.map( FileChannel.MapMode.READ_WRITE,0, 1024 );map() 方法返回一个 MappedByteBuffer,它是 ByteBuffer 的子 类。因此,您可以像使用其他任何 ByteBuffer 一样使用

29、新映射的缓冲区,操作系 统会在需要时负责执行行映射。ByteBuffer的子 类,是文件内容在内存中的映射。这个类的实例需要通过FileChannel 的map() 方法来创建。关于 channel的使用channel有3个主要的 实现: FileChannel:主要实现文件锁,内存映射,跨 连接传输FileInputStream FileOutPutStream RandomAccessFile均有getChannel() 方法,没有构造方法。 Sockets:主要实现非阻塞,可选器,异步 传输 Pipe:一般通道,循环通道Channel代表一个可以被用于Poll操作的对象(可以是文件流也可

30、以使网 络流),Channel能够被注册到一个Selector中。通过调用 Selector的select方法可以从所有的Channel中找到需要服务 的实例(Accept,read )。Buffer对象提供读写数据的缓存。相对于我们熟悉的Stream对象,Buffer提供更好的性能以及更好的编程透明性(人为控制缓存的大小 以及具体的操作)这个包定义了Channel的概念,Channel表现了一个可以进行IO操作的通道(比如,通过FileChannel,我们可以对文件进行读写操作)。java.nio.channels包含了文件系统和网络通讯相关的channel类。这个包通过Selector和S

31、electableChannel这两个类,还定义了一个进行异步(non-blocking)IO操作的API,这对需要高性能IO 的应用非常重要。下面这张UML类图描述了java.nio.channels中interface的关系:ChannelChannel表现了一个可以进行IO操作的通道,该interface 定义 了以下方法:boolean isOpen()该Channel 是否是打开的。void close()关闭这个Channel,相关的资源会被释放。ReadableByteChannel定义了一个可从中读取byte数据的channel interface。int read(ByteB

32、uffer dst)从channel中读取byte数据并写到ByteBuffer 中。返回 读取的byte数。WritableByteChannel定义了一个可向其写byte数据的channel interface。int write(ByteBuffer src)从ByteBuffer中读取byte数据并写到 channel中。返回写出的byte数。ByteChannelByteChannel并没有定义新的方法,它的作用只是把 ReadableByteChannel和WritableByteChannel合并在一起。ScatteringByteChannel继承了ReadableByteCh

33、annel并提供了同时往几个ByteBuffer中写数据的能力。GatheringByteChannel继承了WritableByteChannel并提供了同时从几个ByteBuffer中读数据的能力。InterruptibleChannel用来表现一个可以被异步关闭的Channel。 这表现在两方面:1 当一个InterruptibleChannel的 close()方法被调用时,其它block在这个InterruptibleChannel 的IO 操作上的线程会接收到一个AsynchronousCloseException。2 当一个线程block在InterruptibleChannel

34、 的IO 操作上时,另一个线程调用该线程的interrupt()方法会导致channel被关闭,该线程收到一个ClosedByInterruptException,同时线程的interrupt状态会被设置。接下来的这张UML类图描述了java.nio.channels中类的关系:异步IO的支持可以算是NIO API中最重要的功能,异步IO允 许应用程序同时监控多个channel以提高性能,这一功能是通过Selector,SelectableChannel和SelectionKey这3个类来实现的。SelectableChannel代表了可以支持异步IO操作的channel ,可以将其注册在Se

35、lector上,这种注册的关系由SelectionKey这个类来表现(见UML 图)。Selector这个 类通过select()函数,给应用程序提供了一个可以同时监控多个IO channel的方法:应用程序通过调用select() 函数,让Selector监控注册在其上的多个SelectableChannel ,当有 channel的IO操作可以进行时,select()方法就会返回以让应用程序检查 channel的状态,并作相应的处理。序列化一 JDK类库中的序列化API java.io.ObjectOutputStream代表对象输出流,它的 writeObject(Object obj)

36、方法可对参数指定的obj对象进行序列化,把得到的字节序列写到一个目标输出流中。 java.io.ObjectInputStream代表 对象输入流,它的readObject()方法从一个源输入流中读取字节序列,再把它们反序列化为一个 对象,并将其返回。、 只有实现了Serializable和Externalizable接口的类的对象才能被序列化。Externalizable 接口继承自 Serializable接口,实现Externalizable接口的类完全由自身来控制序列化的行为,而仅实现Serializable接口的类可以 采用默认的序列化方式 。 对象序列化包括如下步骤: 1) 创建一

37、个对象输出流,它可以包装一个其他类型的目标输出流,如文件输出流; 2) 通过对象输出流的writeObject()方法写对象。 对象反序列化的步骤如下: 1) 创建一个对象输入流,它可以包装一个其他类型的源输入流,如文件输入流; 2) 通过对象输入流的readObject()方法读取对象。 二实现Serializable接口 ObjectOutputStream只能对Serializable接口的类的对象进行序列化。默认情况 下,ObjectOutputStream按照默认方式序列化, 这种序列化方式 仅仅对对象的非transient的实例变量进行序列化,而不会序列化 对象 的transien

38、t的实例变量,也不会序列化静态变量。 当ObjectOutputStream按照默认方式反序列化时,具有如下特点: 1) 如果在内存中对象所属的类还没有被加载,那么会先加 载 并初始化这个类。如果在 classpath中不存在相应的类文件,那么会抛出 ClassNotFoundException; 2) 在反序列化时不会调用类的任何构造方法。 如果用户希望控制类的序列化方式,可以在可序列化类中提供以下形式的 writeObject()和readObject()方法。 private void writeObject(java.io.ObjectOutputStream out) throws

39、IOException private void readObject(java.io.ObjectInputStream in) throws IOException, ClassNotFoundException; 当ObjectOutputStream对一个Customer对象进行序列化时,如果该对象具有writeObject() 方法,那么就会 执行这一方法,否则就按默认 方式序列化。在 该对象的writeObjectt()方法中,可以先调用ObjectOutputStream的 defaultWriteObject()方法,使得对象输出流先执行默认的序列化操作。同理可得出反序列化的情

40、况,不过这次是 defaultReadObject()方法。 有些对象中包含一些敏感信息, 这些信息不宜对外公开。如果按照默认方式对它们序列化,那么它们的序列化数据在网络上传输时,可能会被不法份子窃取。对于这类信息,可以对它们进行加密后再序列化,在反序列化时则 需要解密,再恢复 为原来的信息。 默认的序列化方式会序列化整个对象图,这需要递归遍历对 象图。如果 对象图很复杂,递归遍历操作需要消耗很多的空间和时间,它的内部数据 结构为双向列表。 在应用时,如果对某些成员变 量都改为transient类型,将 节省空间和时间,提高序列化的性能。 三 实现Externalizable接口 Extern

41、alizable接口继承自Serializable 接口,如果一个 类实现了Externalizable 接口,那么将完全由这个类控制自身的序列化行为。Externalizable接口声明了两个方法: public void writeExternal(ObjectOutput out) throws IOException public void readExternal(ObjectInput in) throws IOException , ClassNotFoundException 前者负责序列化操作,后者负责反序列化操作。 在对实现了Externalizable接口的类的对象进行反

42、序列化时 ,会先调用类的不带参数的构造方法,这是有别于默认反序列方式的。如果 把类的不带参数的构造方法删除,或者把 该构造方法的访问权 限设置为private、默认或protected级别,会抛出 java.io.InvalidException: no valid constructor异常。 四 可序列化类的不同版本的序列化兼容性 凡是实现Serializable接口的 类都有一个表示序列化版本标识符的静态变量: private static final long serialVersionUID; 以上serialVersionUID的取值是Java运行时环境根据类的内部细节自动生成的。

43、如果 对类的源代码作了修改,再重新编译,新生成的类文件的serialVersionUID的取值有可能也会发生变化。 类的serialVersionUID的默认值完全依赖于Java编译器的实现, 对于同一个类,用不同的Java编译器编译,有可能会导致不 同的serialVersionUID,也有可能相同。为了提高哦啊serialVersionUID的独立性和确定性, 强烈建议在一个可序列化类中显示 的定义serialVersionUID,为它赋予明确的值。显式地定义serialVersionUID有两种用途: 1) 在某些场合,希望类的不同版本 对序列化兼容,因此需要确保类的不同版本具有相同的s

44、erialVersionUID; 2) 在某些场合,不希望类的不同版本对序列化兼容,因此需要确保类的不同版本具有不同的随即访问文件类RandomAccessFile前面的只能按照数据的顺序读或者写数据。RandomAccessFile不属于流,具有随即读写文件的功能。 getFilePointer():返回当前读写指针所处的位置 Seek(long pos):设定读写指针的位置,与文件开 头相隔pos 个字节数 skipBytes(int n):使读写指针从当前位置开始跳过n个字节 Length():返回文件包含的字节数 RandomAccessFile(File file,String mode) RandomAccessFile(String name,String mode)Mode包含r rw实现了DataInput DataOutputJava标准IO Ssytem.in :为InputStream 类型System.out与System.err的区别:为PrintStream类型三个流是一直开着的。System.err无缓冲区,System.out 有缓冲区。 重定向System类 里有三个方法:setIn(InputStream in)setOut(PrintStream out)setErr(PrintStream out)I

展开阅读全文
相关资源
猜你喜欢
相关搜索

当前位置:首页 > 企业管理 > 管理学资料

本站链接:文库   一言   我酷   合作


客服QQ:2549714901微博号:道客多多官方知乎号:道客多多

经营许可证编号: 粤ICP备2021046453号世界地图

道客多多©版权所有2020-2025营业执照举报