Java基礎-面相物件篇

weixin_34075551發表於2018-11-04


成員變數和區域性變數

  • 成員變數:定義在類中

  • 區域性變數:定義在方法中

public class  Car{
  int a;
  public void show(){
    //int b;
    System.out.println(a);
    //System.out.println(b); 沒有賦值,報錯
  }
  public static void main(){
    Car c = new Car();
    c.show();
  }
}

在沒有賦值的情況下,區域性變數會報錯,但是成員變數Java會給為其新增一個預設值。

構造方法

通過new建立物件的過程其實是通過該類的構造方法建立建立物件,如果沒有手動定義構造方法,系統會自動新增一個無參的構造方法。但是如果一旦手動定義了有參的的構造方法,就不能通過無參的構造方法去建立物件。

public class Phone{
    // 如果沒有定義,預設會新增一個沒有任何操作的無參構造
    public Phone(){
        System.out.println("我是無參構造");
    }
    // 構造方法名與類名一致,有參
    public Phone(int age){
        System.out.println(age);
    }
}
public class Telphone{
    public static void main(String[] args){
        Phone phone = new Phone(); // 通過無參構造方法建立物件
        Phone phone = new Phone(20);  // 有參構造建立物件
    }
}

構造方法和普通方法一樣,都有過載的概念。

靜態static

我們知道一個類可以建立多個物件,每個物件都有自己的成員,如果要使得一個類和它的所有物件共享同一個成員(變數、方法),那麼就需要static了。

public class Person{
    static int age = 20;
    public static void main(String[] args){
        // 可以通過類名直接訪問
        System.out.println(Person.age); //20
        // 也可以通過物件名訪問
        Person person = new Person();
        System.out.println(person.age); //20
        // 通過物件修改
        person.age = 30;
        System.out.println(Person.age); // 30
    }
}

可以看出,通過類名和物件名呼叫的都是同一個age。

靜態方法和變數是一樣,這裡不敲程式碼了。但是需要注意幾點:

1、在靜態方法中,不能直接呼叫非靜態的方法和變數。如果想要呼叫可以通過建立物件的方法去呼叫非靜態成員。

2、在普通成員方法中,可以直接訪問同類的非靜態方法和靜態方法。

public class Person{
    String name = "神鵰俠侶";
    static int age = 100;
    //普通方法
    public void show(){
        System.out.println("北京歡迎你");
    }
    // 靜態方法
    publiic static void print(){
        // 靜態方法中不能訪問非靜態
        // System.out.println(name); 
        System.out.println(age); //可以訪問靜態變數
        // 不能直接訪問非靜態方法,可以通過物件訪問
        //show(); 
        Person person = new Person();
        person.show();
    }
}

初始化塊

Java中可以通過初始化塊進行資料賦值。這裡結合static介紹一下普通初始化塊和靜態初始化塊。

public class Hello{
    int num1;
    int num2;
    static int num3;
    public Hello(){
        num1= 10;
        System.out.println("建構函式中給num1賦值");
    }
    {
        num2 = 20;
        System.out.println("初始化塊中給num2賦值");
    }
    static {
        num3 = 30;
        System.out.println("靜態初始化塊中給num3賦值");
    }
    public static void main(String[] args){
        Hello hello = new Hello();
        // 靜態方法中呼叫非靜態成員,通過物件呼叫
        System.out.println("我是num1:"+hello.num1);
        System.out.println("我是num3:"+num3);
        Hello hello2 = new Hello();
    }
}
/*
靜態初始化塊中給num3賦值
初始化塊中給num2賦值
建構函式中給num1賦值
我是num1:10
我是num3:30
初始化塊中給num2賦值
建構函式中給num1賦值

通過列印結果可以看出:靜態初始化塊最先執行,然後執行普通初始化塊,最後才執行構造方法。但是靜態初始化塊只在類載入的時候執行一次,所以當第二次建立物件hello2的時候並沒有執行靜態初始化塊。

內部類

內部類就是定義在一個類之中的類,而這個被定義了內部類的類就是外部類。內部類有四種:

  • 成員內部類(普通內部類)

  • 靜態內部類

  • 方法內部類

  • 匿名內部類

1、成員內部類
  • 成員內部類可以使用任何訪問修飾符修飾

  • 成員內部類中可以訪問任何外部類的成員(不受訪問修飾符限制),外部類不能直接訪問內部類成員,只能通過建立內部類物件訪問。

  • 如果外部類和內部類定義了相同成員名,在內部類中預設訪問內部自己的成員,要訪問外部成員可以使用外部類名.this.成員名

  • 在內部類的靜態方法中不能直接通過new去建立一個內部類物件,而是通過外部類物件.new去建立。如果是普通方法中可以直接建立內部類

  • 定義了內部類的原始檔,變異後會生成兩個class檔案。外部類.class外部類$內部類.class

//外部類HelloWorld
public class HelloWorld{
    //外部類的私有屬性name
    private String name = "imooc";
    //外部類的成員屬性
    int age = 20;
    
    //成員內部類Inner
    public class Inner {
        String name = "xiaoxiao";
        //內部類中的方法
        public void show() { 
            // 訪問外部與內部同名私有變數,用外部類.this.變數
            System.out.println("外部類中的name:" + HelloWorld.this.name);
            //預設訪問的是內部類的變數
            System.out.println("內部類中的name:" + name);
            // 直接訪問外部類成員
            System.out.println("外部類中的age:" + age);
        }
    }
    // 靜態方法中不能直接呼叫內部類方法
    public static void main(String[] args) {
        //先建立外部類的物件
        HelloWorld o = new HelloWorld (); 
        //通過外部類物件.new建立內部類的物件
        Inner inn = o.new Inner();
        //呼叫內部類物件的show方法
        inn.show();
    }
    // 如果是普通方法可以直接建立內部類物件
    /*public void console(){
        Inner inn = new Inner();
        inn.show();
    }*/
}
2、靜態內部類

其實靜態內部類就是給普通內部類新增了static修飾符。但是在使用上還是有多大區別的。

  • 靜態內部類不能直接訪問外部類的非靜態成員,可以通過new建立外部類物件的方式訪問。

  • 如果外部類的靜態成員和內部類成員名相同,可以通過外部類名.成員名訪問該成員,如果成員名不相同,可以直接訪問外部類的靜態成員。

  • 建立靜態內部類物件的時候也不需要向普通內部類那樣先建立外部類了,可以直接通過new建立內部靜態類物件。

public class HelloWorld {
    // 外部類中的靜態變數score
    private static int score = 84;
    private static int age = 100;
    // 建立靜態內部類
    public static class SInner {
        // 內部類中的變數score
        int score = 91;
        public void show() {
            System.out.println("訪問外部類中的靜態變數score:" + HelloWorld.score); //外部類名.外部靜態成員名
            System.out.println("訪問內部類中的score:" + score);
            System.out.println("訪問外部類中的靜態變數age:" + age); //直接外部靜態成員名
        }
    }
    public static void main(String[] args) {
        // 直接建立內部類的物件
        SInner si = new SInner();
        // 呼叫show方法
        si.show();
    }
}
3、方法內部類

方法內部類的位置略有不同,它是在外部類的某一個方法中。並且只在該方法內可用。

public class HelloWorld {
    // 外部類中的show方法
    public void show() { 
        // 定義方法內部類,不能使用訪問控制符合static修飾符
        class MInner {
            int score = 83;
            public int getScore() {
                return score;
            }
        }
        // 建立方法內部類的物件
        MInner mi = new MInner();
        // 呼叫內部類的方法
        int score = mi.getScore();
        System.out.println("成績:" + score );
    }
}

由於方法內部類只能在方法內使用,所以方法內部類不允許使用訪問控制符合static修飾符。

4、匿名內部類

匿名內部類存在的前提是存在一個類(抽象類、具體類都可)或者介面,主要針對不能直接建立物件的抽象類或者介面。應用場景不止一種,剛剛接觸理解可能不到位,就只寫個簡單例子。

public class Main{
    public static void main(){
        Outer o = new Outer();
        o.show();  //我是一個內部類,實現Inter介面
        o.show2(); //我是一個匿名內部類,實現Inter介面
    }
}
//建立一個介面
interface Inter{
    public void print();
}
// 建立外部類
class Outer{
    // 通過建立內部類實現介面,重寫方法
    class Inner implements Inter{
        public void print(){
            System.out.println("我是一個內部類,實現Inter介面");
        }
    }
    public void show(){
        Inner inner = new Inner(); //普通方法中(非靜態),可以直接建立內部類物件
        inner.print();
    }

    // 通過匿名內部類建立,匿名內部類要在方法中
    public void show2(){
        new Inter(){
            public void print(){
                System.out.println("我是一個匿名內部類,實現Inter介面");
            }
        }.print();
    }
}

通過以上程式碼可以看到,通過匿名內部類實現介面的程式碼更簡潔,但是理解起來會稍微吃力一些。

繼承


final關鍵字

final是“最終”的意思,顧名思義,被final修飾的類不能被繼承;修飾的方法不能被重寫;屬性不能被預設初始化賦值,只能在定義時賦值或者在構造方法中賦值;變數不能被二次賦值,只能在宣告的時候賦值,即常量。

super的使用

如果子類重寫了父類的方法或者屬性,通過super可以在子類中呼叫父類的方法和屬性。

public void show(){
    // 直接列印子類自己的屬性age
    System.out.println(age);
    // 通過super呼叫父類的age屬性
    System.out.println(super.age);
}
// 呼叫方法亦是如此

super與構造方法也有很大的聯絡,在一個子類的構造方法中,必須要通過super()呼叫父類的構造方法,如果要寫一定要寫在構造方法的第一行,如果沒寫系統會隱式新增super(),預設呼叫父類無參構造。

如果在父類中自己手動新增了有參的構造方法,系統就不會預設生成一無參的構造方法,此時如果在子類不手動呼叫super(),隱式呼叫無參構造時就會報錯。

Object類

和js一樣Object類是所有類的父類,如果一個類沒有繼承任何類那麼它就預設繼承Object類。可以使用Object類的所有方法,比如toString()equals()。在第一篇基礎文章中已經簡單介紹過equals()了。

多型


所謂多型,就是同一個行為有多種不同的表現形態。在物件導向中,多型的前提一定是繼承關係。在建立物件時可以用父類指向父類物件,也可以用父類指向子類物件。

public class Man extends Person{
    public static void main(){
        Person p = new Person();
        Person p2 = new Man();
    }
}

以上僅僅是舉個例子用來理解多型的引用,當然如果父類指向父類物件,那使用方法時就使用父類的方法,沒有問題;如果父類指向子類物件,那麼使用方法時就指向子類的方法。如果子類沒有重寫該方法就指向子類繼承下來的方法。

引用型別轉換

之前在基礎介紹過基礎型別轉換,其實引用型別轉換也是同樣的,也分為自動型別轉換(子類轉向父類)和強制型別轉換(父類轉向子類)。

還是繼續父類(Person)和子類(Man)的例子:

Man man = new Man (); //建立一個子類物件
Person person = man; // 賦值給一個父類型別的變數,會進行自動型別轉換

如果又要把person變回Man型別呢?這時是父類向子類轉(大轉小),需要進行強制型別轉換。

Man man2 = (Man) person;

但是問題來了,以上程式碼暫時沒有什麼問題,如果我要把person轉成另外一個子類Woman呢?

Woman woman = (Woman) person;

這樣是會報錯的,要想避免這種情況出現,我們需要使用到instanceof 進行判斷。某個物件是否屬於某個型別

if(person instanceof Woman){
    Woman woman = (Woman) person;
}else{
    System.out.println("該物件不屬於Woman型別,不能進行強制型別轉換");
}

抽象類

抽象類的作用其實是來約束子類必須實現哪些方法,而不會關心具體是怎麼實現的。

abstract class Person{
    public abstract void show();
}

以上程式碼是最簡單的抽象類的寫法,通過abstract關鍵字來生命抽象類或者抽象方法。抽象方法沒有方法體,直接以;結束。

定義了抽象方法的類一定是抽象類。

所以,所有要繼承Person的子類,一定要實現show方法,但是這個方法內你要實現什麼無所謂。

介面

這裡額外說明一點:可能抽象類和介面放在多型的標題下不合適。由於是邊學習邊總結,所以筆記是跟著教學走的。

  • 介面沒有構造方法,不能被例項化

  • 一個介面可以繼承多個介面(類只能單繼承)

  • 一個類可以繼承一個父類,實現多個介面,介面直接用,分隔,先寫繼承再寫實現。

  • 介面中的變數一定都是常量(不要糾結有語病),方法一定都是抽象方法。

public (abstract) interface Inter{
    public static final String name = "人類";
    public (abstract) void show();
}
public class Man extents Person implements Inter{
    public void show(){
        System.out.println("必須實現介面的抽象方法");
    }
}

介面中的變數和方法只能使用以上修飾符修飾,括號中的abstract表示可以省略不寫,系統會自動新增。

結合之前學習的匿名內部類,我們來通過匿名內部類來實現一個介面

Inter inter = new Inter(){
    public void show(){
        System.out.println("匿名內部類實現介面");
    }
};
inter.show();

相關文章