淺談Java中向上造型向下造型和接口回調(diào)中的問(wèn)題
最近回顧了一下java繼承中的問(wèn)題,下面貼代碼:
public class Base { protected String temp = "base"; public void fun(){ System.out.print("BASE fun()"); } public static void main(String[] args) { Base b =new Base();//實(shí)例化Base對(duì)象 b.fun(); //調(diào)用父類(lèi)中fun()的方法 System.out.println(b.temp);//訪(fǎng)問(wèn)父類(lèi)中的成員變量temp /*****************************************************/ System.out.println("/***************/"); Son son = new Son();//實(shí)例化Son對(duì)象 son.fun(); //調(diào)用son的fun方法 System.out.println(son.temp);//訪(fǎng)問(wèn)son的成員變量temp Son S_son=new Son(); //實(shí)例化Son對(duì)象 Base B_s =(Base)S_son; //向上造型-----------相當(dāng)于Base s =new Son(); B_s.fun(); //調(diào)用子類(lèi)的fun()方法 System.out.println(B_s.temp);//訪(fǎng)問(wèn)父類(lèi)的成員變量temp /******************************************************/ System.out.println("/***************/"); Base s =new Son();//向上造型 s.fun(); //調(diào)用子類(lèi)的方法 System.out.println(s.temp);//調(diào)用父類(lèi)的成員變量temp Base ba =new Son(); Son so = (Son)ba; //向下造型 so.fun(); //調(diào)用子類(lèi)的fun()方法 System.out.println(so.temp);//訪(fǎng)問(wèn)子類(lèi)的成員變量temp } } class Son extends Base{ protected String temp = "Son"; public void fun(){ System.out.print("SON fun()"); } }
運(yùn)行結(jié)果:
BASE fun()base /***************/ SON fun()Son SON fun()base /***************/ SON fun()base SON fun()Son
結(jié)論總結(jié):
1、父類(lèi)的引用變量可以指向子類(lèi)對(duì)象,子類(lèi)的引用變量不能指向父類(lèi)對(duì)象。
2、向上造型(upcasting):把子類(lèi)對(duì)象直接賦給父類(lèi)引用,向上轉(zhuǎn)型不用強(qiáng)制轉(zhuǎn)型。如:
Base base = new Son(); 向上造型–隱式
3、向下造型(downcasting):把指向子類(lèi)對(duì)象的父類(lèi)引用賦給子類(lèi)引用,要強(qiáng)制轉(zhuǎn)型。
Base b= new Son();
Son s = (Son)base;----------向下造型–必須強(qiáng)制轉(zhuǎn)換,且必須有繼承關(guān)系
4、upcasting 會(huì)丟失子類(lèi)特有的方法,但是子類(lèi)overriding 父類(lèi)的方法,子類(lèi)對(duì)象去 調(diào)用方法有效
5、調(diào)用方法或成員變量的規(guī)律:
前提:子類(lèi)存在重寫(xiě)方法,父類(lèi)和子類(lèi)中同時(shí)有相同名稱(chēng)的成員變量。
(1)當(dāng)進(jìn)行了向上造型的引用型變量—父類(lèi)引用變量只能調(diào)用子類(lèi)的重寫(xiě)方法,但父類(lèi)的引用變量只能訪(fǎng)問(wèn)父類(lèi)中的成員變量
(2)當(dāng)進(jìn)行了向下造型的引用型變量—子類(lèi)引用變量只調(diào)用子類(lèi)重寫(xiě)方法,子類(lèi)的引用變量只能訪(fǎng)問(wèn)子類(lèi)的成員變量。
(3)自己想了個(gè)通俗的說(shuō)法,調(diào)用方法,得當(dāng)前的引用變量指向的對(duì)象;調(diào)用變量,得看當(dāng)前引用變量的類(lèi)型。
即,調(diào)方法,看對(duì)象,調(diào)變量,看類(lèi)型。(可以試驗(yàn)一下)
接口回調(diào)與向上造型
/** * 接口回調(diào),和向上造型的對(duì)比 * @author Character_Painter * */ public class Inter extends Father implements Person,Teacher{ public void study(){ System.out.println("學(xué)習(xí)"); } public void sleep(){ System.out.println("子類(lèi)sleep()方法"); } public static void main(String[] args) { Father f = new Inter(); f.sleep();//向上轉(zhuǎn)型---調(diào)用 Person p=new Inter(); p.eat(); //接口回調(diào)方法 Teacher t = new Inter(); t.teach(); //接口回調(diào)方法 } @Override public void eat() { System.out.println("重寫(xiě)eat()方法"); } @Override public void teach() { System.out.println("重寫(xiě)teache()方法"); } } interface Person{ public void eat(); } interface Teacher{ public void teach(); } class Father{ public void sleep(){ System.out.println("父類(lèi)sleep()方法"); } }
運(yùn)行結(jié)果
子類(lèi)sleep()方法
重寫(xiě)eat()方法
重寫(xiě)teache()方法
總結(jié):
使用接口方式,其目的應(yīng)該是實(shí)現(xiàn)多個(gè)父類(lèi)型的方法實(shí)現(xiàn),強(qiáng)調(diào)一個(gè)多種實(shí)現(xiàn),而向上造型,針對(duì)的是一對(duì)一的繼承關(guān)系,向上造型后,可以調(diào)用子類(lèi)的重寫(xiě)的方法。這就是我認(rèn)為他們的區(qū)別。
補(bǔ)充知識(shí):Java向下轉(zhuǎn)型的意義與塑型的三個(gè)方面
一開(kāi)始學(xué)習(xí) Java 時(shí)不重視向下轉(zhuǎn)型。一直搞不清楚向下轉(zhuǎn)型的意義和用途,不清楚其實(shí)就是不會(huì),那開(kāi)發(fā)的過(guò)程肯定也想不到用向下轉(zhuǎn)型。
其實(shí)向上轉(zhuǎn)型和向下轉(zhuǎn)型都是很重要的,可能我們平時(shí)見(jiàn)向上轉(zhuǎn)型多一點(diǎn),向上轉(zhuǎn)型也比較好理解。
但是向下轉(zhuǎn)型,會(huì)不會(huì)覺(jué)得很傻,我是要用子類(lèi)實(shí)例對(duì)象,先是生成子類(lèi)實(shí)例賦值給父類(lèi)引用,在將父類(lèi)引用向下強(qiáng)轉(zhuǎn)給子類(lèi)引用,這不是多此一舉嗎?我不向上轉(zhuǎn)型也不向下轉(zhuǎn)型,直接用子類(lèi)實(shí)例就行了。
我開(kāi)始學(xué)習(xí)Java時(shí)也是這么想的,這誤區(qū)導(dǎo)致我覺(jué)得向下轉(zhuǎn)型就是沒(méi)用的。
隨著技術(shù)的提升,我在看開(kāi)源的項(xiàng)目學(xué)習(xí),發(fā)現(xiàn)很多地方都用了向下轉(zhuǎn)型的技術(shù),這就讓我重視了起來(lái),想要重新來(lái)復(fù)習(xí)(學(xué)習(xí))這個(gè)知識(shí)點(diǎn)。也是搜索了許多博客文章,但都沒(méi)具體說(shuō)明向下轉(zhuǎn)型,只是給了例子演示怎么使用,反而是向上轉(zhuǎn)型講了一堆(可能是我沒(méi)找到)。
這篇博客就是講向下轉(zhuǎn)型的,那我們就來(lái)學(xué)習(xí)下向下轉(zhuǎn)型,了解下這種特性的意義和使用場(chǎng)景
新建一個(gè)電子產(chǎn)品接口,如下:
public interface Electronics{
}
很簡(jiǎn)單,什么方法都沒(méi)有。
新建一個(gè)Thinkpad筆記本類(lèi),并實(shí)現(xiàn)電子產(chǎn)品接口:
public class Thinkpad implements Electronics{ //Thinkpad引導(dǎo)方法 public void boot(){ System.out.println("welcome,I am Thinkpad"); } //使用Thinkpad編程 public void program(){ System.out.println("using Thinkpad program"); } }
新建一個(gè)Mouse鼠標(biāo)類(lèi),并實(shí)現(xiàn)電子產(chǎn)品接口:
public class Mouse implements Electronics{ //鼠標(biāo)移動(dòng) public void move(){ System.out.println("move the mouse"); } //鼠標(biāo)點(diǎn)擊 public void onClick(){ System.out.println("a click of the mouse"); } }
新建一個(gè)Keyboard鍵盤(pán)類(lèi),并實(shí)現(xiàn)電子產(chǎn)品接口:
public class Keyboard implements Electronics{ //使用鍵盤(pán)輸入 public void input(){ System.out.println("using Keyboard input"); } }
這里子類(lèi)比較多,是為了更好的理解。每個(gè)類(lèi)的方法的邏輯實(shí)現(xiàn)也很簡(jiǎn)單。打印了一行信息
接下來(lái),我們想象一個(gè)情景:我們?nèi)ド坛琴I(mǎi)電子產(chǎn)品,電子產(chǎn)品很多吧,比如筆記本電腦,鼠標(biāo),鍵盤(pán),步步高點(diǎn)讀機(jī)哪里不會(huì)點(diǎn)哪里,我們用的手機(jī),等等,這些都屬于電子產(chǎn)品。電子產(chǎn)品是抽象的。好,那么我們決定買(mǎi)一臺(tái)Thinkpad,一個(gè)鼠標(biāo)和一個(gè)鍵盤(pán)。
這時(shí),我們需要一個(gè)購(gòu)物車(chē)來(lái)裝這些電子產(chǎn)品吧。我們可以添加進(jìn)購(gòu)物車(chē),然后通過(guò)購(gòu)物車(chē)還能知道存放的電子產(chǎn)品數(shù)量,能拿到對(duì)應(yīng)的電子產(chǎn)品。
那么,一個(gè)購(gòu)物車(chē)類(lèi)就出來(lái)了,如下:
import java.util.ArrayList; import java.util.List; public class ShopCar{ private List<Electronics> mlist = new ArrayList<Electronics>(); public void add(Electronics electronics){ mlist.add(electronics); } public int getSize(){ return mlist.size(); } public Electronics getListItem(int position){ return mlist.get(position); } }
List 集合是用來(lái)存放電子產(chǎn)品的,add 方法用來(lái)添加電子產(chǎn)品到購(gòu)物車(chē),getSize 方法用來(lái)獲取存放的電子產(chǎn)品數(shù)量,getListItem 方法用來(lái)獲取相應(yīng)的電子產(chǎn)品。
可以看到 List<Electronics> 用了泛型的知識(shí),至于為什么要用泛型?這個(gè)不做介紹了,泛型很重要的。
而我覺(jué)得比較疑惑的是為什么是放 Electronics 的泛型,而不是放Thinkpad,Mouse,Keyboard,Phone等?
那么如果是List<Thinkpad>,肯定是放不進(jìn)鼠標(biāo)Mouse的吧,難道要生成3個(gè)集合?這里是定義了3個(gè)電子產(chǎn)品類(lèi),但是我如果有100種電子產(chǎn)品呢,要定義100個(gè)集合?
這太可怕了。所以之前,我們寫(xiě)了一個(gè)Electronics接口,提供了一個(gè)Electronics的標(biāo)準(zhǔn),然后讓每一個(gè)Electronics子類(lèi)都去實(shí)現(xiàn)這個(gè)接口。
實(shí)際上這里又涉及到了向上轉(zhuǎn)型的知識(shí)點(diǎn),我們雖然在add 方法將子類(lèi)實(shí)例傳了進(jìn)來(lái)存放,但子類(lèi)實(shí)例在傳進(jìn)去的過(guò)程中也進(jìn)行了向上轉(zhuǎn)型
所以,此時(shí)購(gòu)物車(chē)?yán)锎娣诺淖宇?lèi)實(shí)例對(duì)象,由于向上轉(zhuǎn)型成Electronics,已經(jīng)丟失了子類(lèi)獨(dú)有的方法,以上述例子來(lái)分析,Thinkpad實(shí)例就是丟失了boot() 和program() 這兩個(gè)方法,而Mouse實(shí)例就是丟失了move()和onClick()這兩個(gè)方法
但是實(shí)際使用Thinkpad或Mouse或Keyboard時(shí),這種情況肯定不是我們想要的
接著我們寫(xiě)一個(gè)測(cè)試類(lèi) Test 去測(cè)試購(gòu)物車(chē)?yán)锏碾娮赢a(chǎn)品。
測(cè)試類(lèi) Test 如下:
public class Test{ public static final int THINKPAD = 0; public static final int MOUSE = 1; public static final int KEYBOARD = 2; public static void main(String[] args){ //添加進(jìn)購(gòu)物車(chē) ShopCar shopcar = new ShopCar(); shopcar.add(new Thinkpad()); shopcar.add(new Mouse()); shopcar.add(new Keyboard()); //獲取大小 System.out.println("購(gòu)物車(chē)存放的電子產(chǎn)品數(shù)量為 ——> "+shopcar.getSize()); //開(kāi)始測(cè)試thinkpad電腦 Thinkpad thinkpad = (Thinkpad)shopcar.getListItem(THINKPAD); thinkpad.boot(); thinkpad.program(); System.out.println("-------------------"); //開(kāi)始測(cè)試Mouse鼠標(biāo) Mouse mouse = (Mouse)shopcar.getListItem(MOUSE); mouse.move(); mouse.onClick(); System.out.println("-------------------"); //開(kāi)始測(cè)試Keyboard鍵盤(pán) Keyboard keyboard = (Keyboard)shopcar.getListItem(KEYBOARD); keyboard.input(); } }
運(yùn)行截圖:
舉個(gè)例子分析就好
//開(kāi)始測(cè)試thinkpad電腦 Thinkpad thinkpad = (Thinkpad)shopcar.getListItem(THINKPAD); thinkpad.boot(); thinkpad.program();
shopcar.getListItem(THINKPAD)
這句代碼是獲取到Electronics類(lèi)型的實(shí)例。不是Thinkpad的實(shí)例
通過(guò)向下轉(zhuǎn)型,賦值給子類(lèi)引用
Thinkpad thinkpad = (Thinkpad)shopcar.getListItem(THINKPAD);
這樣子類(lèi)實(shí)例又重新獲得了因?yàn)橄蛏限D(zhuǎn)型而丟失的方法(boot 和program)
總結(jié)一下吧,很多時(shí)候,我們需要把很多種類(lèi)的實(shí)例對(duì)象,全部扔到一個(gè)集合。(這句話(huà)很重要)
在這個(gè)例子里就是把Thinkpad筆記本,Mouse鼠標(biāo),KeyBoard鍵盤(pán)等實(shí)例對(duì)象,全部扔到一個(gè)Shopcar購(gòu)物車(chē)集合。
但是肯定不可能給他們每個(gè)種類(lèi)都用一個(gè)獨(dú)立的集合去存放吧,這個(gè)時(shí)候我們應(yīng)該尋找到一個(gè)標(biāo)準(zhǔn),接口就是一個(gè)標(biāo)準(zhǔn)。這些都是各種電子產(chǎn)品,抽象成電子產(chǎn)品。然后一個(gè)Electronics接口就出來(lái)了。
在回到剛才,我們把很多種類(lèi)的實(shí)例對(duì)象全部扔到一個(gè)集合。或許這樣比較好理解:把很多種類(lèi)的子類(lèi)實(shí)例對(duì)象全部扔到存放父類(lèi)實(shí)例的集合。
經(jīng)過(guò)了這個(gè)過(guò)程,子類(lèi)實(shí)例已經(jīng)賦值給了父類(lèi)引用(即完成了向上轉(zhuǎn)型),但很遺憾的丟失了子類(lèi)擴(kuò)展的方法。
很好的是Java語(yǔ)言有個(gè)向下轉(zhuǎn)型的特性,讓我們可以重新獲得丟失的方法,即強(qiáng)轉(zhuǎn)回子類(lèi)
所以我們需要用到子類(lèi)實(shí)例的時(shí)候,就從那個(gè)父類(lèi)集合里拿出來(lái)向下轉(zhuǎn)型就可以了,一樣可以使用子類(lèi)實(shí)例對(duì)象
……
我在搜索java向下轉(zhuǎn)型的意義時(shí),得到一個(gè)比較好的答案是這樣的:
最大的用處是java的泛型編程,用處很大,Java的集合類(lèi)都是這樣的。
而在Android開(kāi)發(fā)中,我們?cè)贚ayout文件夾,用xml寫(xiě)的控件。為什么能在Activity等組件中通過(guò) findViewById() 方法找到呢?為什么 findViewById(R.id.textview) 方法傳入TextView的id后,還要轉(zhuǎn)型為T(mén)extView呢?這就是 Java 向下轉(zhuǎn)型的一個(gè)應(yīng)用
以上這篇淺談Java中向上造型向下造型和接口回調(diào)中的問(wèn)題就是小編分享給大家的全部?jī)?nèi)容了,希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。
相關(guān)文章
關(guān)于spring項(xiàng)目中無(wú)法加載resources下文件問(wèn)題及解決方法
在學(xué)習(xí)Spring過(guò)程中,TestContext框架試圖檢測(cè)一個(gè)默認(rèn)的XML資源位置,再resources下創(chuàng)建了一個(gè)com.example的文件夾,執(zhí)行時(shí),報(bào)錯(cuò),本文給大家介紹spring項(xiàng)目中無(wú)法加載resources下文件,感興趣的朋友跟隨小編一起看看吧2023-10-10詳解mybatis-plus的 mapper.xml 路徑配置的坑
這篇文章主要介紹了詳解mybatis-plus的 mapper.xml 路徑配置的坑,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2020-08-08Java使用自定義注解實(shí)現(xiàn)函數(shù)測(cè)試功能示例
這篇文章主要介紹了Java使用自定義注解實(shí)現(xiàn)函數(shù)測(cè)試功能,結(jié)合實(shí)例形式分析了java自定義注解在函數(shù)測(cè)試過(guò)程中相關(guān)功能、原理與使用技巧,需要的朋友可以參考下2019-10-10SpringBoot自定義動(dòng)態(tài)數(shù)據(jù)源的流程步驟
動(dòng)態(tài)數(shù)據(jù)源,本質(zhì)上是把多個(gè)數(shù)據(jù)源存儲(chǔ)在一個(gè)?Map?中,當(dāng)需要使用某一個(gè)數(shù)據(jù)源時(shí),使用?key?獲取指定數(shù)據(jù)源進(jìn)行處理,本文將給大家介紹一下SpringBoot自定義動(dòng)態(tài)數(shù)據(jù)源的流程步驟,需要的朋友可以參考下2024-06-06Java中synchronized實(shí)現(xiàn)原理詳解
這篇文章主要介紹了Java中synchronized實(shí)現(xiàn)原理詳解,涉及synchronized實(shí)現(xiàn)同步的基礎(chǔ),Java對(duì)象頭,Monitor,Mark Word,鎖優(yōu)化,自旋鎖等相關(guān)內(nèi)容,具有一定借鑒價(jià)值,需要的朋友可以參考下。2017-11-11Java通過(guò)調(diào)用C/C++實(shí)現(xiàn)的DLL動(dòng)態(tài)庫(kù)——JNI的方法
這篇文章主要介紹了Java通過(guò)調(diào)用C/C++實(shí)現(xiàn)的DLL動(dòng)態(tài)庫(kù)——JNI的方法,非常不錯(cuò),具有參考借鑒價(jià)值,需要的朋友可以參考下2018-01-01使用Java編寫(xiě)一個(gè)簡(jiǎn)單的Web的監(jiān)控系統(tǒng)
這篇文章主要介紹了使用Java編寫(xiě)一個(gè)簡(jiǎn)單的Web的監(jiān)控系統(tǒng)的例子,并且將重要信息轉(zhuǎn)為XML通過(guò)網(wǎng)頁(yè)前端顯示,非常之實(shí)用,需要的朋友可以參考下2015-11-11Java中數(shù)組的一些常見(jiàn)操作和技巧分析
這篇文章主要給大家介紹了關(guān)于Java中數(shù)組的一些常見(jiàn)操作和技巧分析的相關(guān)資料,數(shù)組(Array)是Java中的一種引用數(shù)據(jù)類(lèi)型,是多個(gè)相同類(lèi)型數(shù)據(jù)一定順序排列的集合,并使用一個(gè)名字命名,并通過(guò)編號(hào)的方式對(duì)這些數(shù)據(jù)進(jìn)行統(tǒng)一管理,需要的朋友可以參考下2023-08-08