在 Java 中,經過提供構造器,類的設計者可確保每一個對象都會獲得初始化。Java 會保證初始化的進行。構造器採用與類相同的名稱。java
由於能夠要用多種方式來建立並初始化一個對象,因此就須要多個構造器,而構造器的名稱又須要和類名相同,因此必須容許方法名相同而形式參數不一樣的構造器存在,因此 Java 中有方法重載。算法
class Rock { Rock() { // 默認構造器 } Rock(int i) { // 帶參數的構造器 System.out.println("i=" + i); } void print() { System.out.println("i = null"); } void print(int i) { System.out.println("i = " + i); } } // 初始化 Rock r1 = new Rock(); // 調用默認構造器 Rock r2 = new Rock(1); // 調用帶參數的構造器 r2.print(); r2.print(1);
方法簽名是由方法名和參數類型列表構成的,因此用參數類型列表區分重載方法。不能用返回值區分重載方法編程
因爲基本類型可能會從一個「較小」的類型自動提高爲一個「較大」的類型,因此在重載方法中須要特別注意:數組
int
處理因爲同一類型的對象均可以調用相同的方法,爲了在方法中區分不一樣的對象,會把對象的引用做爲參數傳遞給方法,a.fun(1)
在編譯器內部會被翻譯成ClassName.fun(a, 1)
,咱們能夠經過this
關鍵字在方法中訪問到對象的引用。dom
this
,直接調用便可。public class Flower { int petalCount = 0; String s = "initial value"; Flower(int petals) { petalCount = petals; System.out.println("int arg ,petalCount = " + petalCount); } Flower(String ss) { s = ss; System.out.println("string arg ,s = " + s); } Flower(String s, int petals) { this(petals); // this 只能調用一次構造器 this.s = s; System.out.println("string & int arg, s = " + s + ", petalCount = " + petalCount); } Flower() { this("hello", 24); } public static void main(String[] args) { Flower flower = new Flower(); System.out.println("flower.petalCount = " + flower.petalCount); System.out.println("flower.s = " + flower.s); } } // int arg ,petalCount = 24 // string & int arg, s = hello, petalCount = 24 // flower.petalCount = 24 // flower.s = hello
static 方法就是沒有 this 的方法,在 static 中不能調用非靜態方法,可是反過來能夠。函數
Java 中沒有用於釋放對象的 delete,由於垃圾回收器會自動幫你釋放存儲空間,所以 Java 中沒有析構函數。可是垃圾回收不能徹底代替析構函數,若是但願進行除釋放存儲空間以外的清理工做,咱們須要明確調用某個 Java 方法。例如某個類打開了一個文件,垃圾回收不能自動幫咱們關閉這個文件。爲何這個工做不能有 finalize() 方法來完成呢,緣由其實在上面已經說明了,對象可能不會被垃圾回收,也就是說 finalize() 方法可能永遠都不會被調用。ui
若是 JVM 沒有面臨內存耗盡的狀況,它是不會浪費時間去執行垃圾回收以恢復內存的。this
雖然咱們不能用 finalize() 方法來進行「清理」,可是咱們能夠利用它驗證某個對象的終結條件。仍是剛纔那個打開文件的例子,假設在文件沒有關閉的時候,垃圾回收將對象回收了,這就會產生一些很是難找的 bug。而 finalize() 能夠幫助咱們發現這種 bug。lua
class Book { boolean checkedOut = false; Book(boolean checkOut) { checkedOut = checkOut; } void checkIn() { checkedOut = false; } protected void finalize() { if (checkedOut) { System.out.println("Error: checked out"); } // super.finalize(); } } public class TerminationCondition { public static void main(String[] args) { Book novel = new Book(true); novel.checkIn(); new Book(true); System.gc(); } } // Error: checked out
如上面這個例子,咱們但願 Book 在被回收前已經 checkIn 了,因此咱們在 finalize() 中寫了一個條件語句來判斷。翻譯
System.gc()
強制 GCsuper.finalize();
垃圾回收器會提升對象在堆上建立的速度,這是由於 Java 的堆的實現與 C++ 的不一樣,其更像是一個傳送帶,每分配一個對象,它就往前移動一格,因此「堆指針」 只是簡單的移動到還沒有分配的空間,這意味 Java 中在堆上的分配速度很是快。固然,若是隻是簡單的像傳送帶同樣工做的話,Java 的堆會佔用大量的虛擬內存,進而致使頻繁的頁面調度,並可能會致使內存資源耗盡,所以須要有垃圾回收器的介入。垃圾回收會一邊回收空間一邊對堆進行「緊湊」操做。
幾種常見的垃圾回收機制:
JVM 中採用的垃圾回收機制:
Java 中 JIT(Just-In-Time)技術:
這種技術能夠把程序所有或部分翻譯成本地機器碼,而不是經過 JVM,進而提高程序的運行速度。
當須要裝載某個類時(第一次建立這個類時),編譯器會找到其.class 文件,而後將該類的字節碼裝入內存,此時有兩種作法:
Java 盡力保證:全部變量在使用前都能獲得適當的初始化。
局部變量沒有默認初始值,若有在未初始化前使用它會報錯編譯錯誤,而類變量則有默認初始值。
Java 容許在定義類成員變量的時候爲其賦值進行初始化。非基本類型也能夠,同時可使用已經函數或已經初始化好的變量進行初始化,但要保證初始化順序的正確。
public class InitialValues { boolean t = false; char c = 'a'; byte b = 1; short s = 2; int i = func(s); long l = 4 + i; float f = (float)5.0; // 浮點數字面量是 double 類型的 double d = 6.0; String reference = new String("hello world"); // 非基本類型也能夠 int func(short s) { return s*2; } }
public class InitialValues { boolean t = false; char c = 'a'; byte b = 1; short s = 2; int i = 3; long l = 4; float f; double d; String reference; { f = (float) 1.0; d = 2*f; reference = new String("hello"); reference = reference + f + d; } static { System.out.println("hello"); } static int a; static { System.out.println("A is " + a); a = 2; } }
對象的建立過程:
數組是同類型的、用一個標識符名稱封裝到一塊兒的一個對象序列或基本類型數據序列。
定義方式:
int[] a; //建議使用這種 int a[]; //這樣也能夠, 可是不能指定數組的類型。`int a[3];` 這樣是不容許的
int[] a;
這樣只是定義了一個數組的引用,咱們可使用new
來建立一個數組,也能夠直接初始化數組:
int[] a = new int[3]; // 使用 new 來建立一個數組,這時真實數據會分配在堆中,因此默認值都爲「零」 int[] b = {1, 2, 3}; // 直接初始化一個長度爲3的數組 Integer[] c = new Integer[3]; // 建立一個對象數組,保存引用,這時初始值都爲 null Random rand = new Random(2); int len = rand.nextInt(20); int[] c = new int[len]; // 長度不必定要是一個字面值,能夠是變量
數組初始化的坑點:
InitialValues initialValues = new InitialValues(); initialValues.printInitialValues(); String[] stringArray = {"hello", "world"}; // initialValues.printStringArrary({"hello", "world"}); // 編譯錯誤 initialValues.printStringArrary(stringArray); initialValues.printStringArrary(new String[]{"hello", "world"}); //正確打開方式 int[] intArray = {1, 2, 3, 4}; // initialValues.printIntArray({1, 2, 3, 4}); // 編譯錯誤 initialValues.printIntArray(intArray); initialValues.printIntArray(new int[]{1, 2, 3, 4});
在方法中,用ClassName... ArgName
的形式能夠定義可變參數列表,在方法中,ArgName 本質上是一個數組。在可變列表中可使用任何類型,包括基礎類型。這裏傳入基本類型時,沒有依賴自動裝包和解包,這意味着,ClassName 爲 int 時,ArgName 是一個 int[],而不是 Integer。在重載方法時,應該只在一個方法中使用可變參數列表
static void printArray(Object... args) { for(Object arg: args) { System.out.print(arg + " "); } System.out.println(); } static void f(int required, int... args) { System.out.println("Required: " + required); for(int i: args) { System.out.print(i + " "); } System.out.println(); } public static void main(String[] args) { printArray(1, 2, 3, 4, 5); f(1); f(1, 2, 3); Integ }
toString()
顯示其名稱,ordinal()
表示聲明順序enum EnumDemo { HELLO, WORLD, }; EnumDemo e1 = EnumDemo.HELLO; System.out.println(e1); // 自動調用toString() System.out.println(e1.ordinal()); for(EnumDemo e: EnumDemo.values()) { System.out.println("EnumDemo: " + e + " ordinal " + e.ordinal()); } // HELLO // 0 // EnumDemo: HELLO ordinal 0 // EnumDemo: WORLD ordinal 1
本文首發於Code & Fun