C++ -> Java快速學習-基礎

CGWLMXUP發表於2019-01-25

熟悉C++的夥伴們可以簡單瀏覽一下Java的基礎語法,基本上和C++是相符的,因此這部分學習起來應該很簡單,下面簡單介紹回顧一下Java中的部分基礎知識。

物件導向基本概念
物件,從字面意思來看就是我們面對的物象。由此便可以知道,萬事萬物皆為物件。比如:一臺電腦,一輛汽車,一部手機等等都是物件。

物件導向,從字面意思來看就是我們人面對著一個物件。其實就是指我們從這個物件的整體出發去看它,它由哪些部件組成,它可以做到哪些事情。

比如我們想要買一部手機,我們想要記憶體大一點的,最新款的,CPU 運算快一點的,能實現發簡訊和打電話功能的手機。那麼這部手機是不是物件呢?它不是。當我們買了一部 iPhone 6 後,它滿足我們上面的所有資訊。於是我們拿在手上的這部 iphone 6 就是我們的物件。於是我們知道,物件一定是一個具體的、確定的物體。

而這部手機它的樣式,顏色,大小,產地,編號等等,便是這部手機的“屬性”,這部手機可以打電話、發簡訊,便是它的“行為”。

物件導向的思想,體現的是人所關注物件的資訊聚集在了一個具體的物體上。人們就是通過物件的屬性和行為來了解物件。


對於一個具體的物件而言,比如一部 iphone 6,世上還有許多跟這部手機有著同樣屬性或行為的物件,我們為了方便將它們歸類起來,提取出他們相同的屬性和行為,而我們把歸類起來的這個抽象的概念,稱之為類。

比如每個人就是一個物件,小張是一個物件,小明是一個物件。而每個人雖然不同,但卻有許多相同的屬性和行為,於是我們可以把他們抽象出來,變成一個類,比如人類。

類是封裝物件的屬性和行為的載體,反過來說具有相同屬性和行為的一類實體被稱為類。

由此可以總結出類的定義:

類是相同或相似物件的一種抽象,是物件的一個模板,它描述一類物件的行為和狀態。
類是具有相同屬性和方法(行為)的物件的集合
我們在上面反覆強調物件的屬性和行為,什麼是物件的屬性呢?什麼又是物件的行為呢?

屬性是物件具有的特徵。每個物件的每個屬性都擁有特定值。我們上面講過物件是一個具體並且確定的事物,正是物件屬性的值來區分不同的物件,比如我們可以通過一個人的外貌特徵區分他。

那什麼是物件的行為呢?在計算機中我們通過方法去實現物件的行為,而物件的方法便是物件所具有的操作,比如人會走路、會哭泣、會學習等等都是人的行為,也就是人的方法。

類和物件之間有什麼關係嗎?在上面的講解中大家應該有些瞭解了。類就是物件的抽象(或者模板),物件就是類的具體(或者例項)。比如手機是一個抽象的概念,它代表著類。而一部 iphone 6 便是手機具象化處理的實體,也就是一個物件。

說了那麼多,那我們如何在計算機中定義一個類,如何實現一個類呢? 我們以前說過,Java 是物件導向的語言,而他的體現就在於Java程式都以類 class 為組織單元。而一個類是物件的抽象,所以類由屬性和方法兩部分組成。

定義一個類,主要有三個步驟:

1、定義類名,用於區分不同的類。如下程式碼中 public class 後面跟的就是類名。class是宣告類的關鍵字,類名後面跟上大括號,大括號裡面就是類的一些資訊。public為許可權修飾符。

public class 類名{
//定義屬性部分(成員變數)
屬性1的型別 屬性1;
屬性2的型別 屬性2;
...
//定義方法部分
方法1;
方法2;
...
}
2、編寫類的屬性。物件有什麼,需要通過屬性來表示。屬性的定義是寫在類名後面的大括號裡,在定義屬性時,要明確屬性的型別。在一個類當中可以寫一個或多個屬性。當然也可以不定義屬性。

3、編寫類的方法。方法也是寫在大括號裡面。可以定義一個方法或多個方法,當然也可以不定義方法。

在home/project/目錄下建立原始碼檔案People.java

public class People {
//屬性(成員變數) 有什麼
    double height;  //身高
    int age;     //年齡
    int sex;    //性別,0為男性,非0為女性

//方法 幹什麼
    void cry(){
        System.out.println("我在哭!");
    }
    void laugh(){
        System.out.println("我在笑!");
    }
    void printBaseMes(){
        System.out.println("我的身高是"+height+"cm");
        System.out.println("我的年齡是"+age+"歲");
        if(this.sex==0)
            System.out.println("我是男性!");
        else 
            System.out.println("我是女性!");

    }
}
一個類可以包含以下型別變數:

區域性變數:在方法、構造方法或者語句塊中定義的變數被稱為區域性變數。變數宣告和初始化都是在方法中,方法結束後,變數就會自動銷燬。
成員變數:成員變數是定義在類中,方法體之外的變數。這種變數在建立物件的時候例項化。成員變數可以被類中方法、構造方法和特定類的語句塊訪問。
類變數:也叫靜態變數,類變數也宣告在類中,方法體之外,但必須宣告為static型別。
物件
建立物件的語法如下:

類名 物件名 = new 類名();
比如對People這個類,我想例項化LiLei這個人。LiLei 的資料型別便是 People 這個型別。(類可以看成使我們自己定義的資料型別)

People LiLei = new People();
定義類的時候不會為類開闢記憶體空間,但是一旦建立了物件,系統就會在記憶體中為物件開闢一塊空間,用來存放物件的屬性值和方法。新建一個NewObject.java 檔案。

public class NewObject {
    public static void main(String[] args) {
        People LiLei = new People(); //建立一個People物件LiLei

        LiLei.height =170;
        LiLei.age = 20;
        LiLei.sex = 1;

        LiLei.printBaseMes();
    }
}
編譯執行:

$ javac NewObject.java People.java
$ java NewObject
我的身高是170.0cm
我的年齡是20歲
我是女性!


建立物件後,我們就要使用物件了,使用物件無非就是對屬性和方法進行操作和呼叫。語法如下

//引用物件屬性
物件名.屬性

//引用物件方法
物件名.方法
例如對LiLei的身高賦值,並呼叫哭這個方法

LiLei.height = 170;
LiLei.cry();
剛剛我們引入了成員變數這個概念,那什麼是成員變數呢?成員變數就是指的物件的屬性,是在類中定義,來描述物件的特性。還有一種變數叫區域性變數,它是由類的方法定義,在方法中臨時儲存資料。

在使用時注意,成員變數可以被本類的所有方法所使用,同時可以被與本類有關的其他類所使用。而區域性變數只能在當前的方法中使用。

在這裡我們要講到一個關於作用域的知識了。作用域可以簡單地理解為變數的生存期或者作用範圍,也就是變數從定義開始到什麼時候消亡。

區域性變數的作用域僅限於定義它的方法內。而成員變數的作用域在整個類內部都是可見的。
同時在相同的方法中,不能有同名的區域性變數;在不同的方法中,可以有同名的區域性變數。
成員變數和區域性變數同名時,區域性變數具有更高的優先順序。 大家可以編寫程式碼驗證一下。
構造方法
在物件導向中有一個非常重要的知識點,就是構造方法。每個類都有構造方法,在建立該類的物件的時候他們將被呼叫,如果沒有定義構造方法,Java編譯器會提供一個預設構造方法。 建立一個物件的時候,至少呼叫一個構造方法。比如在新建一個物件new Object(),括號中沒有任何引數,代表呼叫一個無參構造方法(預設構造方法就是一個無參構造方法)。構造方法的名稱必須與類名相同,一個類可以定義多個構造方法。

構造方法的具體內容:

1、構造方法的名稱與類名相同,且沒有返回值。它的語法格式如下:

//與類同名,可以指定引數,沒有返回值
public 構造方法名(){
//初始化程式碼
}
下面是一個構造方法的例子:

public class People{
    //無參構造方法
    public People(){

    }
    //有一個引數的構造方法
    public People(int age){

    }
}
又例如具體的構造方法:

public class People {
//屬性(成員變數)有什麼
    double height;     //身高
    int age;           //年齡
    int sex;       //性別,0為男性,非0為女性

    //建構函式,初始化了所有屬性
    public People(double h, int a, int s){
        height = h;
        age = a;
        sex = s;
    }
}
//建立物件,呼叫我們自己定義的有參構造方法
People XiaoMing = new People(168, 21, 1);
上面的例子中通過new關鍵字將類例項化成物件,而new後面跟的就是構造方法。於是可以知道new + 構造方法可以建立一個新的物件。

2、如果在定義類的時候沒有寫構造方法,系統會預設生成一個無參構造方法,這個構造方法什麼也不會做。

3、當有指定的構造方法時,系統都不會再新增無參構造方法了。

4、構造方法的過載:方法名相同,但引數不同的多個方法,呼叫時會自動根據不同的引數選擇相應的方法。

引用與物件例項
在新建物件例項時,需要為物件例項設定一個物件名,就像這樣

Object object=new Object();
那麼變數object就真的是Object物件麼,這裡其實只是建立了一個object物件的引用。如果同學們學過C語言,這裡就和指標一樣,變數object儲存的其實Object物件的引用,指向了Object物件。

 ----------           ----------
 | object |---------> | Object |
 ----------           ----------
 |        |           |        |
 ----------           ----------
 |        |           |        |
 ----------           ----------
 |        |           |        |
 ----------           ----------
 |        |           |        |
 ----------           ----------
再看下面的例子

Object object1=new Object();
Object object2=object1;
System.out.println(object1==object2);
執行得到的結果為true,說明object1和object2的地址相同(==會比較兩個物件的地址是否相同),它們實際上是引用同一物件,如果改變object1物件內部的屬性,那麼object2的屬性同樣會改變,同學們可以下來驗證這一點。

static
靜態成員
Java 中被 static 修飾的成員稱為靜態成員或類成員。它屬於整個類所有,而不是某個物件所有,即被類的所有物件所共享。靜態成員可以使用類名直接訪問,也可以使用物件名進行訪問。

如:

public class StaticTest{
    public static String string="shiyanlou";
    public static void main(String[] args){
        //靜態成員不需要例項化 直接就可以訪問
        System.out.println(StaticTest.string);
        //如果不加static關鍵字 需要這樣訪問
        StaticTest staticTest=new StaticTest();
        System.out.println(staticTest.string);
        //如果加上static關鍵字,上面的兩種方法都可以使用
    }
}

靜態方法
被static修飾的方法是靜態方法,靜態方法不依賴於物件,不需要將類例項化便可以呼叫,由於不例項化也可以呼叫,所以不能有this,也不能訪問非靜態成員變數和非靜態方法。但是非靜態成員變數和非靜態方法可以訪問靜態方法。

final
final關鍵字可以修飾類、方法、屬性和變數

final 修飾類,則該類不允許被繼承,為最終類
final 修飾方法,則該方法不允許被覆蓋(重寫)
final 修飾屬性:則該類的屬性不會進行隱式的初始化(類的初始化屬性必須有值)或在構造方法中賦值(但只能選其一)
final 修飾變數,則該變數的值只能賦一次值,即常量
如:

//靜態常量
public final static String SHI_YAN_LOU="shiyanlou";
許可權修飾符
程式碼中經常用到private和public修飾符,許可權修飾符可以用來修飾屬性和方法的訪問範圍。

如圖所示,代表了不同的訪問修飾符的訪問範圍,比如private修飾的屬性或者方法,只能在當前類中訪問或者使用。預設是什麼修飾符都不加,預設在當前類中和同一包下都可以訪問和使用。protected修飾的屬性或者方法,對同一包內的類和所有子類可見。public修飾的屬性或者方法,對所有類可見。

我們可以舉一個例子,比如 money,如果我們用private修飾代表著這是私有的,只能我自己可以使用。如果是protected代表著我可以使用,和我有關係的人,比如兒子也可以用。如果是public就代表了所有人都可以使用。

封裝
封裝,即隱藏物件的屬性和實現細節,僅對外公開介面,控制在程式中屬性的讀和修改的訪問級別

這樣做有什麼好處?

只能通過規定的方法訪問資料
隱藏類的例項細節,方便修改和實現。
我們在開汽車的時候,只用去關注如何開車,我們並不在意車子是如何實現的,這就是封裝。

如何去實現類的封裝呢?

修改屬性的可見性,在屬性的前面新增修飾符(private)
對每個值屬性提供對外的公共方法訪問,如建立 getter/setter(取值和賦值) 方法,用於對私有屬性的訪問
在 getter/setter 方法里加入屬性的控制語句,例如我們可以加一個判斷語句,對於非法輸入給予否定。


如果我們沒有在屬性前面新增任何修飾符,我們通過物件就可以直接對屬性值進行修改,沒有體現封裝的特性。這在許多程式設計中都是不安全的,所以我們需要利用封裝,來改進我們的程式碼。

首先在類裡要將屬性前新增private修飾符。然後定義getter和setter方法。修改 People.java 和 NewObject.java 的內容如下。

public class People {
//屬性(成員變數)有什麼,前面新增了訪問修飾符private
//變成了私有屬性,必須通過方法呼叫
    private double height;     //身高

//屬性已經封裝好了,如果使用者需要呼叫屬性
//必須用getter和setter方法進行呼叫
//getter和setter方法需要程式設計師自己定義
    public double getHeight(){    
    //getter 方法命名是get關鍵字加屬性名(屬性名首字母大寫)
    //getter 方法一般是為了得到屬性值
        return height;
    }

//同理設定我們的setter方法
//setter 方法命名是set關鍵字加屬性名(首字母大寫)
//setter 方法一般是給屬性值賦值,所以有一個引數
    public void setHeight(double newHeight){
        height = newHeight;
    }
}

現在 main 函式裡的物件,不能再直接呼叫屬性了,只能通過getter和setter方法進行呼叫。

public class NewObject {

    public static void main(String[] args) {
        People LiLei = new People();    //建立了一個People物件LiLei

        //利用setter方法為屬性賦值
        LiLei.setHeight(170.0);

        //利用getter方法取屬性值
        System.out.println("LiLei的身高是"+LiLei.getHeight());
    }
}

編譯執行:

javac NewObject.java People.java
java NewObject.java
編譯執行:

$ javac NewObject.java People.java
$ java NewObject
LiLei的身高是170.0
this
this關鍵字代表當前物件。使用this.屬性操作當前物件的屬性,this.方法呼叫當前物件的方法。

用private修飾的屬性,必須定義 getter 和 setter 方法才可以訪問到(Eclipse IDEA 等 IDE 都有自動生成 getter 和 setter 方法的功能)。

如下:

    public void setAge(int age) {
        this.age = age;
    }
    public int getAge() {
        return age;
    }
建立好了 getter 和 setter 方法後,我們發現方法中引數名和屬性名一樣。

當成員變數和區域性變數之間發生衝突時,在屬性名前面新增了this關鍵字。 此時就代表將一個引數的值賦給當前物件的屬性。同理this關鍵字可以呼叫當前物件的方法,同學們自行驗證一下吧。

繼承
繼承可以看成是類與類之間的衍生關係。比如狗類是動物類,牧羊犬類又是狗類。於是我們可以說狗類繼承了動物類,而牧羊犬類就繼承了狗類。於是狗類就是動物類的子類(或派生類),動物類就是狗類的父類(或基類)。

所以繼承需要符合的關係是:is-a,父類更通用,子類更具體。

語法:

class 子類 extends 父類
例如我們定義了一個 Animal 類,再建立一個 Dog 類,我們需要它繼承 Animal 類

class Dog extends Animal {
    ...
}
接下來我們就來練習一下吧!

我們先建立一個父類 Animal.java

public class Animal {
    public int legNum;     //動物四肢的數量

    //類方法
    public void bark() {
        System.out.println("動物叫!");
    }
}
接下來建立一個子類Dog.java

public class Dog extends Animal {
}

Dog 類繼承了父類 Animal,我們 Dog 類裡什麼都沒有寫,其實它繼承了父類 Animal,所以 Dog 類擁有 Animal 類的全部方法和屬性(除開 private 方法和屬性)。我們建立一個測試類測試一下。

public class Test{
    public static void main(String[] args) {
        Dog a = new Dog();
        a.legNum = 4;
        a.bark();
    }
}
編譯執行:

$ javac Test.java Animal.java Dog.java
$ java Test
動物叫!
為什麼需要繼承?

如果有兩個類相似,那麼它們會有許多重複的程式碼,導致後果就是程式碼量大且臃腫,後期的維護性不高。通過繼承就可以解決這個問題,將兩段程式碼中相同的部分提取出來組成一個父類,實現程式碼的複用。

繼承的特點:

子類擁有父類除private以外的所有屬性和方法
子類可以擁有自己的屬性和方法
子類可以重寫實現父類的方法
Java 中的繼承是單繼承,一個類只有一個父類
注: Java 實現多繼承的一個辦法是 implements(實現)介面

super
super關鍵字在子類內部使用,代表父類物件。

訪問父類的屬性 super.屬性名
訪問父類的方法 super.bark()
子類構造方法需要呼叫父類的構造方法時,在子類的構造方法體裡最前面的位置:super()
方法過載與重寫
方法過載
方法過載是指在一個類中定義多個同名的方法,但要求每個方法具有不同的引數的型別或引數的個數。方法過載一般用於建立一組任務相似但是引數不同的方法。

public class Test {
    void f(int i) {
        System.out.println("i=" + i);
    }

    void f(float f) {
        System.out.println("f=" + f);
    }

    void f(String s) {
        System.out.println("s=" + s);
    }

    void f(String s1, String s2){
        System.out.println("s1+s2="+(s1+s2));
    }

    void f(String s, int i){
        System.out.println("s="+s+",i="+i);
    }

    public static void main(String[] args) {
        Test test = new Test();
        test.f(3456);
        test.f(34.56f);
        test.f("abc");
        test.f("abc","def");
        test.f("abc",3456);
    }
}
編譯執行:

$ javac Test.java
$ java Test
i=3456
f=34.56
s=abc
s1+s2=abcdef
s=abc,i=3456
方法過載有以下幾種規則:

方法中的引數列表必須不同。比如:引數個數不同或者引數型別不同。
訪問許可權,返回值型別和丟擲的異常不同不能實現過載
過載的方法中允許丟擲不同的異常
可以有不同的返回值型別,但是引數列表必須不同
可以有不同的訪問修飾符
方法重寫
子類可以繼承父類的方法,但如果子類對父類的方法不滿意,想在裡面加入適合自己的一些操作時,就需要將方法進行重寫。並且子類在呼叫方法中,優先呼叫子類的方法。

比如 Animal 類中有bark()這個方法代表了動物叫,但是不同的動物有不同的叫法,比如狗是汪汪汪,貓是喵喵喵。

當然在方法重寫時要注意,重寫的方法一定要與原父類的方法語法保持一致,比如返回值型別,引數型別及個數,和方法名都必須一致。

例如:

public class Animal {
    //類方法
    public void bark() {
        System.out.println("動物叫!");
    }
}
public class Dog extends Animal {
       //重寫父類的bark方法
        public void bark() {
        System.out.println("汪!汪!汪!");
    }
}

寫個測試類來看看輸出結果:

public class Test{
    public static void main(String args[]){
           Animal a = new Animal(); // Animal 物件
        Dog d = new Dog();   // Dog 物件

          Animal b = new Dog(); // Dog 物件,向上轉型為Animal型別,具體會在後面的內容進行詳解

          a.bark();// 執行 Animal 類的方法
         d.bark();//執行 Dog 類的方法
          b.bark();//執行 Dog 類的方法
       }
}
編譯執行:

$ javac Test.java Dog.java Animal.java
$ java Test
動物叫!
汪!汪!汪!
汪!汪!汪!
多型
多型是指允許不同類的物件對同一訊息做出響應。即同一訊息可以根據傳送物件的不同而採用多種不同的行為方式。多型也稱作動態繫結(dynamic binding),是指在執行期間判斷所引用物件的實際型別,根據其實際的型別呼叫其相應的方法。

通俗地講,只通過父類就能夠引用不同的子類,這就是多型,我們只有在執行的時候才會知道引用變數所指向的具體例項物件。

向上轉型
要理解多型必須要明白什麼是"向上轉型",比如,一段程式碼如下,Dog 類是 Animal 類的子類:

Animal a = new Animal();  //a是父類的引用指向的是本類的物件

Animal b = new Dog(); //b是父類的引用指向的是子類的物件
在這裡,可以認為由於 Dog 繼承於 Animal,所以 Dog 可以自動向上轉型為 Animal,所以b是可以指向 Dog 例項物件的。

注:不能使用一個子類的引用去指向父類的物件。

如果定義了一個指向子類物件的父類引用型別,那麼它除了能夠引用父類中定義的所有屬性和方法外,還可以使用子類強大的功能。但是對於只存在於子類的方法和屬性就不能獲取。

新建一個Test.java,例如:

class Animal {
    //父類方法
    public void bark() {
        System.out.println("動物叫!");
    }
}

class Dog extends Animal {

    //子類重寫父類的bark方法
    public void bark() {
        System.out.println("汪、汪、汪!");
    }
    //子類自己的方法
    public void dogType() {
        System.out.println("這是什麼品種的狗?");
    }
}


public class Test {

    public static void main(String[] args) {
        Animal a = new Animal();
        Animal b = new Dog();
        Dog d = new Dog(); 

        a.bark();
        b.bark();
        //b.dogType(); 
        //b.dogType()編譯不通過
        d.bark();
        d.dogType();
    }

}
編譯執行:

$ javac Test.java
$ java Test
動物叫!
汪、汪、汪!
汪、汪、汪!
這是什麼品種的狗?
在這裡,由於b是父類的引用,指向子類的物件,因此不能獲取子類的方法(dogType()方法),同時當呼叫bark()方法時,由於子類重寫了父類的bark()方法,所以呼叫子類中的bark()方法。

因此,向上轉型,在執行時,會遺忘子類物件中與父類物件中不同的方法,也會覆蓋與父類中相同的方法——重寫。(方法名,引數都相同)

多型的實現條件
Java 實現多型有三個必要條件:繼承、重寫和向上轉型(即父類引用指向子類物件)。

只有滿足上述三個條件,才能夠在同一個繼承結構中使用統一的邏輯實現程式碼處理不同的物件,從而達到執行不同的行為。

多型的實現方式
Java中多型的實現方式:繼承父類進行方法重寫,抽象類和抽象方法,介面實現。

抽象類
在定義類時,前面加上abstract關鍵字修飾的類交抽象類。 抽象類中有抽象方法,這種方法是不完整的,僅有宣告而沒有方法體。抽象方法宣告語法如下:

abstract void f();  //f()方法時抽象方法
那我們什麼時候會用到抽象類呢?

在某些情況下,某個父類只是知道其子類應該包含怎樣的方法,但無法準確知道這些子類如何實現這些方法。也就是說抽象類是約束子類必須要實現哪些方法,而並不關注方法如何去實現。
從多個具有相同特徵的類中抽象出一個抽象類,以這個抽象類作為子類的模板,從而避免了子類設計的隨意性。
所以由上可知,抽象類是限制規定子類必須實現某些方法,但不關注實現細節。

那抽象類如何用程式碼實現呢,它的規則如下:

用 abstract 修飾符定義抽象類
用 abstract 修飾符定義抽象方法,只用宣告,不需要實現
包含抽象方法的類就是抽象類
抽象類中可以包含普通的方法,也可以沒有抽象方法
抽象類的物件不能直接建立,通常是定義引用變數指向子類物件。
我們來寫一寫程式碼吧!

1、在home/project/目錄下建立一個抽象類TelePhone.java。

2、填寫需要子類實現的抽象方法。

//抽象方法
public abstract class TelePhone {
    public abstract void call();  //抽象方法,打電話
    public abstract void message(); //抽象方法,發簡訊
}

3、構建子類,並實現抽象方法。新建一個CellPhone.java。

public class CellPhone extends TelePhone {

    @Override
    public void call() {
        System.out.println("我可以打電話!");
    }

    @Override
    public void message() {
        System.out.println("我可以發簡訊!");
    }

    public static void main(String[] args) {
        CellPhone cp = new CellPhone();
        cp.call();
        cp.message();
    }

}
在 CellPhone 類新增 main 方法測試執行結果:

編譯執行:

$ javac CellPhone.java TelePhone.java
$ java CellPhone

我可以打電話!
我可以發簡訊!
介面
介面用於描述類所具有的功能,而不提供功能的實現,功能的實現需要寫在實現介面的類中,並且該類必須實現介面中所有的未實現方法。

介面的宣告語法格式如下:

修飾符 interface 介面名稱 [extends 其他的介面名] {
        // 宣告變數
        // 抽象方法
}
如宣告一個 Animal 介面:

// Animal.java
interface Animal {
        //int x;
        //編譯錯誤,x需要初始化,因為是 static final 型別
        int y = 5;
        public void eat();
        public void travel();
}
注意點:在Java8中

介面不能用於例項化物件
介面中方法只能是抽象方法、default方法、靜態方法
介面成員是 static final 型別
介面支援多繼承
在Java9中,介面可以擁有私有方法和私有靜態方法,但是隻能被該介面中的default方法和靜態方法使用。

多繼承實現方式:

修飾符 interface A extends 介面1,介面2{

}

修飾符 class A implements 介面1,介面2{

實現上面的介面:

// Cat.java
public class Cat implements Animal{

     public void eat(){
         System.out.println("Cat eats");
     }

     public void travel(){
         System.out.println("Cat travels");
     }
     public static void main(String[] args) {
        Cat cat = new Cat();
        cat.eat();
        cat.travel();
    }
}
編譯執行:

$ javac Cat.java Animal.java
$ java Cat
Cat eats
Cat travels
內部類
將一個類的定義放在另一個類的定義內部,這就是內部類。而包含內部類的類被稱為外部類。

內部類的主要作用如下:

內部類提供了更好的封裝,可以把內部類隱藏在外部類之內,不允許同一個包中的其他類訪問該類
內部類的方法可以直接訪問外部類的所有資料,包括私有的資料
內部類所實現的功能使用外部類同樣可以實現,只是有時使用內部類更方便
內部類允許繼承多個非介面型別(具體將在以後的內容進行講解)
注:內部類是一個編譯時的概念,一旦編譯成功,就會成為完全不同的兩類。對於一個名為outer的外部類和其內部定義的名為inner的內部類。編譯完成後出現outer.class和outer$inner.class兩類。所以內部類的成員變數/方法名可以和外部類的相同。

我們通過程式碼來詳細學習一下內部類吧!

成員內部類
// People.java
//外部類People
public class People {
    private String name = "LiLei";         //外部類的私有屬性
    //內部類Student
    public class Student {
        String ID = "20151234";               //內部類的成員屬性
        //內部類的方法
        public void stuInfo(){
            System.out.println("訪問外部類中的name:" + name);
            System.out.println("訪問內部類中的ID:" + ID);
        }
    }

    //測試成員內部類
    public static void main(String[] args) {
        People a = new People();     //建立外部類物件,物件名為a
        Student b = a.new Student(); //使用外部類物件建立內部類物件,物件名為b
        // 或者為 People.Student b = a.new Student();
        b.stuInfo();   //呼叫內部物件的suInfo方法
    }
}
編譯執行:

$ javac People.java                                 $ java People
訪問外部類中的name:LiLei
訪問內部類中的ID:20151234
成員內部類的使用方法:

Student 類相當於 People 類的一個成員變數,所以 Student 類可以使用任意訪問修飾符
Student 類在 People 類裡,所以訪問範圍在類裡的所有方法均可以訪問 People 的屬性(即內部類裡可以直接訪問外部類的方法和屬性,反之不行)
定義成員內部類後,必須使用外部類物件來建立內部類物件,即 內部類 物件名 = 外部類物件.new 內部類();
如果外部類和內部類具有相同的成員變數或方法,內部類預設訪問自己的成員變數或方法,如果要訪問外部類的成員變數,可以使用 this 關鍵字 如上述程式碼中:a.this
注:成員內部類不能含有static的變數和方法,因為成員內部類需要先建立了外部類,才能建立它自己的。

靜態內部類
靜態內部類通常被稱為巢狀類。

// People.java
//外部類People
public class People {
    private String name = "LiLei";         //外部類的私有屬性

/*外部類的靜態變數。
Java 中被 static 修飾的成員稱為靜態成員或類成員。它屬於整個類所有,而不是某個物件所有,即被類的所有物件所共享。靜態成員可以使用類名直接訪問,也可以使用物件名進行訪問。
*/
    static String ID = "510xxx199X0724XXXX"; 

    //靜態內部類Student
    public static class Student {
        String ID = "20151234";               //內部類的成員屬性
        //內部類的方法
        public void stuInfo(){
            System.out.println("訪問外部類中的name:" + (new People().name));
            System.out.println("訪問外部類中的ID:" + People.ID);
            System.out.println("訪問內部類中的ID:" + ID);
        }
    }

    //測試成員內部類
    public static void main(String[] args) {
        Student b = new Student();   //直接建立內部類物件,物件名為b
        b.stuInfo();                 //呼叫內部物件的suInfo方法
    }
}
編譯執行:

$ javac People.java
$ java People
訪問外部類中的name:LiLei
訪問外部類中的ID:510xxx199X0724XXXX
訪問內部類中的ID:20151234
靜態內部類是 static 修飾的內部類,這種內部類的特點是:

靜態內部類不能直接訪問外部類的非靜態成員,但可以通過 new 外部類().成員 的方式訪問
如果外部類的靜態成員與內部類的成員名稱相同,可通過類名.靜態成員訪問外部類的靜態成員;如果外部類的靜態成員與內部類的成員名稱不相同,則可通過成員名直接呼叫外部類的靜態成員
建立靜態內部類的物件時,不需要外部類的物件,可以直接建立 內部類 物件名= new 內部類();
區域性內部類
區域性內部類,是指內部類定義在方法和作用域內。

例如:

// People.java
//外部類People
public class People {    
    //定義在外部類中的方法內:
    public void peopleInfo() {
        final String sex = "man";  //外部類方法中的常量
        class Student {
            String ID = "20151234"; //內部類中的常量
            public void print() {
                System.out.println("訪問外部類的方法中的常量sex:" + sex);
                System.out.println("訪問內部類中的變數ID:" + ID);
            }
        }
        Student a = new Student();  //建立方法內部類的物件
        a.print();//呼叫內部類的方法
    }
    //定義在外部類中的作用域內
    public void peopleInfo2(boolean b) {
        if(b){
            final String sex = "man";  //外部類方法中的常量
            class Student {
                String ID = "20151234"; //內部類中的常量
                public void print() {
                    System.out.println("訪問外部類的方法中的常量sex:" + sex);
                    System.out.println("訪問內部類中的變數ID:" + ID);
                }
            }
            Student a = new Student();  //建立方法內部類的物件
            a.print();//呼叫內部類的方法
        }
    }
    //測試方法內部類
    public static void main(String[] args) {
        People b = new People(); //建立外部類的物件
        System.out.println("定義在方法內:===========");
        b.peopleInfo();  //呼叫外部類的方法
        System.out.println("定義在作用域內:===========");
        b.peopleInfo2(true);
    }
}
編譯執行:

$ javac People.java
$ java People
定義在方法內:===========
訪問外部類的方法中的常量sex:man
訪問內部類中的變數ID:20151234
定義在作用域內:===========
訪問外部類的方法中的常量sex:man
訪問內部類中的變數ID:20151234
區域性內部類也像別的類一樣進行編譯,但只是作用域不同而已,只在該方法或條件的作用域內才能使用,退出這些作用域後無法引用的。

匿名內部類
匿名內部類,顧名思義,就是沒有名字的內部類。正因為沒有名字,所以匿名內部類只能使用一次,它通常用來簡化程式碼編寫。但使用匿名內部類還有個前提條件:必須繼承一個父類或實現一個介面。

例如:

// Outer.java
public class Outer { 

    public Inner getInner(final String name, String city) { 
        return new Inner() { 
            private String nameStr = name; 
            public String getName() { 
                return nameStr; 
            } 
        };
    } 

    public static void main(String[] args) { 
        Outer outer = new Outer(); 
        Inner inner = outer.getInner("Inner", "NewYork"); 
        System.out.println(inner.getName()); 
    } 

interface Inner { 
    String getName(); 
}
執行結果:Inner

匿名內部類是不能加訪問修飾符的。要注意的是,new 匿名類,這個類是要先定義的,如果不先定義,編譯時會報錯該類找不到。

同時,在上面的例子中,當所在的方法的形參需要在內部類裡面使用時,該形參必須為final。這裡可以看到形參 name 已經定義為final了,而形參city 沒有被使用則不用定義為final。

然而,因為匿名內部類沒名字,是用預設的建構函式的,無引數的,如果需要該類有帶引數的建構函式,示例如下:

   public Inner getInner(final String name, String city) { 
        return new Inner(name, city) { 
            private String nameStr = name; 

            public String getName() { 
                return nameStr; 
            } 
        }; 
    } 
注意這裡的形參city,由於它沒有被匿名內部類直接使用,而是被抽象類Inner的建構函式所使用,所以不必定義為final。

package 
為了更好地組織類,Java 提供了包機制,用於區別類名的名稱空間。

包的作用

把功能相似或相關的類或介面組織在同一個包中,方便類的查詢和使用。
包採用了樹形目錄的儲存方式。同一個包中的類名字是不同的,不同的包中的類的名字是可以相同的,當同時呼叫兩個不同包中相同類名的類時,應該加上包名加以區別。
包也限定了訪問許可權,擁有包訪問許可權的類才能訪問某個包中的類。
定義包語法:

package 包名
//注意:必須放在源程式的第一行,包名可用"."號隔開
例如:

//在定義資料夾的時候利用"/"來區分層次
//包中用"."來分層
package com.shiyanlou.java 
不僅是我們這樣利用包名來區分類,系統也是這樣做的。

如何在不同包中使用另一個包中的類?
使用import關鍵字。比如要匯入包com.shiyanlou下People這個類,import com.shiyanlou.People。同時如果import com.shiyanlou.*這是將包下的所有檔案都匯入進來,*是萬用字元。

包的命名規範是全小寫字母拼寫。

相關文章