1. Abstract:
Java將I/O分爲高階I/O與低階I/O,高階I/O在使用上提供更多的讀寫方法,如讀寫int、double、String的資料型態,而低階的I/O大部份只提供write、read的byte[]存取,由於程式大部份的資料都是以字串或其它主要型態資料來運算,所以低階的I/O在使用上不利於程式設計,因此Java將許多好用的方法所有集合成高階I/O; 換言之,低階I/O的主要工做是負責與媒體資料做存取,高階I/O類別主要做資料型態的轉換及提供一些特殊的功能。在使用Java I/O時要謹記的一個重要原則是,在創建一個I/O以前必需先用低階I/O類別來存取媒體資料(如檔案或pipe),之後再使用高階I/O來控制低階I/O類別的動做,這種一層又一層的架構稱I/O Chain。底下爲Java的I/O架構圖,第一個爲以byte爲單位的I/O,第二個則是以char爲單位。
2. File I/O:
A. FileInputStream & FileOutputStream
FileInputStream是讀取檔案用的類別,其建構式有叄個:
public FileInputStream(String strFilename) throws FileNotFoundException
public FileInputStream(File fIn) throws FileNotFoundException
public FileInputStream(FileDescriptor fdObj)
在這裏我只講第一個,這是最直覺的方式,以下的範例1,會一次從e:\test.txt讀10個bytes,將讀入的結果輸出到標準輸出設備,直到檔案結束。在這個範例中要注意的是,available會傳回輸入串流中還有多少個bytes,read則會根據buffer的大小來決定一次讀幾個bytes,並將實際讀到的byte數傳回。
===== 範例 1 =====
import java.io.*;
public class FIn {
public FIn() {
try {
FileInputStream fis = new FileInputStream("e:/in.txt");
while (fis.available() > 0) {
byte[] b = new byte[10];
int nResult = fis.read(b);
if (nResult == -1) break;
System.out.println(new String(b));
}
fis.close();
}
catch (IOException e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
FIn fIn = new FIn();
}
}
FileOutputStream是寫入檔案用的類別,其建構式有四個:
Public FileOutputStream(String strFilename) throws FileNotFoundException
Public FileOutputStream(File fOut) throws FileNotFound Exception
Public FileOutputStream(FileDescriptor fdObj)
public FileOutputStream(String name, boolean append) throws FileNotFoundException
第四個和第一個的差異只在於當檔案存在時,第一個會將原來的檔案內容覆蓋,第四個則能夠選擇覆蓋或將新內容接在原內容後面。範例2以建構式一講解如何寫入一個檔案…在這個範例中要注意的是,fIn每一個讀10個bytes,可是最後一次不必定會讀10個bytes,所以,fOut在write時,要指明要寫幾個bytes到檔案中,不然最後一次仍會寫入10個bytes,因Java在new byte時會先將內容先填0,因此後面的幾個bytes會是0。
===== 範例2 =====
import java.io.*;
public class FOut {
public FOut() {
try {
FileInputStream fIn = new FileInputStream("e:/in.txt");
FileOutputStream fOut = new FileOutputStream("e:/out.txt");
while (fIn.available() > 0) {
byte[] b = new byte[10];
int nResult = fIn.read(b);
if (nResult == -1) break;
fOut.write(b, 0, nResult);
}
fIn.close();
fOut.close();
}
catch (IOException e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
FOut FOut1 = new FOut();
}
}
B. FileReader & FileWriter
FileReader和FileInputStream最大不一樣在於,FileInputStream讀取的單位是byte,FileReader讀取的單位是char。另外要注意的是,在FileInputStream中以available來判斷是否還有資料可讀取,在FileReader中是以ready來判斷,
可是,available是傳回還有多少個bytes能夠讀取,ready則傳回true或false,當傳回true時表示,下次read時保證不會停頓,當傳回false時,表示下次read時」可能」停頓,所謂多是指不保證不會停頓。
Ps. 測試時,in.txt裏放些中文字就能夠看出以byte和以char爲單位有什麼不一樣。
===== 範例 3 =====
import java.io.*;
public class chFIn {
public chFIn() {
try {
FileReader rdFile = new FileReader("e:/in.txt");
while (rdFile.ready()) {
char[] chIn = new char[10];
int nResult = rdFile.read(chIn);
if (nResult == -1) break;
System.out.println(chIn);
}
rdFile.close();
}
catch (IOException e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
chFIn chFIn1 = new chFIn();
}
}
FileWriter和FileOutputStream的最大不一樣也在於寫入單位的不一樣,FileOutputStream爲byte,FileWriter爲char。
===== 範例 4 =====
import java.io.*;
public class chFOut {
public chFOut() {
try {
FileReader rdFile = new FileReader("e:/in.txt");
FileWriter wrFile = new FileWriter("e:/out.txt");
while (rdFile.ready()) {
char[] chIn = new char[10];
int nResult = rdFile.read(chIn);
if (nResult == -1) break;
wrFile.write(chIn, 0, nResult);
}
rdFile.close();
wrFile.close();
}
catch (IOException e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
chFOut chFOut1 = new chFOut();
}
}java
C. BufferedReader & BufferedWriter
File I/O是至關耗時的做業,一般電腦在作處理時,者會創建一個緩衝區,一次讀取或寫入一個區塊,藉由減小I/O次數,來節省時間 ,在Java中的BufferedReader和BufferedWriter就是提供這樣的緩衝功能。
在範例5中,咱們將FileReader導向BufferedReader,將FileWriter導向BufferedWriter,來達到區塊讀取、寫入的目的。BufferedReader提供的readLine一次能夠讀取一行,當遇到檔尾時,會傳回null。BufferedWriter提供的newLine會產生列尾符號,這個列尾符號隨做業系統的不一樣而不一樣,在Windows上爲\r\n,在Unix上爲\n,在Mac上爲\r,這個符號是依據line.separator系統性質而來的。需注意的是,若是將BufferedWriter應用到網路程式時,絕對不要使用newLine,由於絕大多數的網路協定都是以\r\n爲列尾,不會因做業系統不一樣而異。
===== 範例 5 =====
import java.io.*;
public class bufIn {
public bufIn() {
try {
FileReader rdFile = new FileReader("e:/in.txt");
BufferedReader brdFile = new BufferedReader(rdFile);
FileWriter wrFile = new FileWriter("e:/out.txt");
BufferedWriter bwrFile = new BufferedWriter(wrFile);
String strLine;
while ((strLine = brdFile.readLine()) != null) {
bwrFile.write(strLine);
bwrFile.newLine();
}
brdFile.close();
bwrFile.close();
}
catch (IOException e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
bufIn bufIn1 = new bufIn();
}
}
D. File
在檔案處理方面,程式不僅是要對檔案作讀、寫,有時也須要得知檔案的屬性,或刪除、移動、改名,有時則是要找出或列出某目錄下的某些檔案,針對這些運做,Java提供了File這個類別。底下的範例,說明如何使用File類別。
a. 如何得知檔案屬性:
在範例6中需注意的是lastModified傳回的最後更改時間是自1970/1/1 00:00:00算起的時間,單位爲毫秒,因此要用Date將它轉換成日期、時間; 另外getCanonicalPath和getAbsolutePath獲得的值在Windows上會是同樣的,在Unix可能就會不同。
===== 範例 6 =====
import java.io.*;
import java.util.*;
public class FileSpy {
public FileSpy(String strFilename) {
File fFile = new File(strFilename);
if (fFile.exists()) {
System.out.println("Name: " + fFile.getName());
System.out.println("Absolute path: " + fFile.getAbsolutePath());
try {
System.out.println("Canonical path: " + fFile.getCanonicalPath());
}
catch (IOException e) {
e.printStackTrace();
}
if (fFile.canWrite()) System.out.println(fFile.getName() + " is writable");
if (fFile.canRead()) System.out.println(fFile.getName() + " is readable");
if (fFile.isFile()) {
System.out.println(fFile.getName() + " is a file");
}
else if (fFile.isDirectory()) {
System.out.println(fFile.getName() + " is a directory");
}
else {
System.out.println("What is this?");
}
long lngMilliseconds = fFile.lastModified();
if (lngMilliseconds !=0) System.out.println("last modified at " + new Date(lngMilliseconds));
long lngLen = fFile.length();
if (lngLen !=0) System.out.println("size: " + lngLen);
}
else
System.out.println("file not found");
}
public static void main(String[] args) {
if (args.length == 1) {
FileSpy fileSpy1 = new FileSpy(args[0]);
}
else
System.out.println("Usage: java FileSpy Filename");
}
}
b. 創建、刪除、移動、改名:
File類別提供了createNewFile、renameTo、delete做爲創建(createNewFile)、刪除(delete)、移動、改名(renameTo)之用,使用方式以下: (移動和改名都用renameTo,就如在Unix上檔案搬移和改名都用mv同樣)
===== 範例 7 =====
import java.io.*;
public class OperateFile {git
public OperateFile() {
//create new file
File fNewFile = new File("C:/newfile.txt");
try {
if (fNewFile.exists() == false) {
if (fNewFile.createNewFile() == true) {
System.out.println("create c:/newfile.txt success");
}
else {
System.out.println("create c:/newfile.txt fail");
}
}
else {
System.out.println("file already exists");
}
}
catch (IOException e) {
e.printStackTrace();
}架構
//rename file
File fRenameFile = new File("c:/renamefile.txt");
fNewFile.renameTo(fRenameFile);
//remove file
File fRemoveFile = new File("d:/" + fRenameFile.getName());
fRenameFile.renameTo(fRemoveFile);
//delete file
try {
File fDelFile = new File(fRemoveFile.getCanonicalPath());
fDelFile.delete();
}
catch (IOException e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
OperateFile operateFile1 = new OperateFile();
}
}
c. 找出某特定目錄裏的全部檔案:
File類別提供的list和listFiles均可以列出某特定目錄裏的全部檔案,其中list傳回的是String[],listFiles傳回的是File[],這兩個函式都會傳回全部的檔案和目錄。
===== 範例 8 =====
import java.io.*;
public class ListAllFiles {
public ListAllFiles(String strDir) {
File fDir = new File(strDir);
File[] fAllFiles = fDir.listFiles();
for(int i=0; i
if (fAllFiles.isFile())
System.out.println("File: " + fAllFiles.getName());
else
System.out.println("Dir: " + fAllFiles.getName());
}
}
public static void main(String[] args) {
ListAllFiles listAllFiles1 = new ListAllFiles(args[0]);
}
}
3. Network I/O:
Java對網路的支援只有TCP/IP和UDP/IP,提供的類別有URL、URLConnection、Socket、ServerSocket,在這裏我只打算用ServerSocket、Socket爲例,來講明Network I/O。
基本上,Java的I/O無論在任何的媒體上都是將它們視爲stream,因此,網路I/O和檔案I/O原理也是一致的,底下的兩個程式分別爲server socket及client socket。在看範例以前,能夠再複習一下前面的abstract…
===== 範例 9 =====
import java.net.*;
import java.io.*;
public class myServer {
public myServer(String strPort) {
int nPort = new Integer(strPort).intValue();
try {
ServerSocket ss = new ServerSocket(nPort);
Socket s = ss.accept();
OutputStream out = s.getOutputStream();
PrintStream psOut = new PrintStream(out);
String strResponse = "Hello " + s.getInetAddress() + " on port " + s.getPort() + "\r\n";
strResponse += "This is " + s.getLocalAddress() + " on port " + s.getLocalPort() + "\r\n";
psOut.print(strResponse);
s.close();
ss.close();
}
catch (IOException e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
myServer myServer1 = new myServer(args[0]);
}
}
===== 範例 10 =====
import java.net.*;
import java.io.*;
public class myClient {
public myClient(String strIP, String strPort) {
int nPort = new Integer(strPort).intValue();
try {
Socket s = new Socket(strIP, nPort);
InputStream in = s.getInputStream();
BufferedInputStream bisIn = new BufferedInputStream(in);
while (bisIn.available() > 0) {
byte[] b = new byte[30];
int nLen = bisIn.read(b);
System.out.println(new String(b, 0, nLen));
}
}
catch (UnknownHostException e) {
e.printStackTrace();
}
catch (IOException e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
myClient myClient1 = new myClient(args[0], args[1]);
}
}app
4. Object Serialization:
A. 所謂Object Serialization就是把物件的」狀態」儲存成一系列的位元組,而這些位元組在稍候可用來恢復物件。更簡單的說,Object Serialization是讓物件能夠以物件爲儲存單位。在Java中,任何物件要能Serialization,必須implements Serializable這個Interface,如下是一個簡單的程式範例,能夠將物件儲存到e:\point.ser,或從e:\point.ser將物件恢復原值。
===== 範例 11 =====
import java.io.*;
public class ThreeDPoint implements Serializable
{
private double m_dblX, m_dblY, m_dblZ;socket
public ThreeDPoint(double x, double y, double z)
{
m_dblX = x;
m_dblY = y;
m_dblZ = z;
}
public void PrintXYZ()
{
System.out.println("X: " + m_dblX);
System.out.println("Y: " + m_dblY);
System.out.println("Z: " + m_dblZ);
}
public static void main(String[] args)
{
if (args[0].equalsIgnoreCase("w")) {
ThreeDPoint threeDPoint1 = new ThreeDPoint(10 ,20, 30);
try {
FileOutputStream fout = new FileOutputStream("e:\\point.ser");
ObjectOutputStream oout = new ObjectOutputStream(fout);
oout.writeObject(threeDPoint1);
oout.close();
System.out.println("write:");
threeDPoint1.PrintXYZ();
}
catch (Exception e) {
e.printStackTrace();
}
}
else if (args[0].equalsIgnoreCase("r")) {
try {
FileInputStream fin = new FileInputStream("e:\\point.ser");
ObjectInputStream oin = new ObjectInputStream(fin);
Object o = oin.readObject();
ThreeDPoint threeDPoint1 = (ThreeDPoint) o;
oin.close();
System.out.println("read:");
threeDPoint1.PrintXYZ();
}
catch (Exception e) {
}
}
} //end of main
}
B. 在Java中,一個實做某特定介面的類別,其子類別也因繼承的原故而被視爲實做了該介面,所以,許多沒有明確宣告實做Serializable介面的類別,事實上也是能夠被Serialization的。
C. 並不是每一個實做了Serializable介面的物件均可以被Serialization,若是這個物件繼承圖上的祖先,有其中一個是不能夠被Serialization,那麼這個物件就不能夠被Serialization。
5. Formated I/O:
在Java的I/O裏,並無所謂的型別,不論是int、long、double…最後都是以String輸出,因此若是要讓數字以特定格式輸出,需透過Java提供的兩個類別java.text.NumberFormat和java.text.DecimalFormat將數字格式化後再輸出。
範例12簡要說明NumberFormat如何使用,在開始使用NumberFormat時,應先用getInstance取得NumberFormat的實體,範例12中的setMaximumIntegerDigits和setMinimumFractionDigits是用來設定整數和小數的位數,另外還有setMinimumIntegerDigits和setMaximumFractionDigits也是一樣功能。這些設定若有衝突,Java以最後設定的爲準。
===== 範例 12 =====
import java.text.*;
public class myFormat {
public myFormat() {
NumberFormat nf = NumberFormat.getInstance();
double dblNum = Math.PI;
System.out.println(dblNum);
nf.setMaximumIntegerDigits(5);
nf.setMinimumFractionDigits(4);
System.out.println("PI: " + nf.format(dblNum));
}
public static void main(String[] args) {
myFormat myFormat1 = new myFormat();
}
}
另外一個類別DecimalFormat是繼承NumberFormat的子類別,它提供了更強的格式化功能,透過設定pattern,可使咱們的輸出更多樣化,至於Java提供的pattern有那些? 在API Document中有詳細說明! 範例13僅舉其一,說明DecimalFormat如何使用。
===== 範例 13 =====
import java.text.*;
public class myDecimalFormat {
public myDecimalFormat() {
DecimalFormat df = new DecimalFormat("0000.000");
double dblNum = 123.45;
System.out.println("dblNum: " + dblNum);
System.out.println("dblNum: " + df.format(dblNum));
}
public static void main(String[] args) {
myDecimalFormat myDecimalFormat1 = new myDecimalFormat();
}
} 測試