<ruby id="bdb3f"></ruby>

    <p id="bdb3f"><cite id="bdb3f"></cite></p>

      <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
        <p id="bdb3f"><cite id="bdb3f"></cite></p>

          <pre id="bdb3f"></pre>
          <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

          <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
          <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

          <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                <ruby id="bdb3f"></ruby>

                ??碼云GVP開源項目 12k star Uniapp+ElementUI 功能強大 支持多語言、二開方便! 廣告
                [TOC] # 面向對象 ## 1\. Java的四個基本特性,對多態的理解,在項目中哪些地方用到多態 * **Java的四個基本特性** * **抽象**:抽象是將一類對象的共同特征總結出來構造類的過程,包括數據抽象和行為抽象兩方面。抽象只關注對象有哪些屬性和行為,并不關注這些行為的細節是什么。? * **封裝**:通常認為封裝是把數據和操作數據的方法綁定起來,對數據的訪問只能通過已定義的接口。面向對象的本質就是將現實世界描繪成一系列完全自治、封閉的對象。我們在類中編寫的方法就是對實現細節的一種封裝;我們編寫一個類就是對數據和數據操作的封裝。可以說,封裝就是隱藏一切可隱藏的東西,只向外界提供最簡單的編程接口。 * **繼承**:繼承是從已有類得到繼承信息創建新類的過程。提供繼承信息的類被稱為父類(超類、基類);得到繼承信息的類被稱為子類(派生類)。繼承讓變化中的軟件系統有了一定的延續性,同時繼承也是封裝程序中可變因素的重要手段。 * **多態**:多態性是指允許不同子類型的對象對同一消息作出不同的響應。 * **多態的理解(多態的實現方式)** * **方法重載**(overload):實現的是**編譯時的多態性**(也稱為前綁定)。 * **方法重寫**(override):實現的是**運行時的多態性**(也稱為后綁定)。運行時的多態是面向對象最精髓的東西。 * 要實現多態需要做兩件事: * 1. **方法重寫**(子類繼承父類并重寫父類中已有的或抽象的方法); * 2. **對象造型**(用父類型引用引用子類型對象,這樣同樣的引用調用同樣的方法就會根據子類對象的不同而表現出不同的行為)。 * **項目中對多態的應用** * 舉一個簡單的例子,在物流信息管理系統中,有兩種用戶:訂購客戶和賣房客戶,兩個客戶都可以登錄系統,他們有相同的方法 Login,但登陸之后他們會進入到不同的頁面,也就是在登錄的時候會有不同的操作,兩種客戶都繼承父類的 Login 方法,但對于不同的對象,擁有不同的操作。 * **面相對象開發方式優點(B65)** * 較高的**開發效率**:可以把事物進行抽象,映射為開發的對象。 * 保證軟件的**魯棒性**:高重用性,可以重用已有的而且在相關領域經過長期測試的代碼。 * 保證軟件的**高可維護性**:代碼的可讀性非常好,設計模式也使得代碼結構清晰,拓展性好。 ## 2\. 什么是重載和重寫 * **重載**:重載發生在同一個類中,同名的方法如果有不同的參數列表(參數類型不同、參數個數不同或者二者都不同)則視為重載。 * **重寫**:重寫發生在子類與父類之間,重寫要求子類被重寫方法與父類被重寫方法有相同的返回類型,比父類被重寫方法更好訪問,不能比父類被重寫方法聲明更多的異常(里氏代換原則)。根據不同的子類對象確定調用的那個方法。 [![](https://github.com/frank-lam/fullstack-tutorial/raw/master/notes/JavaArchitecture/assets/overloading-vs-overriding.png)](https://github.com/frank-lam/fullstack-tutorial/blob/master/notes/JavaArchitecture/assets/overloading-vs-overriding.png) ## 3\. 面向對象和面向過程的區別?用面向過程可以實現面向對象嗎? * 面向對象和面向過程的區別 * **面向過程**就像是一個細心的管家,事無具細的都要考慮到。而**面向對象**就像是個家用電器,你只需要知道他的功能,不需要知道它的工作原理。 * **面向過程**是一種是“事件”為中心的編程思想。就是分析出解決問題所需的步驟,然后用函數把這些步驟實現,并按順序調用。**面向對象**是以“對象”為中心的編程思想。 * 簡單的舉個例子:汽車發動、汽車到站 * 這對于**面向過程**來說,是兩個事件,汽車啟動是一個事件,汽車到站是另一個事件,**面向過程**編程的過程中我們關心的是事件,而不是汽車本身。針對上述兩個事件,形成兩個函數,之 后依次調用。(事件驅動,動詞為主) * 然而這對于**面向對象**來說,我們關心的是汽車這類對象,兩個事件只是這類對象所具有的行為。而且對于這兩個行為的順序沒有強制要求。(對象驅動,名詞為主,將問題抽象出具體的對象,而這個對象有自己的屬性和方法,在解決問題的時候是將不同的對象組合在一起使用) * 用面向過程可以實現面向對象嗎 ? * 如果是 C 語言來展現出面向對象的思想,C 語言中是不是有個叫結構體的東西,這個里面有自己定義的變量 可以通過函數指針就可以實現對象 ## 4\. 面向對象開發的六個基本原則,在項目中用過哪些原則 * **六個基本原則**(參考《設計模式之禪》) * **單一職責**(Single Responsibility Principle 簡稱 SRP):**一個類應該僅有一個引起它變化的原因**。在面向對象中,如果只讓一個類完成它該做的事,而不涉及與它無關的領域就是踐行了高內聚的原則,這個類就只有單一職責。 * **里氏替換**(Liskov Substitution Principle 簡稱 LSP):**任何時候子類型能夠替換掉它們的父類型**。子類一定是增加父類的能力而不是減少父類的能力,因為子類比父類的能力更多,把能力多的對象當成能力少的對象來用當然沒有任何問題。 * **依賴倒置**(Dependence Inversion Principle 簡稱 DIP):**要依賴于抽象,不要依賴于具體類**。要做到依賴倒置,應該做到:①高層模塊不應該依賴底層模塊,二者都應該依賴于抽象;②抽象不應該依賴于具體實現,具體實現應該依賴于抽象。 * **接口隔離**(Interface Segregation Principle 簡稱 ISP):**不應該強迫客戶依賴于他們不用的方法**。接口要小而專,絕不能大而全。臃腫的接口是對接口的污染,既然接口表示能力,那么一個接口只應該描述一種能力,接口也應該是高度內聚的。 * **最少知識原則**(Least Knowledge Principle 簡稱 LKP):**只和你的朋友談話**。迪米特法則又叫最少知識原則,一個對象應當對其他對象有盡可能少的了解。 * **開閉原則**(Open Closed Principle 簡稱 OCP):**軟件實體應當對擴展開放,對修改關閉**。要做到開閉有兩個要點:①抽象是關鍵,一個系統中如果沒有抽象類或接口系統就沒有擴展點;②封裝可變性,將系統中的各種可變因素封裝到一個繼承結構中,如果多個可變因素混雜在一起,系統將變得復雜而換亂。 * 其他原則 * 合成聚和復用:優先使用聚合或合成關系復用代碼 * 面向接口編程 * 優先使用組合,而非繼承 * 一個類需要的數據應該隱藏在類的內部 * 類之間應該零耦合,或者只有傳導耦合,換句話說,類之間要么沒關系,要么只使用另一個類的接口提供的操作 * 在水平方向上盡可能統一地分布系統功能 * 項目中用到的原則 * 單一職責、開放封閉、合成聚合復用(最簡單的例子就是String類)、接口隔離 ## 5\. 內部類有哪些 可以將一個類的定義放在另一個類的定義內部,這就是內部類。 在 Java 中內部類主要分為成員內部類、局部內部類、匿名內部類、靜態內部類 ### 成員內部類 成員內部類也是最普通的內部類,它是外圍類的一個成員,所以他是可以**無限制的訪問外圍類的所有成員屬性和方法,盡管是private的**,但是外圍類要訪問內部類的成員屬性和方法則需要通過內部類實例來訪問。 ~~~java public class OuterClass { private String str; public void outerDisplay(){ System.out.println("outerClass..."); } public class InnerClass{ public void innerDisplay(){ str = "chenssy..."; //使用外圍內的屬性 System.out.println(str); outerDisplay(); //使用外圍內的方法 } } // 推薦使用getxxx()來獲取成員內部類,尤其是該內部類的構造函數無參數時 public InnerClass getInnerClass(){ return new InnerClass(); } public static void main(String[] args) { OuterClass outer = new OuterClass(); OuterClass.InnerClass inner = outer.getInnerClass(); inner.innerDisplay(); } } -------------------- chenssy... outerClass... ~~~ 在成員內部類中要注意兩點: * 成員內部類中不能存在`static`方法, 但是可以存在`static`域, 前提是需要使用`final`關鍵字進行修飾. * 成員內部類是依附于外圍類的,所以只有先創建了外圍類才能夠創建內部類。 ### 局部內部類 有這樣一種內部類,它是嵌套在方法和作用于內的,對于這個類的使用主要是應用與解決比較復雜的問題,想創建一個類來輔助我們的解決方案,到那時又不希望這個類是公共可用的,所以就產生了局部內部類,局部內部類和成員內部類一樣被編譯,只是它的作用域發生了改變,它只能在該方法和屬性中被使用,出了該方法和屬性就會失效。 ~~~java //定義在方法里: public class Parcel5 { public Destionation destionation(String str){ class PDestionation implements Destionation{ private String label; private PDestionation(String whereTo){ label = whereTo; } public String readLabel(){ return label; } } return new PDestionation(str); } public static void main(String[] args) { Parcel5 parcel5 = new Parcel5(); Destionation d = parcel5.destionation("chenssy"); } } //定義在作用域內: public class Parcel6 { private void internalTracking(boolean b){ if(b){ class TrackingSlip{ private String id; TrackingSlip(String s) { id = s; } String getSlip(){ return id; } } TrackingSlip ts = new TrackingSlip("chenssy"); String string = ts.getSlip(); } } public void track(){ internalTracking(true); } public static void main(String[] args) { Parcel6 parcel6 = new Parcel6(); parcel6.track(); } } ~~~ ### 匿名內部類 匿名內部類也就是沒有名字的內部類。正因為沒有名字,所以匿名內部類只能使用一次,它通常用來簡化代碼編寫。但使用匿名內部類還有個前提條件:必須繼承一個父類或實現一個接口 **實例1:不使用匿名內部類來實現抽象方法** ~~~java abstract class Person { public abstract void eat(); } class Child extends Person { public void eat() { System.out.println("eat something"); } } public class Demo { public static void main(String[] args) { Person p = new Child(); p.eat(); } } ~~~ **運行結果**:eat something 可以看到,我們用 Child 繼承了 Person 類,然后實現了 Child 的一個實例,將其向上轉型為 Person 類的引用 但是,如果此處的 Child 類只使用一次,那么將其編寫為獨立的一個類豈不是很麻煩? 這個時候就引入了匿名內部類 **實例2:匿名內部類的基本實現** ~~~java abstract class Person { public abstract void eat(); } public class Demo { public static void main(String[] args) { Person p = new Person() { public void eat() { System.out.println("eat something"); } }; p.eat(); } } ~~~ **運行結果**:eat something 可以看到,我們直接將抽象類 Person 中的方法在大括號中實現了,這樣便可以省略一個類的書寫,并且,匿名內部類還能用于接口上。 **實例3:在接口上使用匿名內部類** ~~~java interface Person { public void eat(); } public class Demo { public static void main(String[] args) { Person p = new Person() { public void eat() { System.out.println("eat something"); } }; p.eat(); } } ~~~ **運行結果**:eat something 由上面的例子可以看出,只要一個類是抽象的或是一個接口,那么其子類中的方法都可以使用匿名內部類來實現 最常用的情況就是在多線程的實現上,因為要實現多線程必須繼承 Thread 類或是繼承 Runnable 接口 **實例4:Thread類的匿名內部類實現** ~~~java public class Demo { public static void main(String[] args) { Thread t = new Thread() { public void run() { for (int i = 1; i <= 5; i++) { System.out.print(i + " "); } } }; t.start(); } } ~~~ **運行結果**:1 2 3 4 5 **實例5:Runnable接口的匿名內部類實現** ~~~java public class Demo { public static void main(String[] args) { Runnable r = new Runnable() { public void run() { for (int i = 1; i <= 5; i++) { System.out.print(i + " "); } } }; Thread t = new Thread(r); t.start(); } } ~~~ **運行結果**:1 2 3 4 5 ### (四)靜態內部類 關鍵字 static 中提到 static 可以修飾成員變量、方法、代碼塊,其他它還可以修飾內部類,使用 static 修飾的內部類我們稱之為靜態內部類,不過我們更喜歡稱之為嵌套內部類。靜態內部類與非靜態內部類之間存在一個最大的區別,我們知道非靜態內部類在編譯完成之后會隱含地保存著一個引用,該引用是指向創建它的外圍內,但是靜態內部類卻沒有。 1. 它的創建是不需要依賴于外圍類的。 2. 它不能使用任何外圍類的非 static 成員變量和方法。 ~~~java public class OuterClass { private String sex; public static String name = "chenssy"; // 靜態內部類 static class InnerClass1{ // 在靜態內部類中可以存在靜態成員 public static String _name1 = "chenssy_static"; public void display(){ // 靜態內部類只能訪問外圍類的靜態成員變量和方法 // 不能訪問外圍類的非靜態成員變量和方法 System.out.println("OutClass name :" + name); } } // 非靜態內部類 class InnerClass2{ // 非靜態內部類中不能存在靜態成員 public String _name2 = "chenssy_inner"; // 非靜態內部類中可以調用外圍類的任何成員,不管是靜態的還是非靜態的 public void display(){ System.out.println("OuterClass name:" + name); } } // 外圍類方法 public void display(){ // 外圍類訪問靜態內部類:內部類 System.out.println(InnerClass1._name1); // 靜態內部類 可以直接創建實例不需要依賴于外圍類 new InnerClass1().display(); // 非靜態內部的創建需要依賴于外圍類 OuterClass.InnerClass2 inner2 = new OuterClass().new InnerClass2(); // 方位非靜態內部類的成員需要使用非靜態內部類的實例 System.out.println(inner2._name2); inner2.display(); } public static void main(String[] args) { OuterClass outer = new OuterClass(); outer.display(); } } ---------------- Output: chenssy_static OutClass name :chenssy chenssy_inner OuterClass name:chenssy ~~~ ## 6\. 組合、繼承和代理的區別 ### 定義 * 組合:在新類中 new 另外一個類的對象,以添加該對象的特性。 * 繼承:從基類繼承得到子類,獲得父類的特性。 * 代理:在代理類中創建某功能的類,調用類的一些方法以獲得該類的部分特性。 ### 使用場合 * 組合:各部件之間沒什么關系,只需要組合即可。例如組裝電腦,需要 new CPU(),new RAM(),new Disk() ~~~java public class Computer { public Computer() { CPU cpu=new CPU(); RAM ram=new RAM(); Disk disk=new Disk(); } } class CPU{ } class RAM{ } class Disk{ } ~~~ * 繼承:子類需要具有父類的功能,各子類之間有所差異。例如 Shape 類作為父類,子類有 Rectangle,CirCle,Triangle……代碼不寫了,大家都經常用。 * 代理:飛機控制類,我不想暴露太多飛機控制的功能,只需部分前進左右轉的控制(而不需要暴露發射導彈功能)。通過在代理類中 new 一個飛機控制對象,然后在方法中添加飛機控制類的各個需要暴露的功能。 ~~~java public class PlaneDelegation{ private PlaneControl planeControl; //private外部不可訪問 // 飛行員權限代理類,普通飛行員不可以開火 PlaneDelegation(){ planeControl = new PlaneControl(); } public void speed(){ planeControl.speed(); } public void left(){ planeControl.left(); } public void right(){ planeControl.right(); } } final class PlaneControl {// final表示不可繼承,控制器都能繼承那還得了 protected void speed() {} protected void fire() {} protected void left() {} protected void right() {} } ~~~ **說明:** * 繼承:代碼復用,引用不靈活; * 組合:代碼復用, * 接口:引用靈活; * 推薦組合+接口使用,看 IO 中包裝流 FilterInputStream 中的策略模式 ## 7\. 什么是構造函數 構造函數是函數的一種特殊形式。特殊在哪里?構造函數中不需要定義返回類型(void 是無需返回值的意思,請注意區分兩者),且構造函數的名稱與所在的類名完全一致,其余的與函數的特性相同,可以帶有參數列表,可以存在函數的重載現象。 一般用來初始化一些成員變量,當要生成一個類的對象(實例)的時候就會調用類的構造函數。如果不顯示聲明類的構造方法,會自動生成一個默認的不帶參數的空的構造函數。 ~~~java public class Demo{ private int num=0; //無參構造函數 Demo() { System.out.println("constractor_run"); } //有參構造函數 Demo(int num) { System.out.println("constractor_args_run"); } //普通成員函數 public void demoFunction() { System.out.println("function_run"); } } ~~~ 在這里要說明一點,如果在類中我們不聲明構造函數,JVM 會幫我們默認生成一個空參數的構造函數;如果在類中我們聲明了帶參數列表的構造函數,JVM 就不會幫我們默認生成一個空參數的構造函數,我們想要使用空參數的構造函數就必須自己去顯式的聲明一個空參的構造函數。 **構造函數的作用**   通過開頭的介紹,構造函數的輪廓已經漸漸清晰,那么為什么會有構造函數呢?構造函數有什么作用?構造函數是面向對象編程思想所需求的,它的主要作用有以下兩個: * **創建對象**。任何一個對象創建時,都需要初始化才能使用,所以任何類想要創建實例對象就必須具有構造函數。 * **對象初始化**。構造函數可以對對象進行初始化,并且是給與之格式(參數列表)相符合的對象初始化,是具有一定針對性的初始化函數。 ## 8\. 向上造型和向下造型 父類引用能指向子類對象,子類引用不能指向父類對象; **向上造型** 父類引用指向子類對象,例如: ~~~java Father f1 = new Son(); ~~~ **向下造型** 把指向子類對象的父類引用賦給子類引用,需要強制轉換,例如: ~~~java Father f1 = new Son(); Son s1 = (Son)f1; ~~~ 但有運行出錯的情況: ~~~java Father f2 = new Father(); Son s2 = (Son)f2; //編譯無錯但運行會出現錯誤 ~~~ 在不確定父類引用是否指向子類對象時,可以用 instanceof 來判斷: ~~~java if(f3 instanceof Son){ Son s3 = (Son)f3; } ~~~
                  <ruby id="bdb3f"></ruby>

                  <p id="bdb3f"><cite id="bdb3f"></cite></p>

                    <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
                      <p id="bdb3f"><cite id="bdb3f"></cite></p>

                        <pre id="bdb3f"></pre>
                        <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

                        <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
                        <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

                        <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                              <ruby id="bdb3f"></ruby>

                              哎呀哎呀视频在线观看