這節(jié)教程將開始討論對象的生命周期。包括怎樣創(chuàng)建對象、怎樣使用它以及在不使用它的時候?qū)⑺鼜南到y(tǒng)中清除。下面一個一個介紹:
4.1 對象的生命周期
在這一小節(jié)中你可以學(xué)到怎樣創(chuàng)建和使用任何類型的對象,還討論了當對象不再需要的時候系統(tǒng)怎樣清除對象的。
典型的JAVA程序創(chuàng)建對象,對象之間的交互是通過發(fā)送消息來實現(xiàn)的。通過這些對象的交互,JAVA程序可以執(zhí)行一個GUI、運行一個動畫或者通過網(wǎng)絡(luò)發(fā)送和接收信息。一旦對象已經(jīng)完成了任務(wù),它就被作為無用信息被回收,它的資源可以由其它對象回收利用。
以下是一個小的例子程CreateObjectDemo,它創(chuàng)建三個對象:一個是Point對象和兩個Rectange對象,你需要這三個源程序才可以編譯這個程序:
public class CreateObjectDemo {
public static void main(String[] args) {
//創(chuàng)建一個Point對象和兩個Rectangle對象
Point origin_one = new Point(23, 94);
Rectangle rect_one = new Rectangle(origin_one, 100, 200);
Rectangle rect_two = new Rectangle(50, 100);
// 顯示rect_one的寬、高以及面積
System.out.println("Width of rect_one: " + rect_one.width);
System.out.println("Height of rect_one: " + rect_one.height);
System.out.println("Area of rect_one: " + rect_one.area());
// 設(shè)置rect_two的位置
rect_two.origin = origin_one;
// 顯示rect_two的位置
System.out.println("X Position of rect_two: " + rect_two.origin.x);
System.out.println("Y Position of rect_two: " + rect_two.origin.y);
// 移動rect_two并且顯示它的新位置
rect_two.move(40, 72);
System.out.println("X Position of rect_two: " + rect_two.origin.x);
System.out.println("Y Position of rect_two: " + rect_two.origin.y);
}
}
一旦創(chuàng)建了對象,程序就可以操作對象并將它們有關(guān)的一些信息顯示出來,以下是這個程序的輸出結(jié)果:
Width of rect_one: 100
Height of rect_one: 200
Area of rect_one: 20000
X Position of rect_two: 23
Y Position of rect_two: 94
X Position of rect_two: 40
Y Position of rect_two: 72
這一節(jié)使用這個例子來在程序中描述對象的生命周期。從這你可以學(xué)到怎樣編寫代碼來創(chuàng)建、使用對象以及系統(tǒng)怎樣將它從內(nèi)存中清除的。
4.1 對象的生命周期
下面主要分成幾部分來討論:
1. 創(chuàng)建對象
使用對象
清除沒有使用的對象
4.1.1 創(chuàng)建對象
眾所周知,可以從類來創(chuàng)建對象。下面的幾條語句都是用來創(chuàng)建對象的,它們都是來自上面程序CreateObjectDemo程序:
Point origin_one = new Point(23, 94);
Rectangle rect_one = new Rectangle(origin_one, 100, 200);
Rectangle rect_two = new Rectangle(50, 100);
上面第一條語句從Point類創(chuàng)建了一個對象,而第二條和第三條語句是從Rectangle類眾創(chuàng)建了對象。但是每條語句都有三部分組成:
1. 聲明:Point origin_one、Rectangle rect_one以及Rectangle rect_two都是變量的聲明,它們的格式是類型后加變量名。當你創(chuàng)建一個對象的時候,你不必聲明一個變量來引用它。然而,變量生命經(jīng)常出現(xiàn)在創(chuàng)建對象代碼的相同行上。
實例化:new是JAVA運算符,它可以創(chuàng)建新的對象并且為對象分配了內(nèi)存空間。
初始化:new運算符后跟著一個構(gòu)造函數(shù)的調(diào)用。比如Point(23,94)就是一個Point類的構(gòu)造函數(shù)的調(diào)用。這個構(gòu)造函數(shù)初始化了這個新對象。
下頁也對這幾個部分逐個介紹:
4.1.1.1 聲明一個變量來引用對象
從前面的教程,你應(yīng)該知道了如何聲明一個變量了,你可以這樣來編寫:
type name
其中type是數(shù)據(jù)類型,而name是變量名。
除了原始類型(比如int和boolean),JAVA平臺還直接提供了類和接口也是數(shù)據(jù)類型。這樣為了聲明一個變量來引用對象,你可以使用類或者接口的名字作為變量的類型。下面的例程使用了Point和Rectangle類作為類型來聲明變量:
Point origin_one = new Point(23, 94);
Rectangle rect_one = new Rectangle(origin_one, 100, 200);
Rectangle rect_two = new Rectangle(50, 100);
聲明沒有創(chuàng)建新對象。Point origin_one代碼沒有一個新的Point對象,它只是聲明一個變量orgin_one,它將用來引用Point對象。這個引用暫時是空的直到被賦值了。一個空的引用就是一個NULL引用。
4.1.1.2 實例化對象
為了創(chuàng)建一個對象你必須用new來實例化它。New運算符是通過為新對象分配內(nèi)存來實例化一個類的。這個new運算符需要一個后綴參數(shù),即構(gòu)造函數(shù)的一個調(diào)用。構(gòu)造函數(shù)的名字提供了要初始化類的名字。構(gòu)造函數(shù)初始化了新的對象。
New運算符號返回一個引用給它創(chuàng)建的對象的。通常,這個引用被賦值為適當類型的變量。
4.1.1.3 初始化對象
以下是Point類的代碼:
public class Point {
public int x = 0;
public int y = 0;
//一個構(gòu)造函數(shù)
public Point(int x, int y) {
this.x = x;
this.y = y;
}
}
這個類包含了一個構(gòu)造函數(shù)。你可以識別這個構(gòu)造函數(shù)因為它跟類的名字是相同名字的,它沒有任何的返回類型。這個在Point類中的構(gòu)造函數(shù)有兩個整型參數(shù),它是由代碼(int x, int y)來定義的。下面的整數(shù)23和94就是這個參數(shù)的數(shù)值:
Point origin_one = new Point(23, 94);
4.1.1.3 初始化對象
下面是Rectangle類的代碼,它包含了四個構(gòu)造函數(shù):
public class Rectangle {
public int width = 0;
public int height = 0;
public Point origin;
//四個構(gòu)造函數(shù)
public Rectangle() {
origin = new Point(0, 0);
}
public Rectangle(Point p) {
origin = p;
}
public Rectangle(int w, int h) {
this(new Point(0, 0), w, h);
}
public Rectangle(Point p, int w, int h) {
origin = p;
width = w;
height = h;
}
//用于移動rectangle的方法
public void move(int x, int y) {
origin.x = x;
origin.y = y;
}
//用于計算矩形面積的方法
public int area() {
return width * height;
}
}
每一個構(gòu)造函數(shù)可以讓你為矩形的各個方法提供初始數(shù)值,你可以設(shè)置矩形的原點、寬度和高度。如果一個類中有多個構(gòu)造函數(shù),它們的名字都是相同的只是它們有不同類型的參數(shù)或者不同數(shù)目的參數(shù)。JAVA平臺可以根據(jù)參數(shù)的不同數(shù)目和類型類來區(qū)分構(gòu)造函數(shù)。當JAVA平臺遇到的代碼的時候,它就調(diào)用在Rectangle類中的構(gòu)造函數(shù),這個函數(shù)需要一個Point參數(shù)以及兩個整型參數(shù):
Rectangle rect_one = new Rectangle(origin_one, 100, 200);
這個調(diào)用初始化了矩形的原點(orgin_one)。代碼也設(shè)置了矩形的寬度(100)和高度(200)。
4.1.1.3 初始化對象
多個引用可以引用相同的對象。下面的代碼行調(diào)用了需要兩個整型參數(shù)的構(gòu)造函數(shù),它為寬度和高度提供了初始化數(shù)值。如果你仔細看看這個代碼,你會發(fā)現(xiàn)它創(chuàng)建一個Point對象,它的x和y數(shù)值被初始化為0。
下面的Rectangle構(gòu)造函數(shù)沒有任何參數(shù):
Rectangle rect = new Rectangle();
如果一個類沒有顯性聲明任何構(gòu)造函數(shù),JAVA平臺自動提供一個沒有參數(shù)的構(gòu)造函數(shù),這是一個缺省的構(gòu)造函數(shù),它沒有完成任何事情。這樣,所有的類就至少有一個構(gòu)造函數(shù)。
4.1.2 使用對象
一旦你創(chuàng)建了一個對象,你可能想使用它來做一些事情。你可能從它得到一些信息,或者想改變它的狀態(tài)或者讓它來完成一些動作。對象允許你做以下兩件事情:
1. 操作或者檢查它的變量。
調(diào)用它的方法。
4.1.2.1 引用對象的變量
下面是引用對象變量的基本形式,它是使用了有條件的名字即長名字:
objectReference.variableName
當實例變量處在作用域內(nèi)的時候,你可以為實例變量使用一個簡單的名字,也就是說,在對象類的代碼中。處在對象類外面的代碼必須使用有條件的名字。比如,在CreateObjectDemo類中的代碼處在類Rectangle類代碼的外面。所以為了引用Rectangle對象rect_one的origin、width和height變量,CreateObjectDemo必須相應(yīng)使用rect_one.origin、rect_one.width和rect_one.height。這個程序使用了rect_one的width和height:
System.out.println("Width of rect_one: " + rect_one.width);
System.out.println("Height of rect_one: " + rect_one.height);
如果直接使用在CreateObjectDemo類中的變量width和height,那就將產(chǎn)生一個編譯錯誤。在后面,程序還將使用類似的代碼來顯示關(guān)于rect_two的信息。相同類型的對象將有相同實例變量的副本。這樣,每一個Rectangle對象就都有變量origin、width和height了。當你通過對象引用來訪問實例變量的時候,你就引用了特定對象的變量。在CreateObjectDemo程序有兩個對象rect_one和rect_two,它們有不同的origin、width和height變量:
對象的長文件名的第一部分是對象引用,它必須是一個對象的引用。這里你可以使用引用變量的名字,或者你可以使用任何的表達式來返回一個對象引用。重新調(diào)用這個new運算符可以返回一個對象的引用。這樣你可以使用從new返回的數(shù)值來訪問一個新的對象變量:
int height = new Rectangle().height;
這個語句創(chuàng)建了一個新的Rectangle對象并且得到它的height(高度)。從本質(zhì)上講,這條語句計算了Rectangle缺省的高度。這里注意,在這條語句被執(zhí)行后,程序不再有創(chuàng)建了的Rectangle的引用,因為程序不再在變量中存儲這個引用。對象就被取消引用,而它的資源可以JAVA平臺重新使用。
4.1.2.2 關(guān)于變量訪問
利用其它對象和類對對象變量直接的操作是不允許的,因為有可能為變量設(shè)置的數(shù)值沒有任何的意義。比如,我們使用前面教程中的Rectangle類,你可以創(chuàng)建一個矩形,它的width和height都是負的,但是它是沒有意義的。
較好的做法是:不采用直接對變量進行操作,類提供一些方法,其它的對象可以通過這些方法來檢查或者改變變量。這些方法要確保變量的數(shù)值是有意義的。這樣,Rectangle類將提供setWidth、setHeight、getWidth以及getHeight方法來設(shè)置或者獲得寬度和高度。這些用于設(shè)置變量的方法將在調(diào)用者試圖將width和height設(shè)置為負數(shù)的時候匯報一個錯誤。使用方法而不使用直接變量訪問的好處還有:類可以改變變量的類型和名字來存儲width和height而沒有影響它的客戶程序。
但是,在實際情況下,有時允許對對象變量直接訪問。比如,通過定義Point類和Rectangle類為public,它們都允許對它們的成員變量自由訪問。
JAVA編程語言提供了一個反問控制機制,憑它,類可以決定什么其它的類可以直接訪問它的變量。如果其它對象對類直接操作可能導(dǎo)致無意義的話,類可以保護變量。改變這些變量應(yīng)該利用方法調(diào)用來控制。如果類授權(quán)訪問給它的變量,你可以檢查和改變這些變量而但不能造成不利的效果。
4.1.2.2 調(diào)用對象的方法
同樣你可以使用有限制的名字(長名字)來調(diào)用對象的方法。有限制的名字的格式是:在對象引用的后面加上點(.)再跟著方法的名字,即對象引用.方法名字。同樣你還可以利用圓括號(和)來為方法提供參數(shù)。如果方法不需要任何參數(shù)就留空它:
objectReference.methodName(argumentList);
or
objectReference.methodName();
Rectangle類有兩個方法:area和move,即計算矩形的面積和改變矩形的原點。這里是CreateObjectDemo代碼,它調(diào)用這兩個方法:
System.out.println("Area of rect_one: " + rect_one.area());
...
rect_two.move(40, 72);
上面的第一條語句調(diào)用rect_one的area方法并顯示結(jié)果。第二條語句是移動rect_two,因為move方法為對象的原點坐標x和y賦了新值。其中objectReference 必須是一個對象的引用。你可以使用一個變量名字,而且你也可以使用任何表達式來返回對象的引用。而new運算符返回一個對象的引用,因此你可以使用從new返回的數(shù)值來調(diào)用一個新的對象方法:
new Rectangle(100, 50).area()
表達式new Rectangle(100,50)返回一個對象引用,它是引用一個Rectangle對象。上面已經(jīng)提到,你可以使用點符號(.)來調(diào)用新的Rectangle的面積方法以計算新矩形的面積。另外方法area也返回一個數(shù)值。對于這些返回數(shù)值的方法,你可以使用在表達式中使用方法調(diào)用。你可以指定返回的數(shù)值給變量,參見如下的例子:
int areaOfRectangle = new Rectangle(100, 50).area();
這里要提醒一下,在特定對象中調(diào)用一個方法跟發(fā)送一個信息給對象是相同的。
4.1.2.3 關(guān)于方法訪問
在Point和Rectangle類中的方法都被聲明為public,因此它們可以被任何其它的類所訪問。有時,類需要限制訪問它的方法。比如,一個類可能可能有一個方法,只有它的子類才能調(diào)用它。類可以在它用于控制訪問它的變量的時候,使用相同的機制來對它的方法進行控制訪問。
4.1.3 清除沒有使用的對象
有些面向?qū)ο笳Z言需要保持對所有對象的跟蹤,所以需要在對象不再使用的使用來將它從內(nèi)存中清除。管理內(nèi)存是一個很沉悶的事情而且容易出錯。JAVA平臺允許你創(chuàng)建任意個對象(當然會受到系統(tǒng)的限制),所以你也不必要老是要將它清除。JAVA是在當對象不再使用的使用被清除的。這個過程就是所謂的“垃圾收集”。
當對象不再有引用的時候,對象就會被清除,即作為垃圾收集的對象。保留在變量中的引用通常在變量超出作用域的時候被清除。或者,你可以通過設(shè)置變量為NULL來清除對象引用。這里注意,程序中同一個對象可以有多個引用,對象的所有引用必須在對象被作為垃圾收集對象清除之前清除。
下面講講垃圾收集器:
JAVA有一個立即收集器,它周期性地講不再被引用的對象從內(nèi)存中清除。這個垃圾收集器是自動執(zhí)行的,雖然有時候你可能想通過調(diào)用系統(tǒng)類的gc方法來顯性運行垃圾收集程序。比如,你可能想在創(chuàng)建大量垃圾代碼之后或者在需要大量內(nèi)存代碼之前運行垃圾收集器。垃圾收集器從內(nèi)存中清除不再被引用的對象的機制已經(jīng)被要到了VB.net和C#中去了。
最后介紹對象finalize方法:
在一個對象得到垃圾收集之前,垃圾收集器在對象自己調(diào)用對象的finalize方法之后給對象一個機會來從內(nèi)存中清除掉。這個過程就是所說的最后處理。
絕大部分的程序員不得不關(guān)系這個finalize方法的執(zhí)行。在少數(shù)情況下,程序員不得不執(zhí)行finalize方法來釋放資源。
Finalize方法是一個對象類的成員函數(shù),它處在JAVA平臺類分級結(jié)構(gòu)的頂部,而且是所有類的子類。這個類重載了finalize方法來完成對象的最后處理工作。
4.2字符和字符串
字符數(shù)據(jù)(不管是單一字符或者一系列的字符)可以利用java.lang三個類來儲存和操作,這三個類分別為:Character、String和StringBuffer。
JAVA平臺包含了三個類,在處理字符數(shù)據(jù)的時候你可以使用這三個類:
1. Character:這個類的實例可以容納單一的字符數(shù)值。這個同樣定義了簡潔的方法來操作或者檢查單一字符數(shù)據(jù)。
String:這個類用于處理由多個字符組成的不可變數(shù)據(jù)。
StringBuffer:這個類用于存儲和操作由多個字符組成的可變數(shù)據(jù)。
下面開始詳細介紹字符:
字符類型的對象包含了單一字符數(shù)值。你可以在對象需要的時候使用一個字符對象取代原始字符類型。比如,當傳遞一個字符給改變數(shù)值的方法或者當放置一個字符到一個數(shù)據(jù)結(jié)構(gòu)中,比如一個矢量等等。下面的例程序CharacterDemo創(chuàng)建了一些字符對象并顯示一些信息。
這個程序的數(shù)據(jù)結(jié)果為:
a is less than b.
a is equal to a2.
The character a is lowercase.
4.2字符和字符串
CharacterDemo程序調(diào)用以下由Character類提供的構(gòu)造函數(shù)和方法:
Character(char)
Character類只是構(gòu)造函數(shù),它創(chuàng)建了一個Character對象包含了由參數(shù)提供的數(shù)值。一旦Character對象被創(chuàng)建,它包含的數(shù)值就不能改變。
compareTo(Character)
一個實例方法比較了由兩個Character對象容納的數(shù)值:方法被調(diào)用的對象(即例子中的a)以及方法的參數(shù)(即例子中的b)。這個方法返回一個整數(shù)表明在當前對象中的數(shù)值是否大于、等于或者小于參數(shù)給定的數(shù)值。如果字母的數(shù)值較大那么這個字母就較大。
equals(Object)
一個實例方法,它比較當前對象容納的數(shù)值和由其它容納的數(shù)值。這個方法在兩個對象容納的數(shù)值相等的時候返回true。
toString()
一個實例方法,它將對象轉(zhuǎn)換為字符串。結(jié)果的字符串在長度上就是一個Character對象并且它包含了有這個Character對象容納的數(shù)值。
charValue()
一個實例方法,它返回由Character對象容納的數(shù)值作為原始字符數(shù)值。
isUpperCase(char)
類方法決定是否原始字符數(shù)值為大寫。這個是許多Character類方法中的一個,它可以檢查或者操作字符數(shù)據(jù)。
下面我們再談?wù)動趕tring(字符串)有關(guān)的幾個問題:
1. 為什么有兩個String類:String 和StringBuffer
創(chuàng)建Strings 和StringBuffers
存取器函數(shù)
修改StringBuffers
將對象轉(zhuǎn)換為Strings
將String轉(zhuǎn)換為數(shù)字
Strings和JAVA編譯器
下頁接著一個一個加以細講。
1)為什么有兩個String類:String 和StringBuffer
JAVA平臺提供了兩個類:String和StringBuffer,它們可以儲存和操作字符串,即包含多個字符的字符數(shù)據(jù)。這個String類提供了數(shù)值不可改變的字符串。比如,如果你編寫一個方法,它需要字符串數(shù)據(jù)并且方法不能修改字符串,而要將一個String對象傳遞給方法。而這個StringBuffer類提供的字符串進行修改。當你直到字符數(shù)據(jù)要改變的時候你就可以使用StringBuffer。典型地,你可以使用StringBuffers來動態(tài)構(gòu)造字符數(shù)據(jù)。比如,從一個文件讀文本數(shù)據(jù)。因為Strings是常量,所以它用起來比StringBuffers更有效,并且可以共享。因此在允許的情況下還是使用Strings。
下面是一個例程StringsDemo,它反轉(zhuǎn)了字符串中的字符。這個程序同時使用了String和StringBuffer:
public class StringsDemo {
public static void main(String[] args) {
String palindrome = "Dot saw I was Tod";
int len = palindrome.length();
StringBuffer dest = new StringBuffer(len);
for (int i = (len - 1); i >= 0; i--) {
dest.append(palindrome.charAt(i));
}
System.out.println(dest.toString());
}
}
這個程序的輸出為:
doT saw I was toD
2)創(chuàng)建Strings 和StringBuffers
字符串經(jīng)常是由雙引號括起的一系列字符組成。比如,當有以下的字符串,JAVA平臺就創(chuàng)建一個String對象,它的數(shù)值為Goodluck。
"Goodluck"
這個StringsDemo程序使用這個記述來創(chuàng)建字符串,它是由palindrome變量引用的:
String palindrome = "Dot saw I was Tod";
同樣你可以利用其它方法來創(chuàng)建String對象:使用new關(guān)鍵字和構(gòu)造函數(shù)。String類提供了幾個構(gòu)造函數(shù),它允許你提供字符串的初始值,它們是使用不同的資源的,比如字符的數(shù)組、字節(jié)的數(shù)組或者一個StringBuffer。
char[] helloArray = { ‘h‘, ‘e‘, ‘l‘, ‘l‘, ‘o‘ };
helloString = new String(helloArray);
System.out.println(helloString);
上面的這段程序輸出為:hello。
你必須使用new來創(chuàng)建一個StringBuffer。StringsDemo程序創(chuàng)建了SringBuffer,它是由dest引用的,這里使用了設(shè)置緩沖區(qū)容量的構(gòu)造函數(shù)。
String palindrome = "Dot saw I was Tod";
int len = palindrome.length();
StringBuffer dest = new StringBuffer(len);
這個代碼創(chuàng)建了一個字符串的緩沖區(qū),這個緩沖區(qū)的容量等于被palindrome引用的字符串的長度。這個確保了為dest只分配一個內(nèi)存,因為它只有包含被復(fù)制的字符串那么大的空間。通過初始化字符串的緩沖區(qū)的容量,你最小化必須分配的內(nèi)存空間。這就使得你的代碼更加有效,因為內(nèi)存分配是相對高級的操作。
(3)存取器函數(shù)
首先講解一下獲得String或者StringBuffer的長度:
獲得有關(guān)對象信息的方法稱為存取器函數(shù)。你可以用在字符串和字符串緩沖區(qū)的一個存取器方法是length方法,它可以包含在字符串或者字符串緩沖區(qū)的字符的個數(shù)。下面給出一個例程,當這個程序執(zhí)行后,len就將等于17:
String palindrome = "Dot saw I was Tod";
int len = palindrome.length();
除了length,StringBuffer類還有一個方法叫做capacity,它是返回為字符串緩沖區(qū)分配的空間大小而不是空間使用了多少。比如,字符串緩沖區(qū)的容量在StringsDemo例程中被dest引用時,它是不能改變的,雖然它的長度在每次的循環(huán)中增加1。如圖15,給出了在dest里面已經(jīng)有9個字符之后的容量和長度。
(圖15)
一個字符串緩沖區(qū)的長度是它包含的字符的個數(shù)。一個字符串緩沖區(qū)的容量是被分配的字符空間的數(shù)量。而String類沒有capacity方法,因為字符串不能改變。
下面講講索引String或者String Buffer得到字符:
你可以通過使用charAt存取器來在一個特定的位置索引一個字符串或者字符串緩沖區(qū)以得到字符串。第一個字符的索引是0,最后一個字符的索引是length()-1。比如,下面的代碼在字符串中得到索引為9的字符:
String anotherPalindrome = "Niagara. O roar again!";
char aChar = anotherPalindrome.charAt(9);
因為索引是從0開始的,所以索引為9的字符為‘O‘,如圖16所示:
(圖16)
使用charAt方法來在特定的索引下得到字符。如圖16所示給處了計算字符串最后一個字符的索引的方法,你必須將length方法返回的數(shù)值減去1才得到最后一個字符的索引。
(3)存取器函數(shù)
如果你想從一個字符串或者字符串緩沖區(qū)得到多個字符,你可以使用substring方法。這個substring方法有兩個版本,如下表所示:
方法
描述
String substring(int)
返回一個新的字符串,它是這個字符串或者字符串緩沖區(qū)的一個子串。第一個整數(shù)參數(shù)指定了第一個字符的索引。第二個整數(shù)參數(shù)是最后字符-1的索引。這個子串的長度就是第二個整數(shù)減去第一個整數(shù)。如果第二個整數(shù)沒有,那么substring就擴展到整個字符串的結(jié)尾。
String substring(int,int)
下面的代碼是得到字符串"Niagara. O roar again!"從索引為11到索引為15之間的子串,結(jié)果得到單詞"roar":
String anotherPalindrome = "Niagara. O roar again!";
String roar = anotherPalindrome.substring(11, 15);
如圖17所示:
(圖17)
使用substring方法來得到字符串或者字符串緩沖區(qū)的一部分。這里一定要記住索引是從0開始的。
(4)修改StringBuffers
reverseIt方法使用StringBuffer的append方法來增加一個字符到dest字符串末尾:
class ReverseString {
public static String reverseIt(String source) {
int i, len = source.length();
StringBuffer dest = new StringBuffer(len);
for (i = (len - 1); i >= 0; i--) {
dest.append(source.charAt(i));
}
return dest.toString();
}
}
如果這個被增加的字符導(dǎo)致StringBuffer的大小超過當前的容量,這個StringBuffer就會為它分配更多的內(nèi)存。因為內(nèi)存分配示相對高級的操作,所以你可以同時初始化StringBuffer的容量為較合理的大小來使你的代碼更有效率。比如reverseIt方法以初始的容量來構(gòu)造StringBuffer等于源字符串的長度,以只為dest分配一次內(nèi)存。
4)修改StringBuffers
用在reverseIt的append方法只是一個StringBuffer方法的其中一種,它在StringBuffer末尾增補了一個數(shù)據(jù)。有幾種append方法來增補各個類型的數(shù)據(jù)(比如float、int、boolean以及對象)到StringBuffer末尾。但是在增補操作之前,數(shù)據(jù)都要先轉(zhuǎn)換為字符串。
下面講講插入字符:
有時候,你可能想插入數(shù)據(jù)到StringBuffer中間。你可以使用StringBuffer的insert方法來實現(xiàn)。以下的這個例子就是介紹如何將一個字符串插入到StringBuffer中去的:
StringBuffer sb = new StringBuffer("Drink Java!");
sb.insert(6, "Hot ");
System.out.println(sb.toString());
這些代碼將輸出:
Drink Hot Java!
利用StringBuffer的許多insert方法,你可以在插入數(shù)據(jù)之前指定索引。在上面的例子中,"Hot "要插到"Java"的‘J‘之前。因為索引是從0開始的,所以‘J‘的索引為6。為了插入數(shù)據(jù)到StringBuffer之前,使用索引0.為了在StringBuffer的末尾插入數(shù)據(jù),你可以將所以等于StringBuffer的長度或者使用append方法來增補。
下面討論設(shè)置字符:
另外一個有用的StringBuffer修改量是setCharAt,它在StringBuffer的指定位置用參數(shù)列表中的指定字符來替換字符。SetCharAt在你想重新使用StringBuffer的時候是很有用的。
(5)將對象轉(zhuǎn)換為Strings
首先介紹toString方法:
經(jīng)常需要將一個對象轉(zhuǎn)換為String,因為你需要將它傳遞給方法,而這個方法只接收String數(shù)值。前面我們使用reverseIt方法使用了StringBuffer的toString方法來將StringBuffer轉(zhuǎn)換為String對象。
class ReverseString {
public static String reverseIt(String source) {
int i, len = source.length();
StringBuffer dest = new StringBuffer(len);
for (i = (len - 1); i >= 0; i--) {
dest.append(source.charAt(i));
}
return dest.toString();
}
}
所有的類從對象類繼承了toString,并且在java.lang包的許多類重載了這個方法來提供一個很有意義的方法
下面說說valueOf方法:
簡單起見,String類提供了一個類方法valueOf。你可以使用valueOf來轉(zhuǎn)換不同類型的變量為String。比如,為了打印pi的數(shù)值,你可以這樣來編寫代碼:
System.out.println(String.valueOf(Math.PI));
6)將String轉(zhuǎn)換為數(shù)字
String類本身沒有提供任何的方法來轉(zhuǎn)換String為浮點型、整型或者其它的數(shù)值類型。但是,"type wrapper"類的四種類型(integer、Double、Float和Long)提供了一個類方法valueOf來將String轉(zhuǎn)換為那個類型的對象。舉例如下:
String piStr = "3.14159";
Float pi = Float.valueOf(piStr);
(7)Strings和JAVA編譯器
JAVA編譯器使用了String和StringBuffer類來處理字符串和字符串的連接。
下面先講講Strings:
在JAVA種,你可以在雙引號之間指定字符串,如:
"Hello World!"
你可以在任何使用String對象的地方使用字符串。比如,System.out.println接收一個String參數(shù),所以你可以在這里使用字符串了。
System.out.println("Might I add that you look lovely today.");
同樣,你可以使用從字符串直接使用String方法,比如:
int len = "Goodbye Cruel World".length();
因為編譯器自動為字符串創(chuàng)建了一個新的String對象,你可以使用字符串來初始化一個String對象:
String s = "Hola Mundo";
上面的這條語句等價于下面的語句:
String s = new String("Hola Mundo");
最后講講字符串的連接以及+運算符:
在JAVA種,你可以使用+來連接字符串,比如:
String cat = "cat";
System.out.println("con" + cat + "enation");
這條語句看起來有點問題,因為我們直到Strings是不能改變的。但是,在這條語句的背后,編譯器卻使用了StringBuffers來執(zhí)行字符串的連接。上面的語句實際上編譯為:
String cat = "cat";
System.out.println(new StringBuffer().append("con").
append(cat).append("enation").toString());
同樣你可以使用+運算符號來增不一個String:
System.out.println("Java‘s Number " + 1);
在JAVA中,在執(zhí)行字符串連接操作之前編譯器講非String的數(shù)值(如本例的整數(shù)1)轉(zhuǎn)化為一個String對象。
4.3 數(shù)字
為了處理數(shù)值類型,你可以使用Number類。這個Number類是所有在JAVA平臺中所有數(shù)字類的父類。它的子類包括Float、Interger等等。
本節(jié)教程將討論java.lang和它的子類中的Number類。特別地,這節(jié)教程要討論為什么你需要這些類,并指出通用的方法和類變量,以及向你介紹如何將實例轉(zhuǎn)換為字符串。
此外,這節(jié)教程還討論你需要的其它類來處理數(shù)字。比如,如果你需要以某個特殊格式來顯示數(shù)字的時候,你可以使用在java.test中的NumberFormat和DecimalFormat類來格式化它。同時,java.lang中的Math包含了類方法來執(zhí)行數(shù)學(xué)函數(shù)。這個類還有用于三角函數(shù)、指數(shù)函數(shù)等等的方法。如圖18所示:
(圖18)
數(shù)字類包含:
1. Number :這個抽象類Number是Byte、Double、Float、Integer、Long和Short的父類。Number的子類必須提供將數(shù)值轉(zhuǎn)換為byte、double、float、int、long以及short的方法。
Byte:這個類是Number的子類,它是用于字節(jié)數(shù)值的標準wrapper。
Double:這個Double類在對象中包裝了原始類型double。Double類的對象包含單一類型(double)的對象。另外,這個類提供了幾個用于將double轉(zhuǎn)換為String或者將String轉(zhuǎn)換為double的方法。
Float :這個Float類在對象中包裝了原始類型Float。Float類的對象包含單一類型(Float)的對象。另外,這個類提供了幾個用于將Float轉(zhuǎn)換為String或者將String轉(zhuǎn)換為Float的方法。
Integer:這個Integer類在對象中包裝了原始類型Integer。Integer類的對象包含單一類型(Integer)的對象。另外,這個類提供了幾個用于將Integer轉(zhuǎn)換為String或者將String轉(zhuǎn)換為Integer的方法。
Long :這個Long類在對象中包裝了原始類型Long。Long類的對象包含單一類型(Long)的對象。另外,這個類提供了幾個用于將Long轉(zhuǎn)換為String或者將String轉(zhuǎn)換為Long的方法。
Short:這個類是用于short數(shù)值的標準wrapper。
4.3 數(shù)字
8. BigDecimal:不可變的、任意精度的有符號的十進制數(shù)字。BigDecimal包含了任意精度的整型數(shù)和非負的32位的整型數(shù),BigDecimal 提供了用于基本算術(shù)運算、比較、格式轉(zhuǎn)換等等的操作。這個BigDecimal類給了用戶對舍入行為的完全控制,并且強迫用戶為舍棄精度的操作顯性指定一個舍入行為。基于這個目的有八種舍入模式。對BigDecimal有兩種類型的操作:比例/舍入操作和十進制點移動操作。比例/舍入操作返回一個BigDecimal,它的數(shù)值大約或者精確地等于運算對象,但是它的數(shù)值范圍要指定,也就是說,它們可以對它的數(shù)值最小的影響來增加或者減少數(shù)字的精度。十進制點移動操作(movePointLeft和movePointRight)返回一個由運算對象創(chuàng)建的BigDecima,它是通過在指定方向和指定距離來移動十進制點的。為了達到簡短和清晰的目的,偽代碼用在了BigDecimal方法的描述中。比如偽代碼表達式(i j)就代表了“BigDecimal的數(shù)值是由BigDecimal i加上BigDecimal j的數(shù)值”。而偽代碼表達式(i="=j)代表“當且僅當BigDecimal" i的數(shù)值與BigDecimal j相同的時候才為true”。其它的偽代碼表達式也有類似的解釋。
BigInteger:不可變?nèi)我饩鹊恼蛿?shù)。BigInteger為所有的Java原始整型操作以及所有在java.lang.Math中的相關(guān)方法提供相似的操作。另外BigInteger偽模數(shù)運算、GCD計算、位操作等等提供了運算。算術(shù)運算的語義模仿了定義在JAVA語言規(guī)范中的Java的整型算術(shù)運算。比如,如果除數(shù)為0就會導(dǎo)致ArithmeticException(算術(shù)異常)等等。所有在規(guī)范中的溢出都將被忽略,因為BigIntegers將盡量適應(yīng)運算結(jié)果的需要。而移位操作擴展了Java的移位操作,它允許負方向的移位。以負的位距來往右移就相當于往左移位。而無符號的右移運算符(>>>)這里就不再使用。 而按位邏輯操作跟Java的按位操作是相似的。比較運算執(zhí)行有符號的整型比較,它跟Java的是相似的。模數(shù)算術(shù)運算提供計算余數(shù)、執(zhí)行乘冪等方法。這些方法總是返回一個非零的結(jié)果(介于0到-1之間)。為了簡潔和清晰的目的,偽代碼也用在BigInteger方法中。這個偽代碼表達式(i j)代表了“等于BigInteger i加上BigInteger j的BigInteger數(shù)值”。而偽代碼表達式(i="=j)代表了“當且僅當BigInteger" i等于BigInteger j的時候才返回true”。
4.3 數(shù)字
其它的數(shù)據(jù)類型:
1. Boolean :這個Boolean類在對象中包裝了原始類型Boolean。Boolean類的對象包含單一類型(double)的對象。另外,這個類提供了幾個用于將Boolean轉(zhuǎn)換為String或者將String轉(zhuǎn)換為Boolean的方法。
Character :這個Character類在對象中包裝了原始類型Character。Character類的對象包含單一類型(Character)的對象。另外,這個類提供了幾個用于將字符由小寫轉(zhuǎn)換為大寫或者由大寫轉(zhuǎn)換為小寫的方法。
Void:這個Void類是一個占位符類,它保留對代表原始JAVA類void的引用。
非基本算術(shù)類型Math:
這個Math類包含了用于執(zhí)行數(shù)值操作的方法,比如初步的指數(shù)、對數(shù)、平反根以及三角函數(shù)。不象StrictMath類中的有些數(shù)值函數(shù),這個Math類的所有函數(shù)不是定義為返回bit-for-bit相同的結(jié)果。這允許更好的執(zhí)行,因為嚴格的再現(xiàn)情已經(jīng)不需要了。
缺省地,許多Math函數(shù)簡單代表了在StrictMath中的等價函數(shù)。代碼發(fā)生器支持使用特殊平臺的本地庫或者微處理器指令以提供性能更高的Math函數(shù)實行。這樣高性能的執(zhí)行必須符合Math的規(guī)范。
4.4 數(shù)組
在任何的編程語言中,數(shù)組都是一個重要的數(shù)據(jù)結(jié)構(gòu)。數(shù)組是一個固定長度的結(jié)構(gòu),它存儲多個相同類型的數(shù)值。你可以在數(shù)組中集合相同類型的數(shù)值。數(shù)組直接被JAVA編程語言所支持,所以沒有一個數(shù)組類。數(shù)組是一個Object類的一個不明顯的擴展,所以你可以指定一個數(shù)組給一個類型定義為Object的變量。
JAVA平臺集合了它的類到功能包中。不是編寫你自己的類,你可以使用有平臺提供的類。這節(jié)教程中要討論的絕大多數(shù)的類都是java.lang包的成員函數(shù)。所有java.lang中的類對你的程序都是有效的。
數(shù)組的長度在數(shù)組創(chuàng)建的時候就已經(jīng)確定。一旦創(chuàng)建以后,數(shù)組就有了固定長度的結(jié)構(gòu),如圖19所示:
(圖19)
數(shù)組元素就是數(shù)組中的一個數(shù)值,可以通過數(shù)組中的位置來訪問它。
如果你想在一個結(jié)構(gòu)中存儲不同類型的數(shù)據(jù),或者如果你需要一個長度可以動態(tài)改變的結(jié)構(gòu),可以使用Collection,比如Vector而不用數(shù)組。
本教程中將討論以下的幾個方法:
1. 創(chuàng)建和使用數(shù)組合
對象的數(shù)組
數(shù)組的數(shù)組
復(fù)制數(shù)組
4.4.1 創(chuàng)建和使用數(shù)組合
以下是個簡單的程序ArrayDemo,它創(chuàng)建了一個數(shù)組,并放置了一些數(shù)值在上面,然后進行顯示:
public class ArrayDemo {
public static void main(String[] args) {
int[] anArray; // 聲明一個整型數(shù)組
anArray = new int[10]; // 創(chuàng)建一個整型數(shù)組
// 為每一個數(shù)組元素賦值并打印出來
for (int i = 0; i < anArray.length; i++) {
anArray[1] = i;
System.out.print(anArray[1] + " ");
}
System.out.println();
}
}
這節(jié)教程覆蓋了以下這些內(nèi)容:
1. 聲明一個變量來引用一個數(shù)組
創(chuàng)建一個數(shù)組
訪問數(shù)組元素
獲得數(shù)組的大小
數(shù)組初始化程序
下一頁將加以詳細說明。
4.4.1 創(chuàng)建和使用數(shù)組合
(1)聲明一個變量來引用一個數(shù)組
以下的代碼是聲明一個數(shù)組變量:
int[] anArray; // 聲明整型的數(shù)組
象聲明其它類型的變量,一個數(shù)組聲明有兩個組件:數(shù)組的類型和數(shù)組的名字。數(shù)組的類型是這樣來寫的type[],其中type可以是float、boolean、Object、String等,而[]代表了這是一個數(shù)組。這里記住在數(shù)組中的所有元素都是相同類型的。上面的例程中使用了int[],所以數(shù)組anArray就將容納整型數(shù)據(jù)了。以下是聲明其它類型的數(shù)組:
float[] anArrayOfFloats;
boolean[] anArrayOfBooleans;
Object[] anArrayOfObjects;
String[] anArrayOfStrings;
就象聲明其它類型的變量,聲明數(shù)組變量也沒有為數(shù)組元素分配任何內(nèi)存。所以必須在引用數(shù)組之前被數(shù)組賦值。
4.4.1 創(chuàng)建和使用數(shù)組合
(2)創(chuàng)建一個數(shù)組
你可以使用JAVA的new運算符來創(chuàng)建一個數(shù)組。下面的語句是為10個整型元素分配一個數(shù)組內(nèi)存:
anArray = new int[10]; // create an array of integers
總得說來,當創(chuàng)建數(shù)組的時候,你可以使用new操作符,后面跟著數(shù)組元素的數(shù)據(jù)類型然后就是用方括號[和]括起來的元素的數(shù)目,格式如下所示:
new elementType[arraySize]
如果在上面的例程中new語句沒有的話,編譯器將打印除一個如下的錯誤是的編譯發(fā)生錯誤:
ArrayDemo.java:4: Variable anArray may not have been initialized(變量anArray 可能還沒有初始化。)
(3)訪問數(shù)組元素
既然已經(jīng)給數(shù)組分配了內(nèi)存,我們來為數(shù)組元素賦值吧:
for (int i = 0; i < anArray.length; i++) {
anArray[1] = i;
System.out.print(anArray[1] + " ");
}
這部分的代碼給出了怎樣引用一個數(shù)組元素,或者示為它賦予一個數(shù)值,或者訪問數(shù)值。在方括號之間的數(shù)值指示了要訪問的元素的索引。在JAVA中,數(shù)組的索引是從0開始的并且以-1結(jié)束。
4.4.1 創(chuàng)建和使用數(shù)組合
(4)獲得數(shù)組的大小
為了獲得數(shù)組的大小,你可以使用下面的代碼:
arrayname.length
這里還提醒一下:JAVA的新手可能會在length后面加一個圓括號(和)。這樣是否錯誤的,因為length不是一個方法。Length是由JAVA平臺為所有數(shù)組提供的一個屬性。
在上面的例程中的for循環(huán)在anArray的每一個元素進行循環(huán),并給每個元素賦值。這個for循環(huán)使用了anArray.length來決定什么時候終止循環(huán)。
(5)數(shù)組初始化程序
JAVA編程語言為創(chuàng)建和初始化數(shù)組提供了簡捷的語法。一下是這個語法的例子:
boolean[] answers = { true, false, true, true, false };
數(shù)組的length(長度)是由大括號{和}之間的數(shù)值的數(shù)目決定的。
4.4.2 對象的數(shù)組
數(shù)組可以保留引用類型。你可以用創(chuàng)建原始類型數(shù)組的方法來創(chuàng)建這樣一個數(shù)組。以下是一個小的程序ArrayOfStringsDemo,它創(chuàng)建了一個數(shù)組包含了三個String對象并且將這三個字符串以小寫字母的形式打印出來:
public class ArrayOfStringsDemo {
public static void main(String[] args) {
String[] anArray = { "String One", "String Two", "String Three" };
for (int i = 0; i < anArray.length; i++) {
System.out.println(anArray[1].toLowerCase());
}
}
}
這個程序中用單一的語句創(chuàng)建和操作了數(shù)組。但是,你可以創(chuàng)建一個數(shù)組而不用在里面放置任何元素。這對于JAVA新手將是一個容易出錯的地方。假如有以下的代碼:
String[] anArray = new String[5];
一旦這條語句被執(zhí)行了,數(shù)組anArray就存在了并且有充足的空間來容納5個String對象。但是,數(shù)組并不包含任何的字符串,它是空的。程序必須顯性地創(chuàng)建字符串并將他們放置到數(shù)組中。這本來示很顯然的事情,但是許多JAVA的新手會以為上面的語句已經(jīng)創(chuàng)建了數(shù)組并創(chuàng)建了5和個空的字符串在里面了。這樣他們會象如下進行編寫代碼,結(jié)果只會導(dǎo)致一個NullPointerException的異常錯誤:
String[] anArray = new String[5];
for (int i = 0; i < anArray.length; i++) {
// 錯誤:下面的行將導(dǎo)致一個運行錯誤
System.out.println(anArray[1].toLowerCase());
}
4.4.3 數(shù)組的數(shù)組
數(shù)組可以容納數(shù)組。以下的例程ArrayOfArraysDemo創(chuàng)建一個數(shù)組并使用一個初始化程序來包含四個子數(shù)組:
public class ArrayOfArraysDemo {
public static void main(String[] args) {
String[][] cartoons ="
{
{ "Flintstones", "Fred", "Wilma", "Pebbles", "Dino" },
{ "Rubbles", "Barney", "Betty", "Bam Bam" },
{ "Jetsons", "George", "Jane", "Elroy", "Judy", "Rosie", "Astro" },
{ "Scooby Doo Gang", "Scooby Doo", "Shaggy", "Velma", "Fred", "Daphne" }
};
for (int i = 0; i < cartoons.length; i++) {
System.out.print(cartoons[1][0] + ": ");
for (int j = 1; j < cartoons[1].length; j++) {
System.out.print(cartoons[1][j] + " ");
}
System.out.println();
}
}
}
這里注意,所有的子數(shù)組都有不同的長度。子數(shù)組的名字是否cartoons[0]和cartoons[1]等等。
4.4.3 數(shù)組的數(shù)組
跟對象的數(shù)組一樣,你必須顯性地在數(shù)組中創(chuàng)建子數(shù)組。因此如果你不使用初始化程序的話,你必須編寫一些的代碼:
public class ArrayOfArraysDemo2 {
public static void main(String[] args) {
int[][] aMatrix = new int[4][];
for (int i = 0; i < aMatrix.length; i++) {
aMatrix[1] = new int[5]; //create sub-array
for (int j = 0; j < aMatrix[1].length; j++) {
aMatrix[1][j] = i + j;
}
}
//打印
for (int i = 0; i < aMatrix.length; i++) {
for (int j = 0; j < aMatrix[1].length; j++) {
System.out.print(aMatrix[1][j] + " ");
}
System.out.println();
}
}
}
你必須在創(chuàng)建數(shù)組的時候為主數(shù)組指定長度。你可以讓子數(shù)組的長度未指定直到創(chuàng)建了它們。
4.4.4 復(fù)制數(shù)組
使用系統(tǒng)的arraycopy方法來有效地從一個數(shù)組復(fù)制數(shù)據(jù)到另外一個數(shù)組中去。這個arraycopy方法需要5個參數(shù):
public static
void arraycopy(Object source,
int srcIndex,
Object dest,
int destIndex,
int length)
其中兩個Object參數(shù)指定了從哪個數(shù)組復(fù)制以及要復(fù)制到哪個數(shù)組。三個整型參數(shù)指示了每個源數(shù)組和目標數(shù)組的開始位置,以及要復(fù)制的元素的數(shù)目。如圖20所示說明了復(fù)制是如何發(fā)生的:
(圖20)
4.4.4 復(fù)制數(shù)組
下面的程序ArrayCopyDemo使用了arraycopy方法來從copyForm數(shù)組復(fù)制元素到copyTo數(shù)組:
public class ArrayCopyDemo {
public static void main(String[] args) {
char[] copyFrom = { ‘d‘, ‘e‘, ‘c‘, ‘a(chǎn)‘, ‘f‘, ‘f‘, ‘e‘,
‘i‘, ‘n‘, ‘a(chǎn)‘, ‘t‘, ‘e‘, ‘d‘ };
char[] copyTo = new char[7];
System.arraycopy(copyFrom, 2, copyTo, 0, 7);
System.out.println(new String(copyTo));
}
}
程序中調(diào)用arraycopy方法,開始復(fù)制所以為2的源數(shù)組元素。因為數(shù)組的索引是從0開始的,所以復(fù)制是從第3個元素開始的,即從‘c‘開始。Arraycopy方法將復(fù)制的元素復(fù)制到目標數(shù)組中,目標數(shù)組的索引是從0開始的,即復(fù)制到目標數(shù)組的第一個元素開始。這個程序一共復(fù)制了7個元素‘c‘、 ‘a(chǎn)、 ‘f‘、 ‘f‘、 ‘e‘、 ‘i‘和‘n‘。實際上arraycopy方法是從"decaffeinated"復(fù)制"caffein",如圖21所示:
(圖21)
這里注意,目標數(shù)組必須在調(diào)用arraycopy之間分配內(nèi)存,而且這個內(nèi)存空間必須足夠大以容納被復(fù)制的數(shù)據(jù)。