匿名對象
格式:
匿名對象只可以調用一次成員 :
1. 調用一次成員變量 :?new? 類名(實參).成員變量名;
2.調用一次成員方法:??new? 類名(實參).成員方法名(實參);
匿名對象存在的必要:為了提高內存的使用性能,如果有一個變量(對象),在內存中再也不能使用了,那么這個對象很快就會被JMV標記成垃圾,被回收掉。
內部類
實例內部類
格式:
//外部類
class External{private int a1=1;private int a2=2;private static int a3=3;//實例內部類public class Inside{private int a1=1111;private int a4=4;private static final int a5=5;// 在實例內部類中聲明靜態常量//實例內部類對象必須在先有外部類對象前提下才能創建//實例內部類所處的位置與外部類成員位置相同,因此也受public、private等訪問限定符的約束public void way(){//在實例內部類方法中訪問同名的成員時,優先訪問自己的System.out.println(a1);//如果要訪問外部類同名的成員,必須:外部類名稱.this.同名成員 來訪問System.out.println(External.this.a1);//每個實例內部類都有一個指向其外部類實例的隱式引用。即 外部類名稱.this.外部類成員//換句話說,當在 way 中訪問外部類的成員時,它實際上是通過 外部類名稱.this 來引用外部類的實例//所以外部類中的任何成員都可以在實例內部類方法中直接訪問System.out.println(a2);System.out.println(a3);System.out.println(a4);System.out.println(a5);System.out.println("這是實例內部類");}}public void way(){//外部類中,不能直接訪問實例內部類中的成員,如果要訪問必須先要創建內部類的對象。Inside inside = new Inside();System.out.println(inside.a4);System.out.println("這是外部類");}
}
public class Test {public static void main(String[] args) {//獲取實例內部類對象//方法1External external = new External();//創建外部實例External.Inside inside1 = external.new Inside(); // 通過外部類實例創建內部類實例inside1.way();// 調用內部類的方法external.way();//方法2//通過new External() 來創建一個新的外部類實例,并通過 new Inside() 創建對應的內部類實例。External.Inside inside2 = new External().new Inside();// 在一行中創建外部類和內部類實例inside2.way();}
}
靜態內部類
靜態內部類的特點:
- 靜態修飾: 靜態內部類使用
static
關鍵字修飾,意味著它是外部類的一部分,但它的生命周期不依賴于外部類的實例。 - 可以訪問外部類的靜態成員: 靜態內部類可以訪問外部類的靜態變量和靜態方法,但無法直接訪問外部類的實例成員變量和方法。
- 實例化方式: 靜態內部類不依賴于外部類的實例,可以通過外部類名直接實例化,可以直接通過外部類來訪問。
- 可以獨立存在: 靜態內部類是一個獨立的類,可以像普通類一樣被實例化和使用。它不會像實例內部類一樣必須依賴外部類實例來創建。
靜態內部類的使用場景:
- 封裝: 當一些功能和外部類緊密相關,但又不需要依賴外部類的實例時,使用靜態內部類來封裝這些功能。
//外部類
class External{public int a1=1;public static int a2=2;public static void way1(){System.out.println(a2);}//靜態內部類:被static修飾的內部類成員static class Static{//可以訪問外部類的靜態變量public static int a3=3;public int a4=4;public void way(){//System.out.println(a1);//編譯失敗,因為a1不是靜態成員變量System.out.println(a2);//靜態內部類可以訪問外部類的靜態變量和靜態方法System.out.println(a3);System.out.println(a4);}}
}
public class Test {public static void main(String[] args) {//創建靜態內部類對象時,不需要先創建外部類對象External.Static st=new External.Static();st.way();}
}
局部內部類
局部內部類的特點:
- 定義位置: 局部內部類只能定義在外部類的 方法 或 構造函數 內部。
- 作用域: 僅在外部方法內部有效,方法結束后局部內部類實例將被銷毀,不能在外部方法之外被訪問或創建
- 訪問限制: 局部內部類只能訪問方法內的
final
或 有效的 final 局部變量(即那些在方法中沒有被修改過的變量),可以訪問外部類中的實例變量。 - 不可以使用訪問修飾符: 局部內部類不能使用
public
、private
等訪問修飾符,因為它的作用范圍僅限于方法內。 - 不能使用靜態修飾符: 因為局部內部類是方法的一部分,不能定義為靜態的。
局部內部類的使用場景:
- 當某些功能只在方法內部需要時,可以使用局部內部類來實現。
- 適用于需要封裝邏輯但只在某個方法內使用的類,避免在外部暴露不必要的復雜性。
//外部類
class External{public String a1="外部類中的實例成員變量";public final String a2="外部類中不能被修改的實例成員變量";public void display(){System.out.println("外部類的方法");}//外部類的方法public void meth(){String a3="方法中的局部變量";final String a4 = "方法中的局部常量";//局部內部類//局部內部類不能使用 public、private 等訪問修飾符,因為它的作用范圍僅限于方法內。class Inner{String a5="局部內部類中的局部變量";public void way(){String a6="局部內部類方法中的局部變量";//局部內部類可以訪問外部類中的實例變量System.out.println(External.this.a1);System.out.println(a1);System.out.println(a2);//局部內部類可以訪問外部類的方法display();//局部內部類可以訪問外部方法中的未被修改過的局部變量System.out.println(a3);System.out.println(a4);System.out.println(a5);System.out.println(a6);}}//局部內部類只在外部方法的作用域內有效,方法結束后將會被銷毀,不能在外部方法之外被訪問或創建//在meth()方法內部創建局部內部類實例Inner inner = new Inner();inner.way();//調用局部內部類的方法}
}
public class Test {public static void main(String[] args) {External external = new External();external.meth();//調用外部類中的方法,觸發局部內部類的創建}
}
匿名內部類
格式:
匿名類的調用:
方法1:可以直接調用匿名類中特有的方法
方法2:使用場景
匿名類通常作為一個參數傳遞給方法
匿名類的特點:
匿名類本質就是一個子類,匿名類可以作為接口實現或者類繼承的匿名子類。匿名類在定義時就會立即實例化,并且可以用于初始化變量。
代碼實例:
1. 不使用匿名方法
//創建接口
interface Animal{void run();
}
//創建實現接口的方法
class Dog implements Animal{//方法重寫public void run(){System.out.println("跑得快");}
}
public class Test {public static void main(String[] args){running(new Dog());}public static void running(Animal animal){//Animal animal = new Dog;向上轉型animal.run();}
}
2. 使用匿名方法
//創建接口
interface Animal{void run();
}
public class Test {public static void main(String[] args){String name ="小狗";//通過匿名類訪問局部變量,在JDK8版本之前,必須加final關鍵字,表示常量,即不能被修改new Animal(){//方法重寫public void run(){System.out.println(name+"跑得快");}}.run();}
}
//創建接口
interface Animal{void run();
}
public class Test {public static void main(String[] args){String name ="小狗";//通過匿名類訪問局部變量,在JDK8版本之前,必須加final關鍵字,表示常量,即不能被修改running(new Animal(){//方法重寫public void run(){System.out.println(name+"跑得快");}});}public static void running(Animal animal){animal.run();}
}
Object 類
Object
類是所有類的根類,位于 java.lang
包中。這意味著 Java 中的每個類都是 Object
類的子類,直接或間接地繼承了 Object
類的屬性和方法。
方法簽名 | 描述 |
---|---|
public String toString() | 返回該對象的字符串表示。如果子類沒有重寫該方法,則默認返回對象的類名、 |
public boolean equals(Object obj) | 指示某個其他對象是否與此對象相等。默認行為是僅在兩個對象相同(即引用相等)時返回?true 。 |
public int hashCode() | 返回該對象的十進制哈希碼值。默認哈希碼是對象的內存地址轉換成的整數。如果?equals() ?方法被重寫,則通常也需要重寫?hashCode() ?方法,以確保相等的對象有相同的哈希碼。 |
protected Object clone() | 創建并返回此對象的一個副本。默認實現拋出?CloneNotSupportedException ,因此子類需要重寫此方法以支持克隆操作,并實現?Cloneable ?接口 |
1. toString 方法
this.getClass():獲取一個類的字節碼 this.getClass().getName():獲取一個類的全限定名(包名+類名) this.hashCode():獲取一個對象的哈希碼值,是一個十進制數據 Integer.toHexString(十進制數據):把十進制數據轉換成十六進制數據
重寫toString方法
@Overridepublic String toString() {//Animal{name='',age= };return "Animal{" +"name='" + name + '\'' +", age=" + age +'}';}
結論:
如果一個類沒有重寫toString方法,那么打印此類的對象是地址值
如果一個類重寫了toString方法,那么打印此類的對象打印的是內容
2. equals 方法
"=="
1. 比較基本數據類型:比較數據值是否相等
2. 比較引用數據類型:比較地址是否相等
//Object 中的equals方法public boolean equals(Object obj) {//this : 調用此方法中的對象 a1//object : a2return (this == obj);
}
public class Test {public static void main(String[] args) {Animal a1=new Animal("小狗",6);Animal a2=new Animal("小狗",6);System.out.println(a1.equals(a2));//false}
}
重寫equals
@Overridepublic boolean equals(Object o) {//判斷地址是否相同,如果地址相同說明內容一定相同//提高代碼執行效率if (this == o) return true;//getClass():獲取一個類的字節碼對象,同一個類的字節碼對象是相等的//getClass() != o.getClass():保證比較對象的類型是一致的//o == null :如果a2為null,直接返回false,提高代碼的健壯性,不容易報錯//為什么不寫a1是否為null,因為a1為null,代碼在打印時直接報錯,不進入equals方法if (o == null || getClass() != o.getClass()) return false;//向下轉型,目的是為了調用子類特有的成員Animal animal = (Animal) o;//此equals為String類當中的equals,目的是比較字符串的內容return age == animal.age && Objects.equals(name, animal.name);}
結論:
如果一個類沒有重寫equals方法,那么比較對象比較的是地址值是否相等
如果一個類重寫了equals方法,那么比較對象比較的是內容是否相等
3. hashCode 方法
返回該對象的十進制哈希碼值。默認哈希碼是對象的內存地址或字符串或數字轉換成的整數
自定義類的對象哈希碼值默認返回對象的地址
在Object類中
public class Test {public static void main(String[] args) {Animal a1=new Animal("小狗",6);Animal a2=new Animal("小狗",6);System.out.println(a1.hashCode());System.out.println(a2.hashCode());}
}
重寫hashCode方法
@Overridepublic int hashCode() {return Objects.hash(name, age);}
結論:
如果一個類沒有重寫hashCode方法,那么內容相同的對象將返回不同的哈希碼值,即存儲在不同的位置
如果一個類重寫hashCode了方法,那么內容相同的對象將返回相同的哈希碼值,即存儲在相同的位置
但內容相同的對象在通過equals方法比較時,應該具有相同的哈希碼值,所以重寫 hashCode()
方法是為了確保內容相同的對象具有相同的哈希碼值,這樣它們在哈希表中可以被存儲在相同的位置,從而提高哈希表的性能和正確性。
4. clone 方法
1. 淺克隆
拷貝出的新對象,與原對象中的數據一模一樣(引用類型拷貝的只是地址)
import java.util.Arrays;
import java.util.Objects;//Cloneable是一個標記性接口:證明當前類是可以被克隆的class Animal extends Object implements Cloneable{public String name;public int age;public double[] quantity;public Animal(String name, int age, double[] quantity) {this.name = name;this.age = age;this.quantity = quantity;}@Overridepublic String toString() {return "Animal{" +"name='" + name + '\'' +", age=" + age +", quantity=" + Arrays.toString(quantity) +'}';}//淺拷貝@Overrideprotected Object clone() throws CloneNotSupportedException {//調用Object中的clone方法并返回return super.clone();}
}public class Test {public static void main(String[] args) throws CloneNotSupportedException {Animal a3=new Animal("小狗",5,new double[]{20,30,40});System.out.println(a3);System.out.println(a3.quantity);Animal a4=(Animal)a3.clone();System.out.println(a4);System.out.println(a4.quantity);}
}
2. 深克隆
對象中基本類型的數據直接拷貝
對象中的字符串拷貝的是地址
對象中包含的其他對象,不會拷貝地址,會創建新的對象
public Object clone() throws CloneNotSupportedException {Animal animal = (Animal) super.clone();//把對象中的數組單獨克隆一次animal.quantity=animal.quantity.clone();return animal;}
Comparable 接口
使用Arrays.sort
方法對 Animal
對象進行排序,需要元素實現 Comparable
接口或者傳遞一個 Comparator
實例。
1.?實現 Comparable
接口:在 Animal
類中定義排序規則,讓 Animal
類實現 Comparable<Animal>
,并在 compareTo
方法中定義排序規則,如按照 age
升序排序。
import java.lang.reflect.Array;
import java.util.Arrays;
import java.util.Comparator;class Animal implements Comparable<Animal> {public String name;public int age;public Animal(String name, int age) {this.name = name;this.age = age;}@Overridepublic String toString() {return "Animal{" +"name='" + name + '\'' +", age=" + age +'}';}//重寫compareTo方法@Overridepublic int compareTo(Animal o){return this.age-o.age;}
}
public class Test {public static void main(String[] args) {Animal a1=new Animal("大黃" ,5);Animal a2=new Animal("小黑",3);System.out.println(a1.compareTo(a2));//通過compareTo對a1和a2對象的age進行比較Animal[] animals=new Animal[]{new Animal("大黃",7),new Animal("小黑",5),new Animal("小胖",4),new Animal("小白",9)};Arrays.sort(animals);System.out.println(Arrays.toString(animals));}
}
我們發現comparable接口拓展性不強,只能通過age進行比較,對類的侵入性比較強
2. 使用 Comparator
接口:在調用 Arrays.sort
時傳入自定義的比較器。
//使用匿名方法Arrays.sort(animals,new Comparator<Animal>(){//重寫Comparator接口中的compare方法public int compare(Animal o1,Animal o2){return o1.age-o2.age;//按年齡升序排列}});System.out.println(Arrays.toString(animals));//使用匿名方法Arrays.sort(animals,new Comparator<Animal>(){//重寫Comparator接口中的compare方法public int compare(Animal o1,Animal o2){//因為String類實現了Comparable<string>接口//使用可以調用compareTo方法比較兩個字符串大小return o1.name.compareTo(o2.name);//按姓名升序排列}});System.out.println(Arrays.toString(animals));
兩種方法的區別
-
實現
Comparable
:- 優點:直接在類中定義了排序規則,適合需要多次排序的場景,代碼復用性高。
- 缺點:只能實現一種排序規則,無法靈活切換。
-
使用
Comparator
:- 優點:可以為不同場景定義不同的排序規則,靈活性更高。
- 缺點:每次排序都需要定義規則,代碼復用性較低