1. 程式人生 > >Java程式設計思想(4)

Java程式設計思想(4)

第13章 字串

1 String物件是不可變,String類中每一個修改String值的方法實際都是建立一個全新的String物件

2 " + "與" += "是Java中僅有的兩個過載過的操作符,而Java並不允許過載任何操作符。

3 mac下用Java命令

package c06;

public class ArrayApp {
	public static void main(String[] args){		
		String mango = "mango";
		String s = "abc "+mango+" def "+47;
		System.out.println(s);
	}
}

$ cd src      // 要到src目錄下

$ javac c06/ArrayApp.java     //  c06為包目錄名,ArrayApp.java為檔名

$ java c06.ArrayApp      // 執行ArrayApp.java

abc mango def 47         // 執行結果

$ javap -c c06.ArrayApp      // 反編譯

Compiled from "ArrayApp.java"

public class c06.ArrayApp {

  public c06.ArrayApp();

    Code:

       0: aload_0

       1: invokespecial #1                  // Method java/lang/Object."<init>":()V

       4: return

 

  public static void main(java.lang.String[]);

    Code:

       0: ldc           #2                  // String mango

       2: astore_1

       3: new           #3                  // class java/lang/StringBuilder

       6: dup

       7: invokespecial #4                  // Method java/lang/StringBuilder."<init>":()V

      10: ldc           #5                  // String abc

      12: invokevirtual #6                  // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;

      15: aload_1

      16: invokevirtual #6                  // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;

      19: ldc           #7                  // String  def

      21: invokevirtual #6                  // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;

      24: bipush        47

      26: invokevirtual #8                  // Method java/lang/StringBuilder.append:(I)Ljava/lang/StringBuilder;

      29: invokevirtual #9                  // Method java/lang/StringBuilder.toString:()Ljava/lang/String;

      32: astore_2

      33: getstatic     #10                 // Field java/lang/System.out:Ljava/io/PrintStream;

      36: aload_2

      37: invokevirtual #11                 // Method java/io/PrintStream.println:(Ljava/lang/String;)V

      40: return

}

4 StringBuilder,包含insert(),replace(),substring(),reverse(),append(),toString()和delete()

public class ArrayApp {
	public static Random rand = new Random(47);
	public String toString(){
		StringBuilder result = new StringBuilder("[");
		for(int i=0;i<25;i++){
			result.append(rand.nextInt(100));
			result.append(", ");
		}
		result.delete(result.length()-2, result.length());
		result.append("]");
		return result.toString();
	}
	public static void main(String[] args){		
		ArrayApp a = new ArrayApp();
		System.out.println(a);
	}
}

5 如果要列印物件的記憶體地址,可以使用this關鍵字。但如下所示,編譯器看到String物件後面跟個"+",會試著把this轉換成一個String,然後呼叫this.toString()就進入了迴圈的遞迴。所以此處得用super.toString()代替this

public class ArrayApp {
	public static Random rand = new Random(47);
	public String toString(){
		//return "address: "+this+"\n";
		return "address: "+super.toString()+"\n";
	}
	public static void main(String[] args){		
		List<ArrayApp> t = new ArrayList<ArrayApp>();
		for(int i=0;i<10;i++)
			t.add(new ArrayApp());
	
			System.out.println(t);
	}
}

6 String的操作

方法 引數,過載版本 應用
構造器 過載版本:預設版本,String,StringBuilder,StringBuffer,char陣列,byte陣列 建立String物件
length()   String中字元的個數
charAt() 引數:int索引 取得String中該索引位置上的char
getChars(),getBytes() 引數:要複製部分的起點和終點的索引,複製的目標陣列,目標陣列的起始索引 複製char或byte到一個目標陣列中
toCharArray()   生成一個char[ ],包含String的所有字元
equals() 引數:與之比較的String 比較兩個String內容是否相同,相同則返回true
compareTo() 引數:與之比較的String 按詞典順序比較String內容大小,結果為負數,零或正數
contains() 引數:要搜尋的CharSequence String物件包含引數內容則返回true
contentEquals() 引數:與之比較的CharSequence或StringBuffer String物件與引數內容完全一致則返回true
equalsIgnoreCase() 引數:與之比較的String 忽略大小寫,比較兩個String內容是否相同,相同則返回true
regionMatcher() 引數:該String的索引偏移量,另一個String及其索引偏移量,要比較長度 返回boolean結果,以表明所比較區域是否相等
startsWith() 引數:起始String 返回boolean結果,以表明該String是否以此引數起始
endsWith() 引數:字尾String 返回boolean結果,以表明該String是否以此引數字尾
indexOf(),lastIndexOf() 過載版本:char,char與起始索引,String,String與起始索引 如果不包含引數則返回-1,否則返回該引數在String中的起始索引。lastIndexOf()是從後往前搜尋
substring() 過載版本:起始索引,起始索引和終點座標 返回一個新的子String,包含引數指定區域的字串
concat() 引數:要連線的String 返回新的String,內容為原始String連線引數String
replace() 引數:要替換掉的字元 返回替換後的新String
toLowerCase(),toUpperCase()   String全部變成小寫/大寫字元
trim()   去掉String兩端空白字元,返回新String
valueOf() 過載版本:Object;char[];char[]和偏移量和字元個數;boolean;char;int;float;long;double 返回表示引數內容的String,即將其他型別轉換為String型別
intern()   為每個唯一的字元序列生成一個且僅生成一個String引用

7 格式化輸出,如下所示三者輸出結果一樣

int x = 5;
double y = 5.332542;
System.out.println("Row 1: ["+x+" "+y+"]");
System.out.printf("Row 1: [%d %f]\n",x,y);
System.out.format("Row 1: [%d %f]\n",x,y);

8 在Java中,所有新的格式化功能都由java.util.Formatter類處理。可以將Formatter看作一個翻譯器,將格式化字串與資料翻譯成需要的結果。Formatter類將內容翻譯為字串,由PrintStream進行輸出

public class ArrayApp {
	private String name;
	private Formatter f;
	public ArrayApp(String name,Formatter f){
		this.name = name;
		this.f = f;
	}
	public void move(int x,int y){
		f.format("%s The ArrayApp is at (%d,%d)\n", name,x,y);
	}
	public static void main(String[] args){		
		PrintStream outAlias = System.out;
		ArrayApp tommy = new ArrayApp("Tommy",new Formatter(System.out));
		ArrayApp terry = new ArrayApp("Terry",new Formatter(outAlias));
		tommy.move(0, 0);
		terry.move(4, 3);
		tommy.move(3, 4);
		terry.move(2, 5);
		tommy.move(3, 3);
		terry.move(3, 3);
	}
}

9 格式化說明符,語法如下

%[argument_index$][flags][width][.precision]conversion      //  precision前面有個小數點

注:

  • width控制一個域的最小尺寸。預設情況是右對齊,但可以通過使用“-“標誌來改變對齊方向。width可以應用於各種資料型別
  • 不是所有型別都能使用precision,而且不同型別使用情況也不一樣。將precision用於String時表示列印String時輸出字元的最大數量,用於float表示小數點後位數(預設6位小數)。precision不能用於整數,會觸發異常
public class ArrayApp {
	private double total = 0;
	private Formatter f = new Formatter(System.out);
	public void printTitle(){
		f.format("%-15s %5s %10s\n","Item", "Qty","Price");
		f.format("%-15s %5s %10s\n", "----","--","-----");
	}
	public void print(String name,int qty,double price){
		f.format("%-15.15s %5d %10.2f\n", name,qty,price);
		total += price;
	}
	public void printTotal(){
		f.format("%-15s %5s %10.2f\n", "Tax","",total*0.06);
		f.format("%-15s %5s %10s\n", "","","-----");
		f.format("%-15s %5s %10.2f\n", "Total","",total*1.06);
	}
	public static void main(String[] args){
		ArrayApp a = new ArrayApp();
		a.printTitle();
		a.print("Jack's Magic Beans",4,4.25);
		a.print("Princess Peas", 3, 5.1);
		a.print("Three Bears Porridge", 1, 14.29);
		a.printTotal();
	}
}


輸出結果
Item              Qty      Price
----               --      -----
Jack's Magic Be     4       4.25
Princess Peas       3       5.10
Three Bears Por     1      14.29
Tax                         1.42
                           -----
Total                      25.06

10 Formatter型別轉換字元

d 整數型(十進位制)
c Unicode字元
b Boolean值
s String
f 浮點數(十進位制)
e 浮點數(科學計數)
x 整數(十六進位制)
h 雜湊碼(十六進位制)
% 字元"%"
Formatter f = new Formatter(System.out);
char u = 'a';
System.out.println("u = 'a'");
f.format("s: %s\n", u);      // ok, s: a
//f.format("d: %d\n", u);    //error, d != java.lang.Character
f.format("c: %c\n", u);      // ok, c: a
f.format("b: %b\n", u);      // ok, b: true
//f.format("f: %f\n", u);    // error, f != java.lang.Character
//f.format("e: %e\n", u);    // error, e != java.lang.Character
//f.format("x: %x\n", u);    // error, x != java.lang.Character
f.format("h: %h\n", u);      // ok, h: 61

int v = 121;
System.out.println("v = 121");
f.format("s: %s\n", v);     // ok, s: 121
f.format("d: %d\n", v);     // ok, d: 121
f.format("c: %c\n", v);     // ok, c: y 
f.format("b: %b\n", v);     // ok, b: true 
//f.format("f: %f\n", v);   // error, f != java.lang.Integer
//f.format("e: %e\n", v);   // error, e != java.lang.Integer
f.format("x: %x\n", v);     // ok, x: 79
f.format("h: %h\n", v);     // ok, h: 79
 
BigInteger w = new BigInteger("500000000000000000000");
System.out.println("w = 500000000000000000000");
f.format("s: %s\n", w);     // ok, s: 500000000000000000000
f.format("d: %d\n", w);     // ok, d: 500000000000000000000
//f.format("c: %c\n", w);   // error, c != java.math.BigInteger
f.format("b: %b\n", w);     // ok, b: true
//f.format("f: %f\n", w);   // error, f != java.math.BigInteger
//f.format("e: %e\n", w);   // error, e != java.math.BigInteger
f.format("x: %x\n", w);     // ok, x: 1b1ae4d6e2ef500000
f.format("h: %h\n", w);     // ok, h: 31066ab9

double x = 179.543;
System.out.println("x = 179.543");
f.format("s: %s\n", x);     // ok, s: 179.543
//f.format("d: %d\n", x);   // error, d != java.lang.Double
//f.format("c: %c\n", x);   // error, c != java.lang.Double
f.format("b: %b\n", x);     // ok, b: true
f.format("f: %f\n", x);     // ok, f: 179.543000
f.format("e: %e\n", x);     // ok, e: 1.795430e+02
//f.format("x: %x\n", x);   // error, x != java.lang.Double
f.format("h: %h\n", x);     // ok, h: 1ef462c

ArrayApp y = new ArrayApp();
System.out.println("y = new ArrayApp()");
f.format("s: %s\n", y);     // ok, s: [email protected]
//f.format("d: %d\n", y);   // error, d != c06.ArrayApp
//f.format("c: %c\n", y);   // error, c != c06.ArrayApp
f.format("b: %b\n", y);     // ok, b: true
//f.format("f: %f\n", y);   // error, f != c06.ArrayApp
//f.format("e: %e\n", y);   // error, e != c06.ArrayApp
//f.format("x: %x\n", y);   // error, x != c06.ArrayApp
f.format("h: %h\n", y);     // ok, h: 146bf551

boolean z = false;
System.out.println("z = false");
f.format("s: %s\n", z);    // ok, s: false
//f.format("d: %d\n", z);  // error, d != java.lang.Boolean
//f.format("c: %c\n", z);  // error, c != java.lang.Boolean
f.format("b: %b\n", z);    // ok, b: false
//f.format("f: %f\n", z);  // error, f != java.lang.Boolean
//f.format("e: %e\n", z);  // error, e != java.lang.Boolean
//f.format("x: %x\n", z);  // error, x != java.lang.Boolean
f.format("h: %h\n", z);    // ok, h: 4d5

11 對於轉換字元b,只要引數不為null,轉換結果永遠都是true,即使數字0也是true。

12 String.format(),功能跟Formatter類似,但方便不用建立類

public class ArrayApp extends Exception {
	public ArrayApp(int transactionID,int queryID,String message){
		super(String.format("(t%d,q%d) %s",transactionID,queryID,message));
	}

	public static void main(String[] args){
		try{
			throw new ArrayApp(3,7,"Write failed");
		}catch(Exception e){
			System.out.println(e);
		}
	}
}

13  “%010d” 表示輸出寬度為10位的整數,如果變數不夠10位,左側新增0來補充位數

14 在Java中,“\\d”表示一位數字“-?\\d+”表示可能有一個負號,後面跟著一位或多位數字。“(-|\\+)?”表示可能有一個-或+,或者二者都沒有

System.out.println("-1234".matches("-?\\d+"));    // true
System.out.println("5678".matches("-?\\d+"));     // true
System.out.println("+911".matches("-?\\d+"));     // false
System.out.println("+911".matches("(-|\\+)?\\d+"));  // true

15 "\\W"表示非單詞字元“\\w”表示一個單詞字元“n\\W+”表示字母n後面跟著一個或多個非單詞字元

public class ArrayApp {	
	public static String knights = "Then, when you have found the shrubbery, you must "
			+"cut down the mightiest tree in the forest... "
			+ "with... a herring!";
	public static void split(String regex){
		System.out.println(Arrays.toString(knights.split(regex)));
	}		
	public static void main(String[] args){
		split(" ");
		split("\\W+");
		split("n\\W+");
	}
}

輸出
[Then,, when, you, have, found, the, shrubbery,, you, must, cut, down, the, mightiest, tree, in, the, forest..., with..., a, herring!]
[Then, when, you, have, found, the, shrubbery, you, must, cut, down, the, mightiest, tree, in, the, forest, with, a, herring]
[The, whe, you have found the shrubbery, you must cut dow, the mightiest tree i, the forest... with... a herring!]

16 String類自帶一個正則表示式的替換工具

public class ArrayApp {	
	public static String knights = "Then, when you have found the shrubbery, you must "
			+"cut down the mightiest tree in the forest... "
			+ "with... a herring!";
			
	public static void main(String[] args){
		System.out.println(knights.replaceFirst("f\\w+", "located"));
		System.out.println(knights.replaceAll("shrubbery|tree|herring", "banana"));
	}
}

輸出
Then, when you have located the shrubbery, you must cut down the mightiest tree in the forest... with... a herring!
Then, when you have found the banana, you must cut down the mightiest banana in the forest... with... a banana!

17 字元類

點號( . ) 任意字元
[abc] a|b|c,包含a,b和c三者中任意字元
[^abc] 除了a,b和c之外的任意字元
[a-zA-Z] 從a到z或從A到Z的任意字元
[abc[hij]] a|b|c|h|i|j,任意a,b,c,h,i和j字元(並集)
[a-z&&[hij]] 任意h,i或j (交集)
\s 空白字元
\S 非空白字元
\d 數字[0-9]
\D 非數字[^0-9]
\w 單詞字元[a-zA-Z0-9]
\W 非單詞字元

18 正則邏輯操作符

XY Y跟在X後面
X|Y X或Y
(X) 捕獲組

19 邊界匹配符

^ 一行的開始
$ 一行的結束
\b 詞的邊界
\B 非詞的邊界
\G 前一個匹配的結束
// 輸出都是true
for(String pattern:new String[]{"Rudolph","[rR]udolph","[rR][aeiou][a-z]ol.*","R.*"})
			System.out.println("Rudolph".matches(pattern));

20 量詞(X必須要用圓括號括起來)

貪婪型 勉強型 佔有型 如何匹配
X? X?? X?+ 一個或零個X
X* X*? X*+ 零個或多個X
X+ X+? X++ 一個或多個X
X{n} X{n}? X{n}+ 恰好n次X
X{n,} X{n,}? X{n,}+ 至少n次X
X{n,m} X{n,m}? X{n,m}+ 至少n,且不超過m次X

21 Matcher.find()在CharSequence中查詢多個匹配。第二個find()能夠接收一個整數作為引數,該整數表示字串中字元的位置,並以其作為搜尋的起點。

Matcher m = Pattern.compile("\\w+")
		.matcher("Evening is full of the linnet's wings");
while(m.find())
	System.out.print(m.group()+" ");
System.out.println();
int i = 0;
while(m.find(i)){
	System.out.print(m.group()+" ");
	i++;
}

Evening is full of the linnet s wings 
Evening vening ening ning ing ng g is is s full full ull ll l of of f the the he e linnet linnet innet nnet net et t s s wings wings ings ngs gs s 

22 掃描輸入,readLine()讀取一行

public static BufferedReader input = new BufferedReader(
		new StringReader("Sir Robin of Camelot\n22 1.61803"));
public static void main(String[] args){
	try{
		System.out.println("What is your name?");
	String name = input.readLine();
	System.out.println(name);
	System.out.println("How old are you? What is your favorite double?");
	System.out.println("(input: <age> <double>)");
	String numbers = input.readLine();
	System.out.println(numbers);
	String[] numArray = numbers.split(" ");
	int age = Integer.parseInt(numArray[0]);
	double favorite = Double.parseDouble(numArray[1]);
	System.out.format("Hi %s.\n",name);
	System.out.format("In 5 years you will be %d.\n",age+5);
	System.out.format("My favorite double is %f", favorite/2);
	}catch(IOException e){
		e.printStackTrace();
	}
}

輸出
What is your name?
Sir Robin of Camelot
How old are you? What is your favorite double?
(input: <age> <double>)
22 1.61803
Hi Sir Robin of Camelot.
In 5 years you will be 27.
My favorite double is 0.809015

23 Scanner類,Scanner根據空白字元對輸入進行分詞。

Scanner stdin = new Scanner(System.in);
System.out.println("What is your name?");
String name = stdin.nextLine();
System.out.println(name);
System.out.println("How old are you? What is your favorite double?");
System.out.println("(input: <age> <double>)");
String numbers = stdin.nextLine();
System.out.println(numbers);
int age = stdin.nextInt();
double favorite = stdin.nextDouble();
System.out.format("Hi %s.\n",name);
System.out.format("In 5 years you will be %d.\n",age+5);
System.out.format("My favorite double is %f", favorite/2);

 

 

 

 

第14章 型別資訊

1 傳遞this引數給System.out.println(),間接地使用toString()。把Shape子類物件放入List<Shape>的陣列時會向上轉型,會丟失Shape子類物件的具體型別。即對於Shape陣列而言,他們只是Shape類物件。動態呼叫子類的draw()

abstract class Shape {
	void draw() { System.out.println(this+".draw()");}   // this即呼叫自身的toString()函式
	abstract public String toString();
}

class Circle extends Shape{
	public String toString(){ return "Circle";}
}

class Square extends Shape{
	public String toString(){ return "Square";}
}
class Triangle extends Shape {
	public String toString(){ return "Triangle"; }
}


public class ArrayApp {	

public static void main(String[] args){
	List<Shape>shapeList = Arrays.asList(new Circle(),new Square(),new Triangle());
	for(Shape s:shapeList)
		s.draw();
}
}

輸出
Circle.draw()
Square.draw()
Triangle.draw()

2 每個類都有一個Class物件,即每當編寫並且編譯了一個新類,就會產生一個Class物件(更恰當地說,是被儲存在一個同名的.class檔案中)。為了生成這個類物件,執行這個程式的Java虛擬機器(JVM)將使用被稱為“類載入器”的子系統。

3 所有的類都是在對其第一次使用時,動態載入到JVM中的。當程式建立第一個對類的靜態成員的引用時就會載入這個類。這個證明構造器也是類的靜態方法,即使在構造器之前並沒有使用static關鍵字。

4 類載入器首先檢查這個類的Class物件是否已經載入。如果尚未載入,預設的類載入就會根據類名查詢.class檔案。一旦某個類的Class物件被載入記憶體,它就被用來建立這個類的所有物件。

package c06;

class Candy{
	static {
		System.out.println("Loading Candy");
	}
}
class NewGum {
	static {
		System.out.println("Loading Gum");
	}
}
class Cookie2{
	static {
		System.out.println("Loading Cookie");
	}
}

public class ArrayApp {	

public static void main(String[] args){
	System.out.println("inside main");
	new Candy();
	System.out.println("After creating Candy");
	//new NewGum();
	try{
		Class.forName("c06.NewGum");   // 類的全名,即包名.類名
	}catch(ClassNotFoundException e){
		System.out.println("Could not find Gum");
	}
	System.out.println("After Class.forName(\"Gum\")");
	new Cookie2();
	System.out.println("After creating Cookie");
}
}

輸出
inside main
Loading Candy
After creating Candy
Loading Gum
After Class.forName("Gum")
Loading Cookie
After creating Cookie

Class.forName("類的全名(包名.類名)"),返回目標類的一個Class物件引用。

5 Class的部分函式

package c06;
interface HasBatteries {}
interface Waterproof{}
interface Shoots {}
class Toy{
	Toy() {}
	Toy(int i) {}
}
class FancyToy extends Toy implements HasBatteries,Waterproof,Shoots{
	FancyToy(){ super(1); }
}
public class ArrayApp {	

	static void printInfo(Class cc){
		System.out.println("Class name: "+cc.getName()+" is interface? ["
				+cc.isInterface()+"]");
		System.out.println("Simple name: "+cc.getSimpleName());
		System.out.println("Canonical name: "+cc.getCanonicalName());
	}
	public static void main(String[] args){
		Class c = null;
		try{
			c = Class.forName("c06.FancyToy");
		}catch(ClassNotFoundException e){
			System.out.println("Can't find FancyToy");
			System.exit(1);
		}
		printInfo(c);
		for(Class face:c.getInterfaces()){
			printInfo(face);
		}
		Class up = c.getSuperclass();
		Object obj = null;
		try{
			obj = up.newInstance();
		}catch(InstantiationException e){
			System.out.println("Cannot instantiate");
			System.exit(1);
		}catch(IllegalAccessException e){
			System.out.println("Cannot access");
			System.exit(1);
		}
		printInfo(obj.getClass());
	}
}

輸出
Class name: c06.FancyToy is interface? [false]
Simple name: FancyToy
Canonical name: c06.FancyToy
Class name: c06.HasBatteries is interface? [true]
Simple name: HasBatteries
Canonical name: c06.HasBatteries
Class name: c06.Waterproof is interface? [true]
Simple name: Waterproof
Canonical name: c06.Waterproof
Class name: c06.Shoots is interface? [true]
Simple name: Shoots
Canonical name: c06.Shoots
Class name: c06.Toy is interface? [false]
Simple name: Toy
Canonical name: c06.Toy
  • forName():建立一個Class引用。在傳遞給forName()的字串中,必須使用全限定名(包含包名)
  • getName():返回全限定的類名
  • getSimpleName():返回不包含包名的類名
  • getCanonicalName():返回全限定類名
  • isInterface():判斷Class物件是否為介面
  • getInterface():返回Class物件裡的介面Class物件
  • getSuperclass():返回Class物件的基類Class物件
  • getClass():獲取物件的Class引用

newInstance()是實現"虛擬構造器"的一種途徑,允許宣告"我不知道你的確切型別,但是無論如何都要正確建立你自己"。使用newInstance()來建立類,必須帶有預設構造器。

6 Java還提供了另一種方法來生成對Class物件的引用,即使用類字面常量,如FancyToy.class。類字面常量不僅可以應用於普通的類,也可以應用於介面,陣列以及基本資料型別。對於基本資料型別的包裝器類,還有一個標準欄位TYPE,指向對應基本資料型別的Class物件型別。建議使用".class"的形式,與普通類保持一致

...等價於...
boolean.class Boolean.TYPE
char.class Character.TYPE
byte.class Byte.TYPE
short.class Short.TYPE
int.class Integer.TYPE
long.class Long.TYPE
float.class Float.TYPE
double.class Double.TYPE
void.class Void.TYPE

7 當使用".class"來建立對Class物件的引用時,不會自動地初始化該Class物件。為了使用類而做的準備工作實際包含了三個步驟:

  1. 載入。由類載入器執行。該步驟將查詢位元組碼,並從這些位元組碼中建立一個Class物件
  2. 連結。在連結階段將驗證類中的位元組碼,為靜態域分配儲存空間,並且如果必需的話,將解析這個類建立的對其他類的所有引用。
  3. 初始化。如果該類具有超類,則對其初始化,執行靜態初始化器和靜態初始化塊

8 如下程式碼所示,程式碼分析

Class initable = Initable.class;     //  使用.class語法僅獲得對類的Class物件引用並不會引發初始化,即不會輸出"Initializing Initable"

System.out.println("After creating Initable ref");

System.out.println(Initable.staticFinal);  // 如果一個static final值是編譯器常量,如staticFinal,無須對Initable初始化即可讀取

System.out.println(Initable.staticFinal2); // 但staticFinal2不是編譯器常量,使用需要初始化Initable才能讀取,即會輸出"Initializing Initable"

System.out.println(Initable2.staticNonFinal); // 如果一個static成員變數不是final,它被讀取前必須要先進行連結和初始化

try{

Class initable3 = Class.forName("c06.Initable3");  // 獲得Class物件引用同時還會引發初始化

}catch(Exception e){

e.printStackTrace();

}

System.out.println("After creating Initable3 ref");

System.out.println(Initable3.staticNonFinal);

class Initable{
	static final int staticFinal = 47;
	static final int staticFinal2 = ArrayApp.rand.nextInt(1000);
	static {
		System.out.println("Initializing Initable");
	}
}
class Initable2{
	static int staticNonFinal = 147;
	static {
		System.out.println("Initializing Initable2");
	}
}
class Initable3{
	static int staticNonFinal = 74;
	static {
		System.out.println("Initializing Initable3");
	}
}
public class ArrayApp {	

	public static Random rand = new Random(47);
	public static void main(String[] args){
		Class initable = Initable.class;
		System.out.println("After creating Initable ref");
		System.out.println(Initable.staticFinal);
		System.out.println(Initable.staticFinal2);
		System.out.println(Initable2.staticNonFinal);
		try{
		Class initable3 = Class.forName("c06.Initable3");
		}catch(Exception e){
			e.printStackTrace();
		}
		System.out.println("After creating Initable3 ref");
		System.out.println(Initable3.staticNonFinal);
	}
}

輸出
After creating Initable ref
47
Initializing Initable
258
Initializing Initable2
147
Initializing Initable3
After creating Initable3 ref
74

9 Class引用表示的就是它所指向的物件的確切型別,而該物件便是Class類的一個物件。泛型類引用只能賦值為指向其宣告的型別,編譯器會強制執行額外的型別檢查,如genericIntClass不能賦值double.class。而普通的類引用可以重新被賦值為指向任何其他的Class物件,如intClass

Class intClass = int.class;
Class<Integer> genericIntClass = int.class;
genericIntClass = Integer.class;
intClass = double.class;
genericIntClass = double.class;   // error

10 因為Integer繼承於Number,但Integer Class物件並不是Number Class物件的子類。可以使用萬用字元"?"來說放鬆限制。

Class<Number> g = int.class;   // error
Class<?> k = int.class;    // ok
k = double.class;          // ok

11 為了建立一個Class引用,且限定為某種型別或該型別的任何子型別,可以將萬用字元與extends關鍵字結合,建立一個範圍

Class<? extends Number> k = int.class;
k = double.class;
k = Number.class;

12 Class引用的轉型語法,即cast()函式

class Building{}
class House extends Building{}
public class ArrayApp {	

	public static void main(String[] args){
		Building b = new House();
		Class<House> houseType = House.class;
		House h = houseType.cast(b);   // 等價於h = (House)b
		h = (House)b;
	}
}

13 RTTI在Java中還有第三種形式,即關鍵字instanceof,返回一個布林值,判斷物件是否為某個型別的例項。如下所示,在將x轉型為一個Dog前,先判斷物件x是否從屬於Dog類。

if(x instanceof Dog)
   ((Dog)x).bark();

14 工廠方法設計模式 ?

15 instanceof和isInstance()判斷結果一樣,equals()和==判斷結果也一樣。instanceof保持了型別的概念,它表示“你是這個類嗎,或者這個類的子類嗎”

class Base{}
class Derived extends Base{}
public class ArrayApp {	
	static void test(Object x){
		System.out.println("Testing x of type "+x.getClass());
		System.out.println("x instanceof Base "+(x instanceof Base));
		System.out.println("x instanceof Derived " +(x instanceof Derived));
		System.out.println("Base.isInstance(x) "+Base.class.isInstance(x));
		System.out.println("Derived.isInstance(x) "+Derived.class.isInstance(x));
		System.out.println("x.getClass() == Base.class "+(x.getClass() == Base.class));
		System.out.println("x.getClass() == Derived.class "+(x.getClass() == Derived.class));
		System.out.println("x.getClass().equals(Base.class) "+(x.getClass().equals(Base.class)));
		System.out.println("x.getClass().equals(Derived.class) "+(x.getClass().equals(Derived.class)));

	}
	public static void main(String[] args){
		test(new Base());
		test(new Derived());
	}

}

輸出
Testing x of type class c06.Base
x instanceof Base true
x instanceof Derived false
Base.isInstance(x) true
Derived.isInstance(x) false
x.getClass() == Base.class true
x.getClass() == Derived.class false
x.getClass().equals(Base.class) true
x.getClass().equals(Derived.class) false
Testing x of type class c06.Derived
x instanceof Base true
x instanceof Derived true
Base.isInstance(x) true
Derived.isInstance(x) true
x.getClass() == Base.class false
x.getClass() == Derived.class true
x.getClass().equals(Base.class) false
x.getClass().equals(Derived.class) true

16 反射,Class的getMethods()和getConstructors()函式分別返回Method物件陣列和Constructor物件陣列

public class ArrayApp {	
	private static String usage = "usage:\n"
			+"ShowMethods qualified.class.name\n"+
			"To show all methods in class or:\n"+
			"ShowMethods qualified.class.name word\n"+
			"To search for methods involving 'word'";
	private static Pattern p = Pattern.compile("\\w+\\.");
	public static void main(String[] args){
		if(args.length < 1) {
			System.out.println(usage);
			System.exit(0);
		}
		int lines = 0;
		try{
			Class<?> c = Class.forName(args[0]);
			Method[] methods = c.getMethods();
			Constructor[] ctors = c.getConstructors();
			if(args.length == 1){
				for(Method m:methods)
					System.out.println(p.matcher(m.toString()).replaceAll(""));
				for(Constructor ct:ctors)
					System.out.println(p.matcher(ct.toString()).replaceAll(""));
				lines = methods.length + ctors.length;
			}else {
				for(Method m:methods)
					if(m.toString().indexOf(args[1]) != -1){
						System.out.println(p.matcher(m.toString()).replaceAll(""));
						lines++;
					}
				for(Constructor ct:ctors)
					if(c.toString().indexOf(args[1]) != -1){
						System.out.println(p.matcher(ct.toString()).replaceAll(""));
						lines++;
					}
			}
		}catch(ClassNotFoundException e){
			System.out.println("No such class: "+e);
		}
	}
}

17 代理是基本的設計模式之一,它是為了提供額外的或不同的操作,而插入的用來代替“實際”物件的物件。這些操作通常涉及與“實際”物件的通訊,因此代理通常充當著中間人的角色。