## static && final ### static :::success static關鍵字用於表示一個成員變數或者方法屬於類別本身,而不是類別的instance-level。這意味著,不論我們創建多少個類別的object,靜態變數都只有一份資料,所有objectobject共享該靜態變數。 ::: :::info static :一開始就畫記憶體位,全部只有一份(class level) non static:執行到才畫記憶體位址。 加上static後,只會在記憶體存在一份,所有的物件共用這一份。(class level ::: ```=java= public class MyClass { private int nonStaticVariable; public static void staticMethod() { System.out.println(nonStaticVariable); // 這行會導致編譯錯誤 } } ``` :::danger =>class level(static)不能存取instance level(non-static)的method =>instance level 可以存取class level (static)的method ::: :question: 當staticMethod被調用時,nonStaticVariable可能還未被初始化?為什麼說"可能”呢?有什麼情況會已被初始化了呢? ``` ans:在調用之前已經有該class的物件被new出來,並透過物件的屬性調用 ``` :::warning ### extra: Java 的變數依照其存活時間長短分成下列四種: **1. 類別變數 (Class Variables):** * 在class 宣告static的變數,存活時間最長, 編譯時即配置記憶體存放於static區 * 有預設值 **2. 實體變數(Instant Variables):** * 物件的屬性,沒有宣告static * 於物件建立時產生, 存放於 heap 區, 只要物件未消滅皆可存取. * 有預設值 **3. 方法變數 (Method Variables):** * method的參數、method內定義的變數 * 放在stack * 無預設值 * ==屬於local variables== **4. 區塊變數 (Local Variables):** * 即 if, switch, for, while, do-while 等流程控制語法大括弧內所定義之變數 * 放在stack,只能在該語法區塊內存取, ```=java for(int i =0...){ dosomething } for(int i =1...){ dosomething } ``` * 無預設值 * ==屬於local variables== :::: ### final :::success final關鍵字用於表示一個變數是read-only的,如果一個變數被final修飾,那麼它只能被賦值一次,賦值後其值就不能再被改變。 ::: ```=java public static void main(String[] args) { final int X=5; X=10; //error } ``` ::::success 如果一個方法被final修飾,那麼它不能被子類別重寫(能被子類別繼承)。 :::: ```=java class Animal{ final void makesound(){ System.out.println("Some generic sound"); } public void eat(){ System.out.println("eat"); } } class Dog extends Animal{ public void makesound(){ //error System.out.println("Dog sound"); } } ``` :::success 如果一個類別被final修飾,那麼它不能被繼承。 ::: ```=java final class Animal{ void makesound(){ System.out.println("Some generic sound"); } public void eat(){ System.out.println("eat"); } } class Dog extends Animal{ //error .... } ``` ## object(物件) ==java所有的物件都繼承自java.lang.Object (所有的class都繼承自java.lang.Object)== ### .equals() :::success 在Java中,如果想要比較兩個物件是否完全相等,我們需要覆寫或實作 equals() 方法,而非使用 == 運算符號。 ::: :::warning '= ='比較的是兩個物件的記憶體位值 '.equals()'比較的是兩個物件的內容 但,.equals()如果沒有經過覆寫,則會使用java.lang.Object(父類別)的.equals(),就跟'= ='一樣。 ::: ```=java= code ``` :::warning **tips:** 我們在String中可以直接使用.equals()比較內容是因為在String class中已經對.equals()覆寫了。 ![image](https://hackmd.io/_uploads/HyX-2wcZ0.png) ::: [來源](https://blog.csdn.net/wwwwwwwl/article/details/75187869?utm_medium=distribute.pc_relevant.none-task-blog-2~default~baidujs_baidulandingword~default-1-75187869-blog-110070231.235^v43^pc_blog_bottom_relevance_base8&spm=1001.2101.3001.4242.2&utm_relevant_index=4) ### getclass() ::::success 這個方法用來取得該物件的 Class 物件,該 Class 物件代表了該物件的實際類別(class) :::: ```=java= public static void main(String[] args) { String str = "Hello, world!"; Class<?> strClass = str.getClass(); System.out.println("The class of str is " + strClass.getName()); } //Class<?> 表示的是一個未知類型的 Class object。 ``` ``` output: The class of str is java.lang.String ``` ### clone() :::success 在Java中,Object類別提供了一個clone()方法,可以用來創建並返回該物件的一個副本。這個方法預設提供的是shallow copy,稱之為淺層拷貝 ::: #### shallow 如果是一個物件內的元素使用.clone()的話,則會複製該物件內元素的reference(ref 也是一種value) ``` review:在一個物件的屬性 String name="zhang"; //'name'為'zhang'這個物件的reference ``` 會被複製的便是'name',但兩個name都指向同個物件"zhang" 物件"zhang"並沒有被複製。 ![image](https://hackmd.io/_uploads/r1DsgxnZA.png) [來源](https://blog.csdn.net/weixin_34334744/article/details/94113476?spm=1001.2101.3001.6650.2&utm_medium=distribute.pc_relevant.none-task-blog-2%7Edefault%7ECTRLIST%7ERate-2-94113476-blog-107779427.235%5Ev43%5Epc_blog_bottom_relevance_base8&depth_1-utm_source=distribute.pc_relevant.none-task-blog-2%7Edefault%7ECTRLIST%7ERate-2-94113476-blog-107779427.235%5Ev43%5Epc_blog_bottom_relevance_base8&utm_relevant_index=5) ```=java= public class ShallowCopyExample { public static void main(String[] args) { // 建立原始二維陣列 int[][] original = {{1, 2, 3}, {4, 5, 6}}; // 創建原始陣列的淺拷貝 int[][] copied = original.clone(); // 輸出原始陣列和複製陣列的第一個元素 System.out.println("Before modification:"); System.out.println("original[0][0] = " + original[0][0]); System.out.println("copied[0][0] = " + copied[0][0]); // 修改複製陣列的第一個元素 copied[0][0] = 100; // 再次輸出原始陣列和複製陣列的第一個元素 System.out.println("After modification:"); System.out.println("original[0][0] = " + original[0][0]); System.out.println("copied[0][0] = " + copied[0][0]); } } ``` ``` output: Before modification: original[0][0] = 1 copied[0][0] = 1 After modification: original[0][0] = 100 copied[0][0] = 100 ``` :::info 'original' 跟'copied' 都指向{{1,2,3},{4,5,6}}這個二維陣列物件(同個address) 那從original去修改跟從copied去修改,都會造成共同的物件改變。 ::: :::danger shallow並沒有new新的物件出來 再說一次, String name="zhang"; int[][] original = {{1, 2, 3}, {4, 5, 6}}; 等號右邊才是物件,左邊為ref ::: #### deep copy 如果要連reference指向的物件都複製一份,稱為deep copy(深層拷貝) 要實現deep copy,就需要: * 覆寫某個class 繼承自Object的.clone()方法 * 或是不覆寫,手動創立一個新物件 ==共同點都是有新物件產生!!!== ![image](https://hackmd.io/_uploads/rJkbzlnZA.png) ```=java= public class MyClass implements Cloneable { private int[] array; public MyClass(int[] array) { this.array = array; } // Getter for array public int[] getArray() { return array; } @Override public MyClass clone() { int[] clonedArray = array.clone(); // This line creates a copy of the array. It is a primitive-type array and clone() can returned a array like deep-copy return new MyClass(clonedArray); // new了一個新的物件,屬性相同,reference不同 } } ``` ```=java= public class DeepCopyExample { public static void main(String[] args) { // 建立原始二維陣列 int[][] original = {{1, 2, 3}, {4, 5, 6}}; // 創建原始陣列的深層拷貝 int[][] copied = new int[original.length][]; for (int i = 0; i < original.length; i++) { copied[i] = original[i].clone(); } // 輸出原始陣列和複製陣列的第一個元素 System.out.println("Before modification:"); System.out.println("original[0][0] = " + original[0][0]); System.out.println("copied[0][0] = " + copied[0][0]); // 修改複製陣列的第一個元素 copied[0][0] = 100; // 再次輸出原始陣列和複製陣列的第一個元素 System.out.println("After modification:"); System.out.println("original[0][0] = " + original[0][0]); System.out.println("copied[0][0] = " + copied[0][0]); } } ``` ``` output: Before modification: original[0][0] = 1 copied[0][0] = 1 After modification: original[0][0] = 1 copied[0][0] = 100 ``` :::info 因為兩個是分開的物件,所以改變不會互相影響。 ::: --- 如果一個物件的元素是primitive type,則.clone()會複製它們的值並產生新物件。 ``` int array[]={2,3,4}; int array2[]=array.clone(); System.out.println(array==array2);//false ``` :::success <aside> 在Java中,物件元素是primitive type的clone()操作,會進行deep copy(深層拷貝),創建一個新的物件並複製所有的元素。 然而,對於Object[]陣列的clone(),則是shallow copy(淺層拷貝)。這是因為Object[]陣列的元素是reference型態,clone僅僅會複製這些reference,而不會複製reference所指向的物件。因此,原陣列和複製後的陣列中的元素會指向同一個物件。 </aside> ::: :::danger 在使用.clone()的類別需要implements Cloneable: public class MyClass implements Cloneable {} 否則會有CloneNotSupportedException Cloneable介面在Java中是一種標記性介面(marker interface),它不包含任何方法,只是表明這個class的object可以被clone. ::: :question: 在Java中,如果一個method被定義為private,請問它還可以被子類別override嗎 ``` ans:不行,private在只能在同一個class存取,子類別無法override or inherit ``` :question: 那如果在子類別中定義了一樣的method name (argument也一樣),會怎麼樣呢? ``` ans:不會報錯,會視作子類別的新方法,看new的物件是哪個class,就用哪邊的方法。 ```