Java——超類和子類物件之間的轉換
繼承是Java中常用的一項特性,通過繼承我們可以省去很多麻煩。
而Java中超類和子類物件之間的轉換則是很多新手的常遇見的難題,要是處理不好,恐怕會因為這個很特殊的問題導致一些潛在的危險,讓你整整一個晚上都在除錯程式以解決一個讓人抓狂的java.lang.ArrayStoreException異常。
哈哈,所謂救人一bug勝造七級浮屠,今天我們就來理一理Java中超類和子類物件之間的轉換,看看它到底有什麼不一樣!
首先我們要了解一下轉換的概念。
從子類向父類的轉換稱為向上轉換(upcasting),通過向上轉換,我們能夠在編寫程式時採用通用程式設計的思想,在需要使用子類物件的時候,通過把變數定義為父型別,我們可以通過一個變數,使用該父型別的所有子型別例項;從父型別向子型別的轉換稱為向下轉換
那什麼時候可以進行轉換呢?
我們先來說一說向上轉換,這個比較簡單,而且限制還少。
還是老規矩,我們下面來看一段程式碼~~
來呀!!阿福,放程式碼~~~
class Father { }; class Son extends publc Father { };
在上面的程式中Father為超類,son是father的子類
那如何將子類轉換為超類呢?
Father f = new Son(); //父類引用指向子類物件
顯然,這樣就可以了。需要注意,new Son()表示在在堆中分配了一段空間來存放類Son中的資料,並返回一個Son的物件,並賦值給Father的引用f,即f指向子類的物件,此時,子類的物件並沒有定義一個名字。
也就是說上面的語句等價於:
Son s = new Son();
Father f = s;
注:父類不可呼叫子類新增的方法,即f不可呼叫s新增的方法。
好啦,子類轉父類就是這樣簡單,接下來該重點看看父類轉子類啦。
按理論上分析的話,父類轉子類是不可以實現的。因為子類繼承於父類,並新增了一些父類並沒有的東西,也就是說,子類物件一般都比父類物件包含更多的東西。這樣的話,子類如果訪問子類新增的內容, 而這些內容父類並沒有,所以就會報錯。
那Java裡面父類到底可不可以轉子類呢?
可以!但是需要滿足一個前提,即該父類物件已經指向了子類物件。
如:
Father f = new Son(); //父類引用指向子類物件
Son s2 = (Son)f; //可以
因為當子類強制轉換為父類物件時,並沒有實際丟失它原有記憶體空間(比父類多的那些部分)
只是暫時不可訪問,所以能再轉回來。
此外,當該父類是呼叫子類構造器建立的時候,它也可以轉換為子類。
如:
Employee b=new Manager("a",1000,2,1,225);//父類呼叫子類的構造器 建立了一個父類物件
這個原理其實和上面的情況一樣,因為它也是父類引用指向了子類物件,因此也可以進行轉換。
當然,為了防止報ClassCastExcept異常,一般可以在前面加一條判斷句 if(father instanceof Son);
如:
Father f = new Son(); //父類引用指向子類物件
if(father instanceof Son)
{
Son s2 = (Son)f;
}
這樣就可以避免出錯。
通過上文的瞭解,大家可能心裡已經有了一些理解,最後再提一下物件在繼承關係中的改變
物件的賦值是地址標識的傳遞,即兩個物件名共同使用同一段記憶體地址。在Java中,對父類與子類物件之間的賦值作了如下規定:
1、子類物件名可以賦值給父類物件名;但父類物件名不可以賦值給子類物件名。
即:父類物件名=子類物件名;
2、如果一個父類物件名已經被子類物件名所賦值,那可以將父類物件名經強制轉換賦值給子類物件名。
即:子類物件名=(子類類名)父類物件名;
常用的一種形式:方法中形參用父型別,實參用子類的物件名.
總結
對類進行造型轉換的應參考以下原則:
1.總是可以“父=子”賦值。此時不需要型別轉換。
2.可以執行型別轉換“子=(子)父”,但需要執行時進行檢查。如果父類變數引用的是正確的子型別,賦值將執行。如果父類變數引用的是不相關的子型別,將會生成class castException異常。
即:如果父類的例項是在子類的例項上塑造的,“子=(子)父”時就不會丟擲異常。
如:
A 是B的父類。
A a= new B(); //父類A的物件a是在子類B的物件上塑造的。
就可以:
B b= (B)a;
3.決不能在不相關的任何類之間執行類的賦值或者型別轉換。即類的造型轉換僅限於有繼承關係的倆個類的物件之間。
好啦,如果大家還是不太明白,可以試一試下面的程式,親自上手試一試會了解得更透徹一些。
package inheritance;
public class test {
public static void main(String[] args) {
// TODO Auto-generated method stub
Employee e;
Manager boss =new Manager("Searchin",52000,2018,10,25);
Employee[] staff=new Employee[3];
staff[0]=boss;//子類轉父類
Employee b=new Manager("a",1000,2,1,225);//父類呼叫子類的構造器 建立了一個父類物件
//Manager b=new Employee("a",1000,2,1,225);
Manager a=(Manager)staff[0];//強制型別轉換
System.out.println(staff[0].getName());
System.out.println(a.getName());
boss.setBonus(5000);
//staff[0].setBonus(5000);
}
}
package inheritance;
import java.time.*;
public class Employee
{
private String name;
private double salary;
private LocalDate hireDay;
public Employee(String name, double salary, int year, int month, int day)
{
this.name = name;
this.salary = salary;
hireDay = LocalDate.of(year, month, day);
}
public String getName()
{
return name;
}
public double getSalary()
{
return salary;
}
public LocalDate getHireDay()
{
return hireDay;
}
public void raiseSalary(double byPercent)
{
double raise = salary * byPercent / 100;
salary += raise;
}
}
package inheritance;
public class Manager extends Employee
{
private double bonus;
public Manager(String name, double salary, int year, int month, int day)
{
super(name, salary, year, month, day);
bonus = 0;
}
public double getSalary()
{
double baseSalary = super.getSalary();
return baseSalary + bonus;
}
public void setBonus(double b)
{
bonus = b;
}
}