- 零碎要點
java 類的基本文法大都與其他語言一樣,就不再贅述了,将那些不太一樣的地方列為零碎要點,如下。其他的進階部分,接下去會另起一篇。
零碎要點
1、使用 final 來建立常量。
雖然 Java 保留了 const 關鍵字,但暫未使用。
2、變量的參數傳遞一直都是值傳遞。
測試如下:
package com.go.java.test;
public class Val {
private int a;
public Val(int v)
{
a = v;
}
public int GetVal()
{
return a;
}
}
package com.go.java.test;
public class Test {
public static void main(String[] args) {
Val a = new Val();
System.out.println(a.GetVal());
}
static void Fun(Val v) {
Val b = new Val();
v = b;
}
}
輸出為
1
。
顯然,這值得批評,java 的效率,估計被這個拖累不少。
3、不合法的規則:
native 方法可以繞過 Java 語言的存取控制機制,進行某些操作。
如 System 類裡的 in、out、err 聲明分别為:
public final static InputStream in = null;
public final static PrintStream out = null;
public final static PrintStream err = null;
但在 set 函數中,卻對 final 變量的值進行了修改,況且還是用與 Java 不和的引用參數方式進行修改:
public static void setIn(InputStream in) {
checkIO();
setIn0(in);
}
public static void setOut(PrintStream out) {
checkIO();
setOut0(out);
}
public static void setErr(PrintStream err) {
checkIO();
setErr0(err);
}
這種做法,我隻能說請盡量不要這樣用,除非。。。
Java 的 System 類是不允許執行個體化的,聲明為
public final class
,相比而言,C# 的做法更為合理,獨立出一個 Console 類,将 Console 類定義為 static 類,成員也都定義為 static,不允許繼承又不能執行個體化。
Java 6 之後也引入了 Console 類,但隻是用于密碼讀取。
4、static 的使用與 C# 基本一緻,例外的是類的 static 成員可以用類對象調用。
又一個不合理的設計。還是那句話,建議不要使用。
5、Java 與 C# 相同,每個類都可以有 主函數,多個類都有主函數時,需要指定其中一個為入口函數。在 java 裡這樣做比較友善,是以在類的主函數裡進行類的單元測試,和在外面進行單元測試的寫法是差不多的。
在 C# 習慣用 NUnit,在 Java 裡還真不習慣用這種寫法,還是使用 JUnit 吧。
6、this 的使用:
(1)當成員函數的參數與類成員變量名沖突時,用于區分。
(2)在構造函數中調用另一個構造函數。
比如有構造函數
A(int x, int y);
和
A(int x)
,
那麼我們可以在第二個構造函數中使用
this(x, 0);
這樣的方式來簡化代碼。
當然這種方式也可以提取出一個私有函數來實作。
7、初始化塊:
無論是 C++ 或 C#,我們都可以在 成員變量定義時直接賦予一個不涉及到其他變量的值,在 Java 裡,可以在獨立到初始化塊中執行這個指派操作。
Java 裡,有普通變量初始化塊和靜态變量初始化塊,允許所有可以在函數内部出現的文法功能。
package com.go.java.test;
import java.util.Scanner;
public class Init {
int a;
static int b;
{
a = ;
}
static
{
Scanner in = new Scanner(System.in);
System.out.println("set b = ?");
b = in.nextInt();
System.out.println(b);
in.close();
}
}
package com.go.java.test;
public class Test {
public static void main(String[] args) {
new Init();
// Val a = new Val(1);
// System.out.println(a.GetVal());
}
static void Fun(Val v) {
Val b = new Val();
v = b;
}
}
控制台顯示
set b = ?
如果我們直接使用 Java Init調用上面的類 Init,上面控制台那些也會照樣執行,不過,會提示 “main is not define”。解決的方式是在 static 塊的代碼最後加上
System.exit(0);
。
這不是與那些借助漏洞實作那些沒有主函數的 C 代碼是同種性質嗎!
還是那句:不要使用這種寫法。
8、對象的析構處理:
Java 可以為任何一個類提供 finalize 方法,這樣,在垃圾回收該對象前,會先調用這個方法。然而,并不推薦使用這個方法來關閉檔案或釋放句柄等,因為我們并不能确定該方法會在什麼時候被調用。
另外還有個名為
System.runFinalizersOnExit(true);
的方法能夠確定 finalize 方法在 Java 退出前被調用,但該方法并不安全,也不推薦使用。
有一種替代的方法:
使用方法
Runtime.addShutdownHook(Thread hook)
添加關閉鈎,該方法會在 Java 程式退出前調用,
package com.go.java.test;
public class Test {
public static void main(String[] args) {
Thread one = new Thread() {
public void run() {
System.out.println("shut down thread one");
}
};
Runtime.getRuntime().addShutdownHook(one);
System.out.println("start thread one");
// new Init();
// Val a = new Val(1);
// System.out.println(a.GetVal());
}
static void Fun(Val v) {
Val b = new Val();
v = b;
}
}
然而有些資源需要立即釋放,這時,我們就需要使用一個 close() 方法來執行,使用的方法有兩種,一種是在 try-catch-finally 的 finally 塊中調用 close() 方法,另一種是 實作 Closeable 接口,這樣就無需 finally 塊了(不過此時如果 close 函數執行出錯,抛出的是 IOException),在try 塊執行完成或出錯時,都會執行 close() 方法。
相比而言,Go 使用 defer 來執行類似的工作顯得比較簡便、靈活。
9、最後,再說說 Java 的問題:類的預設成員屬性居然是 protected,這又和 C++ / C# 不同了。
通路屬性的規則:
private:對本類可見。
public:對所有類可見。
protected:對本包可見。預設。
雖然一直強調不要省去通路屬性關鍵字,但還有一些人總會遺漏。正如 《Java核心技術 · 基礎知識》裡提到的,Java 自己的庫中都存在這種問題:
/**
* This represents the warning message that is
* to be displayed in a non secure window. ie :
* a window that has a security manager installed that denies
* {@code AWTPermission("showWindowWithoutWarningBanner")}.
* This message can be displayed anywhere in the window.
*
* @serial
* @see #getWarningString
*/
String warningString;
為什麼說這樣做有問題呢,下面是 sublime text 的搜尋結果,warningString 并不可以直接被指派。
public final String getWarningString() {
: return warningString;
}
private void setWarningString() {
: warningString = null;
SecurityManager sm = System.getSecurityManager();
if (sm != null) {
....
// for getting the property! We don't want the
// above checkPermission call to always succeed!
: warningString = AccessController.doPrivileged(
new GetPropertyAction("awt.appletWarning",
"Java Applet Window"));
況且所有使用到 warningString 的外部代碼都是通過 get 和 set 方法實作的,java 真該把它改為 private。
不過還好,看了本地化代碼,C++的,就沒有這個問題
private:
//some other codes
WCHAR * warningString;