6 Java基礎整理 第六-八章

1.封裝

封裝的目的是簡化編程和加強安全性。java

簡化編程是指,封裝可讓使用者沒必要了解具體類的內部實現細節,而只是要經過提供給外部訪問的方法來訪問類中的屬性和方法編程

加強安全性是指,封裝可使某個屬性只能被當前類使用,從而避免被其餘類或對象進行誤操做。安全

如何加強安全性:使用訪問修飾符private修飾屬性網絡

2.構造函數 

構造方法不一樣於普通方法,普通方法表明對象的行爲,而構造方法是提供給系統用於建立對象的方法。函數

構造方法(也稱爲構造函數)是一種特殊的方法,它具備如下特色。工具

  • 構造方法的方法名必須與類名相同。
  • 構造方法沒有返回類型,在方法名前不聲明返回類型(包括 void)。
  • 但其實構造方法是有返回值的,返回的是剛剛被初始化完成的當前對象的引用

構造函數的語法形式:[訪問修飾符] 類名([參數列表]) ;this

類中沒有構造方法時JVM會默認產生一個無參構造方法。可是若是類中已經有任意的構造方法,JVM不會再建立。spa

當使用反射建立對象時,必需要使用無參構造方法。操作系統

3.this關鍵字:

this指向當前對象的引用.net

this常常用於:

  • 區分紅員變量和局部變量,避免命名的煩惱。
  • 調用其餘構造方法。 this調用構造方法,只能是方法內的第一條語句。
private int age ;
...
public void setAge(int age) {
...
this.age = age ;
}

 

 public Student() {
        //調用有一個String參數的構造方法
        this(" WangYun" );
    }

    public Student(String name) {
        //調用有兩個參數的構造方法
         this(name,23);
    }

    public Student(String name, int age) {
        ...
    }

 

4.練習

 

//如下代碼
Student s1 = new Student("zs",18);
Student s2 = new Student("zs",18);
System.out.print(s1 == s2);
System.out.print(s1.getName() == s2.getName());
//執行後,控制檯輸出爲? false true

由於setName()都是雙引號字符串對象,是對常量池中同一個變量的引用。

5.Java包

  • 爲了更好地組織類,Java 提供了包機制。包是類的容器,用於分隔類名空間。
  • 若是沒有指定包名,全部的類都屬於一個默認的無名包。
  • Java 中將實現相關功能的類組織到一個包中。
  1. 提供了相似於操做系統樹形文件夾的組織形式,能分門別類地存儲、管理類,易於查找並使用類。
  2. 解決了同名類的命名衝突問題。例如,學生王雲定義了一個類,類名叫 TestStudent,學生劉靜濤也定義了一個叫 TestStudent 的類。若是在同一個文件夾下,就會產生命名衝突的問題。而使用了包的機制,就能夠把王雲定義的類存放在 wangyun 包下,把劉靜濤定義的類存放在 liujingtao 包下,以後就能夠先經過 wangyun 和 liujingtao 這樣的包名,區分不一樣的目錄,而後再使用 TestStudent 訪問兩個包中各自的類,從而解決了命名衝突的問題。
  3. 包容許在更廣的範圍內保護類、屬性和方法。關於這方面的做用,在本章後面介紹訪問權限的時候,你們就能體會到。

包的聲明語法格式:package pkg1[.pkg2[.pkg3…]];

命名規則:一般包名所有用小寫字母,如今使用最多的規則是使用翻轉的 internet 域名(不含 www、ftp 等訪問協議)。

 java經常使用包:

  • java.lang:lang 是 language 的簡寫,這個包提供 Java 語言的基礎類,例如 String、Math、Integer、System 和 Thread 等。
  • java.util:util 是 utility 的簡寫,組織了 Java 的工具類,包含集合、事件模型、日期和時間設置、國際化和各類實用工具類。
  • java.io:io 是 input 和 output 的合併簡寫,指輸入和輸出,組織了數據流、序列化和文件系統相關的類。
  • java.net:net 即網絡,這個包組織了爲實現網絡應用程序而提供的類。
  • java.awt:抽象窗口工具集(Abstract Window Toolkit),包含用於建立用戶界面和繪製圖形圖像的類。

引用包的兩種方法:import 包名.類名;和import 包名.* ; 第二種形式的包只能引用當前包下的類,不能導入其子包中的類;同時只會導入用到的類。

import語句要寫在package以後,在類定義以前。

java.lang中的類不須要import

6.Java的權限修飾符:

Java 語言中的訪問權限修飾符有 4 種,但卻只有 3 個關鍵字。由於不寫訪問權限修飾符時,在 Java 中被稱爲默認權限(包權限),本課程中以 default 代替。其餘 3 個訪問權限修飾符分別爲 privateprotected 和 public

修飾類:public和default。public表明類能在各類地方使用,default表明類只能在本包內使用。

修飾屬性、構造方法、普通方法:private default protected public。private修飾的成員只能在本類中使用。default修飾的成員只能在本類和本包內使用。protected修飾的成員只能在本類、本包內和子類中使用。public能夠在全部類中使用。

能夠看到,只有成員變量/方法和類/接口才可使用訪問權限修飾符,局部變量不可使用。

7.成員變量:

對象成員變量和類成員變量,對象成員變量能夠在每個對象中建立,不一樣對象的對象成員變量不能共享;類成員變量只建立一次,能夠被全部對象共享。經過在變量前面加上static使成員變量成爲靜態的(類成員變量)。

能夠直接經過類名引用靜態變量,也能夠經過實例名來引用靜態變量,但推薦採用前者,由於採用後者容易混淆靜態變量和實例變量。

 被static修飾的方法稱爲靜態方法/類方法,能夠經過類名.方法名直接調用。

靜態方法中沒法使用實例變量。

Java 類首次裝入 JVM 時,會對靜態成員或靜態塊進行一次初始化,注意此時尚未產生對象。

所以,靜態成員和靜態塊都是和類綁定的,會在類加載時就進行初始化操做

在使用 new 關鍵字建立並初始化對象的過程當中,具體的初始化分爲如下 4 步。

  1. 給對象的實例變量分配空間,默認初始化成員變量
  2. 成員變量聲明時的初始化
  3. 初始化塊初始化
  4. 構造方法初始化

8.單例模式:

單例模式指的是不管建立了多少個引用,在堆中僅僅只有一個實例對象。

單例模式經過private修飾構造方法實現。也稱爲構造方法私有化。

public class Singleton {
private static Singleton instance;
    private Singleton() {
    }
}

同時建立一個堆內存對象供使用。這個方法在沒有對象時建立,存在對象時直接返回這個對象的引用。

   public static Singleton getInstance() {
        if (instance == null) {
             instance = new Singleton();
        }
        return instance;
    }

9.繼承

繼承可使得子類沿用父類的成員(屬性和方法)。當多個子類具備相同的成員時,就能夠考慮將這些成員提取出來,放到父類中,而後再用子類去繼承這個父類,也就是將一些相同的成員提取到了更高的層次中。

繼承能夠大大減小冗餘代碼,提升代碼的複用性。

繼承的關鍵字是extends。繼承的語法形式是class A extends B{}。類A稱爲父類、超類、基類,類B稱爲子類、衍生類和導出類。

  • 子類沒法繼承父類的構造方法。

    構造方法是一種特殊的方法,子類沒法繼承父類的構造方法。

  • 子類不能繼承父類中不符合訪問權限的成員

    子類不能繼承private修飾或者不在同包內的default修飾的父類變量。

10.final關鍵字

final 關鍵字用法以下:

若用於修飾屬性,則該屬性不容許被修改。
若用於修飾父類中的方法,則該方法不容許被子類重寫。
若用於修飾類,則該類不容許被其餘類繼承。

final修飾的方法能夠被重載,但不能被重寫。

11.重寫

  • 重寫方法與被重寫方法同名,參數列表也必須相同。
  • 重寫方法的返回值類型必須和被重寫方法的返回值類型相同或是其子類。
  • 重寫方法不能縮小被重寫方法的訪問權限
  • 子類不能重寫父類的構造方法

12.super關鍵字

super關鍵字用於引用父類的對象。

super()用於調用父類的構造方法來建立子類對象,和this()同樣,必須寫在構造方法中的第一行。

須要注意的是,子類的構造方法中若是不寫 super(),編譯器會幫助你在子類構造方法的第一行加上super()由於在子類中調用父類構造器是「必須的」。但若是父類中只存在有參構造方法,並無提供無參構造方法,則須要在子類構造方法中顯式地調用父類存在的構造器,不然可能由於父類中沒有無參構造器而獲得一個編譯錯誤。

使用super調用重寫方法在父類的方法:super.重寫方法()。其做用是對父類方法進行一些補充。

13.多態

多態能夠優雅的解決程序中的擴展性問題。

在形式上,父類引用能夠指向子類對象。在 Vehicle vehicle = new Car(); 中,子類的 Car 對象賦值給了父類 Vehicle 引用,這稱爲向上轉型

在引用 vehicle 上調用方法,在運行時刻究竟調用的是父類 Vehicle 中的方法仍是子類 Car 中的方法呢?實際須要經過運行時的對象類型來判斷,這稱爲動態綁定

向上轉型和動態綁定就是多態的具體實現機制。

向上轉型的過程當中涉及了繼承、重寫和父類引用子類對象的概念。

向上轉型的對象調用重寫方法,執行的是子類的重寫方法。

向上轉型的好處,是不須要針對父類的多個子類再設計不一樣的方法,減小了代碼量而且增長了可拓展性。

動態綁定是指在編譯期間方法並不會和「引用」的類型綁定在一塊兒,而是在程序運行的過程當中,JVM 須要根據具體的實例對象才能肯定此時要調用的是哪一個方法。重寫方法遵循的就是動態綁定。

例如,子類和父類中都有info()方法。動態綁定就是指,編譯過程當中info()不會和具體的類綁定到一塊兒,而是在運行期間列舉出子類和父類的info()方法,根據當前的實例對象,調用該實例對象的info()方法。

靜態綁定是指程序編譯期的綁定。如下的類信息,使用的就是靜態綁定機制:

  • finalstatic 或 private 修飾的方法,以及重載方法和構造方法。
  • 成員變量。Java不支持(其實是不鼓勵)成員變量的多態行爲。

一種推薦的編程思想是「面向基類」編程。也就是建議將面向的「對象」抽象爲更高層次的基類。

向上轉型的缺點:轉型後的對象沒法調用導出類中的方法。

向下轉型:強制從基類轉向衍生類。

確保向下轉型的正確性:使用instance of運算符。

14.練習

 

 

不管子類構造函數中有沒有super();實際運行時都會調用父類的構造器。

 當子類重寫了父類的方法時,調用主體(即對象)和方法是運行時綁定的;
當子類和父類的屬性重名時,調用主體(即對象)和屬性是編譯時綁定的。

這兩種說法,前一種是方法的動態綁定,後一種是成員變量的靜態綁定。

 

 java在編譯時肯定對象類型,同時對這個對象的成員變量進行靜態編譯。也就是說,sup對象的i屬性是在Super類中加載的。

 

  此時sub對象的i屬性是在sub類中加載的。

 

 

 

 首先,定義了A類型的變量ab,在編譯過程當中會加載A類,此時A中的靜態塊會被執行,打印「1」。而後ab對象被賦值爲實例化對象的引用,此時須要執行B類的構造函數,首先加載B類到內存中,此時B類中的靜態塊會被執行,打印"a"。而後,執行B類的構造函數。由於B類是A類的子類,因此必定會首先執行A的構造函數,所以打印「2」,而後再執行B中的構造函數,打印「b」。第二句語句,由於A類和B類都不是首次加載,因此只會調用B類的構造函數,打印「2」再打印"b"。所以打印出來結果是"1a2b2b"。

//如下代碼
class Student{
    int age = 1;
    public Student(int age) {
        this.age = age;
    }
}
public class Test extends Student{
    public static void main(String[] args) {
        Test t = new Test();
        System.out.println(t.age);
    }
}
//執行後,控制檯輸出爲?  編譯時錯誤(可見錯誤)

使用Test類的構造函數,會首先調用父類的構造函數,因爲當前僅定義了父類的有參構造方法,因此JVM不會再建立父類的無參構造函數,所以程序出現編譯時錯誤。

 

 

//如下代碼
class Student{
    public static void eat() {
        System.out.print(1);
    }
}
public class Test extends Student{
    public static void eat() {
        System.out.print(2);
    }

    public static void main(String[] args) {
        eat();
    }
}
//執行後,控制檯輸出爲?2

此時,沒有指明調用的是哪一個類中的重寫方法,由於入口函數是子類中,因此「就近」調用子類的重寫方法。

//如下代碼
class Student{
    public void eat() {
        System.out.print(1);
    }
}
public class Test extends Student{
    private void eat() {
        System.out.print(2);
    }

    public static void main(String[] args) {
        Test t = new Test();
        t.eat();
    }
}
//執行後,控制檯輸出爲?編譯錯誤

重寫方法中,權限修飾符不能比父類方法的範圍小

 

 

 

 

若是範圍相等,或擴大則不會報錯。

 

 

 

//如下代碼
class Student{
    int i;
    public Student(int i) {
        this.i = i;
    }
}
public class Test extends Student{
    int i = 1;
    public Test(int i) {
        super(10);
        this.i = i;
    }

    public static void main(String[] args) {
        Student t = new Test(100);
        System.out.print(t.i);
    }
}
//執行後,控制檯輸出爲?10

編譯時,首先根據靜態綁定將父類t.i初始化爲0;而後調用Test(100)構造函數時,首先顯式調用父類有參構造函數,此時父類t.i=10,繼續執行剩下的Test類構造函數,子類i=100。最後打印的是父類的t.i=10。

因爲靜態綁定,儘管它是子類對象,可是它在編譯時綁定了父類成員變量,也會一直用父類成員變量。

//如下代碼
class Student{
    int i;
    public Student(int i) {
        this.i = i;
    }
}
public class Test extends Student{
    int i = 1;
    public Test(int i) {
        super(10);
        this.i = i;
    }

    public static void main(String[] args) {
        Test t = new Test(100);
        System.out.print(t.i);
    }
}
//執行後,控制檯輸出爲?100

同理,這個對象的類型是Test類型,因此因爲靜態綁定,t對象的成員變量是Test類中的成員變量。

首先子類初始化t.i=1,而後調用Test類的有參構造函數,其中先顯式調用父類的有參構造函數 ,父類i成員變量初始化爲0,又賦值爲10,而後繼續執行子類構造函數,t.i=100。最後顯示的結果是子類t.i爲100。

因此這種題型只須要肯定對象靜態綁定的類型是什麼類型(即定義爲何類型),跟蹤這個變量的變化就能夠了。

//如下代碼
class Student{
    public Student() {
        System.out.print(1);
    }
}
public class Test extends Student{
    String name;
    public Test(String name) {
        this.name = name;
        System.out.print(2);
    }

    public static void main(String[] args) {
        Test t = new Test("zs");
    }
}
//執行後,控制檯輸出爲?12

首先,t對象靜態綁定Test類,因此t.name默認值爲null。實例化調用Test類的有參構造函數時,首先調用父類的無參構造函數,打印1,而後繼續執行子類的構造函數,t.name="zs",而後打印2。

//如下代碼
class Student{
    int age;
    public Student(int age) {
        this.age = age;
    }
}
public class Test extends Student{
    public Test(int age) {
        super(age);
    }

    public static void main(String[] args) {
        Test t = new Test(10);
        System.out.println(t.age);
    }
}
//執行後,控制檯輸出爲?10

子類繼承了父類的age屬性。

class Student1{
    int age;
    public Student1(int age) {
        this.age = age;
    }
}
public class Test extends Student1{
    int age;
    public Test(int age) {
        super(age);
    }

    public static void main(String[] args) {
        Test t = new Test(10);
        System.out.println(t.age);
    }
}

若是沒有繼承,那麼子類構造函數中僅調用父類構造函數,修改的是父類對象的屬性,並無修改子類age屬性,所以打印默認值0。

15.爲何局部變量不能被賦初值?

緣由是局部變量一旦定義就確定會被用到,須要使用者賦初值0.0不然會引發編譯錯誤。

16.爲何只能在第一行調用this()和super()構造方法?

super()第一行的緣由:若是super()不在第一行,那麼編譯器會自動在第一行補充上super();

this()第一行的緣由:若是this()不在第一行,那麼編譯器會自動在第一行補充上super();

那麼就會出現一個構造方法內,構造出多個對象的狀況。JVM不容許這樣的狀況出現。

相關文章
相關標籤/搜索