我們將會介紹如何在JAVA中進行輸入和輸出操作。
part 1 大體介紹輸入輸出流
在JavaAPI中,可以從其中讀入一個字節序列的對象稱作輸入流,而可以向其中寫入一個字節序列的對象稱爲輸出流。這些字節序列的來源和目的地可以是文件,也可以是網絡連接,甚至內存塊。**抽象類!**InputStream 和 OutputStream 構成了輸入和輸出各類層次的基礎。
part 2.1 讀寫字節虛類的介紹
在InputStream中有許多很有用的抽象方法,其中有一些可能會在子類中重寫,例如:
read()方法。這個方法將讀入一個字節,並返回讀入的字節,或者在遇到輸入源結尾時返回-1。在我們具體的輸入流類中,他往往被重寫以提供適用的功能。
與之類似OutputStream類中也定義了類似的方法——abstract void write(int b)
read和write方法在執行時都將阻塞,直至字節確實被讀入或寫出。
available方法可以讓我們去檢查當前可讀入的字節數量,那我們就可以通過這個方法來避免,read和write沒有可讀信息的阻塞:
int by = in.available();
if(by > 0)
{
byte[] data = new byte[byteAvailable];
in.read(data);
}
當我們完成了對輸入/輸出流的讀寫時,應該通過他們的close方法來關閉它,這可以及時釋放我們有限的系統資源。
我們可以使用衆多繼承InputStream和OutputStream的類,而不是直接使用字節
Tip:我們的讀寫都是按照一個字節進行的,但是Unicode編碼有兩個字節,所以我們對Unicode文本建立了Reader和Writer的抽象類,基本方法與InputStream和OutputStream類似。
part 2.2 輸入/輸出流過濾器的組合
FileInputStream和FileOutputStream可以提供磁盤上文件的輸入流和輸出流,而你只需要提供文件民或者文件的完整路徑即可。但是注意,他們是抽象類InputStream和OutputStream類一樣,是基於一個字節操作的。
FileInputStream fin = new FileInputStream("emplotee.dat");
上面那行代碼可以查看用戶目錄下的名爲employee.dat的文件。(我們當然可以絕對路徑)
我們以後會看到,DataInputStram,只能讀入數值類型。
而FileInpuStream沒有任何讀入數值類型的方法,DataInputStream也沒有任何從文件中獲取數據的方法。
在Java中,我們使用一種機制來融合分離這兩種職責,簡單說,這過程感覺像是過濾一樣,我們可以這樣:
FileInputStram fin = new FileInputStream("emplotee.dat");
DataInputStream din = new DataInputStream(fin);
double x = din.readDouble();
我們可以通過嵌套過濾器來添加多重功能過濾來獲得數據。例如,輸入流在默認情況下是不被緩衝區緩存的,相比之下,請求一個數據塊並將其置於緩衝區會顯得更加高效。如果我們想使用緩衝機制,以及用於文件的數據輸入方法,那麼就需要使用下面這種構造序列:
DataInputStream din = new DataInputStream(
new BufferedInputStream(
new FileInputStream("employee.dat")));
part 2.3 如何寫出文本輸出
對於文本輸出,我們用PrintWriter。這個類擁有以文本格式打印字符串和數字的方法,它還有一個將PrinterWriter鏈接到FileWriter的便捷方法:
PrintWriter out = new PrintWriter("employee.txt","UTF-8");
等同於:
PrintWriter out = new PrintWriter(new FileOutputStream("employee.txt"),"UTF-8");
爲了打印寫出器,需要使用System.out的print, println, printf方法,例如:
//上面有out了
out.print();
part 2.4 如何讀入文本輸入
最簡單的處理任意文本的方式就是我們常見的Scanner類。
我們可以將短小的文本文件,像這樣讀入到這樣的一個字符串:
String content = new String(Files.readALLbYTES(path),charset);
如果我們想將這個文件一行行地讀入,那我們可以這樣:
List<String> lines = Files.readAllLines(path, charset);
如果文件太大,那麼可以將行惰性處理爲一個Stream< String >對象:
try(Stream< String >lines = Files.lines(path, charset))
{
...
}
part 2.3 以文本格式存儲對象
package TextFileTest;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.PrintWriter;
import java.io.UnsupportedEncodingException;
import java.util.Scanner;
public class TextTest {
public static void main(String[] args) throws IOException {
Employee[] staff = new Employee[3];
// 創建數組內容
staff[0] = new Employee("黃一東",10000);
staff[1] = new Employee("黃瑋東",20000);
staff[2] = new Employee("黃瑋琛",30000);
// 寫出通過寫出類來 寫出staff的內容 。 這裏的PrintWriter自己內部會實現FileOutputStream
try(PrintWriter out = new PrintWriter("Employee.dat","UTF-8"))
{
// 調用WriteData 先向文本寫入數組的大小,再根據數組大小分別調用WriteEmpolyee
// 按照格式寫入各個數據
writeData(staff, out);
}
// 通過讀入類 Scanner來讀入employee.dat(UTF-8)的內容 這裏顯式的調用了
// FileInputStream類 組合了流類
try(Scanner in = new Scanner(new FileInputStream("employee.dat"),"UTF-8"))
{
// 這裏調用readData(文件讀入流對象) 先獲取文件第一行保存的對象行數(n),然後再跳到下一行
// 調用readEmployee每次讀取一行內容 循環n次 每次保存內容到數組newStaff中
Employee[] newStaff = readData(in);
for(Employee e:newStaff)
System.out.println(e);
}
}
private static void writeData(Employee[] employees, PrintWriter out)throws IOException{
out.println(employees.length);
for(Employee e:employees)
writeEmployee(out, e);
}
public static void writeEmployee(PrintWriter out, Employee e){
out.println(e.getName() + "|" + e.getSalary());
}
private static Employee[] readData(Scanner in){
// 得到數組大小
int n = in.nextInt();
in.nextLine();
Employee[] employees = new Employee[n];
for(int i = 0; i < n; i++)
{
employees[i] = readEmployee(in);
}
return employees;
}
public static Employee readEmployee(Scanner in){
String line = in.nextLine();
String[] tokens = line.split("\\|");
String name = tokens[0];
int salary = Integer.parseInt(tokens[1]);
return new Employee(name, salary);
}
}