Java的I/O操作
一、概述
Java的IO支持通过java.io包下的类和接口来完成,在java.io包下主要有包括输入、输出两种IO流,每种输入输出流又可分为字节流和字符流两大类。从JDK1.4以后,Java在java.nio包下提供了系列的全新API,通过java.nio,程序可以更高效的进行输入、输出操作。
二、Java I/O类和接口
-
File类
File类直接处理文件和文件系统,它没有指定如何获取信息或将信息保存到文件中,只描述了文件本身的属性。File对象用于获得或者操作与磁盘文件相关联的信息,如存取权限、时间、日期和目录路径等,并且还可以浏览子目录的层次结构。
下面的构造函数可用来创建File对象:
构造方法摘要 | |
---|---|
File(File parent, String child) 根据 parent 抽象路径名和 child 路径名字符串创建一个新 File 实例。 |
|
File(String pathname)
通过将给定路径名字符串转换为抽象路径名来创建一个新 File 实例。 |
|
File(String parent, String child) 根据 parent 路径名字符串和 child 路径名字符串创建一个新 File 实例。 |
|
File(URI uri)
通过将给定的 file: URI 转换为一个抽象路径名来创建一个新的 File 实例。 |
File类定义了许多可以得到File对象标准属性的方法
public class Demo { public static void main(String[] args) { File f=new File("D://hello.java"); System.out.println(f.getParent());//返回此抽象路径名父目录的路径名字符串;如果此路径名没有指定父目录,则返回 null System.out.println(f.getName());//返回由此抽象路径名表示的文件或目录的名称 System.out.println(f.exists());//测试此抽象路径名表示的文件或目录是否存在 System.out.println(f.getAbsoluteFile());// 返回此抽象路径名的绝对路径名形式 System.out.println(f.getAbsolutePath());//返回此抽象路径名的规范路径名字符串 System.out.println(f.getPath());//将此抽象路径名转换为一个路径名字符串 System.out.println(f.hashCode());//计算此抽象路径名的哈希码 System.out.println(f.length());//返回由此抽象路径名表示的文件的长度 System.out.println(f.list());// 返回一个字符串数组,这些字符串指定此抽象路径名表示的目录中的文件和目录 System.out.println(f.mkdir());//创建此抽象路径名指定的目录 } }
2. 流类
Java中把不同的输入输出源抽象画为“流”,通过流的方式允许?Java程序使用相同的方式来访问不同的输入输出源。
字节流类:提供了处理针对字节的IO的丰富环境,顶部类是InputStream和OutputStream,它们均是抽象类。
字符流类:字节流类不能处理Unicode字符,字符流类操作的数据单元为字符,,顶部类是Reader和Writer。
字节流类和字符流类的功能基本一样,只是操作的数据单元不同,其中InputStream和Reader都是将数据抽象为一根水管,程序可以通过read()方法每次抽取一个“水滴”,也可以通过read(char[] cbuf)方法来读取多个“水滴”,程序通过read()方法返回-1来判断是否到了输入流的结束点。
eg.读取文件,统计文件字符数:
public class FileDemo { public static void main(String[] args) { int b=0; try { FileInputStream in=null; in =new FileInputStream("D:\\a.txt"); long num=0; while((b=in.read())!=-1) { System.out.print((char)b); num++; } in.close(); System.out.println(num); } catch (FileNotFoundException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } } }
FileInputStream类创建一个InputStream,可以用来从文件中读取文件。
eg.将一个文件内容拷贝至另一个文件:
public class FileOutStream { public static void main(String[] args) { int b=0; try { FileInputStream in =new FileInputStream("D:\\Eclipse\\workSpace\\day_041602\\src\\day_041602\\TestMain.java"); FileOutputStream out=new FileOutputStream("D:\\hello.java"); while((b=in.read())!=-1) { out.write(b); } in.close(); out.close(); System.out.println("执行完成"); } catch (FileNotFoundException e) { // TODO 自动生成的 catch 块 e.printStackTrace(); } catch (IOException e) { // TODO 自动生成的 catch 块 e.printStackTrace(); } } }
BufferedInputStream、BufferedOutputStream、BufferedReader、BufferedWriter称为缓冲流,它们通过缓冲输入输出来提高性能。
eg.在hello.java文本中输入100个随机数,并在屏幕上显示:
public class BufferWriterDemo { public static void main(String[] args) { try { String s; BufferedWriter bw=new BufferedWriter(new FileWriter("D:\\hello.java")); BufferedReader br=new BufferedReader(new FileReader("D:\\hello.java")); for(int i=0;i<100;i++) { s=String.valueOf(Math.random()); //产生随机数 bw.write(s);//写入hello.java文件中 bw.newLine();//写入一个换行符 } bw.flush();//刷新缓冲 while((s=br.readLine()) != null)//读取一个文本行 { System.out.println(s); } bw.close(); br.close(); } catch (IOException e) { // TODO 自动生成的 catch 块 e.printStackTrace(); } } }
三、NIO
1.概述
NIO使用内存映射的方式处理输入输出,将文件或文件的一段区域映射到内存中,这样就可以像访问内存一样来访问文件了。
相关的包有:java.nio.channels包:主要包含Channel和Selector相关的类,java.nio.charset包:主要包含和字符集相关的类
NIO系基于两个基本的元素:缓冲和通道。缓冲区容纳数据,通道代表队I/O设备的开放式连接。一般而言使用NIO系统,需要获得到I?O设备的一个通道和容纳数据的一个缓冲区,然后可以对缓冲区进行操作,随意输入和输出数据。除此之外,NIO还提供了用于将Unicode字符串映射成字节序列以及逆映射操作的Charset类,还有支持非阻塞式输入输出的Selector类。
2.缓冲区
缓冲(buffer)可以理解成一个容器,它的本质是一个数组,发送到Channel中的所有对象都必须首先放到buffer中,从channel中读取的数据也必须先读到buffer中。
Buffer中有三个重要的参数:
-
- capacity:表示该Buffer的最大存储容量
- limit:第一个不应该被读出或写入的缓冲区位置索引
- position:用于指明下一个可以被读出或写入的缓冲区位置索引
除此之外还有一个可选的mark标记,该mark允许程序直接将position定位到mark处。位置如下所示:
每放入一个数据,position向后移动一位,当Buffer装入数据结束后,调用flip方法,将limit设置为position所在的位置,将position设置为0,这样使得从Buffer中读数据总是从0开始。当Buffer输出数据结束后,Buffer调用 clear方法,它将position置为0,,置limit为capacity,这样为再次向Buffer中装入数据做好准备。Buffer还提供了put和get方法,用于向Buffer中放入数据和读取数据,既支持对单个数据的访问也支持对批量数据的访问。
eg.
public class Test { public static void main(String[] args) { CharBuffer m=CharBuffer.allocate(8); m.put('a'); m.put('b'); m.put('c'); System.out.println("position:"+m.position()); System.out.println("limit:"+m.limit()); m.flip(); System.out.println("第一个元素"+m.get()); System.out.println("第二个元素"+m.get()); System.out.println("position:"+m.position()); } }
执行结果:
3.通道
Channel与传统的InputStream、OutputStream最大的区别在于它提供了一个map方法,通过该map方法可以直接将一块数据映射到内存中。
Channel是一个接口,系统为该接口提供了FileChannel等实现类,所有的Channel都是通过传统节点InputStream、OutputSteam的getChannel方法来返回对应的Channel。
Channel中最常见的三个方法是:map、read和write。其中map将Channel对应的部分或全部数据映射的ByteBuffer,read或write方法有一系列重载的形式用于读取数据。
eg.将WelcomeServlet.java的内容复制到a.txt中去,并在控制台打印处内容。
public class FileChannelTest { public static void main(String[] args) { FileChannel inChannel=null; FileChannel outChannel=null; FileChannel randomChannel=null; File f=new File("D://WelcomeServlet.java"); try { FileInputStream fs=new FileInputStream(f); inChannel=fs.getChannel(); MappedByteBuffer buffer=inChannel.map(FileChannel.MapMode.READ_ONLY,0, f.length());//将inChannel里的全部数据映射成ByteBuffer Charset charset=Charset.forName("GBK"); outChannel=new FileOutputStream("D://a.txt").getChannel(); outChannel.write(buffer); buffer.clear(); CharsetDecoder decoder=charset.newDecoder();//创建解码器对象 CharBuffer charBuffer=decoder.decode(buffer);//使用解码器将ByteBuffer转换为charBuffer System.out.println(charBuffer); //获取对应字符串 } catch (FileNotFoundException e) { // TODO 自动生成的 catch 块 e.printStackTrace(); } catch (IOException e) { // TODO 自动生成的 catch 块 e.printStackTrace(); } } }