一、面向對象——多态
1、多态概述
①什麼是多态?某一事物在不同時刻表現出來的不同狀态。
如:貓可以是貓的類型。貓 m = new貓();同時貓也是動物的一種,也可以把貓稱為動物。
②多态的好處:提高了代碼的可擴充性,前期定義的代碼可以使用後期的内容。
③多态的弊端:前期定義的内容不能使用後期子類的特有功能。
④多态的前提:
- 必須要有關系:繼承,實作。
- 有覆寫。
- 有父類引用指向子類對象。
2、多态時成員的特點
①成員變量:編譯時,參考引用型變量所屬的類中是否有調用的成員變量,有則編譯通過,沒有則編譯失敗。運作時,參考引用型變量所屬的類是否有調用的成員變量,并運作該所屬類中的成員變量。簡單的說,編譯和運作都參考等号的左邊。
②成員函數(非靜态):編譯時,參考引用型變量所屬的類中是否有調用的函數,有則編譯通過,沒有則編譯失敗。運作時,參考的是對象所屬的類是否有調用的函數,簡單的說,編譯看左邊,運作看右邊。
③靜态函數:簡單說,編譯和運作都看做左邊。其實對于靜态方法,是不需要對象的。直接用類名調用即可。
3、多态中的轉型問題
①向上轉型:從子到父,父類引用指向子類對象。
②向下轉型:從父到子,父類引用轉為子類對象。
在多台向下轉型中容易出現的問題,代碼示範如下:
/*
ClassCastException:類型轉換異常
一般在多态的向下轉型中容易出現
*/
class Animal {
public void eat(){}
}
class Dog extends Animal {
public void eat() {}
public void lookDoor() {
}
}
class Cat extends Animal {
public void eat() {
}
public void playGame() {
}
}
class DuoTaiDemo5 {
public static void main(String[] args) {
//記憶體中的是狗
Animal a = new Dog();
Dog d = (Dog)a;
//記憶體中是貓
a = new Cat();
Cat c = (Cat)a;
//記憶體中是貓
Dog dd = (Dog)a; //ClassCastException
}
}
二、面向對象——内部類
1、内部類(inner class)概述
①概念:内部類是指定義在另一個類中的類。當描述事物時,事物的内部還有事物,該事物就用内部類來描述。因為内部事物在使用外部事物的内容。
②内部類通路的特點:
- 内部類可以直接通路外部類中的成員。
- 外部類需要通路内部類,必須建立内部類的對象
- 如果内部類中有靜态成員,那麼外部類也必須是靜态的。
2、内部類的分類
按照内部類在類中定義的位置的不同,可以分為如下兩種格式:
- 成員位置——成員内部類
- 局部位置——局部内部類
①成員内部類
a、外界如何建立對象?外部類名.内部類名 對象名 = 外部類對象.内部類對象;
b、成員内部類常見的修飾符
- private 為了保證資料的安全性
- static 為了讓資料通路更友善。被靜态修飾的成員内部類隻能通路外部的靜态成員。
内部類被靜态修飾後的方法有靜态和非靜态之分,它們的通路和不用靜态不是一樣的。
通路非靜态方法:外部類名.内部類名對象名 = new 外部類名.内部類名();
通路靜态方法:上面建立的對象通路,或者外部類名.内部類名.方法名();
成員内部類面試題:
class Outer {
public int num = 10;
class Inner {
public int num = 20;
public void show() {
int num = 30;
System.out.println(num);//30
System.out.println(this.num);//20
System.out.println(Outer.this.num);//10
}
}
}
class OuterDemo {
public static void main(String[] args) {
Outer.Inner oi = new Outer().new Inner();
oi.show();
}
}
②局部内部類
a、局部内部類可以直接通路外部類的成員。
b、可以建立内部類對象,通過對象調用内部類方法,來使用局部内部類功能。
c、局部内部類通路局部變量的注意事項:
- 必須被final修飾。為什麼?因為局部變量會随着方法的調用完畢而消失,這個時候,局部對象并沒有立馬從堆記憶體中消失,還要使用那個變量。為了讓資料還能繼續被使用,就用fianl修飾,這樣,在堆記憶體裡面存儲的其實是一個常量值。
如下面的代碼所示:
class Outer
{
int x = 3;
void method(final int a)
{
final int y = 4;
//局部内部類
class Inner
{
void function()
{
System.out.println(y);
}
}
new Inner().function();//使用局部内部類中的方法。
}
}
class InnerClassDemo
{
public static void main(String[] args)
{
Outer out = new Outer();
out.method(7);//列印7
out.method(8);//列印8
}
}
③匿名内部類
a、什麼是匿名内部類?匿名内部類其實就是内部類的簡寫格式。
b、匿名内部類的前提:存在一個類或接口。
c、匿名内部類的格式:new 類名或者接口名(){重寫方法}。
d、匿名内部類的本質:是一個繼承了類或者實作了接口的子類匿名對象。
e、匿名内部類的通常使用場景之一:當函數參數是接口類型時,而且接口中的方法不超過三個,可以使用匿名内部類作為實際參數進行傳遞。
匿名内部類在開發中的使用代碼執行個體:
/*
匿名内部類在開發中的使用
*/
interface Person {
public abstract void study();
}
class PersonDemo {
//接口名作為形式參數
//其實這裡需要的不是接口,而是該接口的實作類的對象
public void method(Person p) {
p.study();
}
}
//實作類
class Student implements Person {
public void study() {
System.out.println("好好學習,天天向上");
}
}
class InnerClassTest2 {
public static void main(String[] args) {
//測試
PersonDemo pd = new PersonDemo();
Person p = new Student();
pd.method(p);
System.out.println("--------------------");
//匿名内部類在開發中的使用
//匿名内部類的本質是繼承類或者實作了接口的子類匿名對象
pd.method(new Person(){
public void study() {
System.out.println("好好學習,天天向上");
}
});
}
}
匿名内部類面試題:按照要求補齊代碼 。
interface Inter { void show(); }
class Outer { //補齊代碼 }
class OuterDemo {
public static void main(String[] args) {
Outer.method().show();
}
}
要求在控制台輸出”HelloWorld”
題目分析:Outer.method.show();相當于Inter.in = Outer.method(); in.show();
Outer.method():Outer類中有個靜态的方法method。
.show():method這個方法運算後的結果是一個對象。而且是一個Inter類型的對象。因為隻有Inter類型的對象,才能調用show方法。
是以,補齊後的代碼為:
/*
匿名内部類面試題:
按照要求,補齊代碼
interface Inter { void show(); }
class Outer { //補齊代碼 }
class OuterDemo {
public static void main(String[] args) {
Outer.method().show();
}
}
要求在控制台輸出”HelloWorld”
*/
interface Inter {
void show();
//public abstract
}
class Outer {
//補齊代碼
public static Inter method() {
//子類對象 -- 子類匿名對象
return new Inter() {
public void show() {
System.out.println("HelloWorld");
}
};
}
}
class OuterDemo {
public static void main(String[] args) {
Outer.method().show();
/*
1:Outer.method()可以看出method()應該是Outer中的一個靜态方法。
2:Outer.method().show()可以看出method()方法的傳回值是一個對象。
又由于接口Inter中有一個show()方法,是以我認為method()方法的傳回值類型是一個接口。
*/
}
}
三、面向對象——異常
1、異常的概述
①什麼是異常?
a、異常是指在運作時期發生的不正常情況。在java中用類的形式對不正常情況進行了描述和封裝,描述不正常情況的類,就是異常類。
b、在以往的正常情況,流程代碼和問題處理代碼相結合。現在将正常流程代碼和問題處理代碼相分離,提高閱讀性。
c、其實異常就是java通過面向對象的思想将問題封裝成了對象,用異常類對其進行描述,不同的問題用不同的類進行具體描述。
②異常的由來
在理想狀況下,使用者輸入的資料永遠是正确的,打開的檔案一定存在,并且永遠不會出現bug。但是,在現實中我們往往會遇到許多的問題。如果一個使用者在運作程式期間,由于程式的錯誤或一些外部環境的影響造成資料的丢失,使用者就有可能不再使用這個程式了,為了避免這類事情的發生,至少應該做到以下幾點:
- 向使用者通報錯誤。
- 儲存所有的操作結果。
- 允許使用者以适當的形式退出程式。
2、異常體系
對于程式出現的問題,可能會有很多種,這就意味着描述的類也很多,将其共性進行向上抽取,就形成了異常體系。
異常體系(Throwable):無論是error還是異常,都應該抛出,讓調用者知道并處理。該體系的優點就在于Throwable及其所有的子類都具有可抛性。可抛性是通過兩個關鍵字來展現的,throw和throws,凡是可以被這兩個關鍵字操作的類和對象都具有可抛性。
最終就将問題分成了兩大類:
①error:一般不可處理。java運作時系統的内部錯誤和資源耗盡錯誤。是由jvn抛出的嚴重性問題,這種問題一般不針對性處理,直接修改程式。
②Exception:可處理的。Exception體系。
a、該體系的特點:子類的字尾都是用其父類名作為字尾,閱讀性很強。
b、在java中沒有定義的異常就按照java異常的建立思想,面向對象,進行自定義描述,并封裝成對象。
注意:如果一個類稱為異常類,則必須要繼承異常體系,因為隻要繼承異常體系的子類才具有可抛性。才可以被兩個關鍵字操作。
異常體系如下圖所示:
![](https://img.laitimes.com/img/9ZDMuAjOiMmIsIjOiQnIsIiclRnblN0LclHdpZXYyd2LcBzNvwVZ2x2bzNXak9CX90TQNNkRrFlQKBTSvwFbslmZvwFMwQzLcVmepNHdu9mZvwFVywUNMZTY18CX052bm9CX90zZNhXV610MnRUT4FEVkZXUYpVd1kmYr50MZV3YyI2cKJDT29GRjBjUIF2LcRHelR3LcJzLctmch1mclRXY39jMwgDO1EDM4EDNyYDM1EDMy8CX0Vmbu4GZzNmLn9Gbi1yZtl2Lc9CX6MHc0RHaiojIsJye.jpg)
3、異常的分類
①異常通常分為兩種,一種是編譯時被檢測異常,一種是編譯時不被檢測異常(運作時異常)。
- 編譯時被檢測異常:隻要是Exception及其子類都是,除了RuntimeException體系。這種問題一旦出現,希望在編譯時就進行檢測,讓這種問題有對應的處理。
- 編譯時不檢測異常(運作時異常):就是Exception中的RuntimeException及其子類。這種問題的發生,無法讓功能繼續,運算無法進行,更多的是因為調用者的原因導緻的,或者是引發了内部狀态的改變而導緻的,那麼這種問題一般不處理,直接編譯通過,在運作時,讓調用者調用時的程式強行停止,讓調用者修改程式代碼。“如果出現RuntimeException,那麼就一定是你的問題”。
4、異常的處理
①異常處理機制
在java應用程式中,異常處理機制為:抛出異常,捕獲異常。
- 抛出異常:當一個方法出現錯誤引發異常時,方法建立異常對象并傳遞運作時系統,異常對象中包含了異常類型和異常出現時的程式狀态等異常資訊。運作時系統負責尋找處置異常的代碼并執行。
- 捕獲異常:在方法抛出異常之後,運作時系統将轉為尋找合适的異常處理器(exception handler)。潛在的異常處理器是異常發生時依次存留在調用棧中的方法的集合。當異常處理器所能處理的異常類型與方法抛出的異常類型相符時,即為合适 的異常處理器。運作時系統從發生異常的方法開始,依次回查調用棧中的方法,直至找到含有合适異常處理器的方法并執行。當運作時系統周遊調用棧而未找到合适 的異常處理器,則運作時系統終止。同時,意味着Java程式的終止。
②throws和throw的差別
- throws使用在函數上,throw使用在函數内。
- throws抛出的是異常類,可以抛出多個,用逗号隔開;throw抛出的是異常對象。
③在java中,提供了特有的處理異常的方式,格式如下:
try
{
需要被檢測的代碼;
}
catch(異常類 變量)
{
處理異常的代碼;(處理方式)
}
finally
{
一定會執行的語句;
}
除此之外還有兩種格式,一種是沒有finally代碼塊,一種是沒有catch代碼塊。
注意:在finally中定義的通常是關閉資源代碼,因為資源必須釋放。finally隻有一種情況不會執行:當系統執行到System.exit的時候。
④自定義異常
定義類繼承Exception或者RuntimeException。目的有兩個:一是為了讓該自定義類具有可抛性,二是為了讓該類具備操作異常的共性方法。
當要定義自定義異常資訊時,可以使用父類已經定義好的功能。異常資訊傳遞給父類的構造函數。代碼執行個體如下:
class MyException extends Exception{
MyException(String message){
super(message);
}
}
自定義異常:按照java面向對象的思想,将程式中出現的特殊問題進行封裝。
⑤異常處理原則:
- 函數内容如果要抛出需要檢測的異常,那麼函數上必須要有聲明,否則必須在函數内用try catch進行捕捉,否則編譯失敗。
- 如果調用到了聲明異常的函數,要麼try catch,要麼throws,否則編譯失敗。
- 什麼時候try,什麼時候throws呢?功能内容可以解決就用try…catch;解決不了,用throws告訴調用者,由調用者解決。
- 一個功能如果抛出了多個異常,那麼調用時,必須有對應的多個catch進行針對性的處理,内部有幾個需要檢測的異常,就抛幾個,抛出幾個,就catch幾個。
⑥異常處理注意事項
a、子類在覆寫父類方法時,父類如果抛出了異常,那麼子類的方法隻能抛出父類的異常或者該異常的子類。
b、如果父類抛出了多個異常,呢嗎子類隻能抛出父類異常的子集。簡單的說,子類覆寫父類的異常或者子類或子集。
注意:如果父類的方法沒有抛出異常,那麼子類的方法絕對不能抛出異常。隻能進行捕獲處理。
⑦異常實際應用中的應用和總結
a、在處理運作時異常時,采用邏輯去合理規避同時輔助try—catch處理。
b、在多重catch塊後面。可以加上一個catch(Exception)來處理可能被一樓的異常。
c、對于不确定的代碼,也可以加上try—catch處理潛在的異常。
d、盡量去處理異常,切忌隻是簡單的調用printstack.Trance()去列印輸出。
e、具體如何處理異常,要根據不同的業務需求和異常類型來決定。
f、盡量添加finally代碼塊去釋放占用的資源。
⑧異常綜合練習代碼示範:
/*
畢老師用電腦上課。
開始思考上課中出現的問題。
比如問題是
電腦藍屏。
電腦冒煙。
要對問題進行描述,封裝成對象。
可是當冒煙發生後,出現講課進度無法繼續。
出現了講師的問題:課時計劃無法完成。
*/
//自定義電腦藍屏異常
class LanPingException extends Exception{
LanPingException(String message){
super(message);
}
}
//自定義電腦冒煙異常
class MaoYanException extends Exception{
MaoYanException(String message){
super(message);
}
}
//出現了問題,無法上課,課時計劃無法完成異常
class NoPlanException extends Exception{
NoPlanException(String msg){
super(msg);
}
}
class Computer{
private int state = 3;
//電腦啟動
public void run()throws LanPingException,MaoYanException{
if(state==2)
throw new LanPingException("藍屏了");
if(state==3)
throw new MaoYanException("冒煙了");
System.out.println("電腦運作");
}
//電腦重新開機
public void reset(){
state = 1;
System.out.println("電腦重新開機");
}
}
class Teacher{
private String name;
private Computer cmpt;
//對老師進行初始化
Teacher(String name){
this.name = name;
cmpt = new Computer();
}
//老師講課
public void prelect()throws NoPlanException{
try{
cmpt.run();
}
catch (LanPingException e){
cmpt.reset();
}
catch (MaoYanException e){
test();
throw new NoPlanException("課時無法繼續"+e.getMessage());
}
System.out.println("講課");
}
public void test(){
System.out.println("練習");
}
}
class ExceptionTest {
public static void main(String[] args) {
Teacher t = new Teacher("畢老師");
try{
t.prelect();
}
catch (NoPlanException e){
System.out.println(e.toString());
System.out.println("換老師或者放假");
}
}
}
四、面向對象——包(package)
1、什麼是包?
包其實就是檔案夾。在包中,我們通常存放的是類檔案,因為我們在編寫程式時,難免會出現類名相同的情況。為了友善于對類進行管理,java中就有了包的出現,在不同的包中可以有相同的類名,調用的時候連同包名一起調用即可。包也是一種封裝形式。
報名的書寫規則:
- 包名必須寫在程式的第一行。
- 類檔案的全稱:包名.類名。
2、包的作用
①對子類檔案進行分類管理。将一些相關的類放在同一個包中。
②給類提供多層命名(名稱)空間。
③避免多個類重命名,對于相同的類名,可通過包名将兩者區分。
④包的出現可以将java 的類檔案與源檔案相分離。
3、包與包之間的通路
①要通路其它包中的類,需要定義類的全稱。包名.類名。
②包如果不在目前路徑,需要使用classpath設定環境變量,為JVM指明路徑。
③被通路的包中的類權限必須是public的。被通路的包中的類的方法也必須是public的。
4、四種權限修飾詞
public | protected | default | private | |
同一類中 | ok | ok | ok | ok |
同一包中 | ok | ok | ok | |
子類中 | ok | ok | ||
不同包中 | ok |
5、import(導包)
①導包的原則:用到哪個類,就導哪個類,不要多導。(盡管多導也能運作)
②導包的作用:
- 可以簡化書寫。
- 一個程式檔案中隻有一個package,但可以有多個import。
③導包的注意事項:
- 在導包時,用到哪個寫哪個,盡量不要import pack.*;一般是import pack.Demo;可以節省記憶體空間,也能提高點效率。
- 定義包名不要重複。一般我們定義包名是用url的倒寫形式。如:package cn.itheima.Demo
- 定義包名時,包名都是小寫字母。