前述:利用一段較為充足暑假時間,對以前的Java學習進行一個系統性的回顧,對于部分知識點進行記錄和積累,本部分主要記錄了Java中各種概念和細節基礎,作為筆記,便于理解、學習和記憶,
Java基礎筆記
一 區域變數和成員變數:
- 定義位置不一樣【重點】
- 區域變數:方法的內部
- 成員變數:在方法的外部,直接寫在類當中
- 作用范圍不一樣【重點】
- 區域變數:只有方法當中才可以使用,出了方法就不能再用
- 成員變數:整個類全都可以通用,
- 默認值不一樣【重點】
- 區域變數:沒有默認值,如果要想使用,必須手動進行賦值
- 成員變數:如果沒有賦值,會有默認值,規則和陣列一樣
- 記憶體的位置不一樣(了解)
- 區域變數:位于堆疊記憶體
- 成員變數:位于堆記憶體
- 生命周期不一樣(了解)
- 區域變數:隨著方法進堆疊而誕生,隨著方法出堆疊而消失
- 成員變數:隨著物件創建而誕生,隨著物件被垃圾回收而消失
- 注意:
- 當方法的區域變數和類的成員變數重名的時候,根據"就近原則",優先使用區域變數,
- 如果需要訪問本類當中的成員變數,需要使用格式:this.成員變數名,通過誰呼叫的方法,誰就是this,
1 public class Demo01VariableDifference { 2 3 String name; // 成員變數 4 5 public void methodA() { 6 int num = 20; // 區域變數 7 System.out.println(num); 8 System.out.println(name); 9 } 10 11 public void methodB(int param) { // 方法的引數就是區域變數 12 // 引數在方法呼叫的時候,必然會被賦值的, 13 System.out.println(param); 14 15 int age; // 區域變數 16 // System.out.println(age); // 沒賦值不能用 17 18 // System.out.println(num); // 錯誤寫法! 19 System.out.println(name); 20 } 21 22 }
二 面向物件的三大特征:
封裝、繼承和多型
- 封裝:封裝性就是將一些細節資訊隱藏起來,對外界不可見
- 方法就是一種封裝
- 關鍵字private也是一種封裝
- 一旦使用了private進行修飾,那么本類當中仍然可以隨意訪問,但是超出了本類范圍之外就不能再直接訪問了,
- 間接訪問private成員變數,就是定義一對兒Getter/Setter方法
三 構造方法:
構造方法是專門用來創建物件的方法,當我們通過關鍵字new來創建物件時,其實就是在呼叫構造方法,
- 注意事項:
- 構造方法的名稱必須和所在的類名稱完全一樣,就連大小寫也要一樣
- 構造方法不要寫回傳值型別,連void都不寫
- 構造方法不能return一個具體的回傳值
- 如果沒有撰寫任何構造方法,那么編譯器將會默認贈送一個構造方法,沒有引數、方法體什么事情都不做,
- 一旦撰寫了至少一個構造方法,那么編譯器將不再贈送,
- 構造方法也是可以進行多載(方法名稱相同,引數串列不同)的,
1 public class Student { 2 3 // 成員變數 4 private String name; 5 private int age; 6 7 // 無引數的構造方法 8 public Student() { 9 } 10 11 // 全引數的構造方法 12 public Student(String name, int age) { 13 this.name = name; 14 this.age = age; 15 } 16 }
四 JavaBean:
一個標準的類又稱為JavaBean,通常由四部分組成
- 所有的成員變數都要使用private關鍵字修飾
- 為每一個成員變數撰寫一對兒Getter/Setter方法
- 撰寫一個無引數的構造方法
- 撰寫一個全引數的構造方法
五 匿名物件:
匿名物件就是只有右邊的物件,沒有左邊的名字和賦值運算子
- 注意事項:匿名物件只能使用唯一的一次,下次再用不得不再創建一個新物件,
- 使用建議:如果確定有一個物件只需要使用唯一的一次,就可以用匿名物件,
1 // 匿名物件 2 new Person().name = "張磊"; 3 4 // 普通使用方式 5 Scanner sc = new Scanner(System.in); 6 int num = sc.nextInt(); 7 8 // 匿名物件的方式 9 int num = new Scanner(System.in).nextInt(); 10 System.out.println("輸入的是:" + num); 11 12 // 使用一般寫法傳入引數 13 Scanner sc = new Scanner(System.in); 14 methodParam(sc); 15 16 // 使用匿名物件來進行傳參 17 methodParam(new Scanner(System.in));
六 ArrayList類:
ArrayList中常用的方法
- public boolean add(E e):向集合當中添加元素,引數的型別和泛型一致,回傳值代表添加是否成功,
- 備注:對于ArrayList集合來說,add添加動作一定是成功的,所以回傳值可用可不用,但是對于其他集合(今后學習)來說,add添加動作不一定成功,
- public E get(int index):從集合當中獲取元素,引數是索引編號,回傳值就是對應位置的元素,
- public E remove(int index):從集合當中洗掉元素,引數是索引編號,回傳值就是被洗掉掉的元素,
- public int size():獲取集合的尺寸長度,回傳值是集合中包含的元素個數,
向集合ArrayList當中存盤基本型別資料,必須使用基本型別對應的“包裝類”
| 基本型別 | 包裝類(參考型別,包裝類都位于java.lang包下) |
| byte | Byte |
| short | Short |
| int | Integer 【特殊】 |
| long | Long |
| float | Float |
| double | Double |
| char | Character 【特殊】 |
| boolean | Boolean |
注意事項:
- ArrayList集合的長度可以隨意變化,與陣列不同
- 對于ArrayList來說,有一個尖括號<E>代表泛型,
- 泛型:也就是裝在集合當中的所有元素,全都是統一的什么型別,
- 注意:泛型只能是參考型別,不能是基本型別,
- 對于ArrayList集合來說,直接列印得到的不是地址值,而是內容,如果內容是空,得到的是空的中括號:[]
七 String類:
字串的特點:
- 字串的內容永不可變,【重點】
- 正是因為字串不可改變,所以字串是可以共享使用的,
- 字串效果上相當于是char[]字符陣列,但是底層原理是byte[]位元組陣列,
創建字串的常見3+1種方式
- 三種構造方法:
- public String():創建一個空白字串,不含有任何內容,
- public String(char[] array):根據字符陣列的內容,來創建對應的字串,
- public String(byte[] array):根據位元組陣列的內容,來創建對應的字串,
- 一種直接創建:
- String str = "Hello"; // 右邊直接用雙引號
1 public class Demo01String { 2 3 public static void main(String[] args) { 4 // 使用空參構造 5 String str1 = new String(); // 小括號留空,說明字串什么內容都沒有, 6 System.out.println("第1個字串:" + str1); 7 8 // 根據字符陣列創建字串 9 char[] charArray = { 'A', 'B', 'C' }; 10 String str2 = new String(charArray); 11 System.out.println("第2個字串:" + str2); 12 13 // 根據位元組陣列創建字串 14 byte[] byteArray = { 97, 98, 99 }; 15 String str3 = new String(byteArray); 16 System.out.println("第3個字串:" + str3); 17 18 // 直接創建 19 String str4 = "Hello"; 20 System.out.println("第4個字串:" + str4); 21 } 22 23 }
String當中與轉換相關的常用方法
- public char[] toCharArray():將當前字串拆分成為字符陣列作為回傳值,
- public byte[] getBytes():獲得當前字串底層的位元組陣列,
- public String replace(CharSequence oldString, CharSequence newString):將所有出現的老字串替換成為新的字串,回傳替換之后的結果新字串,
1 public class Demo04StringConvert { 2 3 public static void main(String[] args) { 4 // 轉換成為字符陣列 5 char[] chars = "Hello".toCharArray(); 6 System.out.println(chars[0]); // H 7 System.out.println(chars.length); // 5 8 System.out.println("=============="); 9 10 // 轉換成為位元組陣列 11 byte[] bytes = "abc".getBytes(); 12 for (int i = 0; i < bytes.length; i++) { 13 System.out.println(bytes[i]); 14 } 15 System.out.println("=============="); 16 17 // 字串的內容替換 18 String str1 = "How do you do?"; 19 String str2 = str1.replace("o", "*"); 20 System.out.println(str1); // How do you do? 21 System.out.println(str2); // H*w d* y*u d*? 22 System.out.println("=============="); 23 24 } 25 26 }
八 Static關鍵字:
Static修飾成員變數與方法
- 如果一個成員變數使用了static關鍵字,那么這個變數不再屬于物件自己,而是屬于所在的類,多個物件共享同一份資料
- 一旦使用static修飾成員方法,那么這就成為了靜態方法,靜態方法不屬于物件,而是屬于類的,
- 如果沒有static關鍵字,那么必須首先創建物件,然后通過物件才能使用它,
- 如果有了static關鍵字,那么不需要創建物件,直接就能通過類名稱來使用它,
- 無論是成員變數,還是成員方法,如果有了static,都推薦使用類名稱進行呼叫,
- 靜態變數:類名稱.靜態變數
- 靜態方法:類名稱.靜態方法()
- 注意事項:
- 靜態不能直接訪問非靜態,
因為在記憶體當中是【先】有的靜態內容,【后】有的非靜態內容,
“先人不知道后人,但是后人知道先人,” - 靜態方法當中不能用this,
this代表當前物件,通過誰呼叫的方法,誰就是當前物件,
- 靜態不能直接訪問非靜態,
靜態代碼塊
- 格式:
1 public class 類名稱 { 2 static { 3 // 靜態代碼塊的內容 4 } 5 }
- 特點:
- 當第一次用到本類時,靜態代碼塊執行唯一的一次,
- 靜態內容總是優先于非靜態,所以靜態代碼塊比構造方法先執行,
- 靜態代碼塊的典型用途:用來一次性地對靜態成員變數進行賦值,
九 Arrays類:
java.util.Arrays是一個與陣列相關的工具類,里面提供了大量靜態方法,用來實作陣列常見的操作
- public static String toString(陣列):將引數陣列變成字串(按照默認格式:[元素1, 元素2, 元素3...])
- public static void sort(陣列):按照默認升序(從小到大)對陣列的元素進行排序,
- 備注:
- 如果是數值,sort默認按照升序從小到大
- 如果是字串,sort默認按照字母升序
- 如果是自定義的型別,那么這個自定義的類需要有Comparable或者Comparator介面的支持,
十 繼承:
繼承的基本概念圖

在繼承的關系中,“子類就是一個父類”,也就是說,子類可以被當做父類看待
1 定義父類的格式:(一個普通的類定義) 2 public class 父類名稱 { 3 // ... 4 } 5 6 定義子類的格式: 7 public class 子類名稱 extends 父類名稱 { 8 // ... 9 }
在父子類的繼承關系當中,如果成員變數重名,則創建子類物件時,訪問有兩種方式
- 直接通過子類物件訪問成員變數:等號左邊是誰,就優先用誰,沒有則向上找,
- 間接通過成員方法訪問成員變數:該方法屬于誰,就優先用誰,沒有則向上找,
1 public class Demo01ExtendsField { 2 3 public static void main(String[] args) { 4 Fu fu = new Fu(); // 創建父類物件 5 System.out.println(fu.numFu); // 只能使用父類的東西,沒有任何子類內容 6 System.out.println("==========="); 7 8 Zi zi = new Zi(); 9 10 System.out.println(zi.numFu); // 10 11 System.out.println(zi.numZi); // 20 12 System.out.println("==========="); 13 14 // 等號左邊是誰,就優先用誰 15 System.out.println(zi.num); // 優先子類,200 16 // System.out.println(zi.abc); // 到處都沒有,編譯報錯! 17 System.out.println("==========="); 18 19 // 這個方法是子類的,優先用子類的,沒有再向上找 20 zi.methodZi(); // 200 21 // 這個方法是在父類當中定義的, 22 zi.methodFu(); // 100 23 } 24 25 }
1 public class Fu { 2 3 int numFu = 10; 4 5 int num = 100; 6 7 public void methodFu() { 8 // 使用的是本類當中的,不會向下找子類的 9 System.out.println(num); 10 } 11 12 }
1 public class Zi extends Fu { 2 3 int numZi = 20; 4 5 int num = 200; 6 7 public void methodZi() { 8 // 因為本類當中有num,所以這里用的是本類的num 9 System.out.println(num); 10 } 11 }
各類變數的訪問方式
- 區域變數: 直接寫成員變數名
- 本類的成員變數: this.成員變數名
- 父類的成員變數: super.成員變數名
在父子類的繼承關系當中,創建子類物件,訪問成員方法的規則
- 創建的物件是誰,就優先用誰,如果沒有則向上找,
- 無論是成員方法還是成員變數,如果沒有都是向上找父類,絕對不會向下找子類的
方法覆寫重寫
- 必須保證父子類之間方法的名稱相同,引數串列也相同,
- @Override:寫在方法前面,用來檢測是不是有效的正確覆寫重寫,這個注解就算不寫,只要滿足要求,也是正確的方法覆寫重寫,
- 子類方法的回傳值必須【小于等于】父類方法的回傳值范圍,
- 小擴展提示:java.lang.Object類是所有類的公共最高父類(祖宗類),java.lang.String就是Object的子類,
- 子類方法的權限必須【大于等于】父類方法的權限修飾符,
- 小擴展提示:public > protected > (default) > private
- 備注:(default)不是關鍵字default,而是什么都不寫,留空,、
- 方法的覆寫重寫特點:創建的是子類物件,則優先用子類方法
- 與多載的區別:
- 重寫(Override) :方法的名稱一樣,引數串列【也一樣】,覆寫、覆寫,
- 多載(Overload):方法的名稱一樣,引數串列【不一樣】,
方法覆寫重寫的應用場景

繼承關系中,父子類構造方法的訪問特點
- 子類構造方法當中有一個默認隱含的“super()”呼叫,所以一定是先呼叫的父類構造,后執行的子類構造,
- 子類構造可以通過super關鍵字來呼叫父類多載構造,
- super的父類構造呼叫,必須是子類構造方法的第一個陳述句,不能一個子類構造呼叫多次super構造,
- 總結:子類必須呼叫父類構造方法,不寫則贈送super();寫了則用寫的指定的super呼叫,super只能有一個,還必須是第一個,
1 public class Zi extends Fu { 2 3 public Zi() { 4 super(); // 在呼叫父類無參構造方法 5 // super(20); // 在呼叫父類多載的構造方法 6 System.out.println("子類構造方法!"); 7 } 8 9 public void method() { 10 // super(); // 錯誤寫法!只有子類構造方法,才能呼叫父類構造方法, 11 } 12 13 }
Java繼承的三個特點

十一 super&this:
1、在子類的成員方法中,訪問父類的成員變數, 2、在子類的成員方法中,訪問父類的成員方法, 3、在子類的構造方法中,訪問父類的構造方法,super關鍵字的用法有三種(用于訪問父類的相關內容)
1 public class Zi extends Fu { 2 3 int num = 20; 4 5 public Zi() { 6 super(); 7 } 8 9 public void methodZi() { 10 System.out.println(super.num); // 父類中的num 11 } 12 13 public void method() { 14 super.method(); // 訪問父類中的method 15 System.out.println("子類方法"); 16 } 17 18 }
1 public class Fu { 2 3 int num = 10; 4 5 public void method() { 6 System.out.println("父類方法"); 7 } 8 9 }
1、在本類的成員方法中,訪問本類的成員變數, 2、在本類的成員方法中,訪問本類的另一個成員方法, 3、在本類的構造方法中,訪問本類的另一個構造方法, 在第三種用法當中要注意: this(...)呼叫也必須是構造方法的第一個陳述句,唯一一個, super和this兩種構造呼叫,不能同時使用,this關鍵字的用法有三種(用來訪問本類內容)
1 public class Zi extends Fu { 2 3 int num = 20; 4 5 public Zi() { 6 // super(); // 這一行不再贈送 7 this(123); // 本類的無參構造,呼叫本類的有參構造 8 // this(1, 2); // 錯誤寫法! 9 } 10 11 public Zi(int n) { 12 this(1, 2); 13 } 14 15 public Zi(int n, int m) { 16 } 17 18 public void showNum() { 19 int num = 10; 20 System.out.println(num); // 區域變數 21 System.out.println(this.num); // 本類中的成員變數 22 System.out.println(super.num); // 父類中的成員變數 23 } 24 25 public void methodA() { 26 System.out.println("AAA"); 27 } 28 29 public void methodB() { 30 this.methodA(); 31 System.out.println("BBB"); 32 } 33 34 }
1 public class Fu { 2 3 int num = 30; 4 5 }
十二 抽象類:
相關概念
- 抽象方法:就是加上abstract關鍵字,然后去掉大括號,直接分號結束,
- 抽象類:抽象方法所在的類,必須是抽象類才行,在class之前寫上abstract即可,

抽象類與抽象方法的關系
- 一個抽象類不一定含有抽象方法
- 要保證抽象方法所在的類是抽象類
- 沒有抽象方法的抽象類,也不能直接創建物件,在一些特殊場景下有用途
1 public abstract class MyAbstract {
2 }
如何使用抽象類和抽象方法
- 不能直接創建new抽象類物件,
- 必須用一個子類來繼承抽象父類,
- 子類必須覆寫重寫抽象父類當中所有的抽象方法,
- 覆寫重寫(實作):子類去掉抽象方法的abstract關鍵字,然后補上方法體大括號,
- 創建子類物件進行使用
1 public abstract class Animal { 2 3 // 這是一個抽象方法,代表吃東西,但是具體吃什么(大括號的內容)不確定, 4 public abstract void eat(); 5 6 // 這是普通的成員方法 7 // public void normalMethod() { 8 // 9 // } 10 11 }
1 public class Cat extends Animal { 2 3 @Override 4 public void eat() { 5 System.out.println("貓吃魚"); 6 } 7 8 }
public class DemoMain { public static void main(String[] args) { // Animal animal = new Animal(); // 錯誤寫法!不能直接創建抽象類物件 Cat cat = new Cat(); cat.eat(); } }
十三 介面:
介面就是多個類的公共規范,介面是一種參考資料型別,最重要的內容就是其中的:抽象方法,
- 備注:換成了關鍵字interface之后,編譯生成的位元組碼檔案仍然是:.java --> .class
- 介面的格式:
1 public interface 介面名稱 { 2 // 介面內容 3 }
1、介面不能直接使用,必須有一個“實作類”來“實作”該介面,介面使用步驟:
1 public class 實作類名稱 implements 介面名稱 { 2 // ... 3 }2、介面的實作類必須覆寫重寫(實作)介面中所有的抽象方法,實作:去掉abstract關鍵字,加上方法體大括號, 3、創建實作類的物件,進行使用, 注意事項:如果實作類并沒有覆寫重寫介面中所有的抽象方法,那么這個實作類自己就必須是抽象類,
1 public class Demo01Interface { 2 3 public static void main(String[] args) { 4 // 錯誤寫法!不能直接new介面物件使用, 5 // MyInterfaceAbstract inter = new MyInterfaceAbstract(); 6 7 // 創建實作類的物件使用 8 MyInterfaceAbstractImpl impl = new MyInterfaceAbstractImpl(); 9 impl.methodAbs1(); 10 impl.methodAbs2(); 11 } 12 13 }
在任何版本的Java中,介面都能定義抽象方法
- 格式:
public abstract 回傳值型別 方法名稱(引數串列);
- 注意事項:
- 介面當中的抽象方法,修飾符必須是兩個固定的關鍵字:public abstract
- 這兩個關鍵字修飾符,可以選擇性地省略,(今天剛學,所以不推薦,)
- 方法的三要素,可以隨意定義,
1 public interface MyInterfaceAbstract { 2 3 // 這是一個抽象方法 4 public abstract void methodAbs1(); 5 6 // 這也是抽象方法 7 abstract void methodAbs2(); 8 9 // 這也是抽象方法 10 public void methodAbs3(); 11 12 // 這也是抽象方法 13 void methodAbs4(); 14 15 }
1 public class MyInterfaceAbstractImpl implements MyInterfaceAbstract { 2 @Override 3 public void methodAbs1() { 4 System.out.println("這是第一個方法!"); 5 } 6 7 @Override 8 public void methodAbs2() { 9 System.out.println("這是第二個方法!"); 10 } 11 12 @Override 13 public void methodAbs3() { 14 System.out.println("這是第三個方法!"); 15 } 16 17 @Override 18 public void methodAbs4() { 19 System.out.println("這是第四個方法!"); 20 } 21 }
介面當中也可以定義“成員變數”,但是必須使用public static final三個關鍵字進行修飾(從效果上看,這其實就是介面的【常量】)
- 格式:public static final 資料型別 常量名稱 = 資料值;
- 備注:一旦使用final關鍵字進行修飾,說明不可改變,
- 注意事項:
- 介面當中的常量,可以省略public static final,注意:不寫也照樣是這樣,
- 介面當中的常量,必須進行賦值;不能不賦值,
- 介面中常量的名稱,使用完全大寫的字母,用下劃線進行分隔,(推薦命名規則)
1 public interface MyInterfaceConst { 2 3 // 這其實就是一個常量,一旦賦值,不可以修改 4 public static final int NUM_OF_MY_CLASS = 12; 5 6 }
1 public class Demo05Interface { 2 3 public static void main(String[] args) { 4 // 訪問介面當中的常量 5 System.out.println(MyInterfaceConst.NUM_OF_MY_CLASS); 6 } 7 8 }
從Java 8開始,介面里允許定義默認方法
- 格式:
1 public default 回傳值型別 方法名稱(引數串列) { 2 方法體 3 }
- 備注:介面當中的默認方法,可以解決介面升級的問題
- 使用方法:
- 介面的默認方法,可以通過介面實作類物件,直接呼叫,
- 介面的默認方法,也可以被介面實作類進行覆寫重寫,
1 public class Demo02Interface { 2 3 public static void main(String[] args) { 4 // 創建了實作類物件 5 MyInterfaceDefaultA a = new MyInterfaceDefaultA(); 6 a.methodAbs(); // 呼叫抽象方法,實際運行的是右側實作類, 7 8 // 呼叫默認方法,如果實作類當中沒有,會向上找介面 9 a.methodDefault(); // 這是新添加的默認方法 10 System.out.println("=========="); 11 12 MyInterfaceDefaultB b = new MyInterfaceDefaultB(); 13 b.methodAbs(); 14 b.methodDefault(); // 實作類B覆寫重寫了介面的默認方法 15 } 16 17 }
1 public interface MyInterfaceDefault { 2 3 // 抽象方法 4 public abstract void methodAbs(); 5 6 // 新添加了一個抽象方法 7 // public abstract void methodAbs2(); 8 9 // 新添加的方法,改成默認方法 10 public default void methodDefault() { 11 System.out.println("這是新添加的默認方法"); 12 } 13 14 }
1 public class MyInterfaceDefaultA implements MyInterfaceDefault { 2 @Override 3 public void methodAbs() { 4 System.out.println("實作了抽象方法,AAA"); 5 } 6 }
1 public class MyInterfaceDefaultB implements MyInterfaceDefault { 2 @Override 3 public void methodAbs() { 4 System.out.println("實作了抽象方法,BBB"); 5 } 6 7 @Override 8 public void methodDefault() { 9 System.out.println("實作類B覆寫重寫了介面的默認方法"); 10 } 11 }
從Java 8開始,介面當中允許定義靜態方法
- 格式:
1 public static 回傳值型別 方法名稱(引數串列) { 2 方法體 3 } 4 // 提示:就是將abstract或者default換成static即可,帶上方法體,
- 注意事項:
- 不能通過介面實作類的物件來呼叫介面當中的靜態方法,
- 正確用法:通過介面名稱,直接呼叫其中的靜態方法,
- 格式:介面名稱.靜態方法名(引數);
- 代碼示例:
1 public class Demo03Interface { 2 3 public static void main(String[] args) { 4 // 創建了實作類物件 5 MyInterfaceStaticImpl impl = new MyInterfaceStaticImpl(); 6 7 // 錯誤寫法! 8 // impl.methodStatic(); 9 10 // 直接通過介面名稱呼叫靜態方法 11 MyInterfaceStatic.methodStatic(); 12 } 13 14 }
1 public interface MyInterfaceStatic { 2 3 public static void methodStatic() { 4 System.out.println("這是介面的靜態方法!"); 5 } 6 7 }
1 public class MyInterfaceStaticImpl implements MyInterfaceStatic { 2 }
從Java 9開始,介面當中允許定義私有方法
- 問題描述:我們需要抽取一個共有方法,用來解決兩個默認方法之間重復代碼的問題,但是這個共有方法不應該讓實作類使用,應該是私有化的,
- 解決方案:從Java 9開始,介面當中允許定義私有方法,
- 普通私有方法:解決多個默認方法之間重復代碼問題,只有默認方法可以呼叫,
1 private 回傳值型別 方法名稱(引數串列) { 2 方法體 3 }
- 靜態私有方法:解決多個靜態方法之間重復代碼問題,默認方法和靜態方法可以呼叫,
1 private static 回傳值型別 方法名稱(引數串列) { 2 方法體 3 }
- 代碼示例:
1 public class Demo04Interface { 2 3 public static void main(String[] args) { 4 MyInterfacePrivateB.methodStatic1(); 5 MyInterfacePrivateB.methodStatic2(); 6 // 錯誤寫法! 7 // MyInterfacePrivateB.methodStaticCommon(); 8 } 9 10 }
1 public interface MyInterfacePrivateA { 2 3 public default void methodDefault1() { 4 System.out.println("默認方法1"); 5 methodCommon(); 6 } 7 8 public default void methodDefault2() { 9 System.out.println("默認方法2"); 10 methodCommon(); 11 } 12 13 private void methodCommon() { 14 System.out.println("AAA"); 15 System.out.println("BBB"); 16 System.out.println("CCC"); 17 } 18 19 }
public class MyInterfacePrivateAImpl implements MyInterfacePrivateA { public void methodAnother() { // 直接訪問到了介面中的默認方法,這樣是錯誤的! // methodCommon(); } }
1 public interface MyInterfacePrivateB { 2 3 public static void methodStatic1() { 4 System.out.println("靜態方法1"); 5 methodStaticCommon(); 6 } 7 8 public static void methodStatic2() { 9 System.out.println("靜態方法2"); 10 methodStaticCommon(); 11 } 12 13 private static void methodStaticCommon() { 14 System.out.println("AAA"); 15 System.out.println("BBB"); 16 System.out.println("CCC"); 17 } 18 19 }
1、成員變數其實是常量 格式:總結:在Java 9+版本中,介面的內容可以有
[public] [static] [final] 資料型別 常量名稱 = 資料值;
注意:
常量必須進行賦值,而且一旦賦值不能改變, 常量名稱完全大寫,用下劃線進行分隔, 2、介面中最重要的就是抽象方法 格式:
[public] [abstract] 回傳值型別 方法名稱(引數串列);
注意: 實作類必須覆寫重寫介面所有的抽象方法,除非實作類是抽象類, 3、從Java 8開始,介面里允許定義默認方法 格式:
[public] default 回傳值型別 方法名稱(引數串列) { 方法體 }
注意: 默認方法也可以被覆寫重寫 4、從Java 8開始,介面里允許定義靜態方法 格式:
[public] static 回傳值型別 方法名稱(引數串列) { 方法體 }
注意: 應該通過介面名稱進行呼叫,不能通過實作類物件呼叫介面靜態方法 5、從Java 9開始,介面里允許定義私有很乏 格式:
普通私有方法:private 回傳值型別 方法名稱(引數串列) { 方法體 }
靜態私有方法:private static 回傳值型別 方法名稱(引數串列) { 方法體 }
注意: private的方法只有介面自己才能呼叫,不能被實作類或別人使用,
1、介面是沒有靜態代碼塊或者構造方法的, 2、一個類的直接父類是唯一的,但是一個類可以同時實作多個介面,使用介面的時候,需要注意
1 public class MyInterfaceImpl implements MyInterfaceA, MyInterfaceB { 2 // 覆寫重寫所有抽象方法 3 }3、如果實作類所實作的多個介面當中,存在重復的抽象方法,那么只需要覆寫重寫一次即可, 4、如果實作類沒有覆寫重寫所有介面當中的所有抽象方法,那么實作類就必須是一個抽象類, 5、如果實作類鎖實作的多個介面當中,存在重復的默認方法,那么實作類一定要對沖突的默認方法進行覆寫重寫, 6、一個類如果直接父類當中的方法,和介面當中的默認方法產生了沖突,優先用父類當中的方法,
1 public class Demo01Interface { 2 3 public static void main(String[] args) { 4 Zi zi = new Zi(); 5 zi.method(); 6 } 7 8 }
public class Fu { public void method() { System.out.println("父類方法"); } }
1 public interface MyInterface { 2 3 public default void method() { 4 System.out.println("介面的默認方法"); 5 } 6 7 }
1 public class Zi extends Fu implements MyInterface { 2 }
1 public interface MyInterfaceA { 2 3 // 錯誤寫法!介面不能有靜態代碼塊 4 // static { 5 // 6 // } 7 8 // 錯誤寫法!介面不能有構造方法 9 // public MyInterfaceA() { 10 // 11 // } 12 13 public abstract void methodA(); 14 15 public abstract void methodAbs(); 16 17 public default void methodDefault() { 18 System.out.println("默認方法AAA"); 19 } 20 21 }
1 public interface MyInterfaceB { 2 3 // 錯誤寫法!介面不能有靜態代碼塊 4 // static { 5 // 6 // } 7 8 // 錯誤寫法!介面不能有構造方法 9 // public MyInterfaceA() { 10 // 11 // } 12 13 public abstract void methodB(); 14 15 public abstract void methodAbs(); 16 17 public default void methodDefault() { 18 System.out.println("默認方法BBB"); 19 } 20 21 }
public abstract class MyInterfaceAbstract implements MyInterfaceA, MyInterfaceB { @Override public void methodA() { } @Override public void methodAbs() { } @Override public void methodDefault() { } }
1 public class MyInterfaceImpl /*extends Object*/ implements MyInterfaceA, MyInterfaceB { 2 3 @Override 4 public void methodA() { 5 System.out.println("覆寫重寫了A方法"); 6 } 7 8 9 @Override 10 public void methodB() { 11 System.out.println("覆寫重寫了B方法"); 12 } 13 14 @Override 15 public void methodAbs() { 16 System.out.println("覆寫重寫了AB介面都有的抽象方法"); 17 } 18 19 @Override 20 public void methodDefault() { 21 System.out.println("對多個介面當中沖突的默認方法進行了覆寫重寫"); 22 } 23 }
1.、類與類之間是單繼承的,直接父類只有一個, 2、類與介面之間是多實作的,一個類可以實作多個介面, 3、介面與介面之間是多繼承的, 注意事項: 1)多個父介面當中的抽象方法如果重復,沒關系, 2)多個父介面當中的默認方法如果重復,那么子介面必須進行默認方法的覆寫重寫,【而且帶著default關鍵字】介面的多繼承
1 /* 2 這個子介面當中有幾個方法?答:4個, 3 methodA 來源于介面A 4 methodB 來源于介面B 5 methodCommon 同時來源于介面A和B 6 method 來源于我自己 7 */ 8 public interface MyInterface extends MyInterfaceA, MyInterfaceB { 9 10 public abstract void method(); 11 12 @Override 13 public default void methodDefault() { 14 15 } 16 }
1 public interface MyInterfaceA { 2 3 public abstract void methodA(); 4 5 public abstract void methodCommon(); 6 7 public default void methodDefault() { 8 System.out.println("AAA"); 9 } 10 11 }
1 public interface MyInterfaceB { 2 3 public abstract void methodB(); 4 5 public abstract void methodCommon(); 6 7 public default void methodDefault() { 8 System.out.println("BBB"); 9 } 10 11 }
1 public class MyInterfaceImpl implements MyInterface { 2 @Override 3 public void method() { 4 5 } 6 7 @Override 8 public void methodA() { 9 10 } 11 12 @Override 13 public void methodB() { 14 15 } 16 17 @Override 18 public void methodCommon() { 19 20 } 21 }
十四 多型:
多型的概念
- 代碼當中體現多型性,其實就是一句話:父類參考指向子類物件,
- 格式:
- 父類名稱 物件名 = new 子類名稱();
- 介面名稱 物件名 = new 實作類名稱();
1 public class Demo01Multi { 2 3 public static void main(String[] args) { 4 // 使用多型的寫法 5 // 左側父類的參考,指向了右側子類的物件 6 Fu obj = new Zi(); 7 8 obj.method(); 9 obj.methodFu(); 10 } 11 }
1 public class Fu { 2 3 public void method() { 4 System.out.println("父類方法"); 5 } 6 7 public void methodFu() { 8 System.out.println("父類特有方法"); 9 } 10 11 }
1 public class Zi extends Fu { 2 3 @Override 4 public void method() { 5 System.out.println("子類方法"); 6 } 7 }

使用多型的好處
多型訪問成員變數與成員方法
- 訪問成員變數的兩種方式:
- 直接通過物件名稱訪問成員變數:看等號左邊是誰,優先用誰,沒有則向上找,
- 間接通過成員方法訪問成員變數:看該方法屬于誰,優先用誰,沒有則向上找,
1 public class Demo01MultiField { 2 3 public static void main(String[] args) { 4 // 使用多型的寫法,父類參考指向子類物件 5 Fu obj = new Zi(); 6 System.out.println(obj.num); // 父:10 7 // System.out.println(obj.age); // 錯誤寫法! 8 System.out.println("============="); 9 10 // 子類沒有覆寫重寫,就是父:10 11 // 子類如果覆寫重寫,就是子:20 12 obj.showNum(); 13 } 14 15 }
- 成員方法的訪問規則:
- 看new的是誰,就優先用誰,沒有則向上找,
- 口訣:編譯看左邊,運行看右邊,
- 綜合對比一下:
- 成員變數:編譯看左邊,運行還看左邊,
- 成員方法:編譯看左邊,運行看右邊,
1 public class Demo02MultiMethod { 2 3 public static void main(String[] args) { 4 Fu obj = new Zi(); // 多型 5 6 obj.method(); // 父子都有,優先用子 7 obj.methodFu(); // 子類沒有,父類有,向上找到父類 8 9 // 編譯看左邊,左邊是Fu,Fu當中沒有methodZi方法,所以編譯報錯, 10 // obj.methodZi(); // 錯誤寫法! 11 } 12 13 }
1 public class Fu /*extends Object*/ { 2 3 int num = 10; 4 5 public void showNum() { 6 System.out.println(num); 7 } 8 9 public void method() { 10 System.out.println("父類方法"); 11 } 12 13 public void methodFu() { 14 System.out.println("父類特有方法"); 15 } 16 17 }
1 public class Zi extends Fu { 2 3 int num = 20; 4 5 int age = 16; 6 7 @Override 8 public void showNum() { 9 System.out.println(num); 10 } 11 12 @Override 13 public void method() { 14 System.out.println("子類方法"); 15 } 16 17 public void methodZi() { 18 System.out.println("子類特有方法"); 19 } 20 }
物件的上下轉型

1 /* 2 向上轉型一定是安全的,沒有問題的,正確的,但是也有一個弊端: 3 物件一旦向上轉型為父類,那么就無法呼叫子類原本特有的內容, 4 5 解決方案:用物件的向下轉型【還原】, 6 */ 7 public class Demo01Main { 8 9 public static void main(String[] args) { 10 // 物件的向上轉型,就是:父類參考指向之類物件, 11 Animal animal = new Cat(); // 本來創建的時候是一只貓 12 animal.eat(); // 貓吃魚 13 14 // animal.catchMouse(); // 錯誤寫法! 15 16 // 向下轉型,進行“還原”動作 17 Cat cat = (Cat) animal; 18 cat.catchMouse(); // 貓抓老鼠 19 20 // 下面是錯誤的向下轉型 21 // 本來new的時候是一只貓,現在非要當做狗 22 // 錯誤寫法!編譯不會報錯,但是運行會出現例外: 23 // java.lang.ClassCastException,類轉換例外 24 Dog dog = (Dog) animal; 25 } 26 27 }
1 /* 2 如何才能知道一個父類參考的物件,本來是什么子類? 3 格式: 4 物件 instanceof 類名稱 5 這將會得到一個boolean值結果,也就是判斷前面的物件能不能當做后面型別的實體, 6 */ 7 public class Demo02Instanceof { 8 9 public static void main(String[] args) { 10 Animal animal = new Dog(); // 本來是一只狗 11 animal.eat(); // 狗吃SHIT 12 13 // 如果希望掉用子類特有方法,需要向下轉型 14 // 判斷一下父類參考animal本來是不是Dog 15 if (animal instanceof Dog) { 16 Dog dog = (Dog) animal; 17 dog.watchHouse(); 18 } 19 // 判斷一下animal本來是不是Cat 20 if (animal instanceof Cat) { 21 Cat cat = (Cat) animal; 22 cat.catchMouse(); 23 } 24 25 giveMeAPet(new Dog()); 26 } 27 28 public static void giveMeAPet(Animal animal) { 29 if (animal instanceof Dog) { 30 Dog dog = (Dog) animal; 31 dog.watchHouse(); 32 } 33 if (animal instanceof Cat) { 34 Cat cat = (Cat) animal; 35 cat.catchMouse(); 36 } 37 } 38 39 }
1 public abstract class Animal { 2 3 public abstract void eat(); 4 5 }
1 public class Cat extends Animal { 2 @Override 3 public void eat() { 4 System.out.println("貓吃魚"); 5 } 6 7 // 子類特有方法 8 public void catchMouse() { 9 System.out.println("貓抓老鼠"); 10 } 11 }
1 public class Dog extends Animal { 2 @Override 3 public void eat() { 4 System.out.println("狗吃SHIT"); 5 } 6 7 public void watchHouse() { 8 System.out.println("狗看家"); 9 } 10 }
十五 final關鍵字:
final關鍵字代表最終、不可改變的,它的四種用法
- 可以用來修飾一個類
- 可以用來修飾一個方法
- 還可以用來修飾一個區域變數
- 還可以用來修飾一個成員變數
1 public class Demo01Final { 2 3 public static void main(String[] args) { 4 int num1 = 10; 5 System.out.println(num1); // 10 6 num1 = 20; 7 System.out.println(num1); // 20 8 9 // 一旦使用final用來修飾區域變數,那么這個變數就不能進行更改, 10 // “一次賦值,終生不變” 11 final int num2 = 200; 12 System.out.println(num2); // 200 13 14 // num2 = 250; // 錯誤寫法!不能改變! 15 // num2 = 200; // 錯誤寫法! 16 17 // 正確寫法!只要保證有唯一一次賦值即可 18 final int num3; 19 num3 = 30; 20 21 // 對于基本型別來說,不可變說的是變數當中的資料不可改變 22 // 對于參考型別來說,不可變說的是變數當中的地址值不可改變 23 Student stu1 = new Student("趙麗穎"); 24 System.out.println(stu1); 25 System.out.println(stu1.getName()); // 趙麗穎 26 stu1 = new Student("霍建華"); 27 System.out.println(stu1); 28 System.out.println(stu1.getName()); // 霍建華 29 System.out.println("==============="); 30 31 final Student stu2 = new Student("高圓圓"); 32 // 錯誤寫法!final的參考型別變數,其中的地址不可改變 33 // stu2 = new Student("趙又廷"); 34 System.out.println(stu2.getName()); // 高圓圓 35 stu2.setName("高圓圓圓圓圓圓"); 36 System.out.println(stu2.getName()); // 高圓圓圓圓圓圓 37 } 38 39 }
當final關鍵字用來修飾一個類
- 格式:
1 public final class 類名稱 {
2 // ...
3 }
- 含義:當前這個類不能有任何的子類,(太監類)
- 注意:一個類如果是final的,那么其中所有的成員方法都無法進行覆寫重寫(因為沒兒子,)
1 public final class MyClass /*extends Object*/ { 2 3 public void method() { 4 System.out.println("方法執行!"); 5 } 6 7 }
final關鍵字用來修飾一個方法
- 當final關鍵字用來修飾一個方法的時候,這個方法就是最終方法,也就是不能被覆寫重寫,
- 格式:
1 修飾符 final 回傳值型別 方法名稱(引數串列) { 2 // 方法體 3 }
- 注意事項:對于類、方法來說,abstract關鍵字和final關鍵字不能同時使用,因為矛盾,
1 public abstract class Fu { 2 3 public final void method() { 4 System.out.println("父類方法執行!"); 5 } 6 7 public abstract /*final*/ void methodAbs() ; 8 9 }
final關鍵字用來修飾一個成員變數
- 對于成員變數來說,如果使用final關鍵字修飾,那么這個變數也照樣是不可變,
- 由于成員變數具有默認值,所以用了final之后必須手動賦值,不會再給默認值了,
- 對于final的成員變數,要么使用直接賦值,要么通過構造方法賦值,二者選其一,
- 必須保證類當中所有多載的構造方法,都最侄訓對final的成員變數進行賦值,
1 public class Person { 2 3 private final String name/* = "鹿晗"*/; 4 5 public Person() { 6 name = "關曉彤"; 7 } 8 9 public Person(String name) { 10 this.name = name; 11 } 12 13 public String getName() { 14 return name; 15 } 16 17 // public void setName(String name) { 18 // this.name = name; 19 // } 20 }
額外代碼
1 // 不能使用一個final類來作為父類 2 public class MySubClass /*extends MyClass*/ { 3 }
1 public class Student { 2 3 private String name; 4 5 public Student() { 6 } 7 8 public Student(String name) { 9 this.name = name; 10 } 11 12 public String getName() { 13 return name; 14 } 15 16 public void setName(String name) { 17 this.name = name; 18 } 19 }
1 public class Zi extends Fu { 2 @Override 3 public void methodAbs() { 4 5 } 6 7 // 錯誤寫法!不能覆寫重寫父類當中final的方法 8 // @Override 9 // public void method() { 10 // System.out.println("子類覆寫重寫父類的方法!"); 11 // } 12 }
十六 權限:
Java中有四種權限修飾符:
- public > protected > (default) > private
- 注意事項:(default)并不是關鍵字“default”,而是根本不寫,
| public | protected | (default) | private | |
| 同一個類(我自己) | YES | YES | YES | YES |
| 同一個包(我鄰居) | YES | YES | YES | NO |
| 不同包子類(我兒子) | YES | YES | NO | NO |
| 不同包非子類(陌生人) |
YES |
NO | NO | NO |
十七 內部類:
什么是內部類
- 概念:如果一個事物的內部包含另一個事物,那么這就是一個類內部包含另一個類,
- 例如:身體和心臟的關系,又如:汽車和發動機的關系,
- 分類:
- 成員內部類
- 區域內部類(包含匿名內部類)
成員內部類
- 成員內部類的定義格式:
1 修飾符 class 外部類名稱 { 2 修飾符 class 內部類名稱 { 3 // ... 4 } 5 // ... 6 }
- 注意:內用外,隨意訪問;外用內,需要內部類物件,
- 如何使用成員內部類?有兩種方式:
- 間接方式:在外部類的方法當中,使用內部類;然后main只是呼叫外部類的方法
- 直接方式:公式
- 類名稱 物件名 = new 類名稱();
- 【外部類名稱.內部類名稱 物件名 = new 外部類名稱().new 內部類名稱();】
1 public class Demo01InnerClass { 2 3 public static void main(String[] args) { 4 Body body = new Body(); // 外部類的物件 5 // 通過外部類的物件,呼叫外部類的方法,里面間接在使用內部類Heart 6 body.methodBody(); 7 System.out.println("====================="); 8 9 // 按照公式寫: 10 Body.Heart heart = new Body().new Heart(); 11 heart.beat(); 12 } 13 14 }
1 public class Demo02InnerClass { 2 3 public static void main(String[] args) { 4 // 外部類名稱.內部類名稱 物件名 = new 外部類名稱().new 內部類名稱(); 5 Outer.Inner obj = new Outer().new Inner(); 6 obj.methodInner(); 7 } 8 9 }
1 public class Body { // 外部類 2 3 public class Heart { // 成員內部類 4 5 // 內部類的方法 6 public void beat() { 7 System.out.println("心臟跳動:蹦蹦蹦!"); 8 System.out.println("我叫:" + name); // 正確寫法! 9 } 10 11 } 12 13 // 外部類的成員變數 14 private String name; 15 16 // 外部類的方法 17 public void methodBody() { 18 System.out.println("外部類的方法"); 19 new Heart().beat(); 20 } 21 22 public String getName() { 23 return name; 24 } 25 26 public void setName(String name) { 27 this.name = name; 28 } 29 }
1 // 如果出現了重名現象,那么格式是:外部類名稱.this.外部類成員變數名 2 public class Outer { 3 4 int num = 10; // 外部類的成員變數 5 6 public class Inner /*extends Object*/ { 7 8 int num = 20; // 內部類的成員變數 9 10 public void methodInner() { 11 int num = 30; // 內部類方法的區域變數 12 System.out.println(num); // 區域變數,就近原則 13 System.out.println(this.num); // 內部類的成員變數 14 System.out.println(Outer.this.num); // 外部類的成員變數 15 } 16 17 } 18 19 }
區域內部類
- 概念:如果一個類是定義在一個方法內部的,那么這就是一個區域內部類,
- 區域:只有當前所屬的方法才能使用它,出了這個方法外面就不能用了,
- 定義格式:
1 修飾符 class 外部類名稱 { 2 修飾符 回傳值型別 外部類方法名稱(引數串列) { 3 class 區域內部類名稱 { 4 // ... 5 } 6 } 7 }
- 區域內部類如果希望訪問所在方法的區域變數,那么這個區域變數必須是【有效final的】,
- 備注:從Java 8+開始,只要區域變數事實不變,那么final關鍵字可以省略,
- 原因:
- new出來的物件在堆記憶體當中,
- 區域變數是跟著方法走的,在堆疊記憶體當中,
- 方法運行結束之后,立刻出堆疊,區域變數就會立刻消失,
- 但是new出來的物件會在堆當中持續存在,直到垃圾回收消失,
1 public class DemoMain { 2 3 public static void main(String[] args) { 4 Outer obj = new Outer(); 5 obj.methodOuter(); 6 } 7 8 }
1 class Outer { 2 3 public void methodOuter() { 4 class Inner { // 區域內部類 5 int num = 10; 6 public void methodInner() { 7 System.out.println(num); // 10 8 } 9 } 10 11 Inner inner = new Inner(); 12 inner.methodInner(); 13 } 14 15 }
1 public class MyOuter { 2 3 public void methodOuter() { 4 int num = 10; // 所在方法的區域變數 5 6 class MyInner { 7 public void methodInner() { 8 System.out.println(num); 9 } 10 } 11 } 12 13 }
匿名內部類
- 概念:如果介面的實作類(或者是父類的子類)只需要使用唯一的一次,那么這種情況下就可以省略掉該類的定義,而改為使用【匿名內部類】,
- 格式:
1 介面名稱 物件名 = new 介面名稱() { 2 // 覆寫重寫所有抽象方法 3 };
- 對格式“new 介面名稱() {...}”進行決議:
- new代表創建物件的動作
- 介面名稱就是匿名內部類需要實作哪個介面
- {...}這才是匿名內部類的內容
- 注意問題:
- 匿名內部類,在【創建物件】的時候,只能使用唯一一次:如果希望多次創建物件,而且類的內容一樣的話,那么就需要使用單獨定義的實作類了,
- 匿名物件,在【呼叫方法】的時候,只能呼叫唯一一次:如果希望同一個物件,呼叫多次方法,那么必須給物件起個名字,
- 匿名內部類是省略了【實作類/子類名稱】,但是匿名物件是省略了【物件名稱】
- 強調:匿名內部類和匿名物件不是一回事!!!
1 public class DemoMain { 2 3 public static void main(String[] args) { 4 // MyInterface obj = new MyInterfaceImpl(); 5 // obj.method(); 6 7 // MyInterface some = new MyInterface(); // 錯誤寫法! 8 9 // 使用匿名內部類,但不是匿名物件,物件名稱就叫objA 10 MyInterface objA = new MyInterface() { 11 @Override 12 public void method1() { 13 System.out.println("匿名內部類實作了方法!111-A"); 14 } 15 16 @Override 17 public void method2() { 18 System.out.println("匿名內部類實作了方法!222-A"); 19 } 20 }; 21 objA.method1(); 22 objA.method2(); 23 System.out.println("================="); 24 25 // 使用了匿名內部類,而且省略了物件名稱,也是匿名物件 26 new MyInterface() { 27 @Override 28 public void method1() { 29 System.out.println("匿名內部類實作了方法!111-B"); 30 } 31 32 @Override 33 public void method2() { 34 System.out.println("匿名內部類實作了方法!222-B"); 35 } 36 }.method1(); 37 // 因為匿名物件無法呼叫第二次方法,所以需要再創建一個匿名內部類的匿名物件 38 new MyInterface() { 39 @Override 40 public void method1() { 41 System.out.println("匿名內部類實作了方法!111-B"); 42 } 43 44 @Override 45 public void method2() { 46 System.out.println("匿名內部類實作了方法!222-B"); 47 } 48 }.method2(); 49 } 50 51 }
1 public interface MyInterface { 2 3 void method1(); // 抽象方法 4 5 void method2(); 6 7 }
1 public class MyInterfaceImpl implements MyInterface { 2 @Override 3 public void method1() { 4 System.out.println("實作類覆寫重寫了方法!111"); 5 } 6 7 @Override 8 public void method2() { 9 System.out.println("實作類覆寫重寫了方法!222"); 10 } 11 }
小結類的權限修飾符
- 概念:如果一個事物的內部包含另一個事物,那么這就是一個類內部包含另一個類,
- 例如:身體和心臟的關系,又如:汽車和發動機的關系,
- 分類:
- 成員內部類
- 區域內部類(包含匿名內部類)
轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/36560.html
標籤:其他
