JAVA IO系统

Posted by AaronYang on February 12, 2020

Java IO系统

Java类库中的I/O类分成输入和输出两部分,通过继承,任何自InputStream或Reader派生而来的类都含有名为read()的基本方法,同样,任何自OutputStream或Writer派生而来的类都含有名为write()的基本方法。Java中“流”类库让人迷惑的主要原因在于:创建单一的结果流,却需要叠合多个对象来提供所期望的功能

设计Reader和Writer继承层次主要是为了国际化,老的I/O流继承层次结构仅支持8位字节流,并且不能很好地处理16位的Unicode字符。由于Unicode用于字符国际化,所以添加Reader和Writer继承层次结构就是为了在所有的I/O操作中都支持Unicode。

IO流的典型使用方式

缓冲输入文件

如果想要打开一个文件用于字符输入,可以使用以String或File对象作为文件名的FileInputReader。为了提高速度,我们希望对这个文件进行缓冲,那么我们将所产生的引用传给一个BufferedReader构造器。如果想要打开一个文件用于字节输入,可以使用以String或File对象作为文件名的FileInputStream,之后将用InputStreamReader进行适配后传给BufferedReader构造器。在使用InputStreamReader适配时需要注意编码规则的匹配,否则会出现乱码问题。

//字节流输入
public static String readFile(String filename){
	try{
		FileInputStream fileInputStream = new FileInputStream(filename);
		BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(fileInputStream,"UTF-8"));
		StringBuilder file = new StringBuilder();
		String line;
		while((line = bufferedReader.readLine())!=null){
			file.append(line+"\n");
		}
		fileInputStream.close();
		return file.toString();
	}catch (Exception e){
		e.printStackTrace();
	}
	return "";
}
//字符流输入
public static String readFile(String filename){
    try{
        BufferedReader bufferedReader = new BufferedReader(new FileReader(filename));
        StringBuilder file = new StringBuilder();
        String line;
        while((line = bufferedReader.readLine())!=null){
            file.append(line+"\n");
        }
        bufferedReader.close();
        return file.toString();
    }catch (Exception e){
        e.printStackTrace();
    }
    return "";
}

从内存输入

将文件读入的String结果用来创建StringReader。然后用read()每次读取一个字符。

public static void main(String[] args) throws IOException {
    File path = new File("D:\\Project\\SearchServerForJava\\universe\\utils\\src\\main\\java\\com\\lyricyang\\knowledge\\utils\\mail\\MailUtils.java");
    StringReader in = new StringReader(bufferedInputFile(path.getPath()));
    int c;
    while((c=in.read())!=-1){
        System.out.println((char)c);
    }
}

public static String bufferedInputFile(String filename) throws IOException {
    BufferedReader in = new BufferedReader(new FileReader(filename));
    String s;
    StringBuilder sb = new StringBuilder();
    while((s=in.readLine())!=null){
        sb.append(s + "\n");
    }
    in.close();
    return sb.toString();
}

存储和恢复数据

DataOutputStream数据输出流允许应用程序将基本Java数据类型写到基础输出流中,而DataInputStream数据输入流允许应用程序以机器无关的方式从底层输入流中读取基本的Java类型。无论读写平台有多么不同,Java都可以保证我们可以准确读取数据。

public static void main(String[] args) throws IOException {
    File path = new File("D:\\Project\\SearchServerForJava\\universe\\utils\\src\\main\\java\\com\\lyricyang\\knowledge\\utils\\mail\\test.txt");
    try {
        DataOutputStream out = new DataOutputStream(new FileOutputStream(path.getPath()));
        out.writeUTF("α");
        out.writeInt(123456);
        out.writeBoolean(true);
        out.writeShort((short)123);
        out.writeLong((long)456);
        out.writeDouble(99.88);
        DataInputStream in = new DataInputStream(new FileInputStream(path.getPath()));
        System.out.println(in.readUTF());
        System.out.println(in.readInt());
        System.out.println(in.readBoolean());
        System.out.println(in.readShort());
        System.out.println(in.readLong());
        System.out.println(in.readDouble());
        in.close();
        out.close();
    }catch (Exception e){
        e.printStackTrace();
    }
}

新IO(NIO)

JDK 1.4的java.nio.*包中引入了新的Java I/O类库,其目的在于提高速度。速度的提高来自于所使用的结构更接近于操作系统执行IO的方式:通道和缓冲器

唯一直接与通道交互的缓冲器是ByteBuffer,也就是说,可以存储未加工字节的缓冲器。

public static void main(String[] args) throws IOException {
    File path = new File("D:\\Project\\SearchServerForJava\\universe\\utils\\src\\main\\java\\com\\lyricyang\\knowledge\\utils\\mail\\test.txt");
    FileChannel fc = new FileOutputStream(path.getPath()).getChannel();
    fc.write(ByteBuffer.wrap("Some more".getBytes()));
    fc.close();
    fc = new RandomAccessFile(path.getPath(),"rw").getChannel();
    fc.position(fc.size());
    fc.write(ByteBuffer.wrap("Some more".getBytes()));
    fc.close();
    fc = new FileInputStream(path.getPath()).getChannel();
    ByteBuffer buffer = ByteBuffer.allocate(1024);
    fc.read(buffer);
    buffer.flip();
    while(buffer.hasRemaining()){
        System.out.print((char) buffer.get());
    }
}

transient关键字

当我们对序列化进行控制时,可能某个特定子对象不想让Java的序列化机制自动保存于恢复,则可以用transient关键字逐个字段关闭序列化。

public class Logon implements Serializable
{
    private Date date = new Date();
    private String username;
    private transient String password;

    public Logon(String name, String pwd){
        username = name;
        password = pwd;
    }

    @Override
    public String toString() {
        return "Logon{" +
                "date=" + date +
                ", username='" + username + '\'' +
                ", password='" + password + '\'' +
                '}';
    }

    public static void main(String[] args) throws IOException, ClassNotFoundException {
        Logon a = new Logon("Hulk","mypassword");
        System.out.println("Logon a = " + a);
        ObjectOutputStream out = new ObjectOutputStream(
                new FileOutputStream("D:\\Project\\SearchServerForJava\\universe\\utils\\src\\main\\java\\com\\lyricyang\\knowledge\\utils\\mail\\out.txt")
        );
        out.writeObject(a);
        out.close();
        ObjectInputStream in = new ObjectInputStream(
                new FileInputStream("D:\\Project\\SearchServerForJava\\universe\\utils\\src\\main\\java\\com\\lyricyang\\knowledge\\utils\\mail\\out.txt")
        );
        System.out.println("Recovering object at " + new Date());
        a = (Logon) in.readObject();
        System.out.println("Logon a = " + a);
    }
}