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物件。為了使用類而做的準備工作實際包含了三個步驟:
- 載入。由類載入器執行。該步驟將查詢位元組碼,並從這些位元組碼中建立一個Class物件
- 連結。在連結階段將驗證類中的位元組碼,為靜態域分配儲存空間,並且如果必需的話,將解析這個類建立的對其他類的所有引用。
- 初始化。如果該類具有超類,則對其初始化,執行靜態初始化器和靜態初始化塊
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 代理是基本的設計模式之一,它是為了提供額外的或不同的操作,而插入的用來代替“實際”物件的物件。這些操作通常涉及與“實際”物件的通訊,因此代理通常充當著中間人的角色。