一、多態
1. 概述
- 理解:多態可以理解為事物存在的多種體(表)現形態。
- 例如:
- 動物中的貓和狗。
- 貓這個對象對應的是貓類型,例如:貓 x = new 貓();
- 同時貓也是動物中的一種,也可以把貓稱為動物,例如:動物 y = new 貓();
- 動物就是貓和狗這些具體事物中抽取出來的父類型,父類型的引用指向了子類對象。
- 例如:
- 體現
- 父類的引用指向了子類的對象。
- 父類的引用也可以接收自己子類的對象。
- 例如:Animal a = new Cat();
- 父類型 a 的引用指向了子類的對象。
- 例如:Animal a = new Cat();
- 前提
- 類與類之間必須有關系,要么是繼承,要么是實現。
- 存在重寫(覆蓋),父類中有方法被子類重寫(覆蓋)。
2. 利與弊
利:提高了程序的可擴展性和后期的可維護性。
-
弊:只能使用父類中的引用去訪問父類中的成員。
- 使用了多態,父類型的引用在使用功能時,不能直接調用子類中特有的方法。
- 例如:Animal a = new Cat();//多態的體現
- 假設子類 Cat 中有抓老鼠的特有功能,父類型 a 就不能直接調用該功能,可以理解為 Cat 類型向上轉型,提升了。
- 父類的引用想要調用子類 Cat 中特有的方法,就要將父類的引用強制轉換成子類的類型,向下轉型。例如:Cat c = (Cat)a;
- 注:
- 如果父類可以創建對象的話,例如:Animal a = new Animal(); 就不能進行向下轉型了,Cat c = (Cat)a; 這樣編寫代碼在編譯時會報錯,不要出現將父類對象轉換成子類類型。
- 多態轉換的是父類引用指向了自己子類的對象時,該引用可以被提升,也可以被強制轉換,多態始終都是子類對象在做變化的。
- 例如:Animal a = new Cat();//多態的體現
- 使用了多態,父類型的引用在使用功能時,不能直接調用子類中特有的方法。
-
例如:
3. 特點
1. 成員變量
- 無論編譯還是運行時期,都參考左邊(引用變量)所屬的類。
- 例如:多態中父類引用調用成員變量時,如果父類和子類有同名的成員變量,被調用的則是父類中的成員變量。
2. 非靜態成員函數
- 編譯時期:參考引用型變量所屬的類中是否有調用的方法,如果有,編譯通過,如果沒有,編譯失敗。
- 例如:a.catchMouse();//編譯報錯,這種情況只能通過強制轉換,向下轉型后才可以使用子類中特有的功能。
- 運行時期:參考對象所屬的類中是否有調用的方法,如果父類中有一個非抽象方法,子類繼承后將其重寫(覆蓋)了,多態運行時,父類的引用調用同名函數時,運行的是父類中的方法。
- 總而言之:成員函數在多態調用時,編譯看左邊,運行看右邊。
3. 靜態成員函數
- 無論編譯還是運行時期,都參考左邊,就是父類的引用在調用同名靜態成員函數時,調用的是父類中的靜態成員函數。
- 原因:
當類一被加載時,靜態成員函數隨類綁定在內存中,不需要創建對象,可以通過類名直接調用靜態成員函數,并且父類中的靜態成員函數一般是不被重寫(覆蓋)的。
-
類在方法區中的分配:靜態方法區和非靜態方法區(關鍵字 super 和 this 在非靜態方法區中)。
- 原因:
4. 應用
- 定義工具類,將共同行為封裝在同一個類中。
- 對類型進行抽取(多態的產生)。
- 操作同一個父類型,對其中的子類型均可進行操作。
-
例如:
-
二、內部類
1. 概述
- 概述:將一個類定義在另一個類里面,定義在類里面的那個類稱為內部類(內置類、嵌套類)。
- 理解:當描述事物時,事物的內部還存在事物,事物內的事物使用內部類來描述,印務內部事物在使用外部事物的內容。
- 例如:定義一個人的類,手、腳、心臟等等都屬于人,這些又各自有自己的功能描述,這時候就可以在人這個描述類中,定義一些描述手、腳、心臟的類,也就是定義一些內部類。
- 編譯現象:編譯 Java 文件時,如果類中有內部類,生成的 class 文件中會含有 Test$1.class 等等這樣的文件,編譯器會把內部類翻譯為用 $(美元符號)分隔外部類名和內部類名的類文件,這是內部類的一種編譯現象。
2. 訪問規則
- 外部類要訪問內部類,必須建立內部類的對象。
- 內部類可以直接訪問外部類中的成員(包括私有成員)。
- 原因:因為內部類中持有外部類的引用。
- 格式:外部類名.this
3. 訪問格式
- 內部類定義在外部類的成員位置,而且非私有時,可以在外部的其它類中直接建立內部類對象。
- 格式:外部類名.內部類名 變量名 = 外部類對象.內部類對象;
- 例如:Outer.Inner in = new Outer().new Inner();
- 內部類定義在外部類的成員位置時,可以被成員修飾符所修飾。
- 例如:
- private:內部類在外部類中進行封裝。
- static:內部類有局部 statci 的特性,內部類被 static 修飾后,只能訪問外部類中的 static 成員。
- 外部的其它類訪問 static 內部類中靜態成員的格式:外部類名.內部類名.方法名();
- 例如:Outer.Inner.function();
- 外部的其它類訪問 static 內部類中非靜態成員的格式:new 外部類名.內部類名().方法名();
- 例如:new Outer.Inner().function();
- 外部的其它類訪問 static 內部類中靜態成員的格式:外部類名.內部類名.方法名();
- 例如:
- 注:
- 內部類中定義了靜態成員時,該內部類必須是 static 的。
- 外部類中的靜態方法訪問內部類時,內部類也必須是 static 的。
- 實際開發中,內部類一般定義為 private,很少定義為 public 的。
- 格式:外部類名.內部類名 變量名 = 外部類對象.內部類對象;
- 內部類定義在局部位置
- 不可被成員修飾符所修飾,作用域限定在聲明這個局部內部類的代碼塊中。
- 例如:public、private、static 等修飾符。
- 可以直接訪問外部類中的成員,還持有外部類的引用。
注:內部類不可以訪問所在局部中的非最終變量,只能訪問被 final 修飾的局部變量。
-
例如:
- 分析:上述代碼中的打印結果為 7、8,會改變,并沒有因為被 final 所修飾就無法改變,原因是因為類調用方法結束后,被 final 修飾的變量已經從桟內存中消失,類再次調用方法時,已經是另一個變量了,所以可以重新傳值。
- 不可被成員修飾符所修飾,作用域限定在聲明這個局部內部類的代碼塊中。
4. 匿名內部類
匿名內部類就是內部類的簡寫格式,匿名內部類其實就是一個匿名子類對象,可以理解為帶內容的對象。
格式:new 父類或接口(){定義子類的內容}
-
定義匿名內部類的前提:
- 匿名內部類必須是繼承一個類或者實現接口。
- 特殊情況:所有的類都有一個父類 Object,定義時也可用 Object。
- 匿名內部類必須是繼承一個類或者實現接口。
-
利與弊:
- 利:簡化書寫。
- 弊:
- 不能做強轉的動作。
- 不能直接調用自己的特有方法。
- 如果繼承的父類或接口中方法很多時,使用匿名內部類的可閱讀性會非常差,調用也非常麻煩,因此匿名內部類中定義的方法一般不超過3個。
-
例如:
三、異常
1. 概述
- 異常是 Java 中的重要機制,使用面向對象的思想進行了封裝,通常使用的是異常類。異常類中描述的就是程序中可能出現的錯誤或者問題。
- 程序在運行時出現不正常的情況稱為異常。
- 由來:異常問題也是現實生活中一個具體的事物,可以通過 Java 中類的形式進行描述,并封裝成對象,異常類就是 Java 對不正常的情況進行描述后的對象體現。
- 程序中可能出現的錯誤或者問題:
- 用戶輸入錯誤導致的異常:非法參數、不按正常的使用程序等。
- 設備硬件等發生的錯誤的異常:硬盤損壞等。
- 物理限制的異常:存儲空間不足、SDCard不存在等。
- 代碼錯誤的異常:程序編寫的方法不正確、返回錯誤的參數等。
2. 體系
異常問題的劃分方式
- 嚴重的異常問題。
- 嚴重的異常問題,Java 通過 Error 類進行描述,對于 Error 類一般不編寫針對性的代碼進行處理。
- 非嚴重的異常問題。
-
非嚴重的異常問題,Java 通過 Exception 類進行描述,對于 Exception 類可以使用針對性的代碼進行處理。
注:無論是 Error 或 Exception 都具有一些共性內容,例如:不正常情況的相關信息、引發異常的原因等。
-
體系
- Throwable
- Error//出現重大異常問題。例如:運行的類不存在或者內存溢出等。
- Exception//運行時出現的異常情況。
- RuntimeException//特殊的異常類,Exception 的子類,拋出時不需要做聲明。
- 注:Error 和 Exception 的子類名稱都是以父類名做為后綴。
特點
- 異常體系中所有類以及建立的對象都具備可拋性;
- 可以被 throw 和 throws 關鍵字所修飾操作;
- 只有異常體系才具備這個特點。
3. 分類
編譯時異常
- 異常在編譯時,如果沒有做任何處理(沒有拋也沒有 try),編譯失敗。
- 異常被標識,代表可以被處理。
運行時異常
- 編譯時不需要處理,編譯器不檢查。
- 異常發生時,Java 建議不處理,讓程序停止。
- 需要對代碼進行修正。(例如:RuntimeException 以及 RuntimeException 的子類)
4. 處理
-
Java 中提供了特有的語句進行處理:
try{ 需要檢測的語句; }catch(異常類 變量){ 處理異常的語句(方式); }finally{ 一定要執行的語句; }
-
這些特有語句的處理方式有三種結合情況:
1. try{ 需要檢測的語句; }catch(異常類 變量){ 處理異常的語句(方式); } 2. try{ 需要檢測的語句; }finally{ 一定要執行的語句; } 3. try{ 需要檢測的語句; }catch(異常類 變量){ 處理異常的語句(方式); }finally{ 一定要執行的語句; }
-
注:
- finally 中定義的一般是關閉資源的代碼,資源使用完后必須釋放。
- 如果存在一些功能是必須執行的,可以使用 try{}finally{} 的方式,將必須執行的語句放在 finally 代碼塊中。
- finally 只有一種情況是不會執行的,在進入 finally 之前就先執行了 System.exit(0); 時,已經是退出了程序,finally 就不會執行的。
-
-
throw 和 throws 的用法:
- throw 定義在函數內,用于拋出異常對象。
- throws 定義在函數上,用于拋出異常類,可以拋出多個對象,使用 , 隔開即可。
- 當函數的內容有 throw 拋出異常對象,但沒 try 處理時,必須在函數上聲明,否則會導致編譯失敗。
- 注:RuntimeException 除外,函數內如果拋出的是 RuntimeException 異常,函數上不需要聲明。
-
拋出信息的處理:
函數中出現了 throw 拋出異常對象時,要么是在函數內部進行 try 的處理,要么是在函數上聲明,讓函數的調用者去處理。
-
對于捕獲到的異常對象常見的操作方法:
getMessage();//獲取異常信息,返回字符串 toString();//獲取異常類名和異常信息,返回字符串 printStackTrace();//獲取異常類名、異常信息以及異常在程序中出現的位置,返回值 void,JVM 默認的異常處理機制中,就是調用 PrintStackTrace() 方法來打印異常的堆棧跟蹤信息 printStackTrace(PrintStream s);//將異常內容保存在日志文件中
5. 自定義
- 對于一些特有的問題,這些特有的問題沒有被 Java 所描述封裝為對象,對于這些特有的問題,可以按照 Java 的面向對象思想將問題進行自定義的異常封裝,這就是自定義異常。
- 自定義異常類繼承 Exception 或 RuntimeException
- 讓自定義異常類具備可拋性。
- 讓自定義異常類具備操作異常的共性方法。
- 如何自定義異常信息?
自定義異常信息可以使用父類已定義好的功能,異常信息傳遞給父類的構造函數,由于父類中已經把異常信息的操作都完成了,子類只需在構造函數中,通過 super 語句將異常信息傳遞給父類,就可以通過 getMessage() 方法獲取自定義的異常信息了。
-
例如:
- 說明:定義異常時,如果異常的發生,會導致無法繼續進行運算,則讓自定義異常類繼承 RuntimeException。
-
注意:自定義異常必須是自定義異常類有繼承關系,一般繼承 Exception。
- 自定義異常類繼承 Exception 的原因?
- 異常體系的特點就是異常類和異常對象都具備可拋性,這個可拋性是 Throwable 體系中獨有的特點,只有這個體系中的類和對象才可以被 throw 和 throws 所操作。
- 自定義異常類繼承 Exception 的原因?
- 自定義異常類繼承 Exception 或 RuntimeException
6. 好處與原則
好處
- 將問題進行封裝。
- 將正常流程的代碼和問題處理的代碼分離,便于閱讀。
原則
- 處理方式:try 或 throws。
- 調用到拋出異常的功能時,需要對所有拋出的異常進行處理,一個 try 可以對應多個 catch。
- 多個 catch 時,父類的 catch 需放到最后面,否則編譯會報錯,因為執行了父類的 catch 語句后,其余的 catch 語句將無法執行到。
- catch 中需要定義針對性的處理方式,一般不建議簡單的定義 PrintStackTrace 輸出語句,也不建議不做處理。
-
如果捕獲的異常在本功能內處理不了的話,可繼續在 catch 中拋出。
-
例如:
-
如果捕獲的異常在本功能內處理不了的話,但并不屬于本功能出現的異常,可以將異常轉換后再拋出。
-
如果捕獲的異常在本功能內可以處理的話,但需要將異常產生后和本功能相關的問題提供出去給調用者,可以將異常轉換后再拋出。
-
例如:
-
-
7. 注意事項
- 異常問題在內部被解決就無需聲明。
- catch 是用于處理異常,如果沒有 catch,就代表異常問題沒有被處理,該異常如果是檢測時異常,就必須聲明。
- 子父類重寫(覆蓋)時:
- 子類拋出的異常必須是父類異常的子類或者子集。
- 父類或接口沒有異常拋出時,子類重寫(覆蓋)出現異常時,只能 try,不能拋。
-
例如:
-
四、包(package)
1. 概述
- Java 中將 package 稱為包,相當于文件夾,包里面一般存放的是類文件。由于在編寫程序時,難免存在類名相同的情況,為了便于對類進行分類管理,Java 便提供了包,以包的形式存放不同的類,在不同的包中可以有相同的類名,只需在調用時連同包名一起即可。
- 包也是一種封裝的形式。
2. 作用
- 為了避免多個類重名的情況,出現多個相同名字的類時,可通過包將其區分,避免沖突。
- 對類文件進行分類管理,可以將相關的類放在同個包中。
- 包的出現將 Java 的源文件和類文件分離。
- 給類提供了多層命名空間。
- 例如:
- a 包中的 Demo.class 文件,如果要創建 Demo 對象,在使用時需加上 a.
- a.Demo demo = new a.Demo();
- 例如:
3. 規則
- 包必須寫在程序的第一行,先有包,才知道類文件的存放位置。
- 類的全稱:包.類名。
- 編譯定義了包的程序文件時,在編譯時需指定包的存放目錄。
- 例如:javac -d e:\Demo\mypack 類名.java
4. 包與包之間的訪問
- 被訪問的包里類的權限必須是 public。
- 訪問其它包中的類時,需定義類的全稱:包名.類名。
- 包如果不在當前路徑的話,需使用 classpath 設置環境變量,為 JVM 指明路徑。
- 類中的成員權限必須是 public 或 protected。
類公有后,被訪問的成員也要公有才能被訪問。
protected 是為子類提供的一種權限。
同個包中,protected 只做用為覆蓋。
不同包中的子類可以直接訪問父類中被 protected 權限修飾符所修飾的成員。
-
四種權限修飾符:
- 注:由于被 public 所修飾的類名必須與 java 文件名相同,因此在一個 .java 文件中,不允許出現兩個或以上的公有類或接口。
5. 導入(import)
- 一個程序文件中只有一個 package,可以有多個 import,import 導入的是包中的類,不導入包中的包。
- import 可以簡化類名,在調用其它包中的類時,需要寫類的全稱,就是連同包名一起書寫,當類存在多層包中時,使用 import 導入后,使用該類時,可以不加包名。
- 例如:
- a.b.c.pack.Demo
- 導入的格式為 import a.b.c.pack.Demo;
- 使用該類時,可直接寫 Demo
- 例如:
- 注意事項:
- 導入不同包中有想同類時,必須寫類的全稱以做區分,否則會報錯。
- 定義包名不能重復,可以使用 url 來定義,url 是唯一的。
- 例如:package com.java.Demo
- 導入包時,如果包中有很多類,可以使用通配符 * 代替包中所有的類,但不建議使用通配符 *,因為這樣會將不需要使用的類一同導入,會占用內存空間,編寫程序時,需要使用包里的哪些類就導入哪些類。
6. jar 包
- 類越來越多時,可以使用包類封裝。包越來越多時,可以將包進行壓縮,Java 中通過 jar 工具對包進行壓縮,壓縮后的的后綴名為 jar。
- jar.exe 工具的常用命令:
- 創建 jar 包
- jar -cvf mypack.jar mypack
- 查看 jar 包
- jar -tvf mypack.jar
- 解壓縮
- jar -xvf mypack.jar
- 自定義 jar 包的清單文件
- jar -cvfm mypack.jar mypack.mf mypack
- 創建 jar 包
- 好處:
- 數據庫、驅動、SSH框架等都是以 jar 包體現的。
- 將多個包壓縮為一個 jar 包文件,便于項目攜帶。
- 便于使用,只要在 classpath 設置 jar 包的路徑,即可執行 jar 包中的 java 程序。