05_面向物件基礎篇_03
5.10 static 關鍵字的使用
5.10.1 靜態變數
在程式中如果用static 宣告變數的話,則此變數稱為靜態變數。那麼什麼是靜態變數?使用靜態變數又有什麼好處呢?先來看一下下面的範例:
範例:TestStaticDemo1.java
class Person { String name; String city; int age; public Person(String name, String city, int age) { this.name = name; this.city = city;this.age = age; } public String talk() { return "我是:" + this.name + ",今年:" + this.age + "歲,來自:" + this.city; } } public class TestStaticDemo1 { public static void main(String[] args) { Person p1 = new Person("張三", 25, "中國"); Person p2 = new Person("李四", 30, "中國"); Person p3= new Person("王五", 35, "中國"); System.out.println(p1.talk()); System.out.println(p2.talk()); System.out.println(p3.talk()); } }
輸出結果:
我是:張三,今年:25歲,來自:中國
我是:李四,今年:30歲,來自:中國
我是:王五,今年:35歲,來自:中國
程式說明:
1、程式1~16行宣告一個名為Person的類,含有三個屬性:name、age、city。
2、程式6~11行宣告Person類的一個構造方法,此構造方法分別為各屬性賦值。
3、程式12~15行宣告一個talk()方法,此方法用於返回使用者資訊。
4、程式21~26行分別例項化三個Person物件並呼叫類中的talk()方法,輸出使用者資訊。
由上面的程式可以發現,所有的Person物件都有一個city屬性,而且值也全部相同,如圖5-13所示:
圖5-13 類結構圖
可以試著想一想,現在假設程式產生了50個Person物件,如果想修改所有人的city屬性的話,是不是就要呼叫50遍city屬性,進行重新修改,顯然太麻煩了。所以在java中提供了static關鍵字,用它來修飾類的屬性後,則此屬性就是公共屬性了(或稱為類屬性)。
訪問static成員變數的方式:
物件.成員變數
或
類名.成員變數
將TestStaticDemo1.java程式稍作修改就形成了範例TestStaticDemo2.java,如下所示:
範例:TestStaticDemo2.java
class Person { String name; static String city = "中國"; int age; public Person(String name, int age) { this.name = name; this.age = age; } public String talk() { return "我是:" + this.name + ",今年:" + this.age + "歲,來自:" + city; } } public class TestStaticDemo2 { public static void main(String[] args) { Person p1 = new Person("張三", 25); Person p2 = new Person("李四", 30); Person p3 = new Person("王五", 35); System.out.println("修改之前資訊:" + p1.talk()); System.out.println("修改之前資訊:" + p2.talk()); System.out.println("修改之前資訊:" + p3.talk()); System.out.println(" ************* 修改之後資訊**************"); // 修改後的資訊 p1.city = "美國";
System.out.println("修改之後資訊:" + p1.talk()); System.out.println("修改之後資訊:" + p2.talk()); System.out.println("修改之後資訊:" + p3.talk()); } }
輸出結果:
修改之前資訊:我是:張三,今年:25歲,來自:中國
修改之前資訊:我是:李四,今年:30歲,來自:中國
修改之前資訊:我是:王五,今年:35歲,來自:中國
************* 修改之後資訊**************
修改之後資訊:我是:張三,今年:25歲,來自:美國
修改之後資訊:我是:李四,今年:30歲,來自:美國
修改之後資訊:我是:王五,今年:35歲,來自:美國
程式說明:
1、程式1~15行宣告一個名為Person的類,含有三個屬性:name、age、city。其中city為static型別。
2、程式6~10行宣告Person類的一個構造方法,此構造方法作用是分別為name、age屬性賦值。
3、程式11~14行宣告一個talk()方法,此方法用於返回使用者資訊。
4、程式20~22行分別例項化三個Person物件。
5、程式23~25行分別呼叫類中的talk()方法,輸出使用者資訊。
6、程式28行修改了p1中的city屬性。
從上面的程式中可以發現,程式只在第28行修改了city屬性,而且只修改了一個物件的city屬性,但再次輸出時,發現全部的物件的city值都發生了一樣的變化,這就說明了用static宣告的屬性是所有物件共享的。如下圖5-14所示:
圖5-14 static變數的使用
在圖中可以發現,所有的物件都指向同一個city屬性,只要當中有一個物件修改了city屬性的內容,則所有物件都會被同時修改。
另外,需要注意一點,用static方式宣告的屬性,也可以用類名直接訪問,拿上面的程式來說,如果想修改city屬性中的內容,可以用如下方式:
Person.city = "美國";
所以把用static型別宣告的變數,稱為“類變數”。即用static宣告的變數是屬於類的,而不是屬於具體的某個物件。
小提示:
既然static型別的變數是所有物件共享的記憶體空間,也就是說無論最終有多少個物件產生,也都只有一個static型別的屬性,那可不可以用它來計算類到底產生了多少個例項物件呢?可以想一想,只要一個類產生一個新的例項物件,都會去呼叫構造方法,所以可以在構造方法中加入一些記數操作,如下面的程式:
class Person { static int count = 0; // 宣告一個static型別的整型變數 public Person() { count++; // 增加了一個物件 System.out.println("產生了:" + count + "個物件!"); } } public class TestStaticDemo3 { public static void main(String[] args) { new Person(); new Person(); } }
5.10.2 靜態方法
static既可以在宣告變數時使用,也可以用其來宣告方法,用它宣告的方法有時也被稱為“類方法”,請看下面的範例:
範例:TestStaticDemo4.java
class Person { String name; private static String city = "中國"; int age; public Person(String name, int age) { this.name = name; this.age = age; } public String talk() { return "我是:" + this.name + ",今年:" + this.age + "歲,來自:" + city; } public static void setCity(String c) { city = c; } } public class TestStaticDemo4 { public static void main(String[] args) { Person p1 = new Person("張三", 25); Person p2 = new Person("李四", 30); Person p3 = new Person("王五", 35); System.out.println("修改之前資訊:" + p1.talk()); System.out.println("修改之前資訊:" + p2.talk()); System.out.println("修改之前資訊:" + p3.talk()); System.out.println(" **************修改之後資訊**************"); // 修改後的資訊 Person.setCity("美國"); System.out.println("修改之後資訊:" + p1.talk()); System.out.println("修改之後資訊:" + p2.talk()); System.out.println("修改之後資訊:" + p3.talk()); } }
輸出結果:
修改之前資訊:我是:張三,今年:25歲,來自:中國
修改之前資訊:我是:李四,今年:30歲,來自:中國
修改之前資訊:我是:王五,今年:35歲,來自:中國
**************修改之後資訊**************
修改之後資訊:我是:張三,今年:25歲,來自:美國
修改之後資訊:我是:李四,今年:30歲,來自:美國
修改之後資訊:我是:王五,今年:35歲,來自:美國
程式說明:
1、程式1~19行宣告一個名為Person的類,類中含有一個static型別的變數city,並對此物件進行了封裝。
2、15~18行宣告一個static 型別的方法,此方法也可以用類名直接呼叫,用於修改city屬性的內容。
3、程式第32行由Person呼叫setCity()方法,對city的內容進行修改。
注意:
在使用static型別宣告的方法時需要注意的是:如果在類中聲明瞭一個static型別的屬性,則此屬性既可以在非static型別的方法中使用,也可以在static型別的方法中使用。但在static型別修飾的方法中呼叫非static型別的屬性或方法時,則會出現錯誤。靜態方法不能被覆蓋成非靜態。同樣,非靜態方法也不能被覆蓋成靜態方法。
public class PersonStatic { String name = "張三"; static String city = "中國"; int age; public PersonStatic(String name, int age) { this.name = name; this.age = age; } public void fun() { } public static void print() { fun(); //fun為非static方法 出錯 System.out.println(name); //name為非static變數 出錯 } public String talk() { return "我是:" + this.name + ",今年:" + this.age + "歲,來自:" + city; } }
可以發現,程式中print()方法是static型別的,而name屬性和fun方法都是非static型別的,所以print在呼叫非static型別的name屬性和非static型別的fun方法時就出現了錯誤。解決辦法是:傳遞一個本類的物件進去,通過物件去訪問。如下:
class Person { String name; static String city = "中國"; int age; public Person(String name, int age) { this.name = name; this.age = age; } public void fun() { } public static String talk(Person p) { p.fun(); return "我是:" + p.name + ",今年:" + p.age + "歲,來自:" + p.city; } } public class TestJava2_1 { public static void main(String[] args) { Person p1 = new Person("張三", 25); Person p2 = new Person("李四", 30); Person p3 = new Person("王五", 35); System.out.println("修改之前資訊:" + p1.talk(p1)); System.out.println("修改之前資訊:" + p2.talk(p2)); System.out.println("修改之前資訊:" + p3.talk(p3)); System.out.println(" ************* 修改之後資訊**************"); // 修改後的資訊 Person.city = "美國"; System.out.println("修改之後資訊:" + p1.talk(p1)); System.out.println("修改之後資訊:" + p2.talk(p2)); System.out.println("修改之後資訊:" + p3.talk(p3)); } }
5.10.3 理解 main()方法
在前面的章節中,已經多次看到,如果一個類要被Java直譯器直接裝載執行,這個類中必須有main()方法。有了前面所有的知識,現在可以理解main()方法的含義了。
由於Java虛擬機器需要呼叫類的main()方法,所以該方法的訪問許可權必須是public,又因為Java虛擬機器在執行main()方法時不必建立物件,所以該方法必須是static的,該方法接收一個String型別的陣列引數,該陣列中儲存執行Java命令時傳遞給所執行的類的引數。
向java中傳遞引數可用如下的命令:
java 類名稱 引數1 引數2 引數3
通過執行程式TestMain.java來了解如何向類中傳遞引數以及程式又是如何取得這些引數的,如下所示:
範例:TestMain.java
public class TestMain { /** * public:表示公共方法 * <p> * static:表示此方法為一靜態方法,可以由類名直接呼叫 * <p> * void:表示此方法無返回值 * <p> * main:系統定義的方法名稱 * <p> * String args[]:接收執行時引數 */ public static void main(String[] args) { int j = args.length; // 取得輸入引數的長度 if (j != 2) { System.out.println("輸入引數個數有錯誤!"); System.exit(1); // 退出程式 } for (int i = 0; i < args.length; i++) { System.out.println(args[i]); } } }
執行程式:
java TestMain first second
輸出結果:
first
second
程式說明:
1、程式第13行判斷輸入引數的個數是否為兩個引數,如果不是,則退出程式。
2、所有的接收的引數都被存放在args[]字串陣列之中,用for迴圈輸出全部內容。
需要注意的是,Java裡面的引數傳遞,args[0]已經是傳遞進來的引數了,而C/C++裡面的argv[0]表示程式本身,即Java裡面args是不包含可執行程式本身的。
5.10.4 靜態程式碼塊
一個類可以使用不包含在任何方法體中的靜態程式碼塊,當類被載入時,靜態程式碼塊被執行,且只執行一次,靜態程式碼塊經常用來進行類屬性的初始化。如下面的程式程式碼所示:
範例:TestStaticDemo5.java
class Person { public Person() { System.out.println("1.public Person()"); } // 此段程式碼會優先建構函式執行 static { System.out.println("2.Person類的靜態程式碼塊被呼叫!"); } } public class TestStaticDemo5 { // 執行本程式時,靜態程式碼塊會被自動執行 static { System.out.println("3.TestStaticDemo5類的靜態程式碼塊被呼叫!"); } public static void main(String[] args) { System.out.println("4.程式開始執行!"); // 產生兩個例項化物件 new Person(); new Person(); } }
輸出結果:
3.TestStaticDemo5類的靜態程式碼塊被呼叫!
4.程式開始執行!
2.Person類的靜態程式碼塊被呼叫!
1.public Person()
1.public Person()
程式說明:
1、程式1~12行宣告一個名為Person的類。
2、程式8~11行宣告一個靜態程式碼塊,此程式碼塊放在Person類之中。
3、程式15~18行在類TestStaticDemo5中也宣告一靜態程式碼塊。
4、22、23行分別產生了兩個Person類的匿名物件。
從程式執行結果中可以發現,放在TestStaticDemo5類中的靜態程式碼塊首先被呼叫,這是因為程式首先執行TestStaticDemo5類,所以此程式的靜態程式碼塊會首先被執行。程式在22、23行產生了兩個匿名物件,可以發現Person類中的靜態程式碼塊只執行了一次,而且靜態程式碼塊優先於靜態方法和建構函式,由此可以發現,靜態程式碼塊可以為靜態屬性初始化。
java中存在四種程式碼塊:
1、普通程式碼塊:是直接寫在各個方法中的程式碼塊。
2、構造程式碼塊:是直接寫在類中的程式碼塊,在例項化物件的時候呼叫,而且每例項化一個物件就要呼叫一次構造程式碼塊(可多次呼叫)。構造程式碼塊優先於構造方法執行。
3、靜態程式碼塊:是使用static宣告的程式碼塊,靜態塊只調用一次,而且優先於構造程式碼塊執行。作用:為static型別的屬性初始化。
4、同步程式碼塊:主要出現在多執行緒中。
程式碼塊的執行順序及執行次數:
1、主方法(main)中的靜態程式碼塊優先於主方法執行;
2、主方法(main)優先於其他類中的靜態程式碼塊執行;
3、在普通類中定義的靜態程式碼塊,優先於構造程式碼塊執行;
4、父類的靜態程式碼塊優先於子類的靜態程式碼塊執行;
5、子類的靜態程式碼塊優先於父類的構造程式碼塊執行;
6、不管有多少個例項化物件產生,靜態程式碼塊只執行一次;
7、父類的構造程式碼塊和構造方法,優先於子類的構造程式碼塊;
8、構造程式碼塊優先於構造方法執行。
9、構造程式碼塊和構造方法執行的次數與宣告的物件數相等,父類同樣。
例項:各程式碼塊執行的優先順序
class SuperClass { //父類 static
{ System.out.println("1、父類static程式碼塊"); } { System.out.println("2、父類構造程式碼塊"); } public SuperClass() { System.out.println("3、父類構造方法"); } } class SunClass extends SuperClass { //子類 static { System.out.println("4、子類static程式碼塊"); } { System.out.println("5、子類構造程式碼塊"); } public SunClass() { System.out.println("6、子類構造方法"); } } public class Test { static { System.out.println("7、主類static程式碼塊"); } { System.out.println("8、主類程式碼塊"); } public static void main(String[] args) { // TODO Auto-generated method stub System.out.println("9、主方法開始執行"); SunClass sc1 = new SunClass(); SunClass sc2 = new SunClass(); SunClass sc3 = new SunClass(); System.out.println("10、主方法執行結束"); } }
5.11 構造方法的私有
方法依實際需要,可分為public與private。同樣的,構造方法也有public與private之分。到目前為止,所使用的構造方法均屬於public,它可以在程式的任何地方被呼叫,所以新建立的物件也都可以自動呼叫它。如果構造方法被設為private,則無法在該構造方法所在的類以外的地方被呼叫。請看下面的程式:
範例:TestSingleDemo1.java
public class TestSingleDemo1 { private TestSingleDemo1() { System.out.println("private TestSingleDemo1 ."); } public static void main(String[] args) { new TestSingleDemo1(); } }
輸出結果:
private TestSingleDemo1 .
由上面程式可以發現,程式第3行在宣告構造方法的時候將之宣告為private型別,則此構造方法只能在本類內被呼叫。同時你會發現,而在本程式中的main方法也放在TestSingleDemo1類的內部,所以在本類中可以自己產生例項化物件。
看過上面的程式,這麼做似乎沒有什麼意義,因為一個類最終都會由外部去呼叫,如果這麼做的話,豈不是所有構造方法被私有化了的類都需要這麼去呼叫了嗎?那程式豈不是會有很多的main()方法了嗎?舉這個例子主要是讓大家清楚:構造方法雖然被私有了,但並不一定是說此類不能產生例項化物件,只是產生這個例項化物件的位置有所變化,即只能在本類中產生例項化物件。請看下面的範例:
範例:TestSingleDemo2.java
class Person { String name; // 在本類宣告Person物件p,注意此物件用final標記,表示不能再重新例項化 private static final Person p = new Person(); private Person() { name = "張三"; } public static Person getP() { return p; } } public class TestSingleDemo2 { public static void main(String[] args) { // 宣告一個Person類的物件 Person p = Person.getP(); System.out.println(p.name); } }
輸出結果:
張三
程式說明:
1、程式6~9行將Person類的構造方法封裝起來,外部無法通過其構造方法產生例項化物件。
2、程式第5行宣告一個Person類的例項化物件,此物件是在Person類內部例項化,所以可以呼叫私有構造方法。另外,此物件被標識為static 型別,表示為靜態屬性,同時此物件被私有化,另外在宣告Person物件的時候加上了一個final 關鍵字,此關鍵字表示Person的物件p不能被重新例項化。(關於final後面會詳細介紹)
3、程式10~13定義了一個靜態函式,將類內部例項化的物件傳出。因為建構函式是私有的,所以在外部不能再例項化物件,即不能通過“物件.getP()”的形式呼叫getP,所以getP需要宣告為static的。表示屬於類方法,可以利用“類名.getP()”的形式來呼叫
4、程式第21行宣告一個Person類的物件p,呼叫Person類中的getP()方法,此方法返回Person類的例項化物件。
從上面程式中可以發現,無論在Person類的外部宣告多少個物件,最終得到的都是同一個引用,因為此類只能產生一個例項物件,這種做法在設計模式中稱為單態模式。而所謂設計模式也就是在大量的實踐中總結和理論化之後優選的程式碼結構、程式設計風格以及解決問題的思考方式。有興趣可以研究一下。
如果沒有加上final修飾,則Person p1 = new Person("張三", 25);之後,還可以利用p1 = new Person("李四", 33);來重新例項化。加上final修飾之後,則表面p1是最終變數,不能再例項化。
單態模式例項:
class Person { private String name; private static Person p = null; private Person(String name) { this.name = name; } public static Person getP(String name) { if (p == null) p = new Person(name); return p; } public void disp() { System.out.println("name=" + this.name); } } public class TestSingleDemo { public static void main(String[] args) { // 宣告Person類的物件 Person p1 = Person.getP("aaa"); Person p2 = Person.getP("bbb"); p1.disp(); p2.disp(); } }
兩次結果都輸出aaa。因為從始至終都只有一個例項化物件。p1和p2都是引用的p。對於單例設計模式,典型的應用就是有些遊戲只允許登入一個客戶端,不能在一臺PC機上啟動多個。
5.12 物件陣列的使用
在前面的章節中已介紹過如何以陣列來儲存基本資料型別的變數。相同的,物件也可以用陣列來存放,通過下面兩個步驟來實現:
1、宣告類型別的陣列變數,並用new分配記憶體空間給陣列。
2、用new產生新的物件,並分配記憶體空間給它
例如,要建立三個Person類型別的陣列元素,可用下列的語法:
Person p[]; // 宣告Person類型別的陣列變數
p = new Person[3]; // 用new分配記憶體空間
建立好陣列元素之後,便可把陣列元素指向由Person類所產生的物件:
p[0] = new Person (); p[1] = new Person (); // 用new產生新的物件,並分配記憶體空間給它 p[2] = new Person ();
此時,p[0]、p[1]、p[2]是屬於Person類型別的變數,它們分別指向新建物件的記憶體參考地址。當然也可以寫成如下形式:
Person p[] = new Person[3]; // 建立物件陣列元素,並分配記憶體空間
利用for 迴圈來完成物件陣列內的初始化操作,此方式屬於動態初始化:
for(int i=0;i<p.length;i++) { p[i] = new Person(); }
或者採用靜態方式初始化物件陣列:
Person p[] = {new Person(),new Person(),new Person()};
範例:TestObjectArray.java
class Person { String name; int age; public Person() { } public Person(String name, int age) { this.name = name; this.age = age; } public String talk() { return "我是:" + this.name + ",今年:" + this.age + "歲"; } } public class TestObjectArray { public static void main(String[] args) { Person p[] = { new Person("張三", 25), new Person("李四", 30), new Person("王五", 35) }; for (int i = 0; i < p.length; i++) { System.out.println(p[i].talk()); } } }
輸出結果:
我是:張三,今年:25歲
我是:李四,今年:30歲
我是:王五,今年:35歲
程式說明:
1、程式第22~24行用靜態宣告方式聲明瞭三個物件的Person類的物件陣列。
2、程式第25~28行用for迴圈輸出所有物件,並分別呼叫talk()方法列印資訊。
5.13 Java 基本資料型別的包裝類
在Java 的設計中提倡一種思想:“一切皆物件”,那麼這樣一來就出現了一個矛盾。從資料型別的劃分中可以知道Java中的資料型別分為基本資料型別和引用資料型別。基本資料型別定義的變數怎麼能夠成為物件呢?此時,就需要將基本資料型別進行包裝。將八種基本型別變為一個類的形式,這就是包裝類的作用。這樣就可以以物件的形式操作基本資料型別。
Java裡面八種基本資料型別對應的包裝類如表5-1所示:
表5-1 八種基本資料型別的包裝類
裝箱及拆箱的概念:
將基本資料型別變為包裝類稱為裝箱;
將包裝類的資料型別變為基本資料型別稱為拆箱。
以Integer為例:
public class AA { public static void main(String args[]) { int x = 30; // 基本資料型別 Integer i = new Integer(x); // 裝箱:將基本資料型別變為包裝類 int temp = i.intValue(); // 拆箱:將一個包裝類變為基本資料型別 } }
在JDK1.5之前對於程式本身來說包裝類是不能直接進行“+、-、*、/、++、--”操作的。因為包裝類是一個類。但是在JDK1.5版本之後,對程式的包裝類功能進行了改變,增加了自動裝箱及自動拆箱的功能,而且也可以使用包裝類直接進行數字運算。
public class AA { public static void main(String args[]) { Integer i = 30; // 自動裝箱成 Integer Float f = 30.3f; // 自動裝箱成Float int x = i; // 自動拆箱為int float y = f; // 自動拆箱為float } }
在包裝類中,實際上還存在一個最大的特點,就是可以將字串變為指定的資料型別。包裝類在實際中用得最多的還是在於字串變為基本資料型別的操作上。例如:將一個全由數字組成的字串變為一個int型別的資料。在Integer類中分別提供了以下的方法:
//1. 利用建構函式直接進行轉換 Integer i = new Integer("123"); System.out.println(i.intValue()); //2. 利用parseInt函式轉換 int j = Integer.parseInt("456"); System.out.println(j);
5.14 String 類的常見方法
通過檢視API文件,下面是常用的方法:
public String(char[] value):直接將一個字元陣列變為一個字串
public String(char[] value,int offset,int count):將一個指定範圍的字元陣列變為字串
public String(byte[] bytes):將一個byte陣列變為字串
public String(byte[] bytes,int offset,int length):將一個指定範圍的byte陣列變為字串
public char[] toCharArray():把一個字串變為字元陣列
public char charAt(int index):從字串中取出指定位置的字元
int compareTo(String anotherString) 按字典順序比較兩個字串。如果字串位於anotherString之前,返回一個負數;如果在其後面,返回一個正數;相等,返回0;
public byte[] getBytes():將一個字串變為byte 陣列
public int length():取得字串長度
public int indexOf(String str):從開頭查詢指定字串的位置
public int indexOf(String str,int fromIndex):從指定位置查詢指定字串的位置
public String trim():清除字串左右兩端的空格
public String substring(int beginIndex):從指定位置開始,一直取到尾進行字串的擷取
public String substring(int beginIndex,int end):指定擷取字串的開始點和結束點
public String[] split(String regex):按照指定的字串對字串進行拆分
public String toUpperCase():將一個字串全部變為大寫字母
public String toLowerCase():將一個字串全部變為小寫字母
public boolean startsWith(String prefix):判斷是否以指定的字串開頭
public boolean endsWith(String suffix):判斷是否以指定的字串結尾
public boolean equals(String str):判斷兩個字串內容是否相等
public boolean equalsIgnoreCase(String str):不區分大小寫比較兩個字串是否相等
String replace(char oldChar, char newChar):返回一個新的字串,它是通過用newChar替換此字串中出現的所有oldChar得到的。
public String replaceAll(String regex,String replacement):字串替換
public static String valueOf(XXX X):XXX為基本資料型別
有些時候,需要由較短的字串構建字串,例如按鍵或來自檔案中的單詞,採用字串的連線方式到底此目的效率比較低。每次連線字串時,都會構建一個新的String物件,可以利用Stringbuilder類避免這樣的問題。
如果需要許多小段的字串構建一個字串,那麼應該按照下列步驟來進行。
首先構造一個空的StringBuilder字串構建器:
StringBuilder builder = new StringBuilder();
當每次需要新增一部分內容時,就呼叫append方法:
builder.append(ch); // append 一個字元
builder.append(str); // append 一個字串
當需要構建字串時就呼叫toString()方法,將可以得到一個字串物件:
string completedStr = builder.toString();
在JDK1.5中引入了StringBuilder類,這個類的前身是StringBuffer類,StringBuffer類的效率較低,但允許採用多執行緒的方式執行新增或刪除字串的操作。如果所有字串在一個單執行緒中編輯,則使用StringBuilder代替它,兩個類的API是一樣的。
java.lang.StringBuilder常見類如下:
StringBuilder():構造一個空的字串構建器。
int length():返回字串構建器字元的個數。
StringBuilder append(String str):追加一個字串並返回this。
StringBuilder append(char ch):追加一個字元並返回this。
void setCharAt(int i, char c):設定下標i的值為c。
StringBuilder insert(int offset, String str):在offset位置插入一個字串並返回this。
StringBuilder insert(int offset, char ch):在offset位置插入一個字元並返回this。
StringBuilder delete(int startIndex, int endIndex):刪除[startIndex, endIndex)區間的元素
String toString():返回一個與構建器內容相同的字串。
5.15 Java 文件註釋
在前面已經提到過,Java支援三種形式的註釋。前兩種是//和/*…*/。第三種方式被稱為文件註釋。它以“/**”開始,以“*/”標誌結束。文件註釋提供將程式資訊嵌入到程式中的功能。開發者可以使用javadoc工具將資訊取出,然後轉換為HTML檔案。文件註釋提供了編寫程式文件的便利方式。
5.15.1 javadoc 標記
javadoc程式識別下列標記:
Tag標記意義
正如上面看到的那樣,所有的文件標記都以“(@)”標誌開始。在一個文件註釋中,也可以使用其它的標準HTML標記。然而,一些標記(如標題)是不能使用的,因為它們會破壞由javadoc生成的HTML檔案外觀。
可以使用文件註釋為類、介面、域、構造方法和方法提供文件。在所有這些情況中,文件註釋必須緊接在被註釋的專案之前。為變數做註釋可以使用@see、@since、@serial、@serialField和@deprecated文件標記。為類做註釋可以使用@see、@author、@since、@deprecated和@version 文件標記。為方法做註釋可以用@see、@return、@param、@since、@deprecated、@throws、@serialData和@exception文件標記。而{@link}或{@docRoot}標記則可以用在任何地方。下面詳細列出了每個標記的使用方法:
@ author
標記@author指定一個類的作者,它的語法如下:
@author description
其中,description通常是編寫這個類的作者名字。標記@author只能用在類的文件中。在執行javadoc時,需要指定-author選項,才可將@author域包括在HTML文件中。
@deprecated
@deprecated標記指示不贊成使用一個類或是一個成員。建議使用@see標記指示程式設計師其他的正確的選擇。其語法如下:
@deprecated description
其中description是描述所反對的資訊。由@deprecated標記指定的資訊由編譯器識別,包括在生成的.class檔案中,因此在編譯Java原始檔時,程式設計師可以得到這個資訊。
@deprecated標記可以用於變數,方法和為類做註釋的文件中。
{@docRoot}
{@docRoot}指定當前文件的根目錄路徑。
@exception
@exception標記描述一個方法的異常,其語法如下:
@exception exception-name explanation
其中,異常的完整名稱由exception-name指定,explanation是描述異常是如何產生的字串。@exception只用於為方法做註釋的文件。
{@link}
{@link} 標記提供一個附加資訊的聯機超連結。其語法如下:
{@link name text}
其中,name是加入超連結的類或方法的名字,text是顯示的字串。
@param
@param標記註釋一個方法的引數。其語法如下所示:
@param parameter-name explanation
其中parameter-name指定方法的引數名。這個引數的含義由explanation描述。
@param標記只用在為方法做註釋的文件中。
@return
@return標記描述一個方法的返回值。其語法如下:
@return explanation
其中,explanation 描述方法返回值的型別和含義。@return 標記只用為方法做註釋的文件中。
@see
@see標記提供附加資訊的引用。最常見的使用形式如下所示:
@see anchor
@see pkg.class#member text
在第一種格式中,anchor是一個指向絕對或相對URL的超連結。第二種格式,pkg.class#member指示專案的名字,text是專案的文字顯示。文字引數是可選的,如果不用,顯示由pkg.class#membe指定的專案。成員名,也是可選的。因此,除了指向特定方法或者欄位的引用之外,還可以指定一個引用指向一個包,一個類或是一個介面。名字可以是完全限定的,也可以是部分限定的。但是,成員名(如果存在的話)之前的點必須被替換成一個雜湊字元#。
@serial
@serial標記為預設的可序列化欄位定義註釋文件。其語法如下:
@serial description
其中,description是欄位的註釋。
@serialData
@serialData標記為writeObject( )或者writeExternal( )方法編寫的資料提供文件。其語法如下:
@serialData description
其中,description 是資料的註釋。
@serialField
@serialField標記為ObjectStreamField元件提供註釋,其語法如下:
@serialField name type description
其中,name是域名,type是域的型別,description是域的註釋。
@since
@since標記宣告由一個特定釋出版本引入的類或成員。其語法如下:
@since release
其中,release是指示這個特性可用的版本或釋出的字串。@since標記可以用在為變數、方法和類的做註釋文件中。
@throws
@throws標記與@exception標記的含義相同。
@version
@version標記指示類的版本。其語法如下:
@version info
其中,info是包含版本資訊的字串,典型情況下是如2.2這樣的版本號。@version標記只用在為類的做註釋文件中。在執行javadoc時,指定-version選項,可將@version域包含在HTML文件中。
5.15.2 文件註釋的一般形式
在用/**開頭後,第一行,或者頭幾行是類、變數或方法的主要描述。其後,可以包括一個或多個不同的@標記。每個@標記必須在一個新行的開頭,或者跟隨一個星號(*)之後。同類型的多個標記應該組合在一起。例如,如果有三個@see標記,最好是一個挨著一個。
下面是一個類的文件註釋的例子:
/** * This class draws a bar chart. * @author Herbert Schildt * @version 3.2 */
5.15.3 javadoc 的輸出
javadoc程式將Java程式的原始檔作為輸入,輸出幾個包含該程式文件的HTML檔案。每個類的資訊都在其自己的HTML檔案中。同時,javadoc還輸出一個索引和一個層次結構樹。
Javadoc還可生成其他HTML檔案。不同版本的javadoc工作方式也有所不同,可以閱讀Java開發系統的說明書瞭解所使用的版本的細節處理的規定。
看下面的例子,下面的例子說明了JavaDoc的使用方法:
範例:PersonJavaDoc.java
/** * Title: PersonJavaDoc<br> * <p> * Description: 通過 PersonJavaDoc 類來說明 Java 中的文件註釋<br> * <p> * Copyright:(c) 2014 www.sun.com.cn<br> * <p> * Company : sun java </br> * * @author [email protected] * @version 1.00 */ public class PersonJavaDoc { private String name = "Careers"; private String sex = "male"; private int age = 30; /** * 這是 PersonJavaDoc 物件無引數的構造方法 */ public PersonJavaDoc() { } /** * 這是 PersonJavaDoc 類的有參構造方法 * * @param name PersonJavaDoc 的名字 * @param sex PersonJavaDoc 的性別 * @param age PersonJavaDoc 的年齡 */ public PersonJavaDoc(String name, String sex, int age) { this.name = name; this.sex = sex; this.age = age; } /** * 這是設定 name 的值的方法,將引數 name 的值賦給變數 this.name * * @param name PersonJavaDoc 的名字 */ public void setName(String name) { this.name = name; } /** * 這是取得 name 的值的方法 * * @return 返回 name 的值 */ public String getName() { return name; } /** * 這是設定 age 的值的方法 * * @param age PersonJavaDoc 的年齡 */ public void setAge(int age) { if (age < 0) this.age = 0; else this.age = age; } /** * 這是取得 age 的值的方法 * * @return 返回 age 的值 */ public int getAge() { return age; } /** * 這是設定 sex 的值的方法,將引數 sex 的值賦給變數 this.sex * * @param sex PersonJavaDoc 的性別 */ public void setSex(String sex) { this.sex = sex; } /** * 這是取得 sex 的值的方法 * * @return 返回 sex 的值 */ public String getSex() { return sex; } /** * 這是顯示輸出的方法 */ public void shout() { System.out.println("我是 " + name + ",我性別 " + sex + ",今年 " + age + " 歲!"); } }
之後用javadoc命令進行編譯,請看下面的命令:
javadoc -d PersonJavaDoc -version -author PersonJavaDoc.java
-d:表示生成目錄,目錄名稱為 PersonJavaDoc
-version:表示要求 javadoc 程式在說明檔案中加入版本資訊。
-author:表示要求 javadoc 程式在說明檔案中加入作者資訊。
最終可以發現在硬碟上新生成了一個PersonJavaDoc的資料夾,如圖5-15所示:
圖5-15 PersonJavaDoc資料夾的內容
開啟資料夾裡的index.html檔案可以看見如下所示的介面:
圖5-16 index.html
·本章摘要:
1、“類”是把事物的資料與相關的功能封裝在一起,形成的一種特殊結構,用以表達對真實世界的一種抽象概念。
2、Java把資料成員稱為field(屬性),把方法成員稱為method(方法)。
3、由類所建立的物件稱為instance,譯為“例項”。
4、建立屬於某類的物件,可通過下面兩個步驟來達成:(1)、宣告指向“由類所建立的物件”的變數。(2)、利用new建立新的物件,並指派給步驟一中所建立的變數。
5、要訪問到物件裡的某個屬性(field)時,可通過“物件名稱.屬性”語法來實現,如果要呼叫封裝在類裡的方法,則可通過“物件名稱.method”語法來實現。
6、有些方法不必傳遞任何資料給呼叫端程式,因此是沒有返回值的。若方法本身沒有返回值,則必須在方法定義語句前面加上關鍵字void。
7、私有成員(private member)可限定類中的屬性,被限制成私有的屬性僅能供同一類內的方法所訪問。
8、類外部可訪問到類內部的公有成員(public member)。
9、“封裝”(encapsulation):是把屬性和方法包裝在一個類內以限定成員的訪問,以起到保護資料的作用。
10、構造方法可視為一種特殊的方法,它的主要作用是為所建立的物件賦初值。
11、構造方法的名稱必須與其所屬的類的類名稱相同,且不能有返回值。
12、從某一構造方法內呼叫另一構造方法,是通過this() 這個關鍵字來完成的。
13、構造方法有public與private之分。宣告為public的構造方法可以在程式的任何地方被呼叫,所以新建立的物件都可以自動呼叫它。而被宣告為private的則無法在該構造方法所在的類以外的其它地方被呼叫。
14、如果構造方法省略不寫,Java則會自動呼叫預設的構造方法(預設的構造方法沒有任何引數)。
15、“基本型別的變數”是指用int、double等關鍵字所宣告的變數,而由類宣告而得的變數,稱之為“類型別的變數”,它是屬於“非基本型別的變數”的一種。
16、物件也可以用陣列來存放,但必須有下面兩個步驟:(1)、宣告類型別的陣列變數,並用new分配記憶體空間給陣列。(2)、用new產生新的物件,並分配記憶體空間給它。
17、如果在類Outer的內部再定義一個類Inner,此時類Inner稱為內部類(inner class),而類Outer則稱為外部類(outer class)。