Java IO

253 阅读4分钟

window 默认的编码是gbk。可以使用chcp命令查看,结果为936.

抽象类inputstream (outputstream),面向字节。

这两个方法是针对字节的输入输出流,相对于reader、writer是针对字符的输入输出。从以下数据源读取数据
* 字节数组
* String对象
* 文件
* 管道

主要方法:
* read(),读取输入流的下一个字节。
(在outputstream中对应的是write())。用于读取一个字节。
基本方法很少使用,而是通过叠合多个对象来提供所期待的功能,这种成为装饰器模式。装饰器模式相比继承更好。

* available() ,还有多少字节可以读入,InputStream中直接return 0;

* read(byte[] b) 试图读入多个字节,存入字节数组b,返回实际读入的字节数
* public long skip(long n)throws IOException 跳过n个字节,返回实际跳过的字节数。
* void mark(int readlimit) ,在流的当前位置做个标记,参数readLimit指定这个标记的“有效期“,如果从标记处开始往后,已经获取或者跳过了readLimit个字节,那么这个标记失效,不允许再重新回到这个位置。
*  void reset() ,重定位到最近的标记,回到之前mark的位置。如果没有mark过或者标记失效了,抛出异常。

代码实例

InputStream is = null;
        try {
            //test.txt 内容。123456789
            is = new BufferedInputStream(new FileInputStream("C:\\Users\\jiaoz\\Documents\\test.txt"));
            //BufferedInputStream  是否支持mark(n)
            System.out.println(is.markSupported());
            //读两个字节
            System.out.println((char)is.read());
            System.out.println((char)is.read());

            is.mark(4);
            is.skip(2);
            is.reset();
            System.out.println((char)is.read());
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if (is != null) {
                is.close();
            }
        }

Reader(Writer),面向字符。

与inputStream面向字节不同。
主要方法:
*   read() Reads a single character.
*   read(char[] cbuf)

子类:
* InputStreamReader 适配器类,new InputStreamReader(new FileInputStream(file));参数为InputStream.可以读入一个字符。

* BufferedReader,构造方法 BufferedReader(Reader in)。readline()方法。

输入输出重定向

// 此刻直接输出到屏幕
 System.out.println("hello");
 File file = new File("d:" + File.separator + "hello.txt");
 try{
 System.setOut(new PrintStream(new FileOutputStream(file)));
 }catch(FileNotFoundException e){
 e.printStackTrace();
 }
 System.out.println("这些内容在文件中才能看到哦!");
 }                                                                                    

合并流 SequenceInputStream

 File file1 = new File("d:" + File.separator + "hello1.txt");
 File file2 = new File("d:" + File.separator + "hello2.txt");
 File file3 = new File("d:" + File.separator + "hello.txt");
 InputStream input1 = new FileInputStream(file1);
 InputStream input2 = new FileInputStream(file2);
 OutputStream output = new FileOutputStream(file3);
 // 合并流
 SequenceInputStream sis = new SequenceInputStream(input1, input2);
 int temp = 0;
 while((temp = sis.read()) != -1){
 output.write(temp);
 }
 input1.close();
 input2.close();
 output.close();
 sis.close();

文件压缩 ZipOutputStream类

30M的文件压缩完5M,耗时半分钟,非常慢。跟字节流有关。实例代码:
``` java
File file = new File("C:\\Users\\credit100\\Videos\\30KContainGuanggao.sql");
    File zipFile = new File("C:\\Users\\credit100\\Videos\\30KContainGuanggao.zip");
    System.out.println(file.getName());
    InputStream input = new FileInputStream(file);
    ZipOutputStream zipOut = new ZipOutputStream(new FileOutputStream(zipFile));
    zipOut.putNextEntry(new ZipEntry(file.getName()));
    // 设置注释
    zipOut.setComment("hello");
    int temp = 0;
    while((temp = input.read()) != -1){
        zipOut.write(temp);
    }
    input.close();
    zipOut.close();
```

PushBackInputStream回退流

unread方法,把已经读到的字节放回去,下次read的时候,可以再一次读到。
String str = "hello,rollenholt";
        PushbackInputStream push = null;
        ByteArrayInputStream bat = null;
        bat = new ByteArrayInputStream(str.getBytes());
        push = new PushbackInputStream(bat);
        int temp = 0;
        while ((temp = push.read()) != -1) {
            if (temp == ',') {
                push.unread(temp);
                temp = push.read();
                System.out.print("(回退" + (char) temp + ") ");
            } else {
                System.out.print((char) temp);
            }
        }

ObjectOutputStream 写入对象writeObject(new Object());readObject();

File file = new File("d:" + File.separator + "hello.txt");
 ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(
 file));
 //把二进制文件写入到hello.txt;
 oos.writeObject(new Person("rollen", 20));

RandomAccessFile 独立的IO类

seek(int n),前进那个字节。

管道流

用于多线程任务之间的通信。

nio

用nio读写示例:

        // Write a file:
        FileChannel fc =new FileOutputStream("C:\\Users\\jiaoz\\Documents\\MyCode\\api\\test\\data.txt").getChannel();
        fc.write(ByteBuffer.wrap("Some text ".getBytes()));
        fc.close();

        // Add to the end of the file:
        fc =new RandomAccessFile("C:\\Users\\jiaoz\\Documents\\MyCode\\api\\test\\data.txt", "rw").getChannel();
        fc.position(fc.size()); // Move to the end
        fc.write(ByteBuffer.wrap("Some more".getBytes()));
        fc.close();

        // Read the file:
        fc = new FileInputStream("C:\\Users\\jiaoz\\Documents\\MyCode\\api\\test\\data.txt").getChannel();
        ByteBuffer buff = ByteBuffer.allocate(BSIZE);
        fc.read(buff);
        buff.flip();
        while (buff.hasRemaining()){
            System.out.println();
            System.out.print((char) buff.get());}
    }

FileChannel 类

Java NIO FileChannel类是NIO用于替代使用标准Java IO API读取文件的方法。getChannel()方法获取fileChannel.

java io和nio中做文件复制的代码对比。

  • java io
        BufferedInputStream br=new BufferedInputStream(new FileInputStream(old));
        BufferedOutputStream bw=new BufferedOutputStream(new FileOutputStream(newCopy));
        byte[] bs=new byte[100];
        int n;
        while((n=br.read(bs))!=-1)
            bw.write(bs,0,n);

*java nio

        FileChannel oldfc=new FileInputStream(old).getChannel();
        FileChannel newfc=new FileOutputStream(newCopy).getChannel();
        oldfc.transferTo(0,oldfc.size(),newfc);
nio要比普通的io快很多,普通io的速度跟byte[]数组缓冲器大小有关系。

Buffer缓冲器的细节

buffer有四个重要的指标变量,分别为:

  • mark ,使用mark()方法标记当前postition为mark,reset()方法把position设置为mark
  • position,初始0、put,get方法会改变其值,get(int n)不会改变值。
  • limit,和capacity一直,初始执行最后。
  • capacity 重要的方法:
  • capacity() 返回容量
  • clear() position设为0, limit设为容量,可以调用此方法覆盖buffer
  • hasRemaining() 若position和limit中间有值,返回true

代码示例:

public class UsingBuffers {
 private static void symmetricScramble(CharBuffer buffer){
 while(buffer.hasRemaining()) {
 buffer.mark();
 char c1 = buffer.get();
 char c2 = buffer.get();
 buffer.reset();
 buffer.put(c2).put(c1);
 }
 }
 public static void main(String[] args) {
 char[] data = "UsingBuffers".toCharArray();
 ByteBuffer bb = ByteBuffer.allocate(data.length * 2);
 CharBuffer cb = bb.asCharBuffer();
 cb.put(data);
 print(cb.rewind());
 symmetricScramble(cb);
 print(cb.rewind());
 symmetricScramble(cb);
 print(cb.rewind());
 }
} /* Output:
UsingBuffers
sUniBgfuefsr 
UsingBuffers

buffer中的flip()和rewind()的区别

flip()做两件事儿:

  • 它将 limit 设置为当前 position。
  • 它将 position 设置为 0。

rewind()ONLY 做一件事儿:

  • 它将 position 设置为 0。

内存映射文件

把文件当做内存buffer来操作。使用这种方式,读写性能显著提升。
public class LargeMappedFiles {
 static int length = 0x8FFFFFF; // 128 MB
 public static void main(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’);
 print("Finished writing");
 for(int i = length/2; i < length/2 + 6; i++)
 printnb((char)out.get(i));
 }
}

文件加锁

java文件加锁直接映射到本地操作系统的加锁工具,所以对于其他java虚拟机或者其他非java进程,均可见。