`

Trail: Essential Classes_Lesson: Basic I/O1

 
阅读更多

IO流代表着输入的来源和输出的目的地,如文件,设备,其他程序,内存等

可以有很多类型的数据,如简单的字节,包装过的数据包,本地化过的字符,或者对象

有的流只是传递,有的流可以包装并处理

简单的说,流,就是一个数据的序列

 

所有的字节流都继承自InputStream和OutputStream,字节流是所有io流的基础

 

public class CopyBytes {
    public static void main(String[] args) throws IOException {//一个示例,实际上很少这样用,因为效率太低

        FileInputStream in = null;
        FileOutputStream out = null;

        try {
            in = new FileInputStream("xanadu.txt");
            out = new FileOutputStream("outagain.txt");
            int c;

            while ((c = in.read()) != -1) {//每次读一个字节
                out.write(c);
            }
        } finally {
            if (in != null) {//这一个判断是必须的
                in.close();
            }
            if (out != null) {
                out.close();
            }
        }
    }
}

 

 

InputstreamReader和OutputStreamWriter会将底层的字节流包装成字符流

 

BufferedReader和PrintWriter可以让你一行一行的处理字符 (非字节)

 

import java.io.FileReader;
import java.io.FileWriter;
import java.io.BufferedReader;
import java.io.PrintWriter;
import java.io.IOException;

public class CopyLines {
    public static void main(String[] args) throws IOException {

        BufferedReader inputStream = null;
        PrintWriter outputStream = null;

        try {
            inputStream = new BufferedReader(new FileReader("xanadu.txt"));
            outputStream = new PrintWriter(new FileWriter("characteroutput.txt"));

            String l;
            while ((l = inputStream.readLine()) != null) {//读取一行
                outputStream.println(l);//写入一行
            }
        } finally {
            if (inputStream != null) {
                inputStream.close();
            }
            if (outputStream != null) {
                outputStream.close();
            }
        }
    }
}



 

BufferedInputStream是从内存中读取数据,直到内存的缓冲区空了,才会调用系统最底层的方式去读取原始字节流,所以效率较高

所以他们的构造方法通常传递一个原始的输入流

 

BufferedOutputStream则是缓冲区满了才会写入底层

需要的时候,通常要flush一下,强制传输所有缓存,当然不是缓冲输出流的话调用是无效的

一些额外的构造方法能够让缓冲输出流启用自动flush功能

 

有的流能够改变原始流的格式

Scanner能够分离出输入流的各个部分从而单独处理,一般是用空格,tab或回车分隔

 

s.useDelimiter(",\\s*");//也可以手动更换分隔符

 

 

import java.io.*;
import java.util.Scanner;

public class ScanXan {
    public static void main(String[] args) throws IOException {

        Scanner s = null;

        try {
            s = new Scanner(new BufferedReader(new FileReader("xanadu.txt")));

            while (s.hasNext()) {
                System.out.println(s.next());
            }
        } finally {
            if (s != null) {
                s.close();//Scanner虽然只是处理流,但也是要关闭的,因为它里面有包装的原始输入流

            }
        }
    }
}

 

 

s.useLocale(Locale.US);//也可以设置区域,这样数字中也可能会有千位分隔符之类的

但输出时要想正确显示区域,还要用PrintStream或PrintWriter的format()方法,将输出流分解

 

System.out.format("The square root of %d is %f.%n", i, r);//System.out正好是PrintStream类型

除了%%%n,都需要一个参数,否则会报错

 

当然print和pirntln也有一点点格式化功能,只是调用他们的toString()而已

 

 

InputStreamReader cin = new InputStreamReader(System.in);//跟System.out不同,System.in是个字节流,通常需要包装为字符流

 

要想跟控制台交互,除了使用System.in,System.out,System.err,还可以使用java.util.Console类:

 

<pre>import java.io.Console;
import java.util.Arrays;
import java.io.IOException;

public class Password {
    
    public static void main (String args[]) throws IOException {

        Console c = System.console();//这个代表控制台实例
        if (c == null) {//null代表没有控制台,有可能是运行方式不允许交互
            System.err.println("No console.");
            System.exit(1);
        }

        String login = c.readLine("Enter your login: ");//读取控制台
        char [] oldPassword = c.readPassword("Enter your old password: ");//会以*显示,返回的是字符数组而不是String

        if (verify(login, oldPassword)) {
            boolean noMatch;
            do {
                char [] newPassword1 = c.readPassword("Enter your new password: ");
                char [] newPassword2 = c.readPassword("Enter new password again: ");
                noMatch = ! Arrays.equals(newPassword1, newPassword2);
                if (noMatch) {
                    c.format("Passwords don't match. Try again.%n");
                } else {
                    change(login, newPassword1);
                    c.format("Password for %s changed.%n", login);//控制台输出
                }
                Arrays.fill(newPassword1, ' ');
                Arrays.fill(newPassword2, ' ');
            } while (noMatch);
        }

        Arrays.fill(oldPassword, ' ');
    }

 


Data流可以读写各种原始数据类型和String的二进制读写

 

try {
    while (true) {
        price = in.readDouble();
        unit = in.readInt();
        desc = in.readUTF();
        System.out.format("You ordered %d" + " units of %s at $%.2f%n",
            unit, desc, price);
        total += unit * price;
    }
} catch (EOFException e) {//但Data输入流判断结束是用异常的
}

Data流有个缺点,内部使用浮点数保存货币值,建议使用java.Math.BigDecimal类型,但却不是原始类型了

需要使用Object Stream

 

实现了序列化Serializable接口的对象都可以使用对象流,对象流实现的接口是Data流实现的接口的子类

所以所有方法都继续存在
如果readObject()方法没有返回一个期望的对象,试图转换时会报异常ClassNotFound

 

import java.io.*;
import java.math.BigDecimal;
import java.util.Calendar;
 
public class ObjectStreams {
    static final String dataFile = "invoicedata";
 
    static final BigDecimal[] prices = { 
        new BigDecimal("19.99"), 
        new BigDecimal("9.99"),
        new BigDecimal("15.99"),
        new BigDecimal("3.99"),
        new BigDecimal("4.99") };
    static final int[] units = { 12, 8, 13, 29, 50 };
    static final String[] descs = { "Java T-shirt",
            "Java Mug",
            "Duke Juggling Dolls",
            "Java Pin",
            "Java Key Chain" };
 
    public static void main(String[] args) 
        throws IOException, ClassNotFoundException {
 
  
        ObjectOutputStream out = null;
        try {
            out = new ObjectOutputStream(new
                    BufferedOutputStream(new FileOutputStream(dataFile)));
 
            out.writeObject(Calendar.getInstance());
            for (int i = 0; i < prices.length; i ++) {
                out.writeObject(prices[i]);
                out.writeInt(units[i]);
                out.writeUTF(descs[i]);
            }
        } finally {
            out.close();
        }
 
        ObjectInputStream in = null;
        try {
            in = new ObjectInputStream(new
                    BufferedInputStream(new FileInputStream(dataFile)));
 
            Calendar date = null;
            BigDecimal price;
            int unit;
            String desc;
            BigDecimal total = new BigDecimal(0);
 
            date = (Calendar) in.readObject();
 
            System.out.format ("On %tA, %<tB %<te, %<tY:%n", date);
 
            try {
                while (true) {
                    price = (BigDecimal) in.readObject();
                    unit = in.readInt();
                    desc = in.readUTF();
                    System.out.format("You ordered %d units of %s at $%.2f%n",
                            unit, desc, price);
                    total = total.add(price.multiply(new BigDecimal(unit)));
                }
            } catch (EOFException e) {}
            System.out.format("For a TOTAL of: $%.2f%n", total);
        } finally {
            in.close();
        }
    }
}


在序列化过程中,同一个序列 (输出流) 只会包含一个对象的唯一一份拷贝

 

Object ob = new Object();
out.writeObject(ob);
out.writeObject(ob);
Object ob1 = in.readObject();
Object ob2 = in.readObject();

读出来仍然是同一个对象

 

 

 

 

 

 

分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics