Java面向對象三大特性

封裝

1. 封裝概述

利用抽象數據類型將數據和基於數據的操做封裝在一塊兒,使其構成一個不可分割的獨立實體。 數據被保護在抽象數據類型的內部,儘量地隱藏內部的細節, 只保留一些對外接口使之與外部發生聯繫。用戶無需知道對象內部的細節, 但能夠經過對象對外提供的接口來訪問該對象。程序員

2. 優勢

  • 減小耦合:能夠獨立地開發、測試、優化、使用、理解和修改
  • 減輕維護的負擔:能夠更容易被程序員理解,而且在調試的時候能夠不影響其餘模塊
  • 有效地調節性能:能夠經過剖析肯定哪些模塊影響了系統的性能
  • 提升軟件的可重用性
  • 下降了構建大型系統的風險:即便整個系統不可用,可是這些獨立的模塊卻有多是可用的

3. 成員變量和局部變量的區別

注意:局部變量名能夠和成員變量名同樣,在方法中使用的時候,採用就近原則。bash

4. 類的初始化過程

以Student類爲例:ide

public class Student {
    private String name;
    private int age;

    public Student() {
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }
}
Student s=new Student();複製代碼

具體步驟:函數

  1. 加載Student.class文件進內存
  2. 棧內存爲s開闢空間
  3. 堆內存爲學生對象開闢空間
  4. 對學生成員變量進行默認初始化
  5. 對學生成員變量進行顯示初始化
  6. 經過構造方法對學生對象的成員賦值
  7. 學生成員對象初始化完畢,將對象地址賦值給s變量。
封裝:一個標準的手機類的代碼及測試
手機類
public class SmartPhone {
    private String brand;
    private double price;
    private String color;

    public String getBrand() {
        return brand;
    }

    public void setBrand(String brand) {
        this.brand = brand;
    }

    public double getPrice() {
        return price;
    }

    public void setPrice(double price) {
        this.price = price;
    }

    public String getColor() {
        return color;
    }

    public void setColor(String color) {
        this.color = color;
    }

    public void call(String name){
        System.out.println("打電話給"+name);
    }

    public void sendMessage(String name){
        System.out.println("給"+name+"發短信");
    }

    public void playGame(){
        System.out.println("玩遊戲");
    }
}複製代碼
手機測試類1
public class SmartPhoneDemo {
    public static void main(String[] args) {
        SmartPhone sp=new SmartPhone();
        sp.setBrand("IPhone");
        sp.setPrice(6666);
        sp.setColor("白色");

        System.out.println("使用"+sp.getBrand()+"牌、售價爲"+sp.getPrice()+"元的"
                +sp.getColor()+"的手機");
        sp.call("Jobs");
        sp.sendMessage("Kuke");
        sp.playGame();
    }
}複製代碼

一個手機對象的內存簡圖:性能

手機測試類2
public class SmartPhoneDemo2 {
    public static void main(String[] args) {
        SmartPhone sp=new SmartPhone();
        sp.setBrand("IPhone");
        sp.setPrice(6666);
        sp.setColor("白色");

        System.out.println("使用"+sp.getBrand()+"牌、售價爲"+sp.getPrice()+"元的"
                +sp.getColor()+"的手機");
        sp.call("Jobs");
        sp.sendMessage("Kuke");
        sp.playGame();

        SmartPhone sp2=new SmartPhone();
        sp2.setBrand("小米");
        sp2.setPrice(1000);
        sp2.setColor("黑色");

        SmartPhone sp3=sp;
        sp.setPrice(3555);
        System.out.println("使用"+sp.getBrand()+"牌、售價爲"+sp.getPrice()+"元的"
                +sp.getColor()+"的手機");
    }
}複製代碼

多個手機對象的內存簡圖:測試

5. 靜態變量和成員變量的區別

6. 代碼塊

  • 靜態代碼塊:在類加載的時候就執行,且執行一次。通常用於對對象進行初始化。
  • 構造代碼塊:每次調用構造方法都執行且在構造方法以前執行。通常是對類進行初始化。

執行順序:優化

靜態代碼塊---構造代碼塊---構造方法ui

public class CodeBlock {
    //構造方法
    CodeBlock(){
        int a=10;
        System.out.println(a);
    }

    //構造代碼塊
    {
        int a=100;
        System.out.println(a);
    }

    //靜態代碼塊
    static {
        int a=1000;
        System.out.println(a);
    }

    public static void main(String[] args) {
        CodeBlock codeBlock=new CodeBlock();
    }
}複製代碼

輸出結果:this

1000
100
10複製代碼

繼承

1. 繼承概述

繼承實現了 IS-A 關係,例如 Cat 和 Animal 就是一種 IS-A 關係,所以 Cat 能夠繼承自 Animal,從而得到 Animal 非 private 的屬性和方法。spa

繼承應該遵循里氏替換原則,子類對象必須可以替換掉全部父類對象。

Cat 能夠當作 Animal 來使用,也就是說可使用 Animal 引用 Cat 對象。父類引用指向子類對象稱爲 向上轉型

Animal animal = new Cat();複製代碼

2. 優勢

  • 提升了代碼的複用性
  • 提升了代碼的維護性
  • 在類與之間產生了關係,是多態的前提

3. Java中繼承特色

  • Java只支持單繼承,不支持多繼承
  • Java支持多層繼承

4. Java中繼承注意事項

  • 子類只能只能繼承父類全部非私有成員(成員方法和成員變量)
  • 子類不能繼承父類的構造方法,可是能夠經過super關鍵在去訪問父類構造方法
  • 不要爲了部分功能而去繼承
  • 繼承中類之間體現的是「is a」的關係。

5. 繼承中成員變量的關係

  • 子類中成員變量和父類中成員變量名稱不一樣。
  • 子類中成員變量和父類中成員變量名稱相同。

在子類方法中的查找順序:

在子類方法的局部範圍找,有就使用

在子類的成員範圍找,有就使用

父類的成員範圍找,有就使用

若是還找不到,就報錯

6. 繼承中構造方法的關係

  1. 子類中全部的構造方法默認會都會訪問父類中空參數的構造方法

緣由:由於子類會繼承父類中的數據,可能還會使用父類的數據。因此,子類初始化以前, 必定要完成父類數據的初始化。

  1. 每個子類的構造方法第一條語句默認是:super();
  2. 若是父類中沒有無參構造方法,該怎麼辦呢?

方式一:子類經過super去顯示調用父類其餘的帶參的構造方法

方式二:子類經過this去調用本類的其餘構造方法 (子類必定要有一個去訪問父類的構造方法,不然父類數據就沒有初始化)

/**
* 1. 子類中全部的構造方法默認會都會訪問父類中空參數的構造方法
* 2. 每個子類的構造方法第一條語句默認是:super()
*/
class Father{
    public Father() {
        System.out.println("Father的無參構造函數");
    }
}

class Son extends Father{
    public Son() {
        //super();
        System.out.println("Son的無參構造函數");
    }
    public Son(String name) {
        //super();
        System.out.println("Son的帶參構造函數");
    }
}

public class InheritanceDemo {
    public static void main(String[] args) {
        //使用無參構造函數初始化
        Son son=new Son();
        System.out.println("===========");
        //使用帶參構造函數初始化
        Son son2=new Son("林青霞");
    }
}複製代碼

輸出結果:

Father的無參構造函數
Son的無參構造函數
===========
Father的無參構造函數
Son的帶參構造函數
/**
 * 3. 若是父類中沒有無參構造方法,該怎麼辦呢?

 方式一:子類經過super去顯示調用父類其餘的帶參的構造方法

 方式二:子類經過this去調用本類的其餘構造方法
 (子類必定要有一個去訪問父類的構造方法,不然父類數據就沒有初始化)
 */
class Parent{
    public Parent(String name){
        System.out.println("Parent的帶參構造函數");
    }
}

class Child extends Parent{
    public Child() {
        //子類經過super去顯示調用父類其餘的帶參的構造方法
        super("林青霞");
        System.out.println("Child的無參構造函數");
    }
    public Child(String name) {
        //方式一:子類經過super去顯示調用父類其餘的帶參的構造方法
        //super(name);
        //方式二:子類經過this去調用本類的其餘構造方法
        this();//嗲用Child()的無參構造函數
        System.out.println("Child的帶參構造函數");
    }
}

public class InheritanceDemo2 {
    public static void main(String[] args) {
        Child c = new Child();
        System.out.println("==============");
        Child c2 = new Child("林青霞");
    }
}複製代碼

輸出結果:

Parent的帶參構造函數
Child的無參構造函數
==============
Parent的帶參構造函數
Child的無參構造函數
Child的帶參構造函數複製代碼

7.類中成員變量初始化

一個類的成員變量的初始化:

  1. 默認初始化
  2. 顯式初始化
  3. 構造方法初始化

子類的初始化(分層初始化)

  • 先進行父類的初始化再進行子類的初始化
class X{
    Y b=new Y(); //顯式初始化

    X(){
        System.out.println("X");
    }
}

class Y{
    Y(){
        System.out.println("Y");
    }
}

public class Z extends X{
    Y y=new Y();

    public Z() {
        super();//子類的初始化(分層初始化):先進行父類的初始化,而後再進行子類的初始化。
        System.out.println("Z");
    }

    public static void main(String[] args) {
        new Z();
    }
}複製代碼

輸出結果:

Y
X
Y
Z複製代碼

7. 繼承中成員方法的關係

重寫(Override):子類和父類中出現瞭如出一轍的方法聲明

注意:

  1. 父類中私有成員方法不能被重寫(由於父類中私有成員方法不能被繼承)
  2. 子類重寫父類中方法時,訪問權限不能更低(最好一致)
  3. 父類是靜態方法時,子類也必須經過靜態方法進行重寫。
  4. 子類和父類的聲明最好如出一轍。

8. final關鍵字

final關鍵字是最終的意思,能夠修飾類,修飾變量,修飾成員方法

  • 修飾類:不能被繼承
  • 修飾變量:變量就變成了常量,只能被賦值一次
  • 修飾方法:方法不能被重寫

注意:

  1. final修飾局部變量

在方法內部,該變量不能夠被改變

在方法聲明上,分爲基本類型和引用類型的狀況

(1)基本類型:是值不能變

(2)引用類型:是地址不能變,可是該對象的堆內存中的值是能夠改變的

  1. final修飾變量的初始化時機

在對象構造完畢前便可(非靜態常量),被final修飾的變量只能賦值一次。

繼承:一個標準的動物類、貓類、狗類的代碼及測試
動物類
class Animal{
    private String name;
    private int age;
    private String color;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

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

    public String getColor() {
        return color;
    }

    public void setColor(String color) {
        this.color = color;
    }

    public void eat(){
        System.out.print("吃什麼東西?");
    }
}複製代碼
貓類
class Cat extends Animal{
    @Override
    public void eat() {
        super.eat();
        System.out.println("貓糧");
    }

    public void play(){
        System.out.println("小貓在玩耍");
    }
}複製代碼
狗類
class Dog extends Animal{
    @Override
    public void eat() {
        super.eat();
        System.out.println("狗糧");
    }

    public void bark(){
        System.out.println("小狗汪汪叫");
    }
}複製代碼
測試
public class AnimalDemo {
    public static void main(String[] args) {
        Dog dog=new Dog();
        dog.setName("旺財");
        dog.setAge(12);
        dog.setColor("黃色");
        System.out.println(dog.getName()+"\t"+dog.getAge()+"\t"+dog.getColor());
        dog.eat();
        dog.bark();

        Cat cat=new Cat();
        cat.setName("湯姆");
        cat.setAge(12);
        cat.setColor("藍色");
        System.out.println(cat.getName()+"\t"+cat.getAge()+"\t"+cat.getColor());
        cat.eat();
        cat.play();
    }
}複製代碼

多態

1. 多態概論

某一個事物,在不一樣時刻表現出來的不一樣狀態。 具體來說,就是調用同一方法,會執行不一樣的功能。

實際生活:水在不一樣時刻的狀態。

2. 多態分類

多態分爲編譯時多態和運行時多態:

  • 編譯時多態主要指方法的重載
  • 運行時多態指程序中定義的對象引用所指向的具體類型在運行期間才肯定

3. 運行時多態有三個條件

  • 繼承
  • 覆蓋(重寫)
  • 向上轉型

4. 多態的好處和弊端

好處

提升了代碼的維護性(由繼承保證)

提升了程序的擴展性(由多態保證)

弊端

不能訪問子類特有功能(特有方法)

如何訪問子類中的特有功能?

  • 方法一:將父類的引用強制轉換爲子類的引用,即向下轉型
  • 方法二:建立子類對象調用方法(能夠,可是不合理,而且佔內存)
/**
* 對象間的轉型問題:
    向上轉型:
        Parent f = new Child();
    向下轉型:
        Child z = (Child)f; //要求該f必須是可以轉換爲Zi的。(父到子)
 */
class Parent {
    public void show() {
        System.out.println("show fu");
    }
}

class Child extends Parent {
    public void show() {
        System.out.println("show zi");
    }

    public void method() {
        System.out.println("method zi");
    }

}

public class PolymorphismDemo {
    public static void main(String[] args) {
         Parent fu=new Child();
        fu.show();
        //fu.method();//不能訪問子類特有功能(特有方法)

        Child zi=(Child)fu; //向下轉型
        zi.show();
        zi.method();
    }
}複製代碼

輸出結果:

show zi
show zi
method zi
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() {

    }
}

public class PolymorphismDemo3 {
    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
    }
}複製代碼

多態中對象內存圖

5. 多態中的一些問題

/**
 * 多態中的成員訪問特色:
     A:成員變量
        編譯看左邊,運行看左邊。
    B:構造方法
         建立子類對象的時候,訪問父類的構造方法,對父類的數據進行初始化。
    C:成員方法
        編譯看左邊,運行看右邊。( 因爲成員方法存在方法重寫,因此它運行看右邊。)
    D:靜態方法
        編譯看左邊,運行看左邊。(靜態和類相關,算不上重寫,因此,訪問仍是左邊的)
 */
class Fu{
    public int num=100;

    public void show(){
        System.out.println("show function");
    }

    public static void function(){
        System.out.println("function Fu");
    }
}

class Zi extends Fu {
    public int num = 1000;
    public int num2 = 200;

    @Override
    public void show() {
        System.out.println("show Zi");
    }

    public void method() {
        System.out.println("method zi");
    }

    public static void function() {
        System.out.println("function Zi");
    }
}

public class PolymorphismDemo2 {
    public static void main(String[] args) {
        Fu f = new Zi();
        System.out.println(f.num);

        //System.out.println(f.num2);
        f.show();
        //找不到符號
        //f.method();
        f.function();
    }
}複製代碼

輸出結果:

100
show Zi
function Fu複製代碼
相關文章
相關標籤/搜索