1、Java中“流“類庫讓人迷惑的主要原因:創建單一的結果流,卻需要創建多個對象。
2、使用層疊的數個對象爲單個對象動態地、透明地添加職責的方式,稱作“修飾器“模式。修飾器必須與其所修飾的對象具有相同的接口,這使得修飾器的基本應用具有透明性——我們可以想修飾過或沒有修飾過的對象發送相同的消息。
3、爲什麼使用修飾器?
在直接使用擴展子類的方法時,如果導致產生了大量的、用以滿足所需的各種可能的組合的子類,這是通常就會使用修飾器——處理太多的子類已經不太實際。
4、修飾器的缺點:增加代碼的複雜性。
5、JavaI/O類庫操作不便的原因在於:我們必須創建許多類——“核心”I/O類型加上所有的修飾器,才能得到我們所希望的單個I/O對象。
6、FilterInputStream和FilterOutputStream是用來提供修飾器類接口以控制特定輸入流和輸出流的倆個類。它們分別繼承自I/O類庫中的基類InputStream和OutputStream。
7、DataInputStream是FilterInputStream的直接子類,它允許我們讀取不用的基本類型數據以及String對象。
8、InputStream和OutputStream是面向字節的,而Reader和Writer是面向字符與兼容Unicode。
9、在InputStream和OutputStream的繼承層次結構僅支持8位的字節流,並且不能很好地處理16位的Unicode。由於Unicode用用於字符國際化,所以添加Reader和Write繼承結構就是爲了在所有的I/O操作中都支持Unicode。另外添加了Reader和Write的I/O流類庫使得它的操作比舊類庫更快。
10、但是,Reader和Write並不是用來取代nputStream和OutputStream的,因爲InputStream和OutputStream在面向字節形式的I/O中仍然提供了極有價值的功能。
11、有時,我們需要把來自於“字節”層次結構的類和“字符”層次結構的類結合起來使用。這時,需要使用“適配器(adapter)”類:InputStreamReader可以把InputStream轉換爲Reader,而OutputStreamWriter可以把OutputStream轉換爲Writer。
12、當我們使用DataOutputStream時,寫字符串並且讓DataInputStream能夠恢復它的唯一可靠的做法就是使用UTF-8編碼。UTF-8是Unicode的變體,後者把所有字符都存儲成兩個字節的形式。而我們使用的只是ASCII或者幾乎是ASCII字符(只佔7位),這麼做就顯得極其浪費空間和寬帶,所以UTF-8將ASCII字符編碼成單一字節的形式,而非ASCII字符則編碼成兩到三個字節的形式。另外,字符串的長度存儲在兩個字節中。
13、System.in是一個沒有被加工過的InputStream,所以在讀取System.in之前必須對其進行加工。
14、標準I/O重定向:如果我們突然開始在顯示器上創建大量輸出,而這些輸出滾動得太快以至於無法閱讀是,重定向輸出就顯得極爲有用。重定向有一下方法:
SetIn(InputStream)
SetOut(InputStream)
SetErr(InputStream)
注意:重定向操作的是字節流(InputStream、OutputStream),而不是字符流。
15、JDK1.4的java.nio.*包引入了新的JavaI/O類庫,其目的在於提高速度。速度的提高來自於所使用的結構更接近操作系統的I/O的方式:通道和緩衝器。
我們可以把它想象成一個煤礦,通道是一個包含煤層(數據)的礦藏,而緩衝器則是派送到礦藏的卡車。卡車載滿煤炭而歸,我們再從卡車上獲得煤炭。也就是說,我們並沒有直接和通道交互;只是和緩衝器交互,並把緩衝器派送到通道。通道要麼從緩衝器獲得數據,要麼向緩衝器發送數據。
唯一直接與通道交互的緩衝器是ByteBuffer——也就是說,可以存儲未加工字節的緩衝器。ByteBuffer是一個相當基礎的類:通過告知分配多少存儲空間來創建一個ByteBuffer對象,並且還有一個方法選擇集,用以原始的字節形式或基本數據類型輸出和讀取數據。
16、FileInputStream、FileOutputStreaam以及用於既讀又寫的RandomAccessFile被使用nio重新實現過,都有一個getChannel()方法返回一個FileChannel(通道)。通道是一種相當基礎的東西:可以向它傳送用於讀寫的ByteBuffer,並且可以鎖定文件的某些區域用於獨佔式訪問。Reader和Writer這種字符模式類不能產生通道;但是java.nio.channels.Channels類提供了實用方法,用以在通道中產生Reader和Wirter。
以下
17、轉換數據:緩衝器容納的是普通的字節,爲了把它們轉化成字符,我們要麼在輸入它們的時候對其進行編碼,要麼在將其從緩衝器輸出時對它們進行解碼。請參看下面例子:
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
import java.nio.charset.Charset;
public class GetChannel{
public static void main(String[] args) throws Exception{
//寫入data.txt
FileChannel fc = new FileOutputStream("data.txt").getChannel();
fc.write(ByteBuffer.wrap("Some text".getBytes()));
fc.close();
//讀取data.txt
fc = new FileInputStream("data.txt").getChannel();
ByteBuffer buff = ByteBuffer.allocate(1024);
fc.read(buff);
buff.flip();
System.out.println(buff.asCharBuffer());
//使用系統默認的字符集進行解碼(輸出時對其進行解碼)
buff.rewind();/返回數據開始的部分
String encoding = System.getProperty("file.encoding");
System.out.println("Decodedusing " + encoding + ":"
+ Charset.forName(encoding).decode(buff));
//再次寫入(輸入時對其進行編碼)
fc = new FileOutputStream("data.txt").getChannel();
fc.write(ByteBuffer.wrap("Some text".getBytes("UTF-16BE")));
fc.close();
//再次讀取
fc = new FileInputStream("data.txt").getChannel();
buff = ByteBuffer.allocate(1024);
fc.read(buff);
buff.flip();
System.out.println(buff.asCharBuffer());
//再次寫入(輸入時對其進行編碼)
fc = new FileOutputStream("data.txt").getChannel();
buff = ByteBuffer.allocate(24);
buff.asCharBuffer().put("Some text");
fc.write(buff);
fc.close();
//再次讀取
fc = new FileInputStream("data.txt").getChannel();
buff.clear();
fc.read(buff);
buff.flip();
System.out.println(buff.asCharBuffer());
}
}
運行結果:
18、獲取基本類型:儘管ByteBuffer只能保存字節類型的數據,但是它具有從其所容納的字節中產生出各種不同的類型值的方法。
import java.nio.ByteBuffer;
public class GetData{
private final static int BSIZE = 1024;
public static void main(String[] args){
ByteBuffer bb = ByteBuffer.allocate(BSIZE);
int i = 0;
while(i++ < bb.limit()){
if(bb.get() != 0){
System.out.println("nonzero");
}
}
System.out.println("i = " + i);
bb.rewind();
//char
bb.asCharBuffer().put("Howdy");
char c;
while((c = bb.getChar()) != 0){
System.out.print(c + " ");
}
System.out.println();
bb.rewind();
//short。是一個特例,必須加上(short)進行類型轉換
bb.asShortBuffer().put((short)471142);
System.out.println(bb.getShort());
bb.rewind();
//int
bb.asIntBuffer().put(99471142);
System.out.println(bb.getInt());
bb.rewind();
//long
bb.asLongBuffer().put(99471142);
System.out.println(bb.getLong());
bb.rewind();
//float
bb.asFloatBuffer().put(99471142);
System.out.println(bb.getFloat());
bb.rewind();
//double
bb.asDoubleBuffer().put(99471142);
System.out.println(bb.getDouble());
bb.rewind();
}
}
運行結果:
19、視圖緩衝器:可以讓我們通過某個特定的基本數據類型的視窗查看其底層的ByteBuffer。
ByteBuffer bb = ByteBuffer.allocate(BSIZE);
//asIntBuffer() 創建此字節緩衝區的視圖,作爲 int 緩衝區。
IntBuffer ib = bb.asIntBuffer();
20、不同機器可能會有不同的字節排序方法來存儲數據。默認的ByteBuffer是以高位優先的順序存儲數據的。考慮下面兩個字節:
如果我們以short(ByteBuffer.asShortBuffer)高位優先讀出的是97(0000000001100001),若更改ByteBuffer更改爲地位優先,則讀出的是24832(0110000100000000)。再看下面例子:
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.util.Arrays;
public class Endians{
public static void main(String[] args){
ByteBuffer bb = ByteBuffer.wrap(new byte[12]);
bb.asCharBuffer().put("abcdef");
System.out.println(Arrays.toString(bb.array()));
bb.rewind();
bb.order(ByteOrder.BIG_ENDIAN);//設置爲高位排序
bb.asCharBuffer().put("abcdef");
//array()只能對有數組支持的緩衝器調用
System.out.println(Arrays.toString(bb.array()));
bb.rewind();
bb.order(ByteOrder.LITTLE_ENDIAN);//設置爲低位排序
bb.asCharBuffer().put("abcdef");
System.out.println(Arrays.toString(bb.array()));
}
}
運行結果:
21、如果想把一個字節數組寫到文件中去,那麼應該使用ByteBuffer.wrap()方法把字節數組包裝起來,然後用getChannel()方法在FileOutputStream上打開一個通道,接着將來自於ByteBuffer的數據寫到FileChannel中去。
22、存儲器映射文件:允許我們創建和修改那些因爲太大而不能放入內存的文件。有了存儲器映射文件,我們就可以假定整個文件都在內存中,而且可以完全把它當作非常大的數組來訪問。
儘管“舊”的I/O在用nio實現後性能有所提高,但是“映射文件訪問”往往可以更加顯著地加快速度。
23、文件加鎖機制:允許我們同步訪問某個作爲共享資源的文件。文件鎖對其它操作系統進程是可見的,因爲Java的文件加鎖之間映射到本地操作系統的加鎖工具。另外,利用對映射文件的部分加鎖,可以對巨大的文件進行部分加鎖,以便其他進程可以對修改文件中未被加鎖的部分。
24、壓縮:Java壓縮類庫是按字節方式的,它們繼承自InputStream和OutputStream。
1).GZIP:如果只想對單個數據流(而不是一系列互異數據)進行壓縮,那麼它是比較適合的選擇。下面是對單個文件進行壓縮的例子:
import java.io.BufferedOutputStream;
import java.io.BufferedReader;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.zip.GZIPInputStream;
import java.util.zip.GZIPOutputStream;
public class GZIPcompress{
public static void main(String[] args) throws IOException{
//InputStreamReader中設置字符編碼爲GBK
BufferedReader in = new BufferedReader(new InputStreamReader(new
FileInputStream("test.txt"), "GBK"));
BufferedOutputStream out = new BufferedOutputStream((
new GZIPOutputStream(new FileOutputStream("test.txt.gz"))));
System.out.println("Writingfile");
int c;
while((c = in.read()) != -1){
//使用GBK字符編碼將此 String編碼到 out中
out.write(String.valueOf((char) c).getBytes("GBK"));
}
in.close();
out.close();
System.out.println("ReadingFile");
BufferedReader in2 = new BufferedReader((new InputStreamReader
(new GZIPInputStream(new FileInputStream("test.txt.gz")))));
String s;
while((s = in2.readLine()) != null){
System.out.println(s);
}
}
}
2).用Zip進行多文件壓縮:對於每一個要加入壓縮檔案的文件,都必須調用putNextEntry(),並將其傳遞給一個ZipEntry對象。
25、對象的序列化:將那些實現Serializable接口的對象轉換成一個字節序列,並能夠在以後將這個字節序列完全恢復爲原來的對象。利用它可以實現“輕量持久性”。
1).“持久化”:意味一個對象的生存週期並不取決於程序是否正在執行。通過將一個序列化對象寫入磁盤,然後再重新調用程序時恢復該對象,就能夠實現持久性的效果。
2).“輕量級”:因爲它不能用某種“persistent”(持久)關鍵字來簡單定一個對象,並讓系統自動維護其他細節問題。相反,對象必須在程序中顯示地序列化和反序列化還原。
26、對象序列化的概念加入到語言中是爲了支持兩種主要特性:
1).Java的“遠程方法調用”(Remote Method Invocation,RMI)。
2).Java Beans。
27、
1).序列化:創建OutputStream對象,封裝到ObjectOutputStream,再調用writeObject()
2).反序列化:創建InputStream對象,封裝到ObjectInputStream,再調用readObject()
3).Serializable的對象在還原的過程中,沒有調用任何構造器。
28、對象序列化是面向字節的,因此採用InputStream和OutputStream層次結構。
29、序列化的控制:
1).實現Externalizable接口,接口繼承自Serializable,添加了writeExternal()和readExternal()兩個方法,它們會在序列化和反序列化還原的過程中被自動調用。
2).反序列過程中,對於Serializable對象,對象完全以它存儲的二進制位爲基礎來構造,而不是調用構造器。而Externalizeble對象,普通的缺省構造函數會被調用。
3).transient(瞬時)關鍵字:只能和Serializable對象一起使用。
private transient String password;
password將不會被保存到磁盤中。
30、序列化/反序列化static成員:顯示調用serializeStaticState()和deserializeStaticState()
31、Preferences:用於存儲和讀取用戶的偏好(preferences)以及程序配置項的設置。只能用於小的、受限的數據集合——我們只能保存基本數據類型和字符串,並且每個字符串的存儲類型長度不能超過8K(不是很小,單我們也不想用它來創建任何重要的東西)。它是一個鍵-值集合(類似映射),存儲在一個節點層次結構中。
import java.util.Arrays;
import java.util.Iterator;
import java.util.prefs.BackingStoreException;
import java.util.prefs.Preferences;
public class PreferencesDemo{
public static void main(String[] args) throws BackingStoreException{
Preferences prefs = Preferences.userNodeForPackage(PreferencesDemo.class);
prefs.put("日期", "2012年02月05日");
prefs.put("天氣", "晴");
prefs.putInt("放假多少天了?", 24);
prefs.putBoolean("現在在家?", true);
int usageCount = prefs.getInt("寒假還剩多少天?", 16);
usageCount--;
prefs.putInt("寒假還剩多少天?", usageCount);
Iterator<String> it = Arrays.asList(prefs.keys()).iterator();
while(it.hasNext()){
String key = it.next().toString();
//null作爲缺省
System.out.println(key + ":" + prefs.get(key, null));
}
System.out.println("放假多少天了?" + prefs.getInt("放假多少天了?", 0));
}
}
注意:每次運行,usageCount的值沒都減少1。即每次打印出“寒假還剩多少天?”後面的數字都減少1。然而,在程序第一次運行之後,並沒有任何本地文件出現。Preferences API利用合適的系統資源完成了這個任務,並且這些資源會隨操作系統的不同而不同。例如在windows裏,就使用註冊表。
32、正則表達式:
1). 在Java ,“\\”意味“我正在插入一個正則表達式反斜槓”,那麼隨後的字符具有特殊意義。若想插入一個字面意義上的反斜槓,得這樣子表示“\\\\”。
2).量詞:
·貪婪的:竟可能的模式發現儘可能的匹配。
·勉強的:用問號來指定,匹配滿足模式所需的最少字符數。
·佔有的:只有在Java語言中才可用,並且它也更高效,常常用於防止正則表達式失控,因此可以是正則表達式執行起來更有效。
3).注意abc+與(abc)+的不同。
4).在java中,正則表達式是通過java.util.regex包裏面的Pattern和Matcher類來實現的。
·Pattern對象表示一個正則表達式的編譯版本。靜態的complie()方法將一個正則表達式字符串編譯成Pattern對象。
·Matcher對象有matcher()方法和輸入字符串編譯過的Pattern對象中產生Matcher對象。
5).split()分裂操作將輸入字符串斷開成字符串對象數組。