Java中的IO

       Java中IO主要分为输入、输出两种IO流,每种输入、输出流又分为字节流、字符流。其中字节流以字节为单位来处理输入、输出操作,而字符流以字符为单位来处理输入、输出操作。

1、File

       File类代表文件和目录,如果希望在程序中操作文件和目录,都可以通过File类完成。

(1)访问文件和目录

       1)访问文件名相关方法

                String getName()  File对象的文件名或路径名

                String getPath()   File对象的路径名

                File getAbsoluteFile()   File对象的绝对路径

                String getAbsolutePath()   File对象的绝对路径名

                String getParent()   返回File对象所对应目录的父目录

                boolean renameTo(File newName)   重命名

       2)文件检测相关方法

                boolean exists()

                boolean  canWrite()

                boolean  canRead()

                boolean  isFile()

                boolean  isDirectory()

                boolean  isAbsolute()  文件或目录是否为绝对路径

       3)获取常规文件信息

                long lastModified()

                long length()

       4)文件操作相关方法

                boolean createNewFile()  当File对象不存在时,新建一个File对象

                boolean delete()  删除File对象所对应的文件或目录

                static File createTempFile(String prefix, String suffix)  创建一个临时文件

                static File createTempFile(String prefix, String suffix, File directory)  创建一个临时文件,并指定目录

                void deleteOnExit()  注册一个钩子,指定当Java虚拟机退出后文件或目录删除

       5)目录操作相关方法

                boolean mkdir()  创建目录

                String[] list()  罗列目录下所有文件名或路径名

                File[] listFiles()  

                static File[] listRoots()  列出系统所有的根路径。

(2)文件过滤器

       在File类的list()方法中可以接收一个FilenameFilter参数,通过该参数可以只列出符合条件的文件。在这个FilenameFilter接口中包含一个accept(File dir, String name)方法,该方法将依次对指定File的所有子目录或者文件进行迭代,如果该方法返回true,则list()方法会列出该子目录或文件。(参数dir为目录,name为文件名)

       在Java中可以统一使用“/”来进行文件路径分隔

2、理解JavaIO

       在Java中,按照不同的分类方式,可以将流分为不同类型。

 (1)输入、输出流

        输入流:只能从中读取数据

        输出流:只能往里面写数据

        输入、输出流是从程序运行所在内存的角度来划分的。

        Java的输入流主要由InputStream和Reader作为基类,而输出流主要由OutputStream和Writer作为基类。

 (2)字节流、字符流

        字节流操作的数据单元是8位的字节,字符流操作的数据单位是16位的字符。

        字节流主要由InputStream、OutputStream作为基类,而字符流主要由Reader、Writer作为基类

 (3)节点流、处理流

        从/向一个特定的IO设备读/写数据的流,称为节点流,节点流也称为低级流。节点流直接连接实际的数据源。

处理流用于对一个已存在的流进行连接或封装,通过封装后的流来实现数据的读/写功能。处理流是高级流。

3、字节流和字符流

(1)InputStream和Reader

       InputStream和Reader是所有输入流的抽象基类。

       在InputStream里包含如下三个方法:

              int read()

              int read(byte[] bytes)

              int read(byte[] bytes, int off, int len)

       在Reader里包含如下三个方法:

              int read()

              int read(char[] cbuf)

              int read(char[] cbuf, int off, int len)

       InputStream和Reader都是抽象基类,它们分别有一个用于读取文件的输入流:FileInputStream、FileReader,其都是节点流,会直接和指定文件关联。

       在Java中,关于文件的操作在最后需要进行资源回收。Java 7对于所有IO资源类,都实现了AutoCloseable接口,可以使用如下方式打开资源,然后使用完后无需释放,虚拟机会自动进行资源释放。

(2)OutputStream和Writer

       OutputStream和Writer也类似。两个类都提供了如下三个方法:

              void write(int c)

              void write(byte[]/char[] buf)

              void write(byte[]/char[] buf, int off, int len)

       因为字符流以字符为单位进行操作,故Writer可以使用字符串来代替字符数组。

              void write(String str)

              void write(String str, int off, int len)

       同样,其有各自对应的FileOutputStream和FileWriter两个输出流。

4、输入输出流体系

(1)处理流的用法

       处理流可以隐藏底层设备上节点流的差异,并对外提供更加方便的输入/输出方法。

       识别处理流非常简单,只要流的构造器参数不是物理节点,而是已存在的流,那么这种流一定是处理流。

		try(
			/*
			 * 封装成处理流再进行操作
			 */
			FileOutputStream fos = new FileOutputStream("test.txt");
			PrintStream ps = new PrintStream(fos);
			)
		 {
			ps.println("Hello World");
			ps.println("Benz BMW Audi");
		 }

       PrintStream类的输出功能非常强大,通常如果需要输出文件文本内容,都应该将输出流包装成PrintStream后进行输出。

       在使用处理流包装了底层节点流之后,关闭输入/输出流资源时,只要关闭最上层的处理流即可。关闭最上层的处理流后,系统会自动关闭该被处理流包装的节点流。

(2)输入输出流体系

                                                                    Java输入输出流体系中常用的流分类

分类

字节输入流

字节输出流

字符输入流

字符输出流

抽象基类

InputStream

OutputStream

Reader

Writer

访问文件

FileInputStream

FileOutputStream

FileReader

FileWriter

访问数组

ByteArrayInputStream

ByteArrayOutputStream

CharArrayReader

CharArrayWriter

访问管道

PipedInputStream

PipedOutputStream

PipedReader

PipedWriter

访问字符串

 

 

StringReader

StringWriter

缓存流

BufferedInputStream

BufferedOutputStream

BufferedReader

BufferedWriter

转换流

 

 

InputStreamReader

OutputStreamWriter

对象流

ObjectInputStream

ObjectOutputStream

 

 

抽象基类

FilterInputStream

FilterOutputStream

FilterReader

FilterWriter

打印流

 

PrintStream

 

PrintWriter

推回输入流

PushbackInputStream

 

PushbackReader

 

特殊流

DataInputStream

DataOutputStream

 

 

       通常有一个规则:如果进行输入输出的内容是文本内容,则应该考虑使用字符流;如果进行输入输出的内容是二进制内容,则应该考虑使用字节流。

       上表中列出了一种以数组为物理节点的节点流,字节流以字节数组为节点,字符流以字符数组为节点;这种以数组为物理节点的节点流除在创建节点流对象时需要传入一个字节数组或字符数组外,用法与文件节点流完全相似。与此类似,字符流还可以使用字符串作为物理节点,用于实现从字符串读取内容,或将内容写入字符串的功能。

       缓冲流增加了缓冲功能,可以提高输入、输出的效率,增加缓冲功能后需要使用flush()才可以将缓冲区的内容写入实际的物理节点。

(3)转换流

       输入输出流提供两个转换流,这两个转换流可以用于实现字节流和字符流之间的转换。其中InputStreamReader将字节输入流转换为字符输入流,OutputStreamWriter将字节输出流转换为字符输出流。

       BufferedReader具有一个readLine()方法,可以非常方便的一次读取一行,所有经常把读取文本内容的输入流包装成BufferedReader,用来方便的读取输入流的文本内容。

(4)推回流

       在输入输出流体系中,有两个特殊的流(推回输入流)。PushbackInputStream和PushbackReader,它们提供如下三个方法:

                void unread(byte[]/char[] buf)

                void unread(byte[]/char[] buf, int off, int len)

                void unread(int b)

       推回流都带有一个推回缓冲区,当程序调用unread()方法时,系统将会把指定数组的内容推回到该缓冲区里,而推回输入流每次调用read()方法时总是先从推回缓冲区读取,只有完全读取了推回缓冲区的内容后,但还没有装满read()所需的数组时才会从原输入流中读取。

       在创建PushbackInputStream和PushbackReader时需要指定推回缓冲区大小,默认大小为1。如果程序中推回缓冲区的内容超过了推回缓冲区大小,将引发Pushback buffer overflow的IOException异常。

5、重定向标准输入输出

       在Java中,标准输入输出流分别通过System.in和System.out来代表。

       在System类里提供了三个重定向的标准输入输出流的方法:

                static void setErr(PrintStream err)

                static void setIn(InputStream in)

                static void setOut(PrintStream out)

6、RandomAccessFile

       RandomAccessFile是Java输入输出体系功能最丰富的文件内容访问类,它提供了众多方法访问文件的内容。它既可以读文件,又可以写文件。该类支持“随机访问”文件。

       RandomAccessFile有一个最大的局限是:只能读写文件,不能读写其他IO节点。

       RandomAccessFile包含如下两个方法来操作文件记录指针:

       long getFilePointer()

       void seek(long pos)

       该类的读写方法与InputStream和OutputStream一样,另外还提供了readXxx()和writeXxx()方法来完成输入输出。

       在创建RandomAccessFile类时需要指定访问模式: “r”  “rw”  “rws”  “rwd”

       该类不能对文件进行随机插入内容

7、对象序列化

       对象序列化的目标是将对象保存到磁盘中,或允许在网络中直接传输对象。对象序列化机制允许把内存中的Java对象转换成平台无关的二进制流,从而允许把这种二进制流持久化地保存到磁盘上,通过网络将这种二进制流传输到另外一个网络节点。

       如果需要让某个对象支持序列化机制,则必须让它的类时可序列化的。为了让某个类是可序列化的,该类必须实现如下两个接口之一:

          Serializable

          Externalizable

(1)使用对象流实现序列化

       使用Serializable来实现序列化非常简单,只需让目标类实现该接口即可。该接口为标记接口,无任何方法。

       当需要将对象流操作时,使用ObjectInputStream和ObjectOutputStream进行操作。

       注意:反序列化时必须提供该对象的class文件,否则会导致ClassNotFoundException异常。反序列化机制无需通过构造器来初始化Java对象。

       注意:当一个序列化类有多个父类时,这些父类要么有无参的构造器,要么也是可序列化的,否则反序列化时将抛出InvalidClassException异常。如果父类是不可序列化的,只是带有无参数的构造器,则该父类中定义的成员变量值不会序列化到二进制流中。

(2)对象引用的序列化

       Java序列化机制采用一种特殊的序列化算法,其算法内容如下:

          所有保存到磁盘中的对象都有一个序列化编号

         当程序试图序列化一个对象时,程序将先检查该对象是否已经被序列化过,只有该对象从未被序列化过,系统才会将该对象转 换成字节序列并输出

          如果某个对象已经被序列化过,程序将只是直接输出一个序列化编号,而不是再次重新序列化该对象

          根据以上算法,只有在对象第一次调用writeObject()方法时进行序列化输出,即使后来将对象修改再次调用该方法,也不会写入修改后的结果。

(3)自定义序列化

       可以在不想序列化的成员变量前使用transient关键字修饰,这样改变量就不会序列化。

       在序列化和反序列化过程中需要特殊处理的类应该提供如下特殊签名的方法,这些特殊方法用以实现自定义序列化:

               private void writeObject(java.io.ObjectOutputStream out) throws IOException

               private void readObject(java.io.ObjectInputStream in) throws IOException, ClassNotFoundException

               private void readObjectNoData() throws ObjectStreamException

       writeObject()方法负责写入特定的实例状态,以便相应的readObject()方法可以恢复它。通过重写该方法,程序员可以完全获得对序列化机制的控制,可以自主决定哪些实例变量需要序列化,需要怎样序列化。在默认情况下,该方法会调用out.defaultWriteObject来保存Java对象的各个实例变量,从而可以实现序列化Java对象状态的目的。

       readObject()方法负责从流中读取并恢复对象实例变量,通过重写该方法,程序员可以完全获取对反序列化机制的控制,可以自主决定需要反序列化哪些实例变量,以及如何进行反序列化。在默认情况下,该方法会调用in.defaultReadObject()来恢复Java对象的非瞬态实例变量。readObject()和writeObject()方法相对应。

       当序列化流不完整时,readObjectNoData()方法可以用来正确地初始化反序列化的对象。例如,接收方使用的反序列化类的版本不同于发送方,或者接收方版本扩展的类不是发送方版本扩展的类,或者序列化流被篡改时,系统都会调用该方法来初始化反序列化的对象。

       还有一种更彻底的自定义机制,它甚至可以在序列化对象时将该对象替换成其他对象。如果需要实现序列化某个对象时替换该对象,则应为序列化类提供如下特殊方法:

              ANY-ACCESS-MODIFIER Object writeReplace() throws ObjectStreamException

       该方法由序列化机制调用。系统在序列化某个对象之前,会先调用该对象的writeReplace()和writeObject()两个方法,系统总是先调用被序列化对象的writeReplace()方法,如果该方法返回另外一个对象,系统调用另外一个对象的writeReplace()方法。。。直到不再返回一个对象为止,程序最后调用该对象的writeObject()方法来保存该对象的状态。

       与writeReplace()方法相对的是,序列化机制里还有一个特殊的方法,它可以实现保护性复制整个对象。这个方法时:

              ANY-ACCESS-MODIFIER Object readResolve() throws ObjectStreamException

       该方法会紧跟着readObject()之后被调用,该方法的返回值将会代替原来反序列化的对象,而原来的readObject()反序列化的对象将会被立即丢弃。

(4)另外一种序列化机制

       Java还提供了另外一种序列化机制,这种序列化机制完全由程序员决定存储和恢复对象数据。要实现该目标,Java类必须实现Externalizable接口,该接口定义了两个方法:

              void readExternal(ObjectInput in)

              void writeExternal(ObjectOut out)

       当使用Externalizable机制反序列化时,程序会先使用public的无参构造器创建实例,然后才执行readExternal()方法进行反序列化,故实现Externalizable的序列化对象类必须提供public的无参构造器。 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值