1、java.lang 研究
本篇文章討論那些由java.lang定義的類(lèi)和接口。
正如你所知道的那樣,java.lang被自動(dòng)導入所有的程序。它所包含的類(lèi)和接口對所有實(shí)際的Java程序都是必要的。它是Java最廣泛使用的包。
java.lang包括了下面這些類(lèi):
Boolean Long StrictMath (Java 2,1.3)
Byte Math String
Character Number StringBuffer
Class Object System
ClassLoader Package (Java 2) Thread
Compiler Process >ThreadGroup
Double Runtime ThreadLocal (Java 2)
Float >RuntimePermission (Java 2) Throwable
>InheritableThreadLocal (Java 2) SecurityManager Void
>Integer >Short >
另外還有兩個(gè)由Character定義的類(lèi):Character.Subset和Character.UnicodeBlock,它們是在Java 2中新增加的。
java.lang也定義了如下的接口:
Cloneable
Comparable
Runnable
其中Comparable接口是在Java 2中新增加的。
java.lang中的幾個(gè)類(lèi)包含了過(guò)時(shí)的方法,其中的大多數可以追溯到Java 1.0。在Java2中仍然提供了這些方法,用于支持逐漸減少的老程序,而這些方法在新程序中不被推薦使用。大多數的過(guò)時(shí)方法出現在Java 2之前,因此在這里不討論這些方法。而在Java 2中出現的那些過(guò)時(shí)的方法將被提及。
Java 2也在java.lang包中增加了幾個(gè)新的類(lèi)和方法,這些新類(lèi)和方法被說(shuō)明如下。
在可靠的環(huán)境中,可以在你的多任務(wù)操作系統中使用Java去執行其他特別繁重的進(jìn)程(也即程序)。exec( )方法的幾種形式允許命名想運行的程序以及它們的輸入參數。exec( )方法返回一個(gè)Process對象,這個(gè)對象可以被用來(lái)控制你的Java程序如何與這個(gè)正在運行的新進(jìn)程相互作用。因為Java可以運行在多種平臺和多種操作系統的情況下,exec( )方法本質(zhì)上是依賴(lài)于環(huán)境的。
下面的例子使用exec( )方法裝入Window的簡(jiǎn)單文本編輯器--notepad。顯而易見(jiàn),這個(gè)例子必須在Windows操作系統下運行。
// Demonstrate exec().
class ExecDemo {
public static void main(String args[]) {
Runtime r = Runtime.getRuntime();
Process p = null;
try {
p = r.exec("notepad");
} catch (Exception e) {
System.out.println("Error executing notepad.");
}
}
}
exec( )方法有幾個(gè)形式可用,而在本例子中展示的是最常用的一種。在新程序開(kāi)始運行之后,由exec( )方法返回的Process對象可以被Process方法使用??梢允褂胐estroy( )方法殺死子進(jìn)程。waitFor( )方法暫停你的程序直至子進(jìn)程結束。當子進(jìn)程結束后,exitValue( )方法返回子進(jìn)程返回的值。如果沒(méi)有問(wèn)題發(fā)生,它通常返回0。下面是前面關(guān)于exec( )方法例子的改進(jìn)版本。例子被修改為等待直至正在運行的進(jìn)程退出:
// Wait until notepad is terminated.
class ExecDemoFini {
public static void main(String args[]) {
Runtime r = Runtime.getRuntime();
Process p = null;
try {
p = r.exec("notepad");
p.waitFor();
} catch (Exception e) {
System.out.println("Error executing notepad.");
}
System.out.println("Notepad returned " + p.exitValue());
}
}
當子進(jìn)程正在運行時(shí),可以從它的標準輸入輸出進(jìn)行讀和寫(xiě)。getOutputStream( )方法和getInputStream( )方法返回子進(jìn)程的標準輸入(in)和輸出(out)的句柄。
System類(lèi)保存靜態(tài)方法和變量的集合。標準的輸入,輸出和Java運行時(shí)錯誤輸出存儲在變量in,out和err中。由System類(lèi)定義的方法列在表14-11中。注意當所做操作是安全方式所不允許的時(shí),許多方法引發(fā)一個(gè)安全異常(SecurityException)。應當注意的另一點(diǎn)是:Java 2不贊成使用runFinalizersonExit( )方法。該方法是在Java 1.1中增加的,同時(shí)也被證明是不可靠的。
讓我們看一看System類(lèi)的一些普遍用法。
表11 由Sysem定義的方法
方法 描述
static void arraycopy(Object source, int sourceStart, Object target, int targetStart, int size) 復制數組。被復制的數組由source傳遞,而source中開(kāi)始復制數組時(shí)的下標由sourceStart傳遞。接收復制的數組由target傳遞。而target中開(kāi)始復制數組時(shí)的下標由targetStart傳遞。Size是被復制的元素的個(gè)數
static long currentTimeMillis( ) 返回自1970年1月1日午夜至今的時(shí)間,時(shí)間單位為毫秒。
static void exit(int exitCode) 暫停執行,返回exitCode值給父進(jìn)程(通常為操作系統)。按照約定,0表示正常退出,所有其他的值代表某種形式的錯誤
static void gc( ) 初始化垃圾回收
static Properties getProperties( ) 返回與Java運行系統有關(guān)的屬性類(lèi)(Properties class)將在第15章中介紹)
static String getProperty(String which) 返回與which有關(guān)的屬性。如果期望的屬性沒(méi)有被發(fā)現,返回一個(gè)空對象(null object)
static String getProperty(String which, String default) 返回一個(gè)與which有關(guān)的屬性。如果期望的屬性沒(méi)有被發(fā)現,則返回default
static SecurityManager getSecurityManager( ) 返回當前的安全管理程序,如果沒(méi)有安裝安全管理程序,則返回一個(gè)空對象(null object)
static native int identityHashCode(Object obj) 返回obj的特征散列碼
static void load(String libraryFileName) 載入其文件由libraryFileName指定的動(dòng)態(tài)庫,必須指定其完全路徑
static void loadLibrary(String libraryName) 載入其庫名為libraryName的動(dòng)態(tài)庫
static String mapLibraryName(String lib) 對應名為lib的庫,返回一個(gè)指定平臺的名字(在Java 2中新增加的)
static void runFinalization( ) 啟動(dòng)調用不用的但還不是回收站中的對象的finalize( )方法。
static void setErr(PrintStream eStream) 設置標準的錯誤(err)流為iStream
static void setIn(InputStream iStream) 設置標準的輸入(in)流為oStream
static void setOut(PrintStream oStream) 設置標準的輸出(out)流eStream
static void setProperties(Properties sysProperties) 設置由sysProperties指定的當前系統屬性
Static String setProperty(String which, String v) 將v值賦給名為which的屬性(在Java 2中新增加的)
static void setSecurityManager ( SecurityManager secMan) 設置由secMan指定的安全管理程序
可以發(fā)現System類(lèi)的一個(gè)特別有意義的用法是利用currentTimeMillis( )方法來(lái)記錄你的程序的不同部分的執行時(shí)間。currentTimeMillis( )方法返回自從1970年1月1號午夜起到現在的時(shí)間,時(shí)間單位是毫秒。如果要記錄你的程序中一段有問(wèn)題程序的運行時(shí)間可以在這段程序開(kāi)始之前存儲當前時(shí)間,在該段程序結束之際再次調用currentTimeMillis( )方法。執行該段程序所花費的時(shí)間為其結束時(shí)刻的時(shí)間值減去其開(kāi)始時(shí)刻的時(shí)間值。下面的程序說(shuō)明了這一點(diǎn):
// Timing program execution.
class Elapsed {
public static void main(String args[]) {
long start, end;
System.out.println("Timing a for loop from 0 to 1,000,000");
// time a for loop from 0 to 1,000,000
start = System.currentTimeMillis(); // get starting time
for(int i=0; i < 1000000; i++) ;
end = System.currentTimeMillis(); // get ending time
System.out.println("Elapsed time: " + (end-start));
}
}
這里是程序運行的一個(gè)輸出樣本(記住你的程序的運行結果可能與此不同):
Timing a for loop from 0 to 1,000,000
Elapsed time: 10
使用arraycopy( )方法可以將一個(gè)任意類(lèi)型的數組快速地從一個(gè)地方復制到另一個(gè)地方。這比使用Java中編寫(xiě)的循環(huán)要快的多。下面是一個(gè)用arraycopy( )方法復制兩個(gè)數組的例子。首先,將數組a復制給數組b,接下來(lái),數組a中的所有元素向后移一位,然后數組b中元素向前移一位。
// Using arraycopy().
class ACDemo {
static byte a[] = { 65, 66, 67, 68, 69, 70, 71, 72, 73, 74 };
static byte b[] = { 77, 77, 77, 77, 77, 77, 77, 77, 77, 77 };
public static void main(String args[]) {
System.out.println("a = " + new String(a));
System.out.println("b = " + new String(b));
System.arraycopy(a, 0, b, 0, a.length);
System.out.println("a = " + new String(a));
System.out.println("b = " + new String(b));
System.arraycopy(a, 0, a, 1, a.length - 1);
System.arraycopy(b, 1, b, 0, b.length - 1);
System.out.println("a = " + new String(a));
System.out.println("b = " + new String(b));
}
}
正如從下面的輸出中看到的那樣,可以使用相同的源和目的在任一方向進(jìn)行復制:
a = ABCDEFGHIJ
b = MMMMMMMMMM
a = ABCDEFGHIJ
b = ABCDEFGHIJ
a = AABCDEFGHI
b = BCDEFGHIJJ
下面的屬性在Java 2的所有環(huán)境中可以使用:
file.separator java.vendor.url os.arch
java.class.path java.version os.name
java.class.version java.vm.name os.version
java.ext.dirs java.vm.specification.name Path.separator
java.home java.vm.specification.vendor User.dir
java.specification.name java.vm.specification.version User.home
java.specification.vendor java.vm.vendor User.name
java.specification.version java.vm.version
java.vendor line.separator
可以通過(guò)調用System.getProperty( )方法來(lái)獲得不同環(huán)境變量的值。例如下面的程序顯示當前用戶(hù)目錄的路徑:
class ShowUserDir {
public static void main(String args[]) {
System.out.println(System.getProperty("user.dir"));
}
}
正如我們在第1部分所提及的,Object類(lèi)是其他所有類(lèi)的一個(gè)超類(lèi)。表14-12給出了Object類(lèi)中定義的方法,這些方法對于每一個(gè)對象都是可用的。
表12 由Object定義的方法
方法 描述
Object clone( ) Throws CloneNotSupportedException 創(chuàng )建一個(gè)與調用對象一樣的新對象
Boolean equals(Object object) 如果調用對象等價(jià)于object返回true
void finalize( ) throws Throwable 默認的finalize( )方法。常常被子類(lèi)重載
final Class getClass( ) 獲得描述調用對象的Class對象
int hashCode( ) 返回與調用對象關(guān)聯(lián)的散列碼
final void notify( ) 恢復等待調用對象的線(xiàn)程的執行
final void notifyAll( ) 恢復等待調用對象的所有線(xiàn)程的執行
String toString( ) 返回描述對象的一個(gè)字符串
final void wait( ) throws InterruptedException 等待另一個(gè)執行的線(xiàn)程
final void wait(long milliseconds) throws InterruptedException 等待直至指定毫秒數的另一個(gè)執行的線(xiàn)程
final void wait(long milliseconds, int nanoseconds) throws InterruptedException 等待直至指定毫秒加毫微秒數的另一個(gè)執行的線(xiàn)程
由Object類(lèi)定義的絕大部分方法在本書(shū)其他部分討論。而一個(gè)特別值得關(guān)注的方法是clone( )。clone( )方法創(chuàng )建調用它的對象的一個(gè)復制副本。只有那些實(shí)現Cloneable接口的類(lèi)能被復制。
Cloneable接口沒(méi)有定義成員。它通常用于指明被創(chuàng )建的一個(gè)允許對對象進(jìn)行位復制(也就是對象副本)的類(lèi)。如果試圖用一個(gè)不支持Cloneable接口的類(lèi)調用clone( )方法,將引發(fā)一個(gè)CloneNotSupportedException異常。當一個(gè)副本被創(chuàng )建時(shí),并沒(méi)有調用被復制對象的構造函數。副本僅僅是原對象的一個(gè)簡(jiǎn)單精確的拷貝。
復制是一個(gè)具有潛在危險的操作,因為它可能引起不是你所期望的副作用。例如,假如被復制的對象包含了一個(gè)稱(chēng)為obRef的引用變量,當副本創(chuàng )建時(shí),副本中的obRef如同原對象中的obRef一樣引用相同的對象。如果副本改變了被obRef引用的對象的內容,那么對應的原對象也將被改變。這里是另一個(gè)例子。如果一個(gè)對象打開(kāi)一個(gè)I/O流并被復制,兩個(gè)對象將可操作相同的流。而且,如果其中一個(gè)對象關(guān)閉了流,而另一個(gè)對象仍試圖對I/O流進(jìn)行寫(xiě)操作的話(huà),將導致錯誤。
由于復制可能引起問(wèn)題,因此在Object內,clone( )方法被說(shuō)明為protected。這就意味著(zhù)它必須或者被由實(shí)現Cloneable的類(lèi)所定義的方法調用,或者必須被那些類(lèi)顯式重載以便它是公共的。讓我們看關(guān)于下面每一種方法的例子。
下面的程序實(shí)現Cloneable接口并定義cloneTest( )方法,該方法在Object中調用clone( )方法:
// Demonstrate the clone() method.
class TestClone implements Cloneable {
int a;
double b;
// This method calls Object‘s clone().
TestClone cloneTest() {
try {
// call clone in Object.
return (TestClone) super.clone();
} catch(CloneNotSupportedException e) {
System.out.println("Cloning not allowed.");
return this;
}
}
}
class CloneDemo {
public static void main(String args[]) {
TestClone x1 = new TestClone();
TestClone x2;
x1.a = 10;
x1.b = 20.98;
x2 = x1.cloneTest(); // clone x1
System.out.println("x1: " + x1.a + " " + x1.b);
System.out.println("x2: " + x2.a + " " + x2.b);
}
}
這里,方法cloneTest( )在Object中調用clone( )方法并且返回結果。注意由clone( )方法返回的對象必須被強制轉換成它的適當類(lèi)型(TestClone)。
下面的例子重載clone( )方法以便它能被其類(lèi)外的程序所調用。為了完成這項功能,它的存取說(shuō)明符必須是public,如下所示:
// Override the clone() method.
class TestClone implements Cloneable {
int a;
double b;
// clone() is now overridden and is public.
public Object clone() {
try {
// call clone in Object.
return super.clone();
} catch(CloneNotSupportedException e) {
System.out.println("Cloning not allowed.");
return this;
}
}
}
class CloneDemo2 {
public static void main(String args[]) {
TestClone x1 = new TestClone();
TestClone x2;
x1.a = 10;
x1.b = 20.98;
// here, clone() is called directly.
x2 = (TestClone) x1.clone();
System.out.println("x1: " + x1.a + " " + x1.b);
System.out.println("x2: " + x2.a + " " + x2.b);
}
}
由復制帶來(lái)的副作用最初一般是比較難發(fā)現的。通常很容易想到的是類(lèi)在復制時(shí)是很安全的,而實(shí)際卻不是這樣。一般在沒(méi)有一個(gè)必須的原因的情況下,對任何類(lèi)都不應該執行Cloneable。
Class封裝對象或接口運行時(shí)的狀態(tài)。當類(lèi)被加載時(shí),類(lèi)型Class的對象被自動(dòng)創(chuàng )建。不能顯式說(shuō)明一個(gè)類(lèi)(Class)對象。一般地,通過(guò)調用由Object定義的getClass( )方法來(lái)獲得一個(gè)類(lèi)(Class)對象。由Class定義的一些最常用的方法列在表14-13中。
表13 由Class定義的一些方法
方法 描述
static Class forName(String name) throws ClassNotFoundException 返回一個(gè)給定全名的Class對象
static Class forName(String name, Boolean how, ClassLoader ldr) throws ClassNotFoundException 返回一個(gè)給定全名的Calss對象。對象由ldr指定的加載程序加載。如果how為true,對象被初始化,否則它將不被初始化(在Java 2中新增加的)
Class[ ] getClasses( ) 對每一個(gè)公共類(lèi)和接口,返回一個(gè)類(lèi)(Class)對象。這些公共類(lèi)和接口是調用對象的成員
ClassLoader getClassLoader( ) 返回一個(gè)加載類(lèi)或接口的ClassLoader對象,類(lèi)或接口用于實(shí)例化調用對象
Constructor[ ] getConstructors( ) throws SecurityException 對這個(gè)類(lèi)的所有的公共構造函數,返回一個(gè)Constructor對象
Constructor[ ] getDeclaredConstructors( ) throws SecurityException 對由這個(gè)類(lèi)所聲明的所有構造函數,返回一個(gè)Constructor對象
Field[ ] getDeclaredFields( ) throws SecurityException 對由這個(gè)類(lèi)所聲明的所有域,返回一個(gè)Field對象
Method[ ] getDeclaredMethods( ) throws SecurityException 對由這個(gè)類(lèi)或接口所聲明的所有方法,返回一個(gè)Method對象
Field[ ] getFields( ) throws SecurityException 對于這個(gè)類(lèi)的所有公共域,返回一個(gè)Field對象
Class[ ] getInterfaces( ) 當調用對象時(shí),這個(gè)方法返回一個(gè)由該對象的類(lèi)類(lèi)型實(shí)現的接口數組。當調用接口時(shí),這個(gè)方法返回一個(gè)由該接口擴展的接口數組
Method[ ] getMethods( ) throws SecurityException 對這個(gè)類(lèi)中的所有公共方法,返回一個(gè)Method對象
String getName( ) 返回調用對象的類(lèi)或接口的全名
ProtectionDomain getProtectionDomain( ) 返回與調用對象有關(guān)的保護范圍(在Java 2中新增加的)
Class getSuperclass( ) 返回調用對象的超類(lèi)。如果調用對象是類(lèi)型Object的,則返回值為空(null)
Boolean isInterface( ) 如果調用對象是一個(gè)接口,則返回true。否則返回false
Object newInstance( ) throws IllegalAccessException, InstantiationException 創(chuàng )建一個(gè)與調用對象類(lèi)型相同的新的實(shí)例(即一個(gè)新對象)。這相當于對類(lèi)的默認構造函數使用new。返回新對象
String toString( ) 返回調用對象或接口的字符串表達式
由Class定義的方法經(jīng)常用在需要知道對象的運行時(shí)類(lèi)型信息的場(chǎng)合。如同表14-13中所說(shuō)明的那樣,由Class提供的方法確定關(guān)于特定的類(lèi)的附加信息。例如它的公共構造函數,域以及方法。這對于本書(shū)后面將要討論的Java Beans函數是很重要的。
下面的程序說(shuō)明了getClass( )(從Object繼承的)和getSuperclass( )方法(從Class繼承的):
// Demonstrate Run-Time Type Information.
class X {
int a;
float b;
}
class Y extends X {
double c;
}
class RTTI {
public static void main(String args[]) {
X x = new X();
Y y = new Y();
Class clObj;
clObj = x.getClass(); // get Class reference
System.out.println("x is object of type: " +
clObj.getName());
clObj = y.getClass(); // get Class reference
System.out.println("y is object of type: " +
clObj.getName());
clObj = clObj.getSuperclass();
System.out.println("y‘s superclass is " +
clObj.getName());
}
}
這個(gè)程序的輸出如下所示:
x is object of type: X
y is object of type: Y
y’s superclass is X
抽象類(lèi)ClassLoader規定了類(lèi)是如何加載的。應用程序可以創(chuàng )建擴展ClassLoader的子類(lèi),實(shí)現它的方法。這樣做允許使用不同于通常由Java運行時(shí)系統加載的另一些方法來(lái)加載類(lèi)。由ClassLoader定義的一些方法列在表14-14中。
表14 由CalssLoader定義的一些方法
方法 描述
final Class defineClass(String str, byte b[ ], int index, int numBytes) throws ClassFormatError 返回一個(gè)類(lèi)(Class)對象,類(lèi)的名字在str中,對象包含在由b指定的字節數組中。該數組中對象開(kāi)始的位置下標由index指定,而該數組的長(cháng)度為numBytes。b中的數據必須表示一個(gè)有效的對象
final Class findSystemClass(String name) throws ClassNotFoundException 返回一個(gè)給定名字的類(lèi)(Class)對象
abstract Class loadClass(String name, boolean callResolveClass) throws ClassNotFoundException 如果callResolveClass為true,這個(gè)抽象方法的實(shí)現工具必須加載一個(gè)給定名字的類(lèi),并調用resolveClass( )方法
final void resolveClass(Class obj) 用obj引用的類(lèi)被解析(即,它的名字被輸入在類(lèi)名字空間中)
Math類(lèi)保留了所有用于幾何學(xué),三角學(xué)以及幾種一般用途方法的浮點(diǎn)函數。Math定義了兩個(gè)雙精度(double)常數:E(近似為2.72)和PI(近似為3.14)。
下面的三種方法對一個(gè)以弧度為單位的角度接收一個(gè)雙精度(double)參數并且返回它們各自的超越函數的結果:
方法 描述
Static double sin(double arg) 返回由以弧度為單位由arg指定的角度的正弦值
static double cos(double arg) 返回由以弧度為單位由arg指定的角度的余弦值
static double tan(double arg) 返回由以弧度為單位由arg指定的角度的正切值
下面的方法將超越函數的結果作為一個(gè)參數,按弧度返回產(chǎn)生這個(gè)結果的角度值。它們是其非弧度形式的反。
方法 描述
static double asin(double arg) 返回一個(gè)角度,該角度的正弦值由arg指定
static double acos(double arg) 返回一個(gè)角度,該角度的余弦值由arg指定
static double atan(double arg) 返回一個(gè)角度,該角度的正切值由arg指定
static double atan2(double x, double y) 返回一個(gè)角度,該角度的正切值為x/y
Math定義了下面的指數方法:
方法 描述
static double exp(double arg) 返回arg的e
static double log(double arg) 返回arg的自然對數值
static double pow(double y, double x) 返回以y為底數,以x為指數的冪值;例如pow(2.0, 3.0)返回8.0
static double sqrt(double arg) 返回arg的平方根
Math類(lèi)定義了幾個(gè)提供不同類(lèi)型舍入運算的方法。這些方法列在表15中。
表15 由Math定義的舍入方法
方法 描述
static int abs(int arg) 返回arg的絕對值
static long abs(long arg) 返回arg的絕對值
static float abs(float arg) 返回arg的絕對值
static double abs(double arg) 返回arg的絕對值
static double ceil(double arg) 返回大于或等于arg的最小整數
static double floor(double arg) 返回小于或等于arg的最大整數
static int max(int x, int y) 返回x和y中的最大值
static long max(long x, long y) 返回x和y中的最大值
static float max(float x, float y) 返回x和y中的最大值
static double max(double x, double y) 返回x和y中的最大值
static int min(int x, int y) 返回x和y中的最小值
static long min(long x, long y) 返回x和y中的最小值
static float min(float x, float y) 返回x和y中的最小值
static double min(double x, double y) 返回x和y中的最小值
static double rint(double arg) 返回最接近arg的整數值
static int round(float arg) 返回arg的只入不舍的最近的整型(int)值
static long round(double arg) 返回arg的只入不舍的最近的長(cháng)整型(long)值
除了給出的方法,Math還定義了下面這些方法:
static double IEEEremainder(double dividend, double divisor)
static double random( )
static double toRadians(double angle)
static double toDegrees(double angle)
IEEEremainder( )方法返回dividend/divisor的余數。random( )方法返回一個(gè)偽隨機數,其值介于0與1之間。在大多數情況下,當需要產(chǎn)生隨機數時(shí),通常用Random類(lèi)。toRadians( )方法將角度的度轉換為弧度。而toDegrees( )方法將弧度轉換為度。這后兩種方法是在Java 2中新增加的。
下面是一個(gè)說(shuō)明toRadians( )和toDegrees( )方法的例子:
// Demonstrate toDegrees() and toRadians().
class Angles {
public static void main(String args[]) {
double theta = 120.0;
System.out.println(theta + " degrees is " +
Math.toRadians(theta) + " radians.");
theta = 1.312;
System.out.println(theta + " radians is " +
Math.toDegrees(theta) + " degrees.");
}
}
程序輸出如下所示:
120.0 degrees is 2.0943951023931953 radians.
1.312 radians is 75.17206272116401 degrees.
在Java 2的1.3版本中增加了StrictMath類(lèi)。這個(gè)類(lèi)定義一個(gè)與Math中的數學(xué)方法類(lèi)似的一套完整的數學(xué)方法。兩者的區別在于StrictMath中的方法對所有Java工具保證產(chǎn)生精確一致的結果,而Math中的方法更大程度上是為了提高性能。
Compiler類(lèi)支持創(chuàng )建將字節碼編譯而非解釋成可執行碼的Java環(huán)境。常規的程序不使用它。
Runnable接口以及Thread和ThreadGroup類(lèi)支持多線(xiàn)程編程。下面分別予以說(shuō)明。
注意:關(guān)于管理線(xiàn)程,實(shí)現Runnable接口以及創(chuàng )建多線(xiàn)程程序的概述已在第11章中介紹過(guò)。
Runnable接口必須由啟動(dòng)執行的獨立線(xiàn)程的類(lèi)所實(shí)現。Runnable僅定義了一種抽象方法,叫做run( )。該方法是線(xiàn)程的入口點(diǎn)。它的形式如下所示:
abstract void run( )
所創(chuàng )建的線(xiàn)程必須實(shí)現該方法。
Thread創(chuàng )建一個(gè)新的執行線(xiàn)程。它定義了如下的構造函數:
Thread( )
Thread(Runnable threadOb)
Thread(Runnable threadOb, StringthreadName)
Thread(String threadName)
Thread(ThreadGroup groupOb, Runnable threadOb)
Thread(ThreadGroup groupOb, Runnable threadOb, String threadName)
Thread(ThreadGroup groupOb, String threadName)
threadOb是實(shí)現Runnable接口的類(lèi)的一個(gè)實(shí)例,它定義了線(xiàn)程運行開(kāi)始的地方。線(xiàn)程的名字由threadName指定。當名字未被指定時(shí),Java虛擬機將創(chuàng )建一個(gè)。groupOb指定了新線(xiàn)程所屬的線(xiàn)程組。當沒(méi)有線(xiàn)程組被指定時(shí),新線(xiàn)程與其父線(xiàn)程屬于同一線(xiàn)程組。
下面的常數由Thread定義:
MAX_PRIORITY
MIN_PRIORITY
NORM_PRIORITY
正如所期望的那樣,這些常數指定了最大,最小以及默認的線(xiàn)程優(yōu)先權。
由Thread定義的方法列在表14-16中。在比Java 2早的版本中,Thread中也包括了stop( ),suspend( )以及resume( )方法。然而正如在第11章中解釋的那樣,這些方法由于其固有的不穩定性而在Java 2中被擯棄了。在Java 2中擯棄的還有countStackFrames( )方法,因為它調用了suspend( )方法。
表16 由Thread定義的方法
方法 描述
static int activeCount( ) 返回線(xiàn)程所屬的線(xiàn)程組中線(xiàn)程的個(gè)數
void checkAccess( ) 引起安全管理程序檢驗當前的線(xiàn)程能訪(fǎng)問(wèn)和/或能改變在其上checkAccess( )方法被調用的線(xiàn)程
static Thread currentThread( ) 返回一個(gè)Thread對象,該對象封裝了調用這個(gè)方法的線(xiàn)程
void destroy( ) 終止線(xiàn)程
static int enumerate(Thread threads[ ]) 將當前線(xiàn)程組中的所有Thread對象的拷貝放入threads中。返回線(xiàn)程的個(gè)數
ClassLoader getContextClassLoader( ) 返回用于對這個(gè)線(xiàn)程加載類(lèi)和資源的類(lèi)加載程序(在Java 2中新增加的)
final String getName( ) 返回線(xiàn)程名
final int getPriority( ) 返回線(xiàn)程的屬性設置
final ThreadGroup getThreadGroup( ) 返回調用線(xiàn)程是其中一個(gè)成員的ThreadGroup對象
void interrupt( ) 中斷線(xiàn)程
static boolean interrupted( ) 如果當前執行的線(xiàn)程已經(jīng)被預先設置了中斷,則返回true;否則,返回false
final boolean isAlive( ) 如果線(xiàn)程仍在運行中,則返回true;否則返回false
final boolean isDaemon( ) 如果線(xiàn)程是一個(gè)后臺進(jìn)程線(xiàn)程(Java運行系統的一部分),則返回true;否則返回false
boolean isInterrupted( ) 如果線(xiàn)程被中斷,則返回true,否則返回false
final void join( ) throws InterruptedException 等待直至線(xiàn)程終止
續表
方法 描述
final void join(long milliseconds) throws InterruptedException 等待直到為終止線(xiàn)程而指定的以毫秒計時(shí)的時(shí)間
final void join(long milliseconds, int nanoseconds) throws InterruptedException 等待直到為終止線(xiàn)程而指定的以毫秒加毫微秒計時(shí)的時(shí)間
void run( ) 開(kāi)始線(xiàn)程的執行
void setContextClassLoader(ClassLoader cl) 設置將被調用線(xiàn)程用于cl的類(lèi)加載程序(在Java 2中新增加的)
final void setDaemon(boolean state) 標記線(xiàn)程為后臺進(jìn)程線(xiàn)程
final void setName(String threadName) 將線(xiàn)程的名字設置為由threadName指定的名字
final void setPriority(int priority) 設置由priority指定的線(xiàn)程優(yōu)先權
static void sleep(long milliseconds) throws InterruptedException 以指定的毫秒為單位的時(shí)間長(cháng)度掛起執行的線(xiàn)程
static void sleep(long milliseconds, int nanoseconds) throws InterruptedException 以指定的毫秒加毫微秒為單位的時(shí)間長(cháng)度掛起執行的線(xiàn)程
void start( ) 開(kāi)始線(xiàn)程的執行
String toString( ) 返回線(xiàn)程的等價(jià)字符串形式
static void yield( ) 調用線(xiàn)程將CPU讓給其他的線(xiàn)程
14.13.3 ThreadGroup
線(xiàn)程組(ThreadGroup)創(chuàng )建了一組線(xiàn)程。它定義了如下的兩個(gè)構造函數:
ThreadGroup(String groupName)
ThreadGroup(ThreadGroup parentOb, String groupName)
對于兩種形式,groupName指定了線(xiàn)程組的名字。第一種形式創(chuàng )建一個(gè)新的線(xiàn)程組,該線(xiàn)程組將當前的線(xiàn)程作為它的父線(xiàn)程。在第二種形式中,父線(xiàn)程由parentOb指定。
由ThreadGroup定義的方法列在表14-17中。在比Java 2更早出現的Java版本中,ThreadGroup中也包括了stop( ),suspend( )以及resume( )方法。這些方法由于其本身固有的不穩定性,而在Java 2中被擯棄。
表14-17 由ThreadGroup定義的方法
方法 描述
int activeCount( ) 返回線(xiàn)程組加上以這個(gè)線(xiàn)程作為父類(lèi)的所有線(xiàn)程組中線(xiàn)程的個(gè)數
int activeGroupCount( ) 返回調用線(xiàn)程是父類(lèi)的線(xiàn)程的組數
final void checkAccess( ) 引起安全管理程序檢驗調用線(xiàn)程能訪(fǎng)問(wèn)和/或能改變在其上checkAccess( )方法被調用的線(xiàn)程組
final void destroy( ) 撤消被調用的線(xiàn)程組(以及任一子線(xiàn)程組)
int enumerate(Thread group[ ]) 將構成調用線(xiàn)程組的線(xiàn)程放入group數組中
int enumerate(Thread group[ ], boolean all) 將構成調用線(xiàn)程組的線(xiàn)程放入group數組中。如果all為true,那么線(xiàn)程組的所有子線(xiàn)程組中的線(xiàn)程也被放入group中
int enumerate(ThreadGroup group[ ]) 將調用線(xiàn)程組的子線(xiàn)程組放入group數組中
int enumerate(ThreadGroup group[ ], boolean all) 將調用線(xiàn)程組的子線(xiàn)程組放入group數組中。如果all為true,所有子線(xiàn)程組的子線(xiàn)程組(等等)也被放入group中
final int getMaxPriority( ) 返回對線(xiàn)程組設置的最大優(yōu)先權
final String getName( ) 返回線(xiàn)程組名
final ThreadGroup getParent( ) 如果調用ThreadGroup對象沒(méi)有父類(lèi),則返回null;否則返回調用對象的父類(lèi)
final void interrupt( ) 調用線(xiàn)程組中所有線(xiàn)程的interrupt( )方法(在Java 2中新增加的)
final boolean isDaemon( ) 如果線(xiàn)程組是一個(gè)端口后臺進(jìn)程組,則返回true;否則返回false
boolean isDestroyed( ) 如果線(xiàn)程組已經(jīng)被破壞,則返回true;否則,返回false
void list( ) 顯示關(guān)于線(xiàn)程組的信息
final boolean parentOf(ThreadGroup group) 如果調用線(xiàn)程是group的父線(xiàn)程(或group本身),則返回true;否則返回false
final void setDaemon(boolean isDaemon) 如果isDaemon為true,那么調用線(xiàn)程組被標記為一個(gè)端口后臺進(jìn)程組
final void setMaxPriority(int priority) 對調用線(xiàn)程組設置最大優(yōu)先權priority
String toString( ) 返回線(xiàn)程組的字符串等價(jià)形式
void uncaughtException(Thread thread, Throwable e) 當一個(gè)異常未被捕獲時(shí),該方法被調用
線(xiàn)程組提供了一種方便的方法,可以將一組線(xiàn)程當做一個(gè)單元來(lái)管理。這在想掛起或恢復一些相關(guān)的線(xiàn)程的情況下,是特別有用的。例如假想在一個(gè)程序中,有一組線(xiàn)程被用來(lái)打印文檔,另一組線(xiàn)程被用來(lái)將該文檔顯示在屏幕上,同時(shí)另一組線(xiàn)程將文檔保存為磁盤(pán)文件。如果打印被異常中止了,想用一種很簡(jiǎn)單的方法停止所有與打印有關(guān)的線(xiàn)程。線(xiàn)程組為這種處理提供了方便。下面的程序說(shuō)明了這種用法,在程序中創(chuàng )建兩個(gè)線(xiàn)程組,每一線(xiàn)程組中有兩個(gè)線(xiàn)程:
// Demonstrate thread groups.
class NewThread extends Thread {
boolean suspendFlag;
NewThread(String threadname, ThreadGroup tgOb) {
super(tgOb, threadname);
System.out.println("New thread: " + this);
suspendFlag = false;
start(); // Start the thread
}
// This is the entry point for thread.
public void run() {
try {
for(int i = 5; i > 0; i--) {
System.out.println(getName() + ": " + i);
Thread.sleep(1000);
synchronized(this) {
while(suspendFlag) {
wait();
}
}
}
} catch (Exception e) {
System.out.println("Exception in " + getName());
}
System.out.println(getName() + " exiting.");
}
void mysuspend() {
suspendFlag = true;
}
synchronized void myresume() {
suspendFlag = false;
notify();
}
}
class ThreadGroupDemo {
public static void main(String args[]) {
ThreadGroup groupA = new ThreadGroup("Group A");
ThreadGroup groupB = new ThreadGroup("Group B");
NewThread ob1 = new NewThread("One", groupA);
NewThread ob2 = new NewThread("Two", groupA);
NewThread ob3 = new NewThread("Three", groupB);
NewThread ob4 = new NewThread("Four", groupB);
System.out.println("\nHere is output from list():");
groupA.list();
groupB.list();
System.out.println();
System.out.println("Suspending Group A");
Thread tga[] = new Thread[groupA.activeCount()];
groupA.enumerate(tga); // get threads in group
for(int i = 0; i < tga.length; i++) {
((NewThread)tga[i]).mysuspend(); // suspend each thread
}
try {
Thread.sleep(4000);
} catch (InterruptedException e) {
System.out.println("Main thread interrupted.");
}
System.out.println("Resuming Group A");
for(int i = 0; i < tga.length; i++) {
((NewThread)tga[i]).myresume(); // resume threads in group
}
// wait for threads to finish
try {
System.out.println("Waiting for threads to finish.");
ob1.join();
ob2.join();
ob3.join();
ob4.join();
} catch (Exception e) {
System.out.println("Exception in Main thread");
}
System.out.println("Main thread exiting.");
}
}
該程序的一個(gè)輸出樣本如下所示:
New thread: Thread[One,5,Group A]
New thread: Thread[Two,5,Group A]
New thread: Thread[Three,5,Group B]
New thread: Thread[Four,5,Group B]
Here is output from list():
java.lang.ThreadGroup[name=Group A,maxpri=10]
Thread[One,5,Group A]
Thread[Two,5,Group A]
java.lang.ThreadGroup[name=Group B,maxpri=10]
Thread[Three,5,Group B]
Thread[Four,5,Group B]
Suspending Group A
Three: 5
Four: 5
Three: 4
Four: 4
Three: 3
Four: 3
Three: 2
Four: 2
Resuming Group A
Waiting for threads to finish.
One: 5
Two: 5
Three: 1
Four: 1
One: 4
Two: 4
Three exiting.
Four exiting.
One: 3
Two: 3
One: 2
Two: 2
One: 1
Two: 1
One exiting.
Two exiting.
Main thread exiting.
注意在這個(gè)程序中,線(xiàn)程組A被掛起四秒。由于輸出確認,造成線(xiàn)程O(píng)ne和線(xiàn)程Two暫停,但是線(xiàn)程Three和線(xiàn)程Four仍然運行。四秒鐘之后,線(xiàn)程O(píng)ne和線(xiàn)程Two被恢復。注意線(xiàn)程組A是如何被掛起和恢復的。首先通過(guò)對線(xiàn)程組A調用enumerate( )方法得到線(xiàn)程組A中的線(xiàn)程。然后每一個(gè)線(xiàn)程重復通過(guò)得到的數組而被掛起。為了恢復線(xiàn)程組A中的線(xiàn)程,序列再一次被遍歷,每一個(gè)線(xiàn)程被恢復。最后一點(diǎn):這個(gè)例子使用了Java 2推薦使用的方法去完成掛起和恢復線(xiàn)程的任務(wù)。而沒(méi)有用在Java 2中被擯棄的方法suspend( )和resume( )。
在Java 2的java.lang中增加了兩個(gè)與線(xiàn)程有關(guān)的類(lèi):
ThreadLocal?用于創(chuàng )建線(xiàn)程局部變量。每個(gè)線(xiàn)程都擁有自己局部變量的拷貝。
InheritableThreadLocal?創(chuàng )建可以被繼承的線(xiàn)程局部變量。
在Java 2中增加了一個(gè)稱(chēng)為Package的類(lèi)。這個(gè)類(lèi)封裝了與包有關(guān)的版本數據。包版本信息由于包的增值以及由于Java程序可能需要知道哪些包版本可以利用而變得更加重要。Package中定義的方法列在表14-18中。下面的程序通過(guò)顯示程序當前已知的包而說(shuō)明了Package。
表18 由Package定義的方法
方法 描述
String getImplementationTitle( ) 返回調用包的標題
String getImplementationVendor( ) 返回調用包的實(shí)現程序的程序名
String getImplementationVersion( ) 返回調用包的版本號
String getName( ) 返回調用包的名字
Static Package getPackage(String pkgName) 返回一個(gè)由pkgName指定的Package對象
Static Package[ ] getPackages( ) 返回調用程序當前已知的所有包
String getSpecificationTitle( ) 返回調用包的規格說(shuō)明的標題
String getSpecificationVendor( ) 返回對調用包的規格說(shuō)明的所有者的名字
String getSpecificationVersion( ) 返回調用包的規格說(shuō)明的版本號
Int hashCode( ) 返回調用包的散列碼
Boolean isCompatibleWith(String verNum) throws NumberFormatException 如果verNum小于或等于調用包的版本號,則返回true
Boolean isSealed( ) 如果調用包被封,則返回true;否則返回false
Boolean isSealed(URL url) 如果調用包相對于url被封,則返回true;否則返回false。
String toString( ) 返回調用包的等價(jià)字符串形式
// Demonstrate Package
class PkgTest {
public static void main(String args[]) {
Package pkgs[];
pkgs = Package.getPackages();
for(int i=0; i < pkgs.length; i++)
System.out.println(
pkgs[i].getName() + " " +
pkgs[i].getImplementationTitle() + " " +
pkgs[i].getImplementationVendor() + " " +
pkgs[i].getImplementationVersion()
);
}
}
在Java 2的java.lang中也新增加了RuntimePermission。它與Java的安全機制有關(guān),這里不做進(jìn)一步的討論。
Throwable類(lèi)支持Java的異常處理系統,它是派生所有異常類(lèi)的類(lèi)。在本書(shū)第10章已經(jīng)討論過(guò)它。
SecurityManager是一個(gè)子類(lèi)可以實(shí)現的抽象類(lèi),它用于創(chuàng )建一個(gè)安全管理程序。一般不需要實(shí)現自己的安全管理程序,如果非要這樣做,需要查閱與你的Java開(kāi)發(fā)系統一起得到的相關(guān)文檔。
Java 2在java.lang中新增加了一個(gè)接口:Comparable。實(shí)現Comparable的類(lèi)的對象可以被排序。換句話(huà)說(shuō),實(shí)現Comparable的類(lèi)包含了可以按某種有意義的方式進(jìn)行比較的對象。Comparable接口說(shuō)明了一個(gè)方法,該方法用于確定Java 2調用一個(gè)類(lèi)的實(shí)例的自然順序。該方法如下所示:
int compareTo(Object obj)
這個(gè)方法比較調用對象和obj。如果他們相等,就返回0。如果調用對象比obj小,則返回一個(gè)負值。否則返回一個(gè)正值。
該接口由前面已經(jīng)介紹的幾種類(lèi)實(shí)現。特別是Byte,Character,Double,Float,Long,Short,String以及Integer類(lèi)定義了compareTo( )方法。另外,下一章將會(huì )介紹到,實(shí)現這個(gè)接口的對象可以被使用在不同的集合中。
在Java中定義了兩個(gè)java.lang的子包:java.lang.ref和java.lang.reflect。下面分別予以簡(jiǎn)單介紹。
在前面學(xué)到過(guò),在Java中,垃圾回收工具自動(dòng)確定何時(shí)對一個(gè)對象,沒(méi)有引用存在。然后這個(gè)對象就被認為是不再需要的,同時(shí)它所占的內存也被釋放。在Java 2中新增加的java.lang.ref包中的類(lèi)對垃圾回收處理提供更加靈活的控制。例如,假設你的程序創(chuàng )建了大量的在后面某個(gè)時(shí)間又想重新使用的對象,可以持續保持對這些對象的引用,但是這可能需要更多的內存開(kāi)銷(xiāo)。
作為替代,可以對這些對象定義“軟”引用。如果可以利用的內存接近用完的話(huà),一個(gè)可以“軟實(shí)現”的對象可以從垃圾回收工具中釋放。在那種情況下,垃圾回收工具將這個(gè)對象的“軟”引用設為空(null)。否則,垃圾回收工具保存對象以便以后使用。
程序設計人員具有確定是否一個(gè)“軟實(shí)現”的對象被釋放的能力。如果它被釋放了,可以重新創(chuàng )建它。如果沒(méi)有釋放,該對象對于后面的應用將一直是可以利用的。也可以為對象創(chuàng )建“弱”(weak)和“假想”(phantom)引用,不過(guò)關(guān)于這些以及java.lang.ref包中其他特性的討論已經(jīng)超過(guò)了本書(shū)的范圍。
Reflection是一個(gè)程序分析自己的能力。包java.lang.reflect提供了獲得關(guān)于一個(gè)類(lèi)的域、構造函數、方法和修改符的能力。需要這些信息去創(chuàng )建可以使你利用Java Beans組件的軟件工具。這個(gè)工具使用映射動(dòng)態(tài)地確定組件的特征。這個(gè)主題將在第25章中討論。
另外,包java.lang.reflect包括了一個(gè)可以動(dòng)態(tài)創(chuàng )建和訪(fǎng)問(wèn)數組的類(lèi)。
聯(lián)系客服