天天看點

[Java]Java學習筆記(二)——類的基本文法零碎要點

  • 零碎要點

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;