Java输入输出
简介:Java的IO通过java.io包下的类和接口支持,在java.io包下主要包括输入、输出两种io流,每种输入、输出流又可分为字节流和字符流两大类。其中字节流以字节为单位来处理输入、输出操作,而字符流则以字符来处理输入、输出操作。使用输入机制,允许程序读取外部数据(包括来自磁盘,光盘等存储设备的数据)、用户输入数据;使用输出机制,允许程序记录运行状态,将程序数据输出到磁盘,光盘等存储设备上。
一 File类
简介:File类是java.io包下代表与平台无关的文件和目录,可以通过File类来完成程序中的操作文件和目录等任务。不管是文件还是目录都是使用File来操作的,但不能访问文件本身,访问文件本身则需要使用输入/输出流。
(一)访问文件目录:一旦创建File对象后,就可以调用File对象的方法来访问。以下为常用的操作文件和目录的方法。
(1)访问文件名相关的方法
- String getName():返回次File对象所表示的文件名或路径名,如果不是路径,则返回最后一级子路径名。
- String getPath():返回次File对象所对应的路径名
- File getAbsoluteFile():返回此File对象的绝对路径
- String getAbsolutePath():返回此File对象所对应的绝对路径名
- String getParent():返回此File对象所对应的目录(最后一级子目录)的父目录名
- Boolean renameTo(File newName):重命名此File对象所对应的文件或目录名,命名成功则返回true。
(2)文件检测相关方法
- boolean exists():判断File对象所对应的文件或目录是否存在
- boolean canWrite():判断是否可写
- boolean canRead():判断是否可读
- boolean isFile():判断是否为文件,而不是目录
- boolean isDirectory():判断是否是目录而不是文件
- boolean isAbsolute():判断是否是绝对路径
(3)获取常规文件信息
- long lastModified():返回文件的最后修改时间
- long length():返回文件内容的长度
(4)文件操作相关方法
- boolean createNewFile():新建一个该File对象所指定的新文件,前提是该文件不存在。成功返回true
- boolean delete():删除File对象所对应的文件或路径
- static File createTempFile(String prefix, String suffix):在默认的临时文件目录中创建一个临时的空文件,使用给定前缀、系统生成的随机数和给定的后缀作为文件名
- static File createTempFile(String prefix, String suffix, File directory):在directory所指定的目录中创建一个临时的空文件,使用给定前缀、系统生成的随机数和给定的后缀作为文件名
- void deleteOnExit() :当java虚拟机退出时,删除File对象所对应的文件和目录
(5)目录操作相关的方法
- boolean mkdir():试图创建一个File对象所对应的目录,创建成功返回true。调用此方法必须对应一个路径,而不是一个文件
- String [] list():列出File对象所有子文件名和路径名,返回String数组
- File [] listFiles():列出File对象的所有子文件和路径
- static File[] listRoots() :列出系统所有系统的根路径。
二 java的io流
流的分类
(1)输入流和输出流:输入流主要由InputStream和Reader作为基类,输出流主要由OutputStream和Writer作为基类。
- 只能从中读取数据,而不能向其写入数据
- 只能从中写入数据,而不能从中读取数据字节流和字符流:
(2)字节流操作的数据单元是8位的字节,主要由InputStream和OutputStream作为基类。字符流操作的数据单元是16位的字符,主要由Reader和Writer作为基类
(3)节点流和处理流
- 节点流:可以从/向一个特定的IO设备(磁盘、网络)读/写数据的流,当使用节点流进行输入/输出时,程序直接连接到实际的数据源,和实际的输入/输出节点连接。
- 处理流则用于对一个已存在的流进行连接或封装,通过封装后的流来实现数据读/写功能。
三 字节流和字符流
(1)InputStream和Reader:所有输入流的抽象基类,本身不能创建实例来执行输入,但是所有输入流的模板,都有三种方法,两者的方法类似。
- int read():从输入流中读取单个字节/字符,返回所读取的字节/字符数据,字节/字符数据可直接转换为int类型
- int read(byte[]/char[] :从输入流中最多读取b.length个字节/字符(字节为byte数组,字符为char数组),并将其存在数组b中,返回实际读取的字节/字符个数
- int read(byte[]/char[] b , int off , int len):从输入流中最多读取len个字节/字符的数据,并将其存储在b数组中,放入数组时,从off位置开始,返回实际读取的字符数据。
InputStream和Reader都是抽象类,本身不能创建实例,但分别有一个用于读取文件的输入流:FileInputStream和FileReader,都是节点流,会直接和指定的文件关联。
InputStream和Reader支持如下几个方法来移动指针:
- void mark(int readAheadLimit):在记录指针当前位置记录一个标记
- boolean markSupported():判断此输入流是否支持mark()操作,是否支持记录标记
- void reset():将此流的记录指针重新定位到上一次的记录标记的位置
- long skip(long n):记录指针向前移动n个字节/字符。
代码示例:
创建字节输入流
FileInputStream fis=new FileInputStream(“FileInputStreamTest.java”);
//创建一个长度为1024的数组
byte[] bbuf=new byte[1024];
//用于保存实际读取的字节数
int hasRead=0;
//用于循环重复读取数据
while ((hasRead=fis.read(bbuf))>0)
{
System.out.println(new String (bbuf,0,hasRead));
}
fis.close();
创建字符输入流:
FileReader fr=new FileReader (“FileReaderTest.java”);
//创建一个长度为32的数组,可以完全读取输入流的全部数据
char[] cbuf=new char[32];
//用于保存实际读取的字符数
int hasRead=0;
//用于循环重复读取数据
while ((hasRead=fr.read(cbuf))>0)
{
System.out.println(new String (cbuf,0,hasRead));
}
fis.close();
(2)OutputStream和Writer:两个流都提供了以下几种方法
- void write (int c):将指定的字节/字符输出到输出流中,其中c既可以代表字节也可以代表字符
- void write (byte[]/char[] buf):将字节数组/字符数组中的数据输出到指定的输出流中
- void write (byte[] / char[] buf , int off , int len):将字节数组/字符数组中从off位置开始,长度为len的字节/字符输出到输出流上。
(3)自己领悟的输入流和输出流
非内存中的流转入到“内存”中来的流为输入流,例如从磁盘中读取一个文件中的数据,这个时候就会用到输入流,这时输入流形成的流就会到“内存”中来,可以将其作为输出流输出到“内存”以外的文件中,这时就可以作为输出流传递数据到另外一个文件。暂且这样理解,并不一定就是这样的。
四 输入/输出流体系
(1)处理流的用法
使用处理流来包装节点流,程序通过处理流来执行输入/输出功能,让节点流与底层的I/O设备,文件交互。
(2)输入/输出流体系
分类 | 字节输入流 | 字节输出流 | 字符输入流 | 字符输出流 |
抽象基类 | InputStream | OutputStream | Reader | Writer |
访问文件 | FileInputStream | FileOutputStream | FileReader | FileWriter |
访问数组 | ByteArrayInputStream | ByteArrayOutputStream | CharArrayReader | CharArrayWriter |
访问管道 | PipedInputStream | PipedOutputStream | PipedReader | PipedWriter |
访问字符串 |
|
| StringReader | StringWriter |
缓冲流 | BufferdInputStream | BufferedOutputStream | BufferedReader | BufferedReader |
转换流 |
|
| InputStreamReader | OutputStreamWriter |
对象流 | ObjectInputStream | ObjectOutputStream |
|
|
抽象基类 | FilterInputStream | FilterOutputStream | FilterReader | FilterWriter |
打印流 |
| PrintStream |
| PrintWriter |
推回输入流 | PushbackInputStream |
| PushbackReader |
|
特殊流 | DataInputStream | DataOutputStream |
|
|
注意:进行输入/输出的内容是文本内容,则应该考虑使用字符流,如果内容是二进制内容,则应该考虑使用字节流。
(3)转换流
转换流用于实现将字节流转换为字符流,其中InputStreamReader将字节输入流转换为字符输入流,OutputStreamWriter将字节输出流转换为字符输出流。Java只提供了将字节流转换为字符流的转换流,因为字符流比字节流操作更方便简单。
(4)推回输入流:提供了一下三种方法
- void uunread(byte[]/char[] buf):讲一个字节/字符数组内容推回到缓冲区里,从而重复读取刚刚读取的内容。
- void unread (byte[]/char[] b, int off , int len):将一个字节/字符数组从off开始,长度为len的字节/字符的内容推回到缓冲区里,从而允许重复刚刚读取到的内容
- void unread(int b):将一个字节/字符推回到缓冲区里,从而允许重复刚刚读取的内容。
五 RandomAccessFile
RandomAccessFile是Java输入/输出流体系中功能最丰富的文件内容访问类,提供了很多的方法来访问文件内容,即可以读取文件内容也可以向文件输出数据,支持“随机访问”的方式,程序可以直接跳到文件的任意地方来读写数据,可以向已存在的文件后追加内容,只能读写文件,不能读写其他IO流,对象中包含一个记录指针,用以标记当前读写处的位置。
long getFilePinter():返回文件记录指针的当前位置
void seek(long pos):将文件记录指针定位到pos位置
RandomAccessFile类有两个构造器,一个使用String 参数来制定文件名,一个使用File参数来指定文件本身。创建RandomAccessFile对象还需要指定一个mode参数,指定RandomAccessFile的访问模式。
“r”:以只读方式打开指定文件
“rw”:以读、写方式打开指定文件,如果文件不存在则创建一个文件。
“rws”:以读、写方式打开指定文件,与rw不同,还要求对文件的内容或元数据的每个更新都同步写入到底层存储设别
“rwd”:以读、写方式打开指定文件,与rw不同,要求对文件内容的内阁更新都同步写入到底层存储设备
六 对象序列化
(1)含义和意义
序列化机制允许将实现序列化的Java对象转换成字节序列,这些字节序列可以保存在磁盘上,或通过网络传输,以备以后重新恢复成原来的对象。序列化机制使得对象可以脱离程序的运行而独立存在。
对象的序列化值将一个Java对象写入IO流中,对应的是,对象的反序列化指的是从IO流中恢复该对象。
如果需要让某个对象支持序列化机制,则必须让它们的类是可序列化的,则必须实现两个接口之一:Serializable、Externalizable,实现该接口无需实现任何方法,只是表名该类是可序列化的。类中所有的参数、引用必须都是可序列化的,这样该类才可以序列化。
(2)使用对象流实现序列化
- 创建可序列化对象
一旦某个类实现了Serializable接口,该类的对象就是可序列化的,程序可以通过两个步骤来序列化该对象:
- 1创建一个ObjectOutputStream,这个输出流是一个处理流,所以必须建立在其他节点流的基础之上。
示例代码:ObjectOutputStream oos=new ObjectOutputStream(new FileOutputStream(“object.txt”))
- 2 调用ObjectOutputStream对象的writeObject()方法输出可序列化对象
示例代码:oos.writeObject(per); //per为一对象,该操作将对象输出到输出流上
2. 反序列化对象
2.1创建一个ObjectInputStream输入流,这个输入流是一个处理流,所以必须建立在其他节点流的基础上的。示例代码:
ObjectIutputStream ois=new ObjectIutputStream(new FileIutputStream(“object.txt”))
2.2调用ObjectInputStream对象的readObject()方法读取流中的对象,该方法
返回一个Object类型的Java对象,如果程序中知道该对象的类型,可以直接就爱那个该对象强制转换为其真实的类型。示例代码:
Preson p=new (Preson) ois.readObject();
注意:反序列化读取的仅仅是Java对象的数据,不是Java类,反序列化恢复Java对象时必须提供对象所属类的class文件,否则会引起异常。
(3)对象引用的序列化
某个类想要序列化,其成员变量都要是可序列化的,包括引用类型,如一个类的一成员变量为一个类,则这个类也必须得是可序列化的。
Java序列化机制采用了一个特殊的序列化算法,内容如下:
- 所有保存到磁盘中的对象都有一个序列化编号
- 当程序试图序列化一个对象时,程序将先检查该对象是否已经被序列化过,只有该兑现从未(在本次虚拟机中)被序列化过,系统才会将该对象转换为字节序列并输出。
- 如果某个对象已经序列化过,程序将知识直接输出一个序列化编号,而不是再次重新序列化该对象。
注意:只有第一次调用writeObject()方法来输出对象时才会将对象转换为字节序列,并写入到ObjectOutputStream;在后面程序中即使对象的实例变量发生了变化,再次调用writeObject()方法输出该对象时,改变后的实例变量不会被输出。
(4)过滤功能
ObjectIutputStream增加了setObjectInputFilter()、getObjectOutputFilter()两个方法,其中第一个方法用于为对象输出流设置过滤器。通过ObjectIutputStream反序列化对象时,过滤器的checkInput()方法会自动激发,用于检查序列化数据是否有效。
checkInput()方法有三个返回值:
Status.REJECTED:拒绝恢复,反序列化将会被阻止
Status.ALLOWED :允许恢复,程序将可以执行反序列化
Status.UNDECIDED:未决定状态,程序将继续执行检查
七 NIO
新IO采用内存映射文件的方式来处理输入/输出,新IO将文件或文件的一段区域映射到内存中。Channel(通道)、Buffer(缓冲)是新IO中的两个核心对象,Channel是对传统的输入/输出系统的模拟,在新IO系统中所有的数据都需要通过通道传输,提供了一个map()方法,通过该map()方法可以直接将“一块数据”映射到内存中。Buffer可以被理解为一个容器,本质是一个数组,发送到Channel的所有对象必须首先放在Buffer中,而从Channel中读取的数据也必须先放到Buffer中。
(1)使用Buffer
Buffer是一个抽象类,最常用的子类为ByteBuffer,可以在底层字节数组上进行get/set操作。还有CharBuffer、ShortBuffer等类
Buffer有三个重要概念:
- 容量(capacity):缓冲区的容量表示该Buffer的最大数据容量,即最多可以存储多少数据,不能为负值,创建后无法改变
- 界限(limit):第一个不应该被读出或者写入的缓冲区位置索引,位于limit后面的数据既不能被读,也不能被写
- 位置(position):用于指明下一个可以被读出的或者写入的缓冲区位置索引。
Buffer主要作用为装入数据,然后输出数据。开始时Buffer的position为0,limit为capacity,程序可以通过put()方法向Buffer中放入一些数据(或者从Channel中获取一些数据)每放入一些数据,Buffer的position相应地向后移动一些位置。当Buffer装入数据结束后,调用Buffer的flip()方法,该方法将limit设置为position所在位置,并将position设置为0,这就使得Buffer的读写指针又移到了开始的位置。Buffer调用了flip()方法后,为输出数据做好了准备;当Buffer输出数据后,Buffer调用clear()方法,将position设置为0,limit设置为capacity,再次为向Buffer中装入数据做好了准备。
Buffer提供了以下常用方法:
- int capacity():返回Buffer的capacity大小
- boolean hasRemaining():判断当前位置与界限之间是否还有元素可供处理
- int limit():返回Buffer的limit位置
- Buffer limit(int newLt):重新设置limit的值,并返回一个具有新的limit的缓冲区对象
- Buffer mark():设置mark的值,只能在0和位置之间的mark
- int position():返回位置的值
- Buffer position (int newps):设置Buffer的position,并返回被修改后的Buffer
- int remaining():返回当前位置和界限(limit)之间的元素个数
- Buffer reset():将位置转到mark所在的位置
- Buffer rewind():将位置设置为0,取消设置的mark
另外,所有的Buffer子类都提供了两个方法:get()和put()方法,用于向Buffer中取出和放入数据。
(2)使用Channel
Channel可以直接将指定文件的部分或全部直接映射成Buffer,程序不能直接访问Channel的数据,只能与Buffer进行交互。Channel中最常用的方法是:map()、read()、write(),其中map()方法用于将Channel对应的部分或全数据映射成ByteBuffer。
(3)Paths和Files
Paths用法:
- Path path=Paths.get(“.”);:以当前路径创建Path对象
- path.getNameCount():路径包含的路径数量
- path.getRoot():path的根路径
- path.toAbsolutePath():path的绝对路径
- Path path2=Paths.get(“g:”,”publish”,”codes”);:构建Path对象,返回g:\publish\codes
Files用法:
- Files.copy(Paths.get(“FilesTest.java”),new FileOutputStream(“a.txt”));:复制文件
- Files.isHidden(Paths.get(“FilesTest.java”)):判断是否为隐藏文件
- List<String > lines=Files.readAllLines(Paths.get(“FilesTest.java”),Charset.forName(“gbk”)):一次性读取文件的所有行
- Files.size(Paths.get(“FilesTest.java”)):判断文件的大小
- Files.write(Paths.get(“pome.txt”),pome,Charset.forName(“gbk”)):直接将多个字符串内容pome写入指定文件