1、标准I/O:按照Java标准I/O模型,Java提供了System.in、System.out、System.err。其中System.out、System.err已经事先被包装成了printStream,而System.in却是一个没有被包装的未经加工的InputStream。这意味着我们可以直接使用System.out、System.err,但是使用System.in的时候必须对其进行包装。
例如:BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
2、标准I/O重定向:如下静态方法允许我们对标准输入、标准输出、I/O错误流 重定向:
setIn(InputStream);
setOut(PrintStream);
setErr(PrintStream);
示例:
PrintStream console= System.out;
BufferedInputStream bi = new BufferedInputStream (new FileInputStream("test.java"));
BufferedOutputStream bo= new BufferedOutputStream (new FileOutputStream("test.2java"));
System.setIn(bi);
System.setOut(bo);
System.setErr(bi);
标准输入附接到文件上,并将标准输出及I/O错误附接到另外一个文件上。
注意:I/O重定向操纵的是字节流而不是字符流。
3、Java Nio
3.1、JDK 1.4的java.nio.*包引入了新的JavaI/O类库,其目的是提高速度。实际上,旧的I/O已经使用nio重新实现过,以便充分的利用这种速度的提高,因此即使不显示的使用nio编写代码,也能从中受益。
速度的提高来自于所使用的结构更接近于操作系统执行I/O的方式:通道和缓冲器。我们可以把I/O想象成一个煤矿,通道是矿藏。而缓冲器是运送煤炭的卡车。卡车载满煤炭而归,而我们再从卡车上获得煤炭。也就是说我们并不直接跟通道交互,我们只是和缓冲器交互,并把缓冲器派送到通道。通道要么从缓冲器中获取数据,要么向缓冲器中发送数据。
唯一直接与通道交互的缓冲器是ByteBuffer -- 可以存储未加工字节的缓冲器。该类是一个很基础的类,用于以原始的字节形式或者基本数据类型输出和读取数据,该类无法输出和读取对象,即使是字符串对象也不行。这种处理虽然低级,却正好,因为这是大多数操作系统中更有效的映射方式。
在旧的I/O库中有三个类被修改了 -- FileInputStream、FileOutputStream以及用于既读又写的RandomAccessFile。注意这些字节操纵流,与底层的nio性质一致。Reader和Writer这种面向字符的类不能产生通道。但是,java.nio.chanels.Channels类提供了实用方法,用于在通道中产生Reader和Writer。
示例:FileChannel fc = new FileInputStream(“data.txt”).getChannel();
fc.write(ByteBuffer.wrap("some more".getBytes()));
fc.close();
通道是一种相当基础的东西:可以向它传送用于读写的ByteBuffer,并且可以锁定文件的某些区域用于独占式的访问。
将数据存放到ByteBuffer有两种方式:a)使用put方式,直接进行填充,填入一个或多个字节,或基本数据类型的值。b)使用ByteBuffer的wrap方法,将已存在的字节数组“包装”到ByteBuffer中。一旦如此,就不再复制底层的数组,而是把它作为所产生的ByteBuffer的存储器,称之为数组支持的ByteBuffer。
对于只读访问,我们必须显示地使用静态的allocate()方法来分配ByteBuffer。示例:
fc = new FileInputStream("data,text").getFileChannel();
ByteBuffer bb = ByteBuffer.allocate(1024);
fc.read(bb);
bb,flip();
while(bb.hasRemaining()){
System.out.println((Char)bb.get());
}
一旦调用read方法,就必须调用缓冲器上的flip方法,让它做好别人读取字节的准备。如果打算使用缓冲器进行进一步的read操作,就必须调用clear来为每一个read做好准备。如下:
FileChannel in = new FileInputStream("source,txt").getChannel();
FileChannel out = new FileOutputStream("dest,txt").getChannel();
ByteBuffer buffer= ByteBuffer.allocate(1024);
while(in.read(buffer)!=-1){
buffer.flip();
out.write(buffer);
buffer.clear();
}
上面的程序并不是处理此类操作的理想方式。特殊方法transferTo和transferFrom允许我们将一个通道和另一个通道直接相连:
FileChannel in = new FileInputStream("source,txt").getChannel();
FileChannel out = new FileOutputStream("dest,txt").getChannel();
in.transferTo(0,in.size(),out);ByteBuffer还有一个rewind方法,调用该方法是为了返回到数据开始的部分。
java.nio.CharBuffer这个类,它的toString方法是这样定义的:“返回包含一个缓冲器中所有字符的字符串”。ByteBuffer可以看作是具有asCharBuffer方法的CharBuffer.
示例:
FileChannel fc = new FileInputStream(“data.txt”).getChannel();
fc.write(ByteBuffer.wrap("some more".getBytes()));
fc.close();
ByteBuffer buffer= ByteBuffer.allocate(1024);
------------------------------------------------------------------------------------------buff.clear();
fc.read(buffer);
buffer.flip();
System.out.println(buffer.asCharBuffer());
-->????
可以看到输出的是乱码,为什么呢?这是因为缓冲器容纳的是普通的字节,为了把它们转换成字符,我们要么在输入它们的时候对其进行编码,要么在将其从缓冲器输出的时候对其进行解码。如下所示,我们可以使用System.getProperty("file.encoding");发现默认字符集,它会产生代表字符集名称的字符串。把该字符串传送给CharSet.forName()用以产生CharSet对象,使用它对字符串进行编码。CharSet.forName(System.getProperty("file.encoding")).encode(buffer);此时在进行上面的操作就可以看到正确的输出“some more”了。另一种做法是在写入的时候,进行编码即可。示例:fc.write(ByteBuffer.wrap("some more".getBytes(“UTF-16BE”)));
在分配一个ByteBuffer之后,可以通过检测它的值得方式来查看缓冲器的分配方式是否将其内容自动置零。
3.2、视图缓冲器
向ByteBuffer插入基本类型数据最简单的方式是通过视图缓冲器:利用asCharByteBuffer、asShortByteBuffer等可以获取视图缓冲器。使用put方法得时候,需注意:put方法适用于所有的基本类型,除了ShortByteBuffer的put方法需要类型转换之外,其余的不需要进行类型转换。ByteBuffer仍旧是实际存储数据的地方,对视图的任何操作都会反映到实际的ByteBuffer的。一旦底层Bytebuffer填满了整数或者基本类型时,就可以向直接被写到通道中。
3.3、字节的排放顺序
ByteBuffer按照高位优先(将最重要的字节存放在地址最低的存储器单元)的形式存储数据,并且数据在网上传送时也常常使用高位优先的形式。
ByteBuffer是将数据移近移出通道的唯一方式,并且我们只能创建一个独立的基本类型缓冲器,或者使用“as”方法从ByteBuffer中获得。
3.4、内存映射文件
内存映射文件允许我们创建和修改那些因为太大而不能放入内存的文件。有了 内存映射文件,我们就可以假定这个文件都放在内存中,而且可以完全把它当作非常大的数组来访问。这种方法极大的简化了用于修改文件的代码。
public class LargeMappedFiles{
static int length = 0x8FFFFFF;//128MB
public static void mian(String[] args) throws Exception{
MappedByteBuffer out = new RandomAccessFile("test.dat","rw").getChannel().map(FileChannel.MapMode.READ_WRITE,0,length);
for(int i=0;i<length;i++){
out.put((byte)‘x’);
}
for(int i=length/2;i < length/2 + 6;i++){
println((char)out.get(i));
}
}
}
MappedByteBuffer 是一种特殊类型的直接缓冲器。注意我们必须指定映射文件的初始位置和映射区域的长度。这意味着我们可以映射某个大文件的较小的部分。MappedByteBuffer 由ByteBuffer继承而来,因此它具有ByteBuffer的所有方法。前面程序创建的文件是128MB,这可能比操作系统所允许一次载入内存的空间大。但似乎我们可以一次访问到整个文件,因为只有一部分文件放入内存,文件的其他部分被交换了出去。用这种方式,很大的文件(可达2GB)也可以很容易的修改。注意底层操作系统的文件映射工具是用来最大化的提高性能。