输入/输出
主要是总结了一下文件操作以及序列化,NIO。
1.File类
访问文件名相关的方法:
String getName()
String getPath()
File getAbsoluteFile()
String getAbsolutePath()
String getParent()
文件检测相关方法
boolean exists()
boolean canWrite()
boolean canRead()
boolean isFile()
boolean isDirectory()
获取常规文件信息
long lastModified()
long length()
文件操作相关方法
boolean createNewFile():如果File对象对应文件不存在,该方法会新建一个指定的新文件
boolean delete():删除File对象对应的文件或路径
目录操作相关方法
boolean mkdir():创建一个File对象对应的目录
String[] list():列出File对象所有子文件名和路径名
File[] listFiles(): 列出File对象所有子文件和路径
static File[] listRoots():列出系统所有根路径
例子:
public class FileTest {
public static void main(String[] args) {
File file = new File(".");
System.out.println(file.getName());
System.out.println(file.getParent()); // 相对路径的父路径
System.out.println(file.getAbsoluteFile()); // 绝对路径
System.out.println(file.getAbsoluteFile().getParent()); // 上一级路径
try {
File tmpFile = File.createTempFile("aaa",".txt",file);
tmpFile.deleteOnExit();
File newFile = new File(System.currentTimeMillis() + "");
System.out.println("newFile是否存在:" + newFile.exists());
newFile.createNewFile(); // 创建文件
newFile.mkdir(); // 创建目录
String[] fileList = file.list(); // 列出当前目录下所有文件和路径
System.out.println("当前路径下所有文件和路径如下:");
for (String fileName : fileList)
{
System.out.println(fileName);
}
File[] roots = File.listRoots(); // 列出所有磁盘根路径
System.out.println("系统所有根路径如下");
for (File root : roots)
{
System.out.println(root);
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
2.Java的IO流
InputStream/Reader:所有输入流的基类,前者是字节输入流,后者是字符输入流
OutputStream/Writer:所有输出流的基类,前者是字节输出流,后者是字符输出流
3.对象序列化
是什么:实现序列化的Java对象转换成字符序列,这些字符序列可以保存到磁盘上。
为什么:为了让对象可以脱离程序的运行而独立存在。
主要是Serializable这个接口
使用对象流实现序列化
1.创建一个ObjectOutputStream,这是一个输出流
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(“Object.txt”));
2.调用writeObject()方法输出可序列化对象
oos.writeObject(per);
3.Person类实现Serializable接口
public class Person implements Serializable
{
...
}
4.使用ObjectOutputStream将Person对象写入磁盘文件
public class WriteObject
{
public static void main(String[] args)
{
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(“Object.txt”))){
Person per = new Person();
oos.writeObject(per); // 将per对象写入输出流
}
}
}
从二进制流中恢复Java对象
1.创建ObjectInputStream输入流
ObjectInputStream ois = new ObjectInputStream(new FileInputStream(“Object.txt”));
2.调用readObject()方法读取流中的对象
Person p = (Person)ois.readObject();
4.NIO
由于流输入/输出一次只能处理一个字节,为了解决效率不高的问题,从JDK1.4开始加入了一些列改进输入/输出处理的新功能,这些功能简称新IO(NIO)
概述
新IO将文件或文件的一段区域映射到内存中,这样就可以像访问内存一样访问文件,大大提高了处理输入\输出的速度
相关包如下:
java.nio:包含于Buffer相关的类
java.nio.channels:包含Channel和Selector相关的类
java.nio.charset:包含字符集相关的类
java.nio.channels.spi:包含与Channel相关的服务提供者编程接口
java.nio.charset.spi:包含字符集相关的服务提供者编程接口
Channel和Buffer是其中核心。
Channel与传统的InputStream、OutputStream最大区别是它提供了一个map()方法,可以将数据映射到内存中。
Buffer本质上是一个数组,发送到Channel的对象都要先放到Buffer中,Channel读取数据也要到Buffer中读取。
Buffer中有3个重要概念
- capacity:缓冲区的容量,创建后不可改变,不可为负值
- limit:第一个不应该被读出或者写入的缓冲区位置索引,换句话说,这个索引后的区域都无法读写
- position:用于指明下一个位置索引
例子:
public class BufferTest {
public static void main(String[] args) {
//创建Buffer
CharBuffer buff = CharBuffer.allocate(8);
System.out.println("capacity:" + buff.capacity());
System.out.println("limit:" + buff.limit());
System.out.println("position:" + buff.position());
// 存放数据
buff.put("a");
buff.put("b");
buff.put("c");
System.out.println("放入三个元素后:position: " + buff.position() );
// 调用flip()
buff.flip();
System.out.println("执行flip后,limit:" + buff.limit());
System.out.println("执行flip后,position:" + buff.position());
//调用clear()
buff.clear();
System.out.println("执行clear后,limit:" + buff.limit());
System.out.println("执行clear后,position:" + buff.position());
// 读取数据
buff.get(2);
System.out.println("执行读取数据后,position:" + buff.position());
System.out.println("执行读取数据后,内容并没有被清除:" + buff.get(2));
}
}
打印结果:
capacity:8
limit:8
position:0
放入三个元素后:position: 3
执行flip后,limit:3
执行flip后,position:0
执行clear后,limit:8
执行clear后,position:0
执行读取数据后,position:0
- flip():把limit设置为position所在位置,将position设置为0,也就是做好输出数据准备
- clear():将position设置为0,将limit设置为capacity,为再次输入数据做好准备,但是内容并没有被清空。
上面这个程序的执行过程就是
- 加入三个元素后,position来到了3
- 执行flip(),limit设置为position的值,也就是3,position设置为0
- 执行clear(),position设置为0
为什么在写入数据完成后,需要调用flip()方法呢,因为这个时候limit到了position的位置,也就是说后面的无数据区域都无法访问,避免了读到null值。
5.使用Channel
Channel最常用的三类方法shi map()\read()\write(),其中map()方法用于将Channel对应的数据映射成ByteBuffer
例子:
public class FileChannelTest {
public static void main(String[] args) {
File f = new File("FileChannelTest.java");
try {
FileChannel inChannel = new FileInputStream(f).getChannel();
FileChannel outChannel = new FileOutputStream("a.txt").getChannel();
MappedByteBuffer buffer = inChannel.map(FileChannel.MapMode.READ_ONLY,0,f.length());
Charset charset = Charset.forName("GBK");
outChannel.write(buffer);
CharsetDecoder decoder = charset.newDecoder();
CharBuffer charBuffer = decoder.decode(buffer);
System.out.println(charBuffer);
} catch (java.io.IOException e) {
e.printStackTrace();
}
}
}
通过MappedByteBuffer将channel中的数据映射成ByteBuffer,然后写入FileChannel。
除了这种一次性把全部数据映射的方式,我们也可以采取类似传统字节流的方式
例子:
public class ReadFile {
public static void main(String[] args) throws IOException {
FileInputStream fis = new FileInputStream("ReadFile.java");
FileChannel fcin = fis.getChannel();
ByteBuffer byteBuffer = ByteBuffer.allocate(64);
while (fcin.read(byteBuffer) != -1){
byteBuffer.flip();
Charset charset = Charset.forName("GBK");
CharsetDecoder decoder = charset.newDecoder();
CharBuffer cbuff = decoder.decode(byteBuffer);
System.out.println(cbuff);
cbuff.clear();
}
}
}
通过flip()和clear()进行读取的限制,防止读出null值。