繼承與多型以及包的使用
- 1.繼承
- 1.1繼承的基本使用
- 1.2 protected 關鍵字
- 1.3 final 關鍵字
- 2.多型
- 2.1向上轉型
- 2.2動態系結
- 2.3方法重寫
- 2.4向下轉型
- 2.6 super 關鍵字
- 2.6.1 super 關鍵字的基本用法
- 2.6.3 this和super的區別
- 3.包的使用
- 3.1匯入包中的類
- 3.2常見系統包
1.繼承
為什么要有繼承?
多個類中存在相同屬性和行為時,將這些內容抽取到單獨一個類中,
那么多個類無需再定義這些屬性和行為,只要繼承那個類即可,
此處的多個類稱為子類(派生類),單獨的這個類稱為父類(基類 或超類),可以理解為:“子類 is a 父類”
1.1繼承的基本使用
類繼承語法規則:
class 子類 extends 父類{ }
繼承的作用:
- 降低代碼的冗余度,提高代碼復用率
- 繼承的出現,更有利于功能的擴展,
- 繼承的出現讓類與類之間產生了關系,提供了多型的前提
注意:
- 子類不能直接訪問父類中私有的(private)的成員變數和方法
- Java只支持單繼承和多層繼承,不允許多重繼承
- 一個子類只能有一個父類一個父類可以派生出多個子類
如下代碼示例:
class Animal {
public String name;
public Animal(String name) {
this.name = name;
}
public void eat(String food) {
System.out.println(this.name + "正在吃" + food);
}
}
class Cat extends Animal {
public Cat(String name) {
// 使用 super 呼叫父類的構造方法.
super(name);
}
}
class Bird extends Animal {
public Bird(String name) {
super(name);
}
public void fly() {
System.out.println(this.name + "正在飛 ︿( ̄︶ ̄)︿");
}
}
public class Test {
public static void main(String[] args) {
Cat cat = new Cat("小黑");
cat.eat("貓糧");
Bird bird = new Bird("圓圓");
bird.fly();
}
}
1.2 protected 關鍵字
剛才我們發現, 如果把欄位設為 private, 子類不能訪問. 但是設成 public, 又違背了我們 “封裝” 的初衷.
兩全其美的辦法就是 protected 關鍵字.
- 對于類的呼叫者來說, protected 修飾的欄位和方法是不能訪問的
- 對于類的 子類 和 同一個包的其他類 來說, protected 修飾的欄位和方法是可以訪問的
四種權限修飾符:

1.3 final 關鍵字
曾經我們學習過 final 關鍵字, 修飾一個變數或者欄位的時候, 表示 常量 (不能修改).
final int a = 10;
a = 20; // 編譯出錯
final 關鍵字也能修飾類, 此時表示被修飾的類就不能被繼承
final public class Animal {
...
}
public class Bird extends Animal {
...
}
// 編譯出錯
Error:(3, 27) java: 無法從最終com.bit.Animal進行繼承
final 關鍵字的功能是 限制 類被繼承,我們平時使用的String字串類,就是被final修飾的,不能被繼承,
2.多型
2.1向上轉型
在剛才的例子中, 我們寫了形如下面的代碼:
Bird bird = new Bird("圓圓");
這個代碼也可以寫成這個樣子
Bird bird = new Bird("圓圓");
Animal bird2 = bird;
// 或者寫成下面的方式
Animal bird2 = new Bird("圓圓");
此時 bird2 是一個父類 (Animal) 的參考, 指向一個子類 (Bird) 的實體. 這種寫法稱為 向上轉型.
向上轉型是子類物件轉成父類物件
向上轉型發生的時機:
- 直接賦值
- 方法傳參
- 方法回傳
直接賦值的方式我們已經演示了. 另外兩種方式和直接賦值沒有本質區別
方法傳參
代碼示例:
public class Test {
public static void main(String[] args) {
Bird bird = new Bird("圓圓");
feed(bird);
}
public static void feed(Animal animal) {
animal.eat("谷子");
}
}
// 執行結果
圓圓正在吃谷子
此時形參 animal 的型別是 Animal (基類), 實際上對應到 Bird (父類) 的實體.
方法回傳
代碼示例
public class Test {
public static void main(String[] args) {
Animal animal = findMyAnimal();
}
public static Animal findMyAnimal() {
Bird bird = new Bird("圓圓");
return bird;
}
}
此時方法 findMyAnimal 回傳的是一個 Animal 型別的參考, 但是實際上對應到 Bird 的實體
2.2動態系結
當子類和父類中出現同名方法的時候, 再去呼叫會出現什么情況呢?
對前面的代碼稍加修改, 給 Bird 類也加上同名的 eat 方法, 并且在兩個 eat 中分別加上不同的日志.
如下:
// Animal.java
public class Animal {
protected String name;
public Animal(String name) {
this.name = name;
}
public void eat(String food) {
System.out.println("我是一只小動物");
System.out.println(this.name + "正在吃" + food);
}
}
// Bird.java
public class Bird extends Animal {
public Bird(String name) {
super(name);
}
public void eat(String food) {
System.out.println("我是一只小鳥");
System.out.println(this.name + "正在吃" + food);
}
}
// Test.java
public class Test {
public static void main(String[] args) {
Animal animal1 = new Animal("圓圓");
animal1.eat("谷子");
Animal animal2 = new Bird("扁扁");
animal2.eat("谷子");
}
}
// 執行結果
我是一只小動物
圓圓正在吃谷子
我是一只小鳥
扁扁正在吃谷子
此時, 我們發現:
- animal1 和 animal2 雖然都是 Animal 型別的參考, 但是 animal1 指向 Animal 型別的實體, animal2 指向Bird 型別的實體.
- 針對 animal1 和 animal2 分別呼叫 eat 方法, 發現 animal1.eat() 實際呼叫了父類的方法, 而
animal2.eat() 實際呼叫了子類的方法.
因此, 在 Java 中, 呼叫某個類的方法, 究竟執行了哪段代碼 (是父類方法的代碼還是子類方法的代碼) , 要看究竟這個引
用指向的是父類物件還是子類物件. 這個程序是程式運行時決定的(而不是編譯期), 因此稱為 動態系結
2.3方法重寫
定義:在子類中可以根據需要對從父類中繼承來的方法進行改造,也稱
為方法的重置、覆寫,在程式執行時,子類的方法將覆寫父類的方法,
要求:
- 子類重寫的方法必須和父類被重寫的方法具有相同的方法名稱、引數串列
- 子類重寫的方法的回傳值型別不能大于父類被重寫的方法的回傳值型別
- 子類重寫的方法使用的訪問權限不能小于父類被重寫的方法的訪問權限,子類不能重寫父類中宣告為private權限的方法
- 子類方法拋出的例外不能大于父類被重寫方法的例外
注意:
子類與父類中同名同引數的方法必須同時宣告為非static的(即為重寫),或者同時宣告為static的(不是重寫),因為static方法是屬于類的,子類無法覆寫父類的方法
方法重寫舉例1:
public class Person {
public String name;
public int age;
public String getInfo() {
return "Name: "+ name + "\n" +"age: "+ age;
}
}
public class Student extends Person {
public String school;
public String getInfo() { //重寫方法
return "Name: "+ name + "\nage: "+ age
+ "\nschool: "+ school;
}
public static void main(String args[]){
Student s1=new Student();
s1.name="Bob";
s1.age=20;
s1.school="school2";
System.out.println(s1.getInfo()); //Name:Bob age:20 school:school2
}
}
方法重寫舉例2:
class Parent {
public void method1() {}
}
class Child extends Parent {
//非法,子類中的method1()的訪問權限private比被覆寫方法的訪問權限public小
private void method1() {}
}
public class UseBoth {
public static void main(String[] args) {
Parent p1 = new Parent();
Child c1 = new Child();
p1.method1();
c1.method1();
}
}
2.4向下轉型
向下轉型就是父類物件轉成子類物件. 相比于向上轉型來說, 向下轉型沒那么常見,
但是也有一定的用途.
- 從子類到父類的型別轉換可以自動進行
- 從父類到子類的型別轉換必須通過造型(強制型別轉換)實作
- 無繼承關系的參考型別間的轉換是非法的
- 在造型前可以使用instanceof運算子測驗一個物件的型別
對于 Animal animal = new Bird(“圓圓”) 這樣的代碼:
編譯器檢查有哪些方法存在, 看的是 Animal 這個型別
執行時究竟執行父類的方法還是子類的方法, 看的是 Bird 這個型別.
那么想實作剛才的效果, 就需要向下轉型.
// (Bird) 表示強制型別轉換
Bird bird = (Bird)animal;
bird.fly();
// 執行結果
圓圓正在飛
為了讓向下轉型更安全, 我們可以先判定一下看看 animal 本質上是不是一個 Bird 實體, 再來轉換
Animal animal = new Cat("小貓");
if (animal instanceof Bird) {
Bird bird = (Bird)animal;
bird.fly();
}
instanceof 可以判定一個參考是否是某個類的實體. 如果是,則回傳 ture, 這時再進行向下轉型就比較安全了,
2.6 super 關鍵字
2.6.1 super 關鍵字的基本用法
在Java類中使用super來呼叫父類中的指定操作:
- super可用于訪問父類中定義的屬性
- super可用于呼叫父類中定義的成員方法
- super可用于在子類構造器中呼叫父類的構造器
注意:
- 尤其當子父類出現同名成員時,可以用super表明呼叫的是父類中的成員
super的追溯不僅限于直接父類 - super和this的用法相像,this代表本類物件的參考,super代表父類的記憶體
空間的標識
示例1使用了 super 來呼叫父類的構造器(這個代碼前面已經寫過了)
public Bird(String name) {
super(name);
}
示例2使用 super 來呼叫父類的普通方法
public class Bird extends Animal {
public Bird(String name) {
super(name);
}
@Override
public void eat(String food) {
// 修改代碼, 讓子呼叫父類的介面.
super.eat(food);
System.out.println("我是一只小鳥");
System.out.println(this.name + "正在吃" + food);
}
}
2.6.3 this和super的區別

3.包的使用
包 (package) 是組織類的一種方式.
使用包的主要目的是保證類的唯一性.
例如, 你在代碼中寫了一個 Test 類. 然后你的同事也可能寫一個 Test 類. 如果出現兩個同名的類, 就會沖突, 導致代碼不能編譯通過
3.1匯入包中的類
包 (package) 是組織類的一種方式.
使用包的主要目的是保證類的唯一性
代碼示例:
public class Test {
public static void main(String[] args) {
java.util.Date date = new java.util.Date();
// 得到一個毫秒級別的時間戳
System.out.println(date.getTime());
}
}
可以使用 import 陳述句匯入包
import java.util.Date;
public class Test {
public static void main(String[] args) {
Date date = new Date();
// 得到一個毫秒級別的時間戳
System.out.println(date.getTime());
}
}
如果需要使用 java.util 中的其他類, 可以使用 import java.util.*
注意:Java是用到包中的那個類就匯入那個類
但是我們更建議顯式的指定要匯入的類名. 否則還是容易出現沖突的情況.
例如:
import java.util.*;
import java.sql.*;
public class Test {
public static void main(String[] args) {
// util 和 sql 中都存在一個 Date 這樣的類, 此時就會出現歧義, 編譯出錯
Date date = new Date();
System.out.println(date.getTime());
}
}
// 編譯出錯
在這種情況下我們就需要完整的包名
import static java.lang.Math.*;
public class Test {
public static void main(String[] args) {
double x = 30;
double y = 40;
// 靜態匯入的方式寫起來更方便一些.但不推薦
// double result = Math.sqrt(Math.pow(x, 2) + Math.pow(y, 2));
double result = sqrt(pow(x, 2) + pow(y, 2));
System.out.println(result);
}
}
3.2常見系統包
- java.lang:系統常用基礎類(String、Object),此包從JDK1.1后自動匯入,
- java.lang.reflect:java 反射編程包;
- java.net:進行網路編程開發包,
- java.sql:進行資料庫開發的支持包,
- java.util:是java提供的工具程式包,(集合類等) 非常重要
- java.io:I/O編程開發包
轉載請註明出處,本文鏈接:https://www.uj5u.com/ruanti/341933.html
標籤:其他
上一篇:指標進階兩萬字總結(深入理解字符指標、指標陣列、陣列指標、陣列及指標傳參、函式指標、函式指標陣列、回呼函式等,指標筆試題總結)
