Java面向?qū)ο笤斀?/h1>

Java OOP

什么是面向?qū)ο笏枷耄?/h2>

把一組數(shù)據(jù)和處理他們的方法組成對(duì)象(object),把相同行為的對(duì)象歸納為類(class),通過類的封裝(encapsulation)隱藏內(nèi)部細(xì)節(jié)刀崖,通過繼承(inheritance)實(shí)現(xiàn)類的的特化(specialization)/泛化(generalization)仅财,通過多態(tài)(polymorphism)實(shí)現(xiàn)基于對(duì)象類型的動(dòng)態(tài)分派(dynamic dispatch). --- 來源于知乎(如何用一句話說明什么是面向?qū)ο笏枷?)

這個(gè)定義指出了面向?qū)ο蟮乃膫€(gè)特點(diǎn):抽象、封裝属韧、繼承和多態(tài)安拟。

類和對(duì)象

對(duì)象

具有一組數(shù)據(jù)和處理這些數(shù)據(jù)的方法組成一個(gè)對(duì)象。例如:

對(duì)象 person

數(shù)據(jù) 姓名宵喂,年齡

方法 說話糠赦,吃飯,睡覺

類可以看成是對(duì)象的抽象锅棕,是可以生成眾多對(duì)象的一個(gè)模板拙泽。例如我們有一個(gè)Person類,有姓名年齡兩個(gè)數(shù)據(jù)裸燎。這就是個(gè)模板顾瞻,它不代表一個(gè)人person,但是能夠通過這個(gè)模板創(chuàng)造出眾多person顺少。

// 定義類
權(quán)限修飾符 class 類名 {
    屬性
    方法  
}

public class Person {
    String name; //名字
    int age;     //年齡
    // 吃飯方法
    void eat() {
        
    }
    // 說話方法
    void speak() {
        
    }
}

構(gòu)造函數(shù)(Constructor)

  • 構(gòu)造函數(shù)概念

    構(gòu)造函數(shù)是用來初始化對(duì)象的一段代碼塊朋其。它具有如下特點(diǎn):

    1. 沒有返回值王浴。
    2. 構(gòu)造函數(shù)的名字和類名必須一致脆炎。
    3. 不能有static, final, abstract, synchronised 等關(guān)鍵字修飾。
  • 如何用構(gòu)造函數(shù)初始化對(duì)象

    // Java通過new關(guān)鍵字生成對(duì)象氓辣,并且調(diào)用Person類的構(gòu)造函數(shù)初始化這個(gè)新的對(duì)象
    Person person = new Person();
    
  • 構(gòu)造函數(shù)的類型

    • 默認(rèn)構(gòu)造函數(shù): 如果一個(gè)類沒有構(gòu)造函數(shù),編譯器會(huì)在編譯階段自動(dòng)添加一個(gè)無參的構(gòu)造函數(shù)秒裕。

      默認(rèn)構(gòu)造函數(shù)
    • 無參數(shù)的構(gòu)造函數(shù): 沒有參數(shù)的構(gòu)造函數(shù),和默認(rèn)的構(gòu)造函數(shù)的一個(gè)區(qū)別是無參的構(gòu)造函數(shù)里面可以有代碼钞啸,而默認(rèn)構(gòu)造函數(shù)的函數(shù)體里面沒有任何代碼几蜻。

    • 有參數(shù)的構(gòu)造函數(shù): 構(gòu)造函數(shù)有參數(shù)

      public class Person {
          int age;
          String name;
          
          Person(int age, String name) {
              this.age = age;
              this.name = name;
          }
          
          public static void main(String[] args) {
              Person person = new Person(18, "Johnny");
              System.out.println(person.name);
              System.out.println(person.age);
          }
      }
      
    > 注意:如果類有構(gòu)造函數(shù),則編譯器不會(huì)在編譯階段添加默認(rèn)構(gòu)造函數(shù)体斩。所以如果在類有有參數(shù)的構(gòu)造函數(shù)但是沒有無參數(shù)的構(gòu)造函數(shù)的情況下梭稚,調(diào)用無參數(shù)的構(gòu)造函數(shù)編譯器會(huì)報(bào)錯(cuò)。
    > ![](https://upload-images.jianshu.io/upload_images/4401597-84dde9ec70a7f84c.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/485) 
  • 父類的構(gòu)造函數(shù)---super()

    如果子類的構(gòu)造函數(shù)沒有調(diào)用父類的特定構(gòu)造函數(shù)絮吵,編譯器會(huì)默認(rèn)添加調(diào)用父類的無參數(shù)構(gòu)造函數(shù)(或者默認(rèn)構(gòu)造函數(shù)) --- super()弧烤。

    public class Person {
        int age;
        String name;
        
        public Person() {
            System.out.println("Person init");
        }
        
        public static void main(String[] args) {
            Man man = new Man(18, "Johnny");
        }
        
    }
    
    class Man extends Person {
        
        public Man(int age, String name) {
            System.out.println("Man init");
        }
        
    }
    

    結(jié)果:

    Person init
    Man init
    

    如果父類沒有默認(rèn)構(gòu)造函數(shù)(或者說無參構(gòu)造函數(shù)), 則編譯器會(huì)報(bào)錯(cuò)。
    所以需要指定一個(gè)父類的構(gòu)造函數(shù)蹬敲,例如 super(age, name);

    public class Person {
        int age;
        String name;
        
        public Person(int age, String name) {
            this.age = age;
            this.name = name;
            System.out.println("Person init");
        }
        
        public static void main(String[] args) {
            Man man = new Man(18, "Johnny");
            System.out.println(man.name); // Johnny
        }
        
    }
    
    class Man extends Person {
        
        public Man(int age, String name) {
            super(age, name);
            System.out.println("Man init");
        }
        
    }
    

static關(guān)鍵字

static關(guān)鍵字可以修飾類暇昂、變量、方法和代碼塊伴嗡。被static修飾的成員屬于類急波,而不是對(duì)象。
  • static修飾成員變量

    當(dāng)所有的對(duì)象的某個(gè)屬性都是一樣的時(shí)候應(yīng)考慮使用static關(guān)鍵字修飾瘪校。例如我們需要表示體育行業(yè)的一些人澄暮。

    public class Person {
        int age;
        String name;
        String industry = "sport"; // 體育行業(yè)
        
        public Person(int age, String name) {
            this.age = age;
            this.name = name;
        }
        
        public static void main(String[] args) {
            Person personA = new Person(30, "姚明");
            Person personB = new Person(31, "劉翔");
            Person personC = new Person(32, "李娜");          
        }
    }
    

    內(nèi)存布局示意圖:

    內(nèi)存布局示意圖

    存在兩個(gè)主要問題:1名段,每個(gè)對(duì)象都有一片內(nèi)存存儲(chǔ)相同的一個(gè)內(nèi)容;2泣懊,如果需要統(tǒng)一修改這個(gè)變量吉嫩,每個(gè)對(duì)象都需要修改。

    解決方案:用static修飾變量 static String industry = "sport";

    內(nèi)存布局示意圖:

    內(nèi)存布局示意圖
由于static修飾的屬性是公共屬性嗅定,屬于類自娩。可以通過**“類.屬性名”** 的方式進(jìn)行獲取和修改渠退。例如

```
Person.industry = "music";              // 修改
System.out.println(Person.industry);  // 獲取 
```
  • static修飾方法

    Java的類的方法是存儲(chǔ)在靜態(tài)存儲(chǔ)區(qū)的忙迁。如下圖:

    內(nèi)存布局示意圖:


    事實(shí)上,多個(gè)對(duì)象對(duì)應(yīng)的是同一個(gè)方法碎乃,也就是說所有對(duì)象的方法是相同的姊扔。 static修飾方法在數(shù)據(jù)存儲(chǔ)方面沒有區(qū)別。最大的作用是可以用 “類.方法名”直接調(diào)用梅誓,避免了先要new出對(duì)象的繁瑣和資源消耗

    Person.talk();

  • static代碼塊(static block)

    靜態(tài)代碼塊的作用是統(tǒng)一初始化靜態(tài)變量恰梢,只會(huì)在類加載的時(shí)候執(zhí)行一次, 可以用來優(yōu)化程序性能梗掰。如果有多個(gè)靜態(tài)代碼塊嵌言,會(huì)按照書寫的順序依次執(zhí)行多個(gè)靜態(tài)代碼塊

    public class StaticBlockDemo {
    
        static int number;
        static String str;
        
        static { // 靜態(tài)代碼塊1執(zhí)行1次
            number = 10;
            str = "name";
        }
        
        static { // 靜態(tài)代碼塊2及穗,在靜態(tài)代碼塊1之后執(zhí)行1次
            number = 20;
            str = "name2";
        }
        
        public static void main(String[] args) {
            System.out.println(number);
            System.out.println(str);
        }
        
    }
    

    知識(shí)擴(kuò)展:靜態(tài)代碼塊摧茴、構(gòu)造代碼塊、構(gòu)造函數(shù)和普通代碼塊的區(qū)別埂陆?

    1. 執(zhí)行順序:靜態(tài)代碼塊 -> 構(gòu)造代碼塊 -> 構(gòu)造函數(shù) -> 代碼塊
    2. 執(zhí)行次數(shù):靜態(tài)代碼塊只在類的加載時(shí)執(zhí)行一次;構(gòu)造代碼塊在每次構(gòu)造函數(shù)調(diào)用前調(diào)用苛白,如果有多個(gè)構(gòu)造函數(shù),特定構(gòu)造函數(shù)有可能執(zhí)行也可能不執(zhí)行;代碼塊在函數(shù)執(zhí)行時(shí)調(diào)用焚虱。
    public class StaticBlockDemo {
        
        static int number;
            
        static {
            number = 10;
            System.out.println("靜態(tài)代碼塊");
        }
            
        {
            System.out.println("構(gòu)造代碼塊");
        }
            
        public StaticBlockDemo() {
            System.out.println("構(gòu)造函數(shù)");
        }
            
        public void func() {
            {
                int a = 10;
                System.out.println("代碼塊");
            }
        }
            
        public static void main(String[] args) {
            new StaticBlockDemo().func(); // 靜態(tài)代碼塊 -> 構(gòu)造代碼塊 -> 構(gòu)造函數(shù) -> 代碼塊  
        }
            
    }
    
  • 靜態(tài)內(nèi)部類(static class)

    如果一個(gè)類要被聲明為static的购裙,只有一種情況,就是靜態(tài)內(nèi)部類鹃栽。

    • 靜態(tài)內(nèi)部類只能訪問靜態(tài)的成員變量和方法躏率;
    • 靜態(tài)內(nèi)部類不需要引用外部類
    public class Outer {
        
        int age;  // 普通變量
        static String name; // 靜態(tài)變量
        
        static { // 靜態(tài)代碼塊
            name = "Johnny";
        }
        
        { // 構(gòu)造代碼塊
            age = 20;
        }
        
        // 靜態(tài)內(nèi)部類
        public static class StaticInner {
            public void sayName() {
                System.out.println(name);
            }
        }
        
        // 普通的內(nèi)部類
        public class NormalInner {
            public void sayAge() {
                System.out.println(age);
            }
        }
        
        public static void main(String[] args) {
            // 靜態(tài)內(nèi)部類的初始化
            StaticInner staticInner = new Outer.StaticInner();
            staticInner.sayName(); // Johnny
            
            // 普通內(nèi)部類的初始化
            Outer outer = new Outer();
            NormalInner normalInner = outer.new NormalInner();
            normalInner.sayAge(); // 20
        }
        
    }
    

繼承(Inheritance)

繼承是指一個(gè)類獲取另外一個(gè)類的屬性和方法的過程,繼承的主要目的是復(fù)用代碼谍咆。獲取另外一個(gè)類的特性的類被稱為子類禾锤,而特性被另外一個(gè)類利用的類則相對(duì)應(yīng)稱為父類。
  • 繼承語法 --- extends

    class Son extends Parent
    {
    }
    
  • 繼承類型

    Java的類只支持前面三種繼承類型:

    • 單繼承:一個(gè)類只繼承另外一個(gè)類
    Class A
    {
       public void methodA()
       {
         System.out.println("Method A");
       }
    }
    
    Class B extends A
    {
       public void methodB()
       {
         System.out.println("Method B");
       }
       
       public static void main(String args[])
       {
         B b = new B();
         b.methodA(); //可以調(diào)用父類的方法
         b.methodB(); //調(diào)用本地方法
      }
    }
    
    • 多級(jí)繼承:一級(jí)一級(jí)繼承摹察,有多個(gè)層次的繼承關(guān)系
    Class A {
       public void methodA() {
         System.out.println("Class A method");
       }
    }
    
    Class B extends A {
        public void methodB() {
            System.out.println("class B method");
        }
    }
    
    Class C extends B {
       public void methodC() {
         System.out.println("class C method");
       }
       
       public static void main(String args[]) {
         C c = new C();
         c.methodA(); //可以調(diào)用祖父類的方法
         c.methodB(); //可以調(diào)用父類的方法
         c.methodC(); //調(diào)用本地方法
      }
    }
    
    • 層次繼承:多個(gè)類繼承自同一個(gè)父類
    class A {
       public void methodA() {
          System.out.println("method of Class A");
       }
    }
    
    class B extends A {
       public void methodB() {
          System.out.println("method of Class B");
       }
    }
    
    class C extends A {
      public void methodC() {
         System.out.println("method of Class C");
      }
    }
    
    class D extends A {
      public void methodD() {
         System.out.println("method of Class D");
      }
    }
    
    class JavaExample
    {
      public static void main(String args[]) {
         B obj1 = new B();
         C obj2 = new C();
         D obj3 = new D();
         // 所有的類都有A的方法
         obj1.methodA();
         obj2.methodA();
         obj3.methodA();
      }
    }
    

    Java的類不支持多繼承是因?yàn)槿绻麅蓚€(gè)父類有同樣的方法恩掷,子類不知道到底調(diào)用哪個(gè)父類的方法,造成混亂供嚎。但是Java中有一個(gè)接口的概念黄娘,是可以多繼承的峭状。后面會(huì)介紹。

super

super是一個(gè)Java關(guān)鍵字逼争,相當(dāng)于是指當(dāng)當(dāng)前對(duì)象的直接父類中的成員

  • 獲取父類的屬性值(也包括static修飾的值)

    class SuperClass {
        int number = 10;
        static int number2 = 30;
    }
    
    public class SubClass extends SuperClass {
        int number = 20;
        
        private void printNumber() {
            System.out.println(number);       // 20
            System.out.println(super.number); // 10
            System.out.println(super.number2);// 30
        }
        
        public static void main(String[] args) {
            SubClass subClass = new SubClass();
            subClass.printNumber();
        }
        
    }
    
  • 顯示調(diào)用父類的無參構(gòu)造函數(shù)或者有參構(gòu)造函數(shù) --- 詳見構(gòu)造函數(shù)章節(jié)

  • 方法覆寫(Override)

    子類可以直接繼承父類的方法优床,但是有時(shí)候需要修改父類的方法,我們稱之為方法覆寫誓焦。方法覆寫是指子類定義了一個(gè)返回值胆敞,參數(shù)類型和參數(shù)數(shù)量都和父類都完全一致的方法。

    注意事項(xiàng):

    1. 方法覆寫是子類的訪問權(quán)限(具體后面介紹)(Java中有四種訪問權(quán)限,并且public > default > protected > private)不能比父類的訪問權(quán)限更嚴(yán)格杂伟。例如如果父類的訪問權(quán)限是default移层,則子類的訪問權(quán)限只能是public 或者 default。
    1. private, static, final 修飾的方法不能被覆寫赫粥,因?yàn)檫@些方法都是本類所獨(dú)有的观话。雖然可以在子類里面存在和父類里面一樣 static 和 private 所修飾的方法,但是和父類沒有任何關(guān)系越平,不能稱之為方法覆寫频蛔。
    • 不需要覆寫:正常的情況是直接從父類繼承過來方法
    class Parentclass {
        public void print() {
            System.out.println("Parentclass Print");
        }
    }
    
    class Subclass extends Parentclass {
    }
    
    public class Test {
        public static void main(String args[]) {
            Subclass sub = new Subclass();
            sub.print(); // 方法從父類繼承而來 ---> Parentclass Print
        }
    }
    
    • 覆寫情形一:從父類繼承方法邏輯,子類添加一些邏輯
    class SuperClass {
        void print() {
            System.out.println("super print");
        }
    }
    
    public class SubClass extends SuperClass {
        void print() {
            super.print(); //父類的邏輯
            System.out.println("sub print"); //子類的邏輯
        }
        
        public static void main(String[] args) {
            SubClass subClass = new SubClass();
            subClass.print();
        }
    }   
    
    • 覆寫情形二:子類獨(dú)立的邏輯
    class SuperClass {
        void print() {
            System.out.println("super print"); //父類的邏輯
        }
    }
    
    public class SubClass extends SuperClass {
        void print() {
            System.out.println("sub print"); //子類的邏輯
        }
        
        public static void main(String[] args) {
            SubClass subClass = new SubClass();
            subClass.print();
        }
    }   
    
  • this && super

    this是指當(dāng)前對(duì)象本身秦叛。在構(gòu)造函數(shù)一節(jié)已經(jīng)有接觸過this關(guān)鍵字晦溪,this和super一樣也可以調(diào)用屬性、調(diào)用方法和調(diào)用構(gòu)造函數(shù)书闸。那這兩個(gè)關(guān)鍵字有什么區(qū)別呢尼变?

    關(guān)鍵字 this super
    本質(zhì) 一個(gè)指向本對(duì)象的指針 一個(gè)Java關(guān)鍵字
    調(diào)用方法 訪問本類中的方法 直接訪問父類中的方法
    訪問屬性 訪問本類中的屬性,沒有繼續(xù)在父類中尋找 訪問父類中的屬性
    調(diào)用構(gòu)造器 調(diào)用本類構(gòu)造器浆劲,放在構(gòu)造器首行 調(diào)用父類構(gòu)造器,放在子類構(gòu)造器的首行

    說明:構(gòu)造函數(shù)里面只能調(diào)用一次super()或者this(), 并且super()或者this()必須放在第一行哀澈,是Java為了保證創(chuàng)建對(duì)象的不出現(xiàn)異常情況牌借。

方法重載(Method Overloading)

方法重載是指一個(gè)類可以有多個(gè)方法名相同但是方法的參數(shù)個(gè)數(shù)或者參數(shù)類型不同的多個(gè)方法存在,和構(gòu)造函數(shù)可以有多個(gè)類似

  • 重載的類型

    • 參數(shù)個(gè)數(shù)不同

      add(int, int)
      add(int, int, int)
      
    • 參數(shù)類型不同

      add(int, int)
      add(int, float)
      
    • 參數(shù)的順序不同

      add(int, float)
      add(float, int)
      

    注意事項(xiàng): 方法的返回值不同不能算是方法重載割按,并且編譯器會(huì)報(bào)錯(cuò)膨报。

      int add(int, int)
      float add(int, int)
    

綁定(binding)

綁定是指將一個(gè)方法的調(diào)用與方法所在的類關(guān)聯(lián)起來。Java中的綁定分為靜態(tài)綁定和動(dòng)態(tài)綁定适荣,又被稱作前期綁定和后期綁定现柠。

  • 靜態(tài)綁定是指編譯器能在編譯時(shí)確定的綁定。主要是static, private, final 修飾的方法弛矛。因?yàn)檫@些方法不能夠被子類重寫够吩。

    class Human {
        public static void walk() {
            System.out.println("Human walks");
        }
    }
    
    class Boy extends Human{
        
        public static void walk() {
            System.out.println("Boy walks");
        }
        
        public static void main( String args[]) {
    
            Human obj = new Boy();
            Human obj2 = new Human();
            obj.walk();  // Human walks --- 方法綁定的是Human類
            obj2.walk(); // Human walks --- 方法綁定的是Human類
            
        }
    }
    
  • 動(dòng)態(tài)綁定是指編譯器不能在編譯時(shí)確定的綁定。方法覆寫是一個(gè)典型的動(dòng)態(tài)綁定丈氓,因?yàn)楦割惡妥宇惗加邢嗤姆椒ㄖ苎挥性谶\(yùn)行時(shí)才能確定是調(diào)用的那個(gè)類的方法强法。

    class Human {
        public void walk() {
            System.out.println("Human walks");
        }
    }
    
    class Boy extends Human{
        
        public void walk() {
            System.out.println("Boy walks");
        }
        
        public static void main( String args[]) {
    
            Human obj = new Boy();
            Human obj2 = new Human();
            obj.walk();  // Boy walks  --- 方法綁定的是Boy類
            obj2.walk(); // Human walks --- 方法綁定的是Human類
        }
    }
    

多態(tài)性(Polymorphism)

多態(tài)字面意思就是多種狀態(tài),例如同樣的一個(gè)方法具有不同功能的特性湾笛,多態(tài)也可以分為編譯時(shí)多態(tài)和運(yùn)行時(shí)多態(tài)饮怯。

  • 運(yùn)行時(shí)多態(tài)---方法覆寫為代表

    Animal.java

    public class Animal {
       public void say() {
          System.out.println("Animal");   
       }
    }
    

    Dog.java

    class Dog extends Animal {
        
        @Override
        public void say() {
            System.out.println("Dog");
        }
        
        public static void main(String args[]){
            Animal obj = new Dog();
            obj. say(); // Dog
        }
    }
    

    Cat.java

    public class Cat extends Animal {
    
        @Override
        public void say() {
            System.out.println("Cat");
        }
        
        public static void main(String args[]){
            Animal obj = new Cat();
            obj. say(); // Cat
        }
    }
    

    通過代碼我們可能得到,雖然Cat和Dog對(duì)象都是調(diào)用的say()方法嚎研,但是表現(xiàn)卻有很大差異蓖墅。

  • 編譯時(shí)多態(tài)---方法重載為代表

    class AddClass
    {
        void add(int a) {
           System.out.println ("a: " + a);
        }
        
        void add(int a, int b) {
           System.out.println ("a and b: " + a + "," + b);
        }
        
        double add(double a) {
           System.out.println("double a: " + a);
           return a*a;
        }
    }
    
    class PolyDemo
    {
        public static void main (String args [])
        {
            AddClass obj = new AddClass();
            obj.add(10);      // a: 10
            obj.add(10, 20);  // a and b: 10,20
            double result = obj.add(5.5); // double a: 5.5
            System.out.println("result : " + result); // result : 30.25
        }
    }
    

    通過代碼我們可能得到,雖然obj都是調(diào)用的add()方法临扮,但是表現(xiàn)卻有很大差異置媳。

擴(kuò)展:抽象類的抽象方法和接口也是實(shí)現(xiàn)多態(tài)的兩種方式,會(huì)在接下來的章節(jié)詳細(xì)介紹公条。

抽象類(Abstract Class)

abstract關(guān)鍵字修飾的類就是抽象類(Abstract Class)拇囊,抽象類里面可以有抽象方法(沒有方法體)也可以有普通方法。但是普通類里面不能定義抽象方法靶橱。

注意:抽象類不能被實(shí)例化寥袭,就是說不能被用來創(chuàng)建對(duì)象。

  • 抽象類應(yīng)用場景关霸?

    想象一下這種情況传黄,有一個(gè)動(dòng)物類,有一個(gè)發(fā)聲(sound)的方法队寇,動(dòng)物類有一些子類膘掰,例如貓類,狗類佳遣,獅子類识埋,老虎類等等,這些子類也需要有覆寫發(fā)聲(sound)的方法零渐,但是動(dòng)物類沒有一個(gè)通用的發(fā)聲(sound)需要窒舟,所以父類的sound其實(shí)是不需要執(zhí)行任何操作的。

    所以抽象類的場景就是父類有方法不需要任何操作诵盼,但是子類必須覆寫這個(gè)方法惠豺。

```
abstract class Animal {
    abstract void sound(); // 這個(gè)方法讓子類去自己實(shí)現(xiàn)
    
    void anothermethod() { // 普通的方法
        System.out.println("another method");
    }
}

class Dog extends Animal {
    @Override
    void sound() { // 必須實(shí)現(xiàn)這個(gè)方法
        System.out.println("wang wang wang");
    }
}
```
  • 抽象類的規(guī)則

    1. 如果父類有方法不需要自己實(shí)現(xiàn),就可以把父類申明為抽象類(abstract class)风宁。繼承抽象類的子類必須實(shí)現(xiàn)未實(shí)現(xiàn)的抽象方法洁墙。
    2. 抽象類不能被實(shí)例化,如果要使用必須用一個(gè)子類繼承抽象類并實(shí)現(xiàn)抽象方法戒财,然后用子類對(duì)象調(diào)用相應(yīng)的方法热监。
    3. 抽象類的子類如果實(shí)現(xiàn)抽象類的所有方法,那子類也必須定義為抽象類固翰,子類也不能被實(shí)例化狼纬。

    抽象類為什么不能實(shí)例化羹呵?因?yàn)槌橄箢惒皇峭暾念悾械姆椒]有實(shí)現(xiàn)疗琉。如果允許抽象類實(shí)例化冈欢,那么對(duì)象調(diào)用未實(shí)現(xiàn)的方法就會(huì)出現(xiàn)問題∮颍可以理解抽象類為一個(gè)模板凑耻,必須繼承抽象類然后再使用。

接口(Interface)

接口和類相似但是又不是一個(gè)類柠贤,接口的方法都不能有實(shí)現(xiàn)(完全抽象)香浩,接口的屬性訪問修飾是public,static臼勉,final

  • 定義

    接口定義用interface關(guān)鍵字邻吭,interface里面的所有方法都不需要實(shí)現(xiàn)。

    interface MyInterface {
        // 屬性只能是常量宴霸,且必須是 public static final 修飾
        int number = 1; // 等價(jià)于 public static final int number = 1;
        
        // 接口所有的方法都是抽象的
        public void func1();
        public void func2();
    }
    

    接口的使用是用 類 implements 接口, 需要實(shí)現(xiàn)接口的所有方法囱晴。

    class InterfaceDemo implements MyInterface {
    
        @Override
        public void func1() {
            System.out.println("implementation of func1");
        }
    
        @Override
        public void func2() {
            System.out.println("implementation of func2");
        }
        
    }
    
  • 接口繼承

    接口里的方法沒有實(shí)現(xiàn),所以不能實(shí)現(xiàn)另外(implements)一個(gè)接口瓢谢,但是可以繼承(extends)另外一個(gè)接口畸写。

    interface Interface1 {
        public void func1();
    }
    
    interface Interface2 extends Interface1 { // 接口繼承
        public void func2();
    }
    
    class InterfaceDemo implements Interface2 {
    
        @Override
        public void func1() {
            System.out.println("func1");
        }
    
        @Override
        public void func2() {
            System.out.println("func2");
        }
        
    }
    
  • 標(biāo)記接口(Marker Interface)

    標(biāo)記接口有時(shí)也叫標(biāo)簽接口(Tag interface),即接口不包含任何方法. JDK里的Serializable接口就是一個(gè)標(biāo)記接口. 和名字對(duì)應(yīng)氓扛,標(biāo)記接口的作用就是標(biāo)識(shí)某個(gè)類型或者元數(shù)據(jù)枯芬。

    public interface Serializable {
    }
    
  • 嵌套接口

    接口可以定義在接口或者類的內(nèi)部,這些接口被稱為嵌套接口采郎,或者也可以稱為內(nèi)部接口千所。例如Java的Map接口內(nèi)部定義的Entry接口,我們可以用Map.Entry去訪問這個(gè)接口尉剩,而不能直接用Entry去訪問它真慢。

  • 接口的一些關(guān)鍵點(diǎn)總結(jié)

    1. 接口不能初始化對(duì)象
    2. 接口的方法都是抽象方法,接口的屬性都是 public static final且必須賦值
    3. 接口的使用是實(shí)現(xiàn)implements
    4. 接口的方法都是abstract public理茎,所以實(shí)現(xiàn)的類前面都必須加上public
    5. 類必須實(shí)現(xiàn)接口的所有方法,否則只能定義為抽象類
    6. 接口不能定位為private和protected
    7. 接口可以繼承其他的接口管嬉,但是不能實(shí)現(xiàn)其他接口
    8. 一個(gè)類可以實(shí)現(xiàn)任意數(shù)量的接口
    9. 如果有多個(gè)接口里面有相同的方法皂林,只需要實(shí)現(xiàn)一個(gè)即可。
    10. 如果多個(gè)接口里面有方法名和參數(shù)一致但是返回值不一樣的情況是不能允許的蚯撩。
    11. 如果多個(gè)接口有相同的屬性名础倍,可以通過接口.屬性加以區(qū)分。例如A.x 和 B.x
  • 接口和抽象類的區(qū)別

    抽象類 接口
    抽象類只能繼承其他的類或者抽象類 接口只能繼承接口
    抽象類可以有個(gè)抽象方法和正常方法 接口都是抽象方法
    定義抽象方法前面的abstract是必須的 接口都是abstract方法胎挎,所以abstract可寫可不寫
    抽象類的屬性可以是protected和public的訪問權(quán)限 接口都是public的訪問權(quán)限
    抽象類的屬性的權(quán)限沒有限制 接口屬性是public static final

封裝(Encapsulation)

封裝包括隱藏?cái)?shù)據(jù)和功能的具體實(shí)現(xiàn)沟启,是將該類中所有對(duì)象的屬性和行為隱藏起來忆家,并為其他對(duì)象提供一些訪問的方法。例如:把屬性定義為私有的德迹,定義一些外部類能訪問的方法對(duì)私有屬性進(jìn)行操作芽卿;再例如把Person類的speak功能做為一個(gè)方法,外部直接調(diào)用speak功能胳搞。

public class Person {
    // 一些私有的屬性卸例,外部類不能訪問
    private int age;
    private String name;
    
    // 定義一些外部類能訪問的方法,對(duì)私有屬性進(jìn)行操作
    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
    
    public void speak() {
        System.out.println(this.name);
    }
    
    public static void main(String[] args) {
        
        Person person = new Person();
        person.setName("Johnny");
        person.setAge(18);
        System.out.println(person.name);
        System.out.println(person.age);
        
        person.speak();
    }
    
}
  • 封裝的好處

    1. 可以隱藏內(nèi)部屬性肌毅,且可以把屬性變?yōu)橹蛔x(只有g(shù)et方法)或者只寫(只有set方法)或者讀寫皆可(有set和get方法)筷转。
    2. 外部類可以不用關(guān)心內(nèi)部實(shí)現(xiàn),只需要調(diào)用就行悬而。
    3. 增加代碼的可復(fù)用性呜舒,因?yàn)榉椒梢栽谌魏螘r(shí)間被修改,外部不需要關(guān)心笨奠,只要調(diào)用同樣的一個(gè)方法可以實(shí)現(xiàn)多種功能袭蝗。
  • 包(Package)

    Java中的包(Package)用來組織類和接口。系統(tǒng)定義了一些包艰躺,開發(fā)者也可以自定義自己的包呻袭。以java.util.Random; 為例: java是一個(gè)頂級(jí)包,util是一個(gè)工具類包腺兴,而Random是util包下面的一個(gè)類左电。

    • 包的優(yōu)勢

      1. 代碼復(fù)用---項(xiàng)目中可能有很多重復(fù)的功能需要實(shí)現(xiàn),這樣就可以把這些功能用一個(gè)包組織起來页响,當(dāng)需要使用的時(shí)候直接引用包就可以了篓足。
      2. 代碼結(jié)構(gòu)更清晰---可以用包把各個(gè)功能模塊區(qū)分出來,每個(gè)包負(fù)責(zé)相應(yīng)的功能闰蚕,這樣代碼結(jié)構(gòu)更清晰栈拖,且使用起來更有效率。
      3. 解決命名沖突---在不同的包里面可以有相同的類没陡,這樣就解決了命名沖突涩哟。
    • 包的使用方式

      新建一個(gè)名稱為com.johnny.Calculate的包(package),然后在這個(gè)包里面新建一個(gè)Calculator類, Calculator類里面添加一個(gè)add方法盼玄。

      package com.johnny.Calculate;
      
      public class Calculator {
          
          public int add(int num1, int num2) {
              return num1 + num2;
          }
      }
      

      說明:類的頂部需要有包的申明贴彼。package com.johnny.Calculate;

      使用方法一: 不引用包,直接用包.類來使用類埃儿。

      package com.johnny;
      
      public class Math {
          public static void main(String[] args) {
              // 直接用包名.類名 不需要import包
              com.johnny.Calculate.Calculator calculator = new com.johnny.Calculate.Calculator();
              int result = calculator.add(10, 20);
              System.out.println(result); // 30
          }
      }
      

      使用方法二:在com.johnny包的Math類中引入包import com.johnny.Calculate.Calculator;, 然后就可以直接使用Calculator類器仗。

      package com.johnny;
      import com.johnny.Calculate.Calculator;
      
      public class Math {
          public static void main(String[] args) {
              Calculator calculator = new Calculator();
              int result = calculator.add(10, 20);
              System.out.println(result); // 30
          }
      }
      
      1. 包的申明package com.johnny;必須寫在引入包import com.johnny.Calculate.Calculator;的前面
      2. import的作用就是為了減少使用類或者接口時(shí)前面需要帶上一長串的包名,讓代碼的書寫更加的簡潔和易讀。類似于不用import時(shí)使用一個(gè)類不許用全名精钮,而import后只需要叫名就可以了威鹿。
    • 子包

      一個(gè)包在另外一個(gè)包里面,就把這個(gè)包稱為子包轨香。再以import java.util.Random;為例:util包就是java包的子包忽你,上面例子com.johnny.Calculate, Calculate包就是johnny包的子包,johnny包是com包的子包弹沽。包的層級(jí)用"."連接檀夹。

- **static引包**      
    
    我們普通引包主要是為了復(fù)用其他包的public的類和接口,而static引包是為了使用其他包定義的靜態(tài)的屬性和方法策橘,而不需要在屬性和方法前面加上類名炸渡。
    `java.lang.Math`包中有很多數(shù)學(xué)計(jì)算的靜態(tài)方法,例如sqrt,tan等,正常的引用方法如下:
    
    ```
    // 普通引包-- import包名
    import java.lang.Math;
    
    public class Demo {
        
        public static void main(String[] args) {
            // 如果是普通引包,使用類.靜態(tài)方法和類.靜態(tài)屬性
            double number1 = Math.sqrt(4);
            System.out.println(number1); // 2.0
        }
        
    }
    ```
    
    ```
    // 靜態(tài)引包--在import 和 包名.類名 中間加上 static 關(guān)鍵字
    import static java.lang.Math.*;
    
    public class Demo {
        
        public static void main(String[] args) {
            // 如果是靜態(tài)引包剑肯,可以直接使用靜態(tài)方法和靜態(tài)屬性
            double number1 = sqrt(4);
            System.out.println(number1); // 2.0
        }
        
    }
    ```
    
    > 說明:當(dāng)使用類的靜態(tài)屬性或者靜態(tài)方法比較頻繁時(shí)候比較實(shí)用,例如需要頻繁進(jìn)行數(shù)學(xué)計(jì)算方法時(shí)建議用static引入吼畏。靜態(tài)引入的缺點(diǎn)是對(duì)包的信息不了解,容易造成沖突和混淆嘁灯。
    
- ** 關(guān)于包的兩點(diǎn)說明**

    1. 多個(gè)包中可能出現(xiàn)命名沖突泻蚊,此時(shí)使用時(shí)需要用**包名.類名**。
        例如A包里面有一個(gè)Person類丑婿,B包里面也有一個(gè)Person類性雄。此時(shí)不能申明引入兩個(gè)Person類,因?yàn)橛忻麤_突羹奉。
        
        ```
        import A.Person;
        import B.Person; // 會(huì)有編譯錯(cuò)誤秒旋,因?yàn)槊麤_突了
        ```
        
        可以用通配符引入的方式即引入包下的所有類去解決,使用時(shí)候需要指明包名。
        
        ```
        import A.*;
        import B.*;
        
        public class PackageDemo {
            
            public static void main(String[] args) {
                
                A.Person personA = new A.Person();
                B.Person personB = new B.Person();
                
            }
        }
        ```
        
    2. 用通配符引入的時(shí)候需要特別注意诀拭,假設(shè)有個(gè)A包迁筛,A包下面有Person類,A包下面還有一個(gè)B包耕挨,B包下面有Dog细卧,Cat兩個(gè)類。如果用 `import A.*;` 此時(shí)只引入了Person類筒占,并未引入了B包下的Dog和Cat類酒甸。如果使用`import A.B.*;`, 此時(shí)引入的是Dog和Cat類。如果要把三個(gè)類都引入需要使用`import A.*; import A.B.*;`
  • 訪問修飾符

    Java中的訪問修飾符有四種赋铝,public,protected, default, private,對(duì)應(yīng)的訪問權(quán)限如下;

    訪問修飾符 同類 同包不同類(不含子類) 同包子類 不同包子類 不同包不同類(不含子類)
    public YES YES YES YES YES
    protected YES YES YES YES NO
    default YES YES YES NO NO
    private YES NO NO NO NO

final

final關(guān)鍵字能修飾屬性沽瘦,方法和類革骨,接下來進(jìn)行詳細(xì)介紹农尖。

  • final修飾變量

    final修飾的變量可以被認(rèn)為是常量, 變量一旦賦值后不能修改良哲。

    public class Person {
        final String name = "Johnny"; //申明時(shí)賦值
        
        public void changeName() {
            name = "Liu"; //此時(shí)編譯器會(huì)報(bào)錯(cuò)
        }
    }
    

    final修飾的變量可以不在申明時(shí)候定義盛卡,此時(shí)必須在構(gòu)造函數(shù)中進(jìn)行賦值,且以后不能修改筑凫。用于定義不變的的一些變量滑沧。

    public class Person {
        
        final String name;
        
        public Person(String name) {
            this.name = name;
        }
        
    }
    

    如果final申明的static變量在申明時(shí)未賦值,則必須在static代碼塊中賦值.

    public class Person {
        
        final static String name;
        
        static {
            name = "Johnny";
        }
        
    }
    
    
  • final修飾方法

    final修飾的方法是不能被子類重寫的巍实。否則會(huì)編譯錯(cuò)誤滓技。

    說明點(diǎn):構(gòu)造函數(shù)不能定義為final;

  • final修飾類

    final修飾的類是不能被子類化的棚潦,也就是說其他類不能繼承final類令漂。Java中的String,Integer 等類都是final類丸边。
    系統(tǒng)提供的final類 --- enum

    在其他語言中叠必,枚舉(enum)一般是一系列整型或者字符串集合,但是Java中的枚舉其實(shí)是一個(gè)final類妹窖,可以表示一系列的數(shù)據(jù)的集合纬朝,也能有方法和變量。

    用枚舉定義常量

    enum WeekDay {  
        Monday, Tuesday, Wednesday, Thursday, Friday, Saturday, Sunday;
    }
    
    System.out.println(WeekDay.Monday);  // Monday 注意此時(shí)不是字符串
    

    枚舉可以添加變量和方法

    enum PersonEnum {   
        Johnny(18), Lan(17), Wen(1);
        
        private int age;
        
        // 定義了屬性必須要有構(gòu)造方法骄呼,且必須為私有方法
        private PersonEnum(int age) {
            this.setAge(age);
        }
    
        public int getAge() {
            return age;
        }
    
        public void setAge(int age) {
            this.age = age;
        }
    }
    

    枚舉可以實(shí)現(xiàn)接口

    
    public interface EnumInterface {
        void speak();
    }
    
    // 實(shí)現(xiàn)接口
    enum InterfaceEnum implements EnumInterface {
        A, B;
        
        @Override
        public void speak() {
            System.out.println(this);
        }
        
    }
    
    // 使用
    InterfaceEnum.A.speak(); //A
    
    

    說明:枚舉可以使用在switch語句中共苛。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者

  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市谒麦,隨后出現(xiàn)的幾起案子俄讹,更是在濱河造成了極大的恐慌,老刑警劉巖绕德,帶你破解...
    沈念sama閱讀 218,858評(píng)論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件患膛,死亡現(xiàn)場離奇詭異,居然都是意外死亡耻蛇,警方通過查閱死者的電腦和手機(jī)踪蹬,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,372評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來臣咖,“玉大人跃捣,你說我怎么就攤上這事《嵘撸” “怎么了疚漆?”我有些...
    開封第一講書人閱讀 165,282評(píng)論 0 356
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經(jīng)常有香客問我娶聘,道長闻镶,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,842評(píng)論 1 295
  • 正文 為了忘掉前任丸升,我火速辦了婚禮铆农,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘狡耻。我一直安慰自己墩剖,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,857評(píng)論 6 392
  • 文/花漫 我一把揭開白布夷狰。 她就那樣靜靜地躺著岭皂,像睡著了一般。 火紅的嫁衣襯著肌膚如雪孵淘。 梳的紋絲不亂的頭發(fā)上蒲障,一...
    開封第一講書人閱讀 51,679評(píng)論 1 305
  • 那天,我揣著相機(jī)與錄音瘫证,去河邊找鬼揉阎。 笑死,一個(gè)胖子當(dāng)著我的面吹牛背捌,可吹牛的內(nèi)容都是我干的毙籽。 我是一名探鬼主播,決...
    沈念sama閱讀 40,406評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼毡庆,長吁一口氣:“原來是場噩夢(mèng)啊……” “哼坑赡!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起么抗,我...
    開封第一講書人閱讀 39,311評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤毅否,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后蝇刀,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體螟加,經(jīng)...
    沈念sama閱讀 45,767評(píng)論 1 315
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,945評(píng)論 3 336
  • 正文 我和宋清朗相戀三年吞琐,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了捆探。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,090評(píng)論 1 350
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡站粟,死狀恐怖黍图,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情奴烙,我是刑警寧澤助被,帶...
    沈念sama閱讀 35,785評(píng)論 5 346
  • 正文 年R本政府宣布剖张,位于F島的核電站,受9級(jí)特大地震影響恰起,放射性物質(zhì)發(fā)生泄漏修械。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,420評(píng)論 3 331
  • 文/蒙蒙 一检盼、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧翘单,春花似錦吨枉、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,988評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至认臊,卻和暖如春圃庭,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背失晴。 一陣腳步聲響...
    開封第一講書人閱讀 33,101評(píng)論 1 271
  • 我被黑心中介騙來泰國打工剧腻, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人涂屁。 一個(gè)月前我還...
    沈念sama閱讀 48,298評(píng)論 3 372
  • 正文 我出身青樓书在,卻偏偏與公主長得像,于是被迫代替她去往敵國和親拆又。 傳聞我的和親對(duì)象是個(gè)殘疾皇子儒旬,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,033評(píng)論 2 355

推薦閱讀更多精彩內(nèi)容

  • 1. Java基礎(chǔ)部分 基礎(chǔ)部分的順序:基本語法,類相關(guān)的語法帖族,內(nèi)部類的語法栈源,繼承相關(guān)的語法,異常的語法竖般,線程的語...
    子非魚_t_閱讀 31,639評(píng)論 18 399
  • 廢話不多說甚垦,自己進(jìn)入今天的主題 1、面向?qū)ο蟮奶卣饔心男┓矫妫?答:面向?qū)ο蟮奶卣髦饕幸韵聨讉€(gè)方面: - 抽象:...
    傳奇內(nèi)服號(hào)閱讀 2,351評(píng)論 1 31
  • 晶捻激,小毛毛制轰,嗯!有點(diǎn)小冷酷胞谭,不過挺小巧的垃杖,如你的名字一樣。 芮逸嬌丈屹,床下的床鋪空空的调俘,有沒有想起以前給你講的那個(gè)特...
    小智噠噠閱讀 119評(píng)論 0 0
  • 這個(gè)周末彩库,我在廣州玩了一場人生游戲肤无,從20歲開始玩起,我在40的時(shí)候就破產(chǎn)骇钦,最后選擇“自殺”宛渐。 回來后,我一直在想...
    彭小六閱讀 6,588評(píng)論 11 163
  • 日暮蒼山遠(yuǎn)眯搭,天寒白屋貧窥翩。柴門聞犬吠,風(fēng)雪夜歸人鳞仙。 -劉長卿《逢雪宿芙蓉山主人》 1 ...
    eileen01閱讀 490評(píng)論 5 1