面向對象(下)
八、繼承
8.1、繼承的概述
當多個類具有相同的屬性和行爲時,可以將這些相同的屬性和行爲抽取到一個獨立的類中;多個類就不需要再單獨定義這些內容,只要繼承獨立的類即可,簡化了代碼書寫。
抽取而成的獨立類稱爲父類或者超類,被抽取的多個類稱爲子類;父類就是子類共性的抽取,子類可以訪問父類的成員,private修飾的除外。
在java中,體現類之間的繼承關係用關鍵字:extends,格式爲:class Son extends Father{}。
8.2、繼承的特點
在java中,只支持單繼承,也支持多層繼承,但不支持多繼承!
多繼承的弊端:子類同時繼承多個父類時,稱之爲多繼承。因爲當兩個父類具有相同的方法,子類要執行哪個就不確定了;爲了提高代碼的安全性,所以java不支持多繼承,而是用多實現的機制代替了多繼承。
8.3、super關鍵字
super關鍵字:代表父類的內存空間的標識;代表父類對象的引用。
用法:(1)、當子父類同名的成員時,可以用super進行區分;
(2)、子類要調用父類的構造函數時,可以使用super語句。其實子類構造數都隱藏了super()語句,默認調用父類的無參構造函數。注意:super語句必須寫在第一行。
super和this的異同點:在構造函數時,super和this語句都必須放在第一行,所以super和this不能同時出現在同一個構造函數中;super代表的是父類的引用,this代表的是子類中的引用。
8.4、函數覆蓋(Override)
函數覆蓋(Override):當子父類中出現一模一樣的方法時,子類的方法會覆蓋了父類的方法,這種現象就稱之爲覆蓋,也就是重寫或者複寫。
覆蓋的應用:當子類需要用到父類中的功能,而該功能又需要實現新的內容時,可以用覆蓋;覆蓋既沿襲了父類的功能,也定義了子類的特有功能。
注意:(1)、父類中的私有方法不可覆蓋,因爲父類的私有方法子類根本不可見;
(2)、子類的方法權限一定要大於等於父類的方法權限;
(3)、靜態只能覆蓋靜態。
覆蓋與重載的區別:子父類中的方法一模一樣時,才能構成覆蓋;在一個類中,方法名相同,參數列表不同就可以構成重載,與返回值無關。
8.5、子類的實例化過程
子類的實例化過程:當要創建一個子類的對象時,系統會先創建父類中的數據,然後才創建子類中的數據。即:父類對象-à子類對象。子類中所有的構造函數默認都會訪問父類中的無參構造函數,因爲子類中的每一個構造函數第一行都默認隱藏了super();語句。當要調用父類中的有參構造函數時,必須在子類構造函數的第一行寫上super(參數列表);語句。
8.6、final關鍵字
final是java中的一個關鍵字,意爲:終極,最後的。
final關鍵字可以修飾類、方法、變量。final修飾的類稱爲終極類,不可以被繼承;final修飾的方法稱爲終極方法,不可以被覆蓋;final修飾的變量稱爲常量,值不可被修改,只能賦值一次。
下面用代碼演示繼承關係
package itheima.day07;
//演示繼承關係
public class ExtendsDemo1 {
public static void main(String[] args) {
// fu() run...1 --> zi() run...1
Zi z1 = new Zi();
// fun(int) run...15 --> zi(int) run...15
Zi z2 = new Zi(15);
}
}
//父類
class Fu{
protected int num =1;
// 父類的無參構造函數
Fu(){
System.out.println("fu() run..."+num);
}
// 父類的有參構造函數
Fu(int num){
this.num = num;
System.out.println("fun(int) run..."+num);
}
}
//子類
class Zi extends Fu{
// 子類的無參構造函數
Zi(){
// super();第一行默認隱藏了super();語句,用於初始化時調用父類的無參構造函數
System.out.println("zi() run..."+num);
}
// 調用父類的有參構造函數
Zi(int num){
super(num);
System.out.println("zi(int) run..."+num);
}
}
九、抽象類
9.1、抽象類與抽象方法的概述
抽象類:聲明瞭方法,但沒有具體的方法實現(方法體),而是將方法的實現交給子類去實現,這樣的類就叫做抽象類,抽象類用abstract關鍵字修飾,格式爲:abstract class AbsClass{}。
注意:抽象類也可以沒有抽象方法,在AWT中的適配器就是一個例子,適配器繼承於監聽器,但監聽器中的方法都是抽象方法,適配器實現了監聽器中的方法,但創建對象毫無意義,所以聲明爲抽象類。
抽象方法:定義了方法名稱,但並沒有具體的實現,這樣的方法就叫做抽象方法,抽象方法有關鍵字abstract修飾,格式爲:修飾符 abstract 返回值類型 函數名(參數列表);。
抽象類的由來:從多個事物中將共性的、本質的內容抽取出來。例如:狼和狗共性都是犬科,犬科就是抽象出來的概念。
抽象方法的由來:多個對象都具備相同的功能,但是功能具體內容有所不同,那麼在抽取過程中,只抽取了功能定義,並未抽取功能主體,那麼只有功能聲明,沒有功能主體的方法稱爲抽象方法。例如:狼和狗都有吼叫的方法,可是吼叫內容是不一樣的。所以抽象出來的犬科雖然有吼叫功能,但是並不明確吼叫的細節。
9.2、抽象類特點
特點:抽象類不可以用new創建對象,子類必須實現出抽象類中的抽象方法,否則該子類也是抽象類,子類實現了抽象類中的全部抽象方法後纔可以new創建對象。
下面代碼演示抽象類:
package itheima.day07;
/*
需求:公司中普通員工有姓名,工號,薪水,工作內容。
經理除了有姓名,工號,薪水,還有獎金,工作內容。
對給出需求進行數據建模。
*/
/*
思路:首先把普通員工和經理的共性內容抽取出來,形成一個抽象類
然後在普通員工類和經理類中中分別繼承並實現抽象類中的共性內容
經理類中加入自己特有的內容
*/
public class AstractClassTest {
public static void main(String[] args) {
// 普通員工
Professional pro = new Professional("zhangsan","1010",5000);
// 普通員工工作
pro.work();
// 經理
Manager man =new Manager("lisi","1000",10000,100);
// 經理工作
man.work();
}
}
//職工抽象類
//普通員工與經理抽取而成的抽象類
abstract class Emploee{
private String name;
private String id;
private double pay;
Emploee(String name,String id,double pay){
this.name = name;
this.id = id;
this.pay = pay;
}
public abstract void work();
}
//普通員工類
class Professional extends Emploee{
Professional(String name,String id,double pay){
super(name,id,pay);
}
// 實現抽象方法,普通員工工作
public void work(){
System.out.println("professional work...");
}
}
//經理類
class Manager extends Emploee{
private int hours;
Manager(String name,String id,double pay,int hours){
super(name,id,pay);
this.hours = hours;
}
// 實現抽象方法,經理工作
public void work(){
System.out.println("manager work...");
}
}
十、接口
10.1、接口的概述
接口:因爲多繼承帶來的不安全性,所以java不支持多繼承;接口的出現就是爲了代替C++中的多繼承。在java中,一個類可以實現多個接口,稱爲多實現。接口的格式爲:interface{}。
接口的成員修飾符固定,成員變量:public static final;成員函數:public abstract。
10.2、抽象類與接口的共性與區別:
共性:都是不斷抽取出來的抽象概念,不可以用new創建對象。
區別:(1)、抽象類體現繼承關係,一個類只能單繼承;接口體現實現關係,一個類可
以多實現。
(2)、抽象類是繼承,是“is a”關係;接口是現實,是“like a”關係.
(3)、抽象類可以定義非抽象方法,供子類直接使用;接口的方法都是抽象的,
接口中的成員都有固定修飾符。
下面代碼演示接口:
package itheima.day07;
//演示接口的功能
public class InterfaceDemo {
public static void main(String[] args) {
SmokerStudent zhangsan = new SmokerStudent();//zhangsan是個抽菸的學生
zhangsan.smoke();//zhangsan抽菸
zhangsan.study();//zhangsan學習
zhangsan.sleep();//zhangsan睡覺
Non_SmokeStudent lisi = new Non_SmokeStudent();//lisi是個不抽菸的學生
lisi.study();//lisi學習
lisi.sleep();//lisi睡覺
}
}
//抽菸的接口
interface Smoking{
public void smoke();
}
//學生的抽象類
abstract class Student{
abstract void study();
void sleep(){
System.out.println("student sleep...");
}
}
//抽菸的學生,繼承學生類,實現抽菸的接口
class SmokerStudent extends Student implements Smoking{
// 覆蓋學習的方法
void study(){
System.out.println("study...");
}
// 實現抽菸的方法
public void smoke(){
System.out.println("這是一羣爲國家財政收入做出偉大貢獻的哥們...");
}
}
//不抽菸的學生,只繼承學生類
class Non_SmokeStudent extends Student{
// 覆蓋學習方法
void study(){
System.out.println("study...");
}
}
十一、多態
11.1、多態的概述
多態:一種事物的多種形態,叫做多態。例如:動物中有狗,貓等;對於狗來說,既可以稱爲狗,也可以稱爲動物,這就是多態的體現。
代碼體現:因爲狗也是動物,所以在程序中可以:動物 d = new狗();簡單說:父類或者接口的引用指向自己的子類對象!
11.2多態的特點
多態的特點:多態的發生必須存在具有繼承關係的子父類中或者有實現關係的接口子類中;成員函數在編譯時期取決於引用變量所屬的類中是否有所調用的成員,運行時期取決於對象所屬的類中是否有所調用的成員;成員變量只取決於變量所屬的類。
好處:多態可以提高程序的擴展性和後期的維護性。
下面代碼演示多態:
package itheima.day08;
//演示多態
public class DuotaiDemo {
public static void main(String[] args) {
// 父類的引用指向子類的對象,多態的體現
Animal cat = new Cat();
cat.eat();
// cat.catchMouse();//編譯通不過
function(new Dog());//Animal a = new Dog();多態!
}
public static void function(Animal a){
a.eat();
if(a instanceof Cat){
// 向下類型轉換
Cat c = (Cat)a;
// 轉換之後可以使用子類特有的功能
c.CatchMouse();
}
else if(a instanceof Dog){
Dog d = (Dog)a;
d.kanjia();
}
}
}
//動物類,抽象
abstract class Animal{
public abstract void eat();
}
//狗
class Dog extends Animal{
public void eat(){
System.out.println("坑骨頭");
}
public void kanjia(){
System.out.println("看家");
}
}
// 貓
class Cat extends Animal{
public void eat(){
System.out.println("喫老鼠");
}
public void CatchMouse(){
System.out.println("抓老鼠");
}
}
11.3、多態擴展--Object類中的多態
在java中,Object類是所有類直接或者間接的父類,類似於上帝。根據共性抽取原則,可以把所有對象都應該具有的功能都定義在Object類中,但父類並不知道子類的所需,所有我們有時候必須覆蓋Object類提供的方法,例如:equal()方法,toString()方法,爲了更好的理解,下面代碼演示:
package itheima.day08;
public class ObjectDemo {
/*
Object是所有對象的直接或者間接父類,上帝。
該類中定義了所有對象都具備的功能
boolean equals(Object obj)
指示其他某個對象是否與此對象“相等”。
String toString()
返回該對象的字符串表示
*/
public static void main(String[] args) {
Demo d1 = new Demo(1);
Demo d2 = new Demo(2);
System.out.println(d1.equals(d2));//false
System.out.println(d1.toString());//Demo:1
System.out.println(d2.toString());//Demo:2
}
}
//extends Object ,Demo類其實繼承了Object類
class Demo{
private int num;
Demo(int num){
this.num = num;
}
// 根據自己需要,複寫了Object類中的equals()方法
public boolean equals(Object obj){
if(!(obj instanceof Demo))
return false;
Demo d = (Demo)obj;
return this.num ==d.num;
}
// 根據自己需要,複寫了Object類中的toString()方法
public String toString(){
return "Demo:"+num;
}
}
十二、內部類與匿名內部類
12.1、內部類
在生活中,有些事物內部還有事物,比如:人體中有心臟;這種情況在java中被描述爲內部類
內部類:將一個類定義在另外一個類中,裏面的那個類就稱爲內部類。
特點:內部類可以直接訪問外部類中的成員,包括私有成員;外部類要訪問內部類中的成員必須要建立內部類的對象。
注意:內部類可以定義在成員位置上,這時可以被private、static成員修飾符修飾,被static修飾的內部類只能訪問外部類中的靜態成員;內部類還可以定義在局部位置上,這時要訪問局部中的局部變量,但必須要被final修飾。
下面代碼演示內部類:
package itheima.day09;
//演示內部類
public class InnerClassDemo {
public static void main(String[] args) {
Outer out = new Outer();
out.method();
// 創建內部類的方式
Outer.Inner in = new Outer().new Inner();
in.function();
}
}
//外部類
class Outer{
// 外部類成員變量
private int num = 10;
// 內部類
class Inner{
// 內部類成員變量
private int num = 1;
void function(){
// 引用的是內部類的成員變量
System.out.println("Inner:"+num);
// 當內部類的成員變量與外部類的成員變量重名時,用Outer.this.區分
System.out.println("Inner:"+Outer.this.num);
}
}
void method(){
// 外部類使用內部類方法時,必須先創建內部類對象
Inner in = new Inner();
in.function();
}
}
12.2、匿名內部類
匿名內部類:內部類的簡化形式。
前提:匿名內部類必須繼承或實現一個外部類或接口。
格式:new外部類名或者接口名(){覆蓋類或者接口中的代碼,(也可以自定義內容。)}
使用方法:通常在使用方法是接口類型參數,並該接口中的方法不超過三個時,可以將匿名內部類作爲參數傳遞。
下面代碼演示匿名內部類:
package itheima.day09;
//演示匿名內部類
interface Inter{//匿名內部類的前提
void method();
}
class Test{
static Inter function(){
// 匿名內部類
return new Inter(){
public void method(){
System.out.println("metod run...");
}
};
}
}
public class InnerClassTest {
public static void main(String[] args) {
Test.function().method();
}
}