Java面向對象編程(二)

關鍵字 —— this


一、this關鍵字的使用:

1.this可以用來修飾、調用:屬性、方法、構造器。


2.this修飾屬性和方法: this理解為:當前對象 或 當前正在創建的對象。

  2.1 在類的方法中,我們可以使用"this.屬性"或"this.方法"的方式,調用當前對象屬性或方法。但是,通常情況下,我們都選擇省略"this."。特殊情況下,如果方法的形參和類的屬性同名時,我們必須顯式的使用"this.變數"的方式,表明此變數是屬性,而非形參。
  2.2 在類的構造器中,我們可以使用"this.屬性"或"this.方法"的方式,調用當前正在創建的對象屬性或方法。 但是,通常情況下,我們都選擇省略"this."。特殊情況下,如果構造器的形參和類的屬性同名時,我們必須顯式的使用"this.變數"的方式,表明此變數是屬性,而非形參。

二、 this調用構造器
  ① 我們在類的構造器中,可以顯式的使用"this(形參列表)"方式,調用本類中指定的其他構造器。
  ② 構造器中不能通過"this(形參列表)"方式調用自己。
  ③ 如果一個類中有n個構造器,則最多有 n - 1構造器中使用了"this(形參列表)"。
  ④ 規定:"this(形參列表)"必須聲明在當前構造器的首行。
  ⑤ 構造器內部,最多只能聲明一個"this(形參列表)",用來調用其他的構造器。

 

 

package、import的使用

一、package關鍵字的使用
  1.為了更好的實現項目中類的管理,提供包的概念
  2.使用package聲明類或介面所屬的包,聲明在源文件的首行
  3.包,屬於標識符,遵循標識符的命名規則、規範(xxxyyyzzz)、「見名知意」
  4.每 "." 一次,就代表一層文件目錄。
   補充:同一個包下,不能命名同名的介面、類。
  不同的包下,可以命名同名的介面、類。

 

 

二、import關鍵字的使用
  1. 在源文件中顯式的使用import結構導入指定包下的類、介面
  2. 聲明在包的聲明和類的聲明之間
  3. 如果需要導入多個結構,則並列寫出即可
  4. 可以使用"xxx.*"的方式,表示可以導入xxx包下的所有結構
  5. 如果使用的類或介面是java.lang包下定義的,則可以省略import結構
  6. 如果使用的類或介面是本包下定義的,則可以省略import結構
  7. 如果在源文件中,使用了不同包下的同名的類,則必須至少有一個類需要以全類名的方式顯示。
  8. 使用 "xxx.* " 方式表明可以調用xxx包下的所有結構。但是如果使用的是xxx子包下的結構,則仍需要顯式導入
  9. import static:導入指定類或介面中的靜態結構:屬性或方法。

 

繼承性


 繼承:將多個類中存在的相同屬性和行為,全都放進一個類中,那麼多個類無需再定義這些屬性和行為,只要繼承那個類即可。

一、繼承性的格式:  

class 子類A extends 父類B{}

  A:子類、派生類、subclass

  B:父類、超類、基類、superclass

 

二、繼承的優點:

  1.提高了程式碼的復用性:多個類相同的成員可以放到同一個類中。

  2.提高了程式碼的維護性:如果功能的程式碼需要修改,修改一處即可。

  3.讓類與類之間產生了關係,是多態的前提。其實這也是繼承的一個弊端:類的耦合性很強

設計原則:高內聚低耦合。 簡單的理解: 內聚就是自己完成某件事情的能力。 耦合就是類與類之間的關係。

 

三、注意事項:

  1.子類繼承父類後就獲取了直接父類以及所有間接父類中聲明的所有屬性和方法。所有的類除(java.lang.Object類之外)都直接或間接的繼承於java.lang.Object類

 

  

 

 

 

 

2.子類不能繼承父類的構造方法,但是可以通過super關鍵字去訪問父類構造方法。 不要為了部分功能而去繼承。

 

四、繼承的特點

  1.java類的單繼承性,一個子類只能有一個父類。

  2.一個類可以被多個子類繼承。

  3.子類直接繼承的父類,稱為:直接父類。間接繼承的父類稱為:間接父類

 

 

super關鍵字


1. super代表父類存儲空間的標識(引用)(可操作父類中成員)

2. super可以用來調用:屬性、方法、構造器。

3. super的使用:調用屬性和方法

  3.1 我們可以在子類的方法或構造器中。通過使用"super.屬性"或"super.方法"的方式,顯式的調用父類中聲明的屬性或方法。但是,通常情況下,我們習慣省略"super."

  3.2 特殊情況:當子類和父類中定義了同名的屬性時,我們要想在子類中調用父類中聲明的屬性,則必須顯式的使用"super.屬性"的方式,表明調用的是父類中聲明的屬性。
  3.3 特殊情況:當子類重寫了父類中的方法以後,我們想在子類的方法中調用父類中被重寫的方法時,則必須顯式的使用"super.方法"的方式,表明調用的是父類中被重寫的方法。


4.super調用構造器
  4.1 我們可以在子類的構造器中顯式的使用"super(形參列表)"的方式,調用父類中聲明的指定的構造器
  4.2 "super(形參列表)"的使用,必須聲明在子類構造器的首行!
  4.3 我們在類的構造器中,針對於"this(形參列表)"或"super(形參列表)"只能二選一,不能同時出現
  4.4 在構造器的首行,沒有顯式的聲明"this(形參列表)"或"super(形參列表)",則默認調用的是父類中空參的構器:super()
  4.5 在類的多個構造器中,至少有一個類的構造器中使用了"super(形參列表)",調用父類中的構造器。

 

注意:

  1.要想初始化子類,就必須先初始化父類(也就是調用父類的構造方法)

  2.super(...)和this(...)必須出現在構造方法的第一條語句上。子類中會默認

  3.如果調用了多次super,相當於調用多次父類構造方法,就是對父類數據進行多次初始化
  4.每個類只能初始化一次。

class Father{
    int age;
    public Father6(){
        System.out.println("這是父類中無參構造方法");
    }

    public Father6(String name){
        System.out.println("父類的有參構造");
    }
}

class Son6 extends Father6{
    public Son6(){
//        this("隨便給");
        super("隨便給");
//        super();
        System.out.println("子類的無參構造方法");
//        this("隨便給");
    }

    public Son6(String name){
        super("隨便給");
        System.out.println("子類的帶參數的構造方法");
    }
}

public class ExtendsDemo8 {
    public static void main(String[] args) {
        Son6 s = new Son6();
    }
super、this實例

 

 

 

 

Overload(重載) 與 Override(重寫)


一、 override(方法重寫)

  1.定義:子類繼承父類後,可以對父類中同名同參數的方法,進行覆蓋操作。

    2.重寫的規則:

    a. 子類重寫的方法必須和父類被重寫的方法具有相同的方法名稱、參數列表。
    b. 子類重寫的方法的返回值類型不能大於父類被重寫的方法的返回值類型。
    c. 子類重寫的方法使用的訪問許可權不能小於父類被重寫的方法的訪問許可權。
    d. 子類不能重寫父類中聲明為private許可權的方法。
    e. 子類方法拋出的異常不能大於父類被重寫方法的異常。
 

二、區別:

方法的重寫:子類中出現了和父類中一模一樣的方法聲明,叫做重寫,也被叫做方法的覆蓋。

方法的重載:本類中出現了方法名一樣,參數列表不一樣的方法,與返回值無關的方法,叫重載。

 

三、注意事項:

  1.父類中被private修飾的方法不能被修飾。

  2.子類重寫父類的方法時,訪問許可權不能降低。

  3.父類中方法是靜態的,子類重寫時也必須用靜態方法重寫。

 

 

多態性


 

一、多態的定義:某一個事物,在不同時刻體現出的不同狀態。

  對象的多態性:父類的引用指向子類的對象

二、多態的前提:

  1. 要有類的繼承關係。
  2. 要有方法的重寫。
  3. 父類指向子類。格式:父 f  = new 子();
多態情況下,「看左邊」:看的是父類的引用(父類中不具備子類特有的方法)
      「看右邊」:看的是子類的對象(實際運行的是子類重寫父類的方法)
 

三、多態的優點:

  1. 提高了程式的維護性(由繼承保證)
  2. 提高了程式的擴展性(由多態保證)

四、多態的弊端:

  1. 不能通過多態去訪問父類與子類同名的方法(解決方法:super關鍵字)
  2. 不能通過多態訪問子類特有的方法,但是通過轉換可以進行訪問。

   多態中強制轉換有:a.向上轉型:子類  變數名  =  (子類)父類

             b.向下轉型:父→子

             c.匿名對象:((子類)父類變數). 方法

虛擬方法調用(Virtual Method Invocation)
正常的方法調用
Person e = new Person();
e.getInfo();
Student e = new Student();
e.getInfo();
虛擬方法調用(多態情況下) 子類中定義了與父類同名同參數的方法,在多態情況下,將此時父類的方法稱為虛擬方法,父類根據賦給它的不同子類對象,動態調用屬於子類的該方法。這樣的方法調用在編譯期是無法確定的。多態是運行時行為
Person e = new Student();
e.getInfo(); //調用Student類的getInfo()方法
編譯時類型和運行時類型
編譯時e為Person類型,而方法的調用是在運行時確定的,所以調用的是Student類 的getInfo()方法。——動態綁定
虛擬方法調用(Virtual Method Invocation)

面試題 : 多態是運行時行為。只有在運行時才知道new的是誰。

 

instanceof操作符


 

x  instanceof A:檢驗x是否為類A的對象,返回值為boolean型。

要求:1. 要求x所屬的類與類A必須是子類和父類的關係,否則編譯錯誤。

    2.如果x屬於類A的子類B,x instanceof A值也為true。x是變數名,A是類型。
使用場景:為了避免在向下轉型時出現ClassCastException的異常,我們在向下轉型之前,先進行instanceof的判斷,一旦返回true,就進行向下轉型。如果返回false,不進行向下轉型。如果 a instanceof A返回true,則 a instanceof B也返回true.其中,類B是類A的父類。

 

Object類的使用


Object類是所有Java類的根父類。

如果在類的聲明中未使用extends關鍵字指明其父類,則默認父類為java.lang.Object類。

Object類中的功能(屬性、方法)具有通用性。

  屬性:無

  方法:finalize(在垃圾回收之前調用,一般不主動調用,由Java中垃圾回收器自動調用)、equals()、toString()、getClass()、hashCode()、clone()

Object類知聲明了一個空參的構造器。

 

==操作符與equals方法
import java.util.Date;

/*
 * 
 * 面試題: == 和 equals() 區別
 * 
 * 一、回顧 == 的使用:
 * == :運算符
 * 1. 可以使用在基本數據類型變數和引用數據類型變數中
 * 2. 如果比較的是基本數據類型變數:比較兩個變數保存的數據是否相等。(不一定類型要相同)
 *    如果比較的是引用數據類型變數:比較兩個對象的地址值是否相同.即兩個引用是否指向同一個對象實體
 * 補充: == 符號使用時,必須保證符號左右兩邊的變數類型一致。
 * 
 * 二、equals()方法的使用:
 * 1. 是一個方法,而非運算符
 * 2. 只能適用於引用數據類型
 * 3. Object類中equals()的定義:
 *    public boolean equals(Object obj) {
            return (this == obj);
      }
 *    說明:Object類中定義的equals()和==的作用是相同的:比較兩個對象的地址值是否相同.即兩個引用是否指向同一個對象實體
 * 
 * 4. 像String、Date、File、包裝類等都重寫了Object類中的equals()方法。重寫以後,比較的不是
 *    兩個引用的地址是否相同,而是比較兩個對象的"實體內容"是否相同。
 *    
 * 5. 通常情況下,我們自定義的類如果使用equals()的話,也通常是比較兩個對象的"實體內容"是否相同。那麼,我們
 *    就需要對Object類中的equals()進行重寫.
 *    重寫的原則:比較兩個對象的實體內容是否相同.
 */
public class EqualsTest {
    public static void main(String[] args) {
        
        //基本數據類型
        int i = 10;
        int j = 10;
        double d = 10.0;
        System.out.println(i == j);//true
        System.out.println(i == d);//true
        
        boolean b = true;
//        System.out.println(i == b);
        
        char c = 10;
        System.out.println(i == c);//true
        
        char c1 = 'A';
        char c2 = 65;
        System.out.println(c1 == c2);//true
        
        //引用類型:
        Customer cust1 = new Customer("Tom",21);
        Customer cust2 = new Customer("Tom",21);
        System.out.println(cust1 == cust2);//false
        
        String str1 = new String("atguigu");
        String str2 = new String("atguigu");
        System.out.println(str1 == str2);//false
        System.out.println("****************************");
        System.out.println(cust1.equals(cust2));//false--->true
        System.out.println(str1.equals(str2));//true
        
        Date date1 = new Date(32432525324L);
        Date date2 = new Date(32432525324L);
        System.out.println(date1.equals(date2));//true
        
        
    }
}
== 和 equals() 區別

 

 

toString


 

Object類中toString()的使用:

  1. 當我們輸出一個對象的引用時,實際上就是調用當前對象的toString()
  2. Object類中toString()的定義:

public String toString() {
return getClass().getName() + "@" + Integer.toHexString(hashCode());
}

  

toString()方法在Object類中定義,其返回值是String類型,返回類名和它的引用地址。 
在進行String與其它類型數據的連接操作時,自動調用toString()方法
Date now=new Date();
System.out.println(「now=」+now); 相當於System.out.println(「now=」+now.toString()); 
可以根據需要在用戶自定義類型中重寫toString()方法
如String 類重寫了toString()方法,返回字元串的值。
s1=「hello」;
System.out.println(s1);//相當System.out.println(s1.toString()); 基本類型數據轉換為String類型時,調用了對應包裝類的toString()方法
int a=10; System.out.println(「a=」+a);
toString()方法

  3. 像String、Date、File、包裝類等都重寫了Object類中的toString()方法。使得在調用對象的toString()時,返回"實體內容"資訊
  4. 自定義類也可以重寫toString()方法,當調用此方法時,返回對象的"實體內容"

 

 包裝類(Wrapper)的使用


 

/*
 * Java中的JUnit單元測試
 * 
 * 步驟:
 * 1.選中當前工程 - 右鍵選擇:build path - add libraries - JUnit 4 - 下一步
 * 2.創建Java類,進行單元測試。
 *   此時的Java類要求:① 此類是public的  ②此類提供公共的無參的構造器
 * 3.此類中聲明單元測試方法。
 *   此時的單元測試方法:方法的許可權是public,沒有返回值,沒有形參
 * 
 * 4.此單元測試方法上需要聲明註解:@Test,並在單元測試類中導入:import org.junit.Test;
 * 
 * 5.聲明好單元測試方法以後,就可以在方法體內測試相關的程式碼。
 * 6.寫完程式碼以後,左鍵雙擊單元測試方法名,右鍵:run as - JUnit Test(eclipse中)
 * 
 * 說明:
 * 1.如果執行結果沒有任何異常:綠條
 * 2.如果執行結果出現異常:紅條
 */

import java.util.Date;

public class JUnitTest {
    
    int num = 10;
    
    @Test
    public void testEquals(){
        String s1 = "MM";
        String s2 = "MM";
        System.out.println(s1.equals(s2));
        //ClassCastException的異常
//        Object obj = new String("GG");
//        Date date = (Date)obj;
        
        System.out.println(num);
        show();
    }
    
    public void show(){
        num = 20;
        System.out.println("show()....");
    }
    
    @Test
    public void testToString(){//測試誰就單擊選中誰
        String s2 = "MM";
        System.out.println(s2.toString());
    }
}
Java中JUnit單元測試

 一、針對八種基本數據類型定義相應的引用類型—包裝類(封裝類)

  有了類的特點,就可以調用類中的方法,Java才是真正的面向對象

二、基本數據類型、包裝類、String三者之間的轉換:

import org.junit.Test;

/*
 * 包裝類的使用:
 * 1.java提供了8種基本數據類型對應的包裝類,使得基本數據類型的變數具有類的特徵
 * 
 * 2.掌握的:基本數據類型、包裝類、String三者之間的相互轉換
 * 
 * 
 * 
 */
public class WrapperTest {
    
    //String類型 --->基本數據類型、包裝類:調用包裝類的parseXxx(String s)
    @Test
    public void test5(){
        String str1 = "123";
        //錯誤的情況:
//        int num1 = (int)str1;不行
//        Integer in1 = (Integer)str1;強轉需要子父類之間的關係
        //可能會報NumberFormatException
        int num2 = Integer.parseInt(str1);
        System.out.println(num2 + 1);
        
        String str2 = "true1";
        boolean b1 = Boolean.parseBoolean(str2);
        System.out.println(b1);
    }
    
    //基本數據類型、包裝類--->String類型:調用String重載的valueOf(Xxx xxx)
    @Test
    public void test4(){
        
        int num1 = 10;
        //方式1:連接運算
        String str1 = num1 + "";
        //方式2:調用String的valueOf(Xxx xxx)
        float f1 = 12.3f;
        String str2 = String.valueOf(f1);//"12.3"
        
        Double d1 = new Double(12.4);
        String str3 = String.valueOf(d1);
        System.out.println(str2);
        System.out.println(str3);//"12.4"
        
    }
    
    /*
     * JDK 5.0 新特性:自動裝箱 與自動拆箱
     */
    @Test
    public void test3(){
//        int num1 = 10;
//        //基本數據類型-->包裝類的對象
//        method(num1);
        
        //自動裝箱:基本數據類型 --->包裝類
        int num2 = 10;
        Integer in1 = num2;//自動裝箱
        
        boolean b1 = true;
        Boolean b2 = b1;//自動裝箱
        
        //自動拆箱:包裝類--->基本數據類型
        System.out.println(in1.toString());
        
        int num3 = in1;//自動拆箱
        
    }
    
    public void method(Object obj){
        System.out.println(obj);
    }
    
    //包裝類--->基本數據類型:調用包裝類Xxx的xxxValue()
    @Test
    public void test2(){
        Integer in1 = new Integer(12);
        
        int i1 = in1.intValue();
        System.out.println(i1 + 1);
        
        
        Float f1 = new Float(12.3);
        float f2 = f1.floatValue();
        System.out.println(f2 + 1);
    }
    
    //基本數據類型 --->包裝類:調用包裝類的構造器
    @Test
    public void test1(){
        
        int num1 = 10;
//        System.out.println(num1.toString());
        Integer in1 = new Integer(num1);
        System.out.println(in1.toString());
        
        Integer in2 = new Integer("123");
        System.out.println(in2.toString());
        
        //報異常
//        Integer in3 = new Integer("123abc");
//        System.out.println(in3.toString());
        
        Float f1 = new Float(12.3f);
        Float f2 = new Float("12.3");
        System.out.println(f1);
        System.out.println(f2);
        
        Boolean b1 = new Boolean(true);
        Boolean b2 = new Boolean("TrUe");
        System.out.println(b2);
        Boolean b3 = new Boolean("true123");
        System.out.println(b3);//false
        
        
        Order order = new Order();
        System.out.println(order.isMale);//false
        System.out.println(order.isFemale);//null
    }
    
}

class Order{
    
    boolean isMale;
    Boolean isFemale;
}
基本數據類型、包裝類、String三者之間的轉換

 

 

三、包裝類面試題:

Object o1 = true ? new Integer(1) : new Double(2.0);
System.out.println(o1);
//1.0
//解析:編譯時就要求類型是一致的,int型自動轉換為double類型的
包裝類面試題1
public void method1() {
Integer i = new Integer(1);
Integer j = new Integer(1);
System.out.println(i == j);// false
/*
Integer內部定義了IntegerCache結構,IntegerCache中定義了Integer[],保存了從-128~127範圍的整數。如果我們使用自動裝箱的方式,給Integer賦值的範圍在-128~127範圍內時,可以直接使用數組中的元素,不用再去new了。目的:提高效率
*/        

Integer m = 1;
Integer n = 1;
System.out.println(m == n);//true
//

Integer x = 128;//相當於new了一個對象
Integer y = 128;//相當於new了一個對象
System.out.println(x == y);//false
//

}
包裝類面試題2