自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

Java 面向?qū)ο笠挥[

開(kāi)發(fā) 后端
學(xué) Java 的朋友都知道,Java 是一門典型的面向?qū)ο蟮母呒?jí)程序設(shè)計(jì)語(yǔ)言,但有些朋友可能不清楚面向?qū)ο笤?Java 中是怎么體現(xiàn)的。這篇文章就向大家分享下 Java 在面向?qū)ο蠓矫娴囊恍┲R(shí)。

[[404430]]

本文轉(zhuǎn)載自微信公眾號(hào)「蝸?;ヂ?lián)網(wǎng)」,作者白色蝸牛。轉(zhuǎn)載本文請(qǐng)聯(lián)系蝸牛互聯(lián)網(wǎng)公眾號(hào)。

本文大綱:

前言

學(xué) Java 的朋友都知道,Java 是一門典型的面向?qū)ο蟮母呒?jí)程序設(shè)計(jì)語(yǔ)言,但有些朋友可能不清楚面向?qū)ο笤?Java 中是怎么體現(xiàn)的。這篇文章就向大家分享下 Java 在面向?qū)ο蠓矫娴囊恍┲R(shí)。

Java 語(yǔ)言簡(jiǎn)介

Java 語(yǔ)言特點(diǎn)

首先我們看下 Java 的語(yǔ)言特點(diǎn),如圖所示。

Java 是純粹的面向?qū)ο笳Z(yǔ)言,它因統(tǒng)一的字節(jié)碼文件和差異化的 JDK 而具有平臺(tái)無(wú)關(guān)的特性。

Java 內(nèi)置豐富的類庫(kù),使開(kāi)發(fā)者效率大為提升。它支持 web,廣泛應(yīng)用于各大互聯(lián)網(wǎng)企業(yè)的網(wǎng)站后臺(tái),像阿里美團(tuán)都在使用。

Java 的安全性也很出眾,通過(guò)沙箱安全模型保證其安全性,能夠有效防止代碼攻擊。

Java 也具備很強(qiáng)的健壯性,比如它是強(qiáng)類型的,支持自動(dòng)化的垃圾回收器,有完善的異常處理機(jī)制和安全檢查機(jī)制。

與 C++ 比較

比較點(diǎn) C++ Java
語(yǔ)言類型 編譯型語(yǔ)言 解釋編譯混合型語(yǔ)言
執(zhí)行速度
是否跨平臺(tái)
面向?qū)ο?/td> 面向?qū)ο蠛兔嫦蜻^(guò)程混合 純面向?qū)ο?/td>
指針 無(wú)
多繼承 支持 不支持
內(nèi)存管理 手動(dòng) 自動(dòng)

從語(yǔ)言類型上看,C++ 的代碼編譯好,就能被計(jì)算機(jī)直接執(zhí)行,它是編譯型語(yǔ)言,而 Java 經(jīng)過(guò) javac 把 java 文件編譯成 class 文件后,還需要 JVM 從 class 文件讀一行解釋執(zhí)行一行,它是解釋編譯混合型語(yǔ)言。也就是中間多了 JVM 這一道,Java 也具備了跨平臺(tái)特性,而 C++ 就沒(méi)有這個(gè)優(yōu)勢(shì)。

從面向?qū)ο蟮慕嵌壬峡?,C++ 是在 C 的基礎(chǔ)上的新的探索和延伸,因此它是面向?qū)ο蠛兔嫦蜻^(guò)程混合的,而 Java 就是純粹的面向?qū)ο蟆?/p>

此外,C++ 有指針的概念,Java 沒(méi)有。C++ 支持多繼承,Java 不支持。C++ 需要手動(dòng)進(jìn)行內(nèi)存管理,Java 通過(guò)垃圾回收機(jī)制實(shí)現(xiàn)了內(nèi)存的自動(dòng)管理。

面向?qū)ο笏枷?/h2>

我們總在提面向?qū)ο?,那面向?qū)ο缶烤故莻€(gè)什么東西呢?在面向?qū)ο蟪霈F(xiàn)之前的面向過(guò)程又是怎么回事呢?

其實(shí)無(wú)論是面向?qū)ο筮€是面向過(guò)程,都是我們?cè)诰幊虝r(shí)解決問(wèn)題的一種思維方式。

只是在最初,人們分析解決問(wèn)題的時(shí)候,會(huì)把所需要的步驟都列出來(lái),然后通過(guò)計(jì)算機(jī)中的函數(shù)把這些步驟挨個(gè)實(shí)現(xiàn),這種過(guò)程化的敘事思維,就是面向過(guò)程思想。

你比如,把一頭大象放進(jìn)冰箱,通常會(huì)怎么做呢?

我們的習(xí)慣性思維是會(huì)分為三步,第一步,把冰箱門打開(kāi),第二步,把大象推進(jìn)去,第三步,把冰箱門關(guān)閉(假設(shè)大象很乖,冰箱很大,門能關(guān)住)。

這種方式固然可行,但當(dāng)場(chǎng)景發(fā)生變化時(shí),比如大象變成豬,冰箱變成衣柜,類似的步驟用面向過(guò)程編碼的話就要再寫一遍。這樣就導(dǎo)致代碼開(kāi)發(fā)變成了記流水賬,久而久之就成為面條代碼。

我們仔細(xì)分析面向過(guò)程的這些步驟,會(huì)發(fā)現(xiàn)都是命令式的動(dòng)賓結(jié)構(gòu):開(kāi)冰箱門,推大象,場(chǎng)景切換下就是開(kāi)衣柜門,推豬。你會(huì)發(fā)現(xiàn)從這兩種場(chǎng)景下是可以找到共性的,就是冰箱門和衣柜門都有打開(kāi)和關(guān)閉的特點(diǎn),大象和豬都能走路,所以能被人推進(jìn)去。

當(dāng)我們的視角不再是流程,而是操作對(duì)象的時(shí)候,冰箱門和衣柜門都可以抽象成門,有打開(kāi)和關(guān)閉的特點(diǎn),大象和豬都可以抽象成動(dòng)物,有走路的特點(diǎn)。按這個(gè)思路,我們可以把這件事簡(jiǎn)化成主謂結(jié)構(gòu):門打開(kāi),動(dòng)物走進(jìn)去,門關(guān)閉。

這種把事情分解成各個(gè)對(duì)象,描述對(duì)象在整個(gè)事情中的行為,就是面向?qū)ο笏枷搿?/p>

你會(huì)發(fā)現(xiàn),面向過(guò)程更講事情的步驟,面向?qū)ο蟾v對(duì)象的行為。

面向?qū)ο罂梢曰趯?duì)象的共性做抽象,為軟件工程的復(fù)用和擴(kuò)展打好了堅(jiān)實(shí)的基礎(chǔ)。這也是為什么在很多大型軟件開(kāi)發(fā)選型上,大多會(huì)使用面向?qū)ο笳Z(yǔ)言編程。

面向?qū)ο蠡A(chǔ)

Java 作為純面向?qū)ο笳Z(yǔ)言,我們有必要了解下面向?qū)ο蟮幕A(chǔ)知識(shí)。

面向?qū)ο笥兴拇筇卣?,是抽象,封裝,繼承和多態(tài)。也有很多人認(rèn)為是三大特征,不包括抽象,但我覺(jué)得抽象才是面向?qū)ο笏枷胱顬楹诵牡奶卣鳎渌齻€(gè)特征無(wú)非是抽象這個(gè)特征的實(shí)現(xiàn)或擴(kuò)展。

我總結(jié)了下這四大特征在面向?qū)ο箢I(lǐng)域分別解決了什么問(wèn)題,再逐一介紹:

  • 抽象:解決了模型的定義問(wèn)題。
  • 封裝:解決了數(shù)據(jù)的安全問(wèn)題。
  • 繼承:解決了代碼的重用問(wèn)題。
  • 多態(tài):解決了程序的擴(kuò)展問(wèn)題。

抽象

抽象是面向?qū)ο蟮暮诵奶卣?,良好的業(yè)務(wù)抽象和建模分析能力是后續(xù)封裝、繼承和多態(tài)的基礎(chǔ)。

面向?qū)ο笏季S中的抽象分為歸納和演繹兩種。

歸納是從具體到本質(zhì),從個(gè)性到共性,將一類對(duì)象的共同特征進(jìn)行歸一化的邏輯思維過(guò)程。比如我們把見(jiàn)到的像大象,老虎,豬這些能動(dòng)的有生命的對(duì)象,歸納成動(dòng)物。

演繹是從本質(zhì)到具體,從共性到個(gè)性,將對(duì)象逐步形象化的過(guò)程。比如從生物到動(dòng)物,從動(dòng)物到鳥(niǎo)類。演繹的結(jié)果不一定是具體的對(duì)象,也可以是像鳥(niǎo)類這種抽象結(jié)果,因此演繹仍然是抽象思維,而非具象思維。

Java 中的 Object 類是任何類的默認(rèn)父類,是對(duì)萬(wàn)物的抽象。這就是我們常說(shuō)的:萬(wàn)物皆對(duì)象。

看一看 java.lang.Object 類的源碼,我們基本能看到 Java 世界里對(duì)象的共同特征。

getClass() 說(shuō)明了對(duì)象是誰(shuí),toString() 是對(duì)象的名片,clone() 是繁殖對(duì)象的方式, finalize() 是銷毀對(duì)象的方式,hashCode() 和 equals() 是判斷當(dāng)前對(duì)象與其他對(duì)象是否相等的方式,wait() 和 notify() 是對(duì)象間通信與協(xié)作的方式。

類的定義

除了 JDK 中提供的類之外,我們也可以基于自己業(yè)務(wù)場(chǎng)景的抽象定義類。

我們看下 Java 語(yǔ)法中的 class(類)是怎么構(gòu)成的。

以下是概覽圖,我們按圖介紹。

我們先關(guān)注圖中的黃色區(qū)塊,在 Java 里就叫 class(類)。

好比一個(gè)事物有屬性和能力一樣,比如人有名字,人能吃飯。對(duì)應(yīng)到 Java class 里就是變量和方法,即紅色區(qū)塊和紫色區(qū)塊。

變量分為成員變量,靜態(tài)變量和局部變量三種,方法分為構(gòu)造方法、實(shí)例方法和靜態(tài)方法三種。

我們舉個(gè)例子來(lái)說(shuō)明下,假設(shè)全世界的面包數(shù)量就 100 個(gè),并且生產(chǎn)已經(jīng)停滯,而且只有蝸牛和小白兩個(gè)人能吃到,我們就可以按以下的代碼來(lái)描述這兩個(gè)人吃面包的過(guò)程以及面包的情況。

  1. package cn.java4u.oo; 
  2.  
  3.  
  4. /** 
  5.  * @author 蝸牛 
  6.  * @from 公眾號(hào):蝸?;ヂ?lián)網(wǎng) 
  7.  */ 
  8. public class Person { 
  9.  
  10.     /** 
  11.      * [成員變量]需要被實(shí)例化后使用,每個(gè)實(shí)例都有獨(dú)立空間,通過(guò) 對(duì)象.成員變量名 訪問(wèn) 
  12.      * 名字 
  13.      */ 
  14.     String name
  15.  
  16.  
  17.     /** 
  18.      * [靜態(tài)變量]用 static 修飾,無(wú)需實(shí)例化即可使用,每個(gè)實(shí)例共享同一個(gè)空間,通過(guò) 類名.靜態(tài)變量名 訪問(wèn) 
  19.      * 面包數(shù)量 
  20.      */ 
  21.     static int breadNum; 
  22.  
  23.     /** 
  24.      * [方法] 
  25.      * 吃一個(gè)面包 
  26.      * 
  27.      * @param num 方法入?yún)?,要吃面包的個(gè)數(shù) 
  28.      */ 
  29.     void eatBread(int num) { 
  30.  
  31.         //  num 是[局部變量] 
  32.         breadNum = breadNum - num; 
  33.  
  34.         System.out.println(name + "吃了 " + num + " 個(gè)面包,全世界的面包還剩 " + breadNum + " 個(gè)!"); 
  35.     } 
  36.  
  37.     /** 
  38.      * [構(gòu)造方法] 
  39.      * 參數(shù)為空 
  40.      */ 
  41.     public Person() { 
  42.     } 
  43.  
  44.     /** 
  45.      * [構(gòu)造方法] 
  46.      * 
  47.      * @param name 此為構(gòu)造方法的輸入?yún)?shù),和成員變量有關(guān) 
  48.      */ 
  49.     public Person(String name) { 
  50.         this.name = name
  51.     } 
  52.  
  53.     /** 
  54.      * [靜態(tài)方法] 
  55.      */ 
  56.     static void testStaticMethod() { 
  57.  
  58.         // 通過(guò)構(gòu)造方法,初始化名字叫蝸牛的人 
  59.         Person woniu = new Person("蝸牛"); 
  60.  
  61.         // 通過(guò)構(gòu)造方法,初始化名字叫小白的人 
  62.         Person xiaobai = new Person("小白"); 
  63.  
  64.         // 假設(shè)全世界的面包數(shù)量就 100 個(gè),并且生產(chǎn)已經(jīng)停滯 
  65.         Person.breadNum = 100; 
  66.  
  67.         // 蝸牛吃五個(gè)面包 
  68.         woniu.eatBread(5); 
  69.  
  70.         // 小白吃六個(gè)面包 
  71.         xiaobai.eatBread(6); 
  72.  
  73.         // 打印成員變量和靜態(tài)變量的值 
  74.         System.out.println(woniu.name + "和" + xiaobai.name + "吃飽后,世界只剩 " + Person.breadNum + " 個(gè)面包了!"); 
  75.  
  76.     } 

變量

首先定義了一個(gè)名字叫 Person 的類,表示人,然后定義了一個(gè)成員變量 name ,表示人的名字。成員變量也叫實(shí)例變量,實(shí)例變量的特點(diǎn)就是,每個(gè)實(shí)例都有獨(dú)立的變量,各個(gè)實(shí)例之間的同名變量互不影響。

其次定義了一個(gè)靜態(tài)變量 breadNum ,表示面包的數(shù)量,靜態(tài)變量用 static 修飾。靜態(tài)變量相對(duì)于成員變量就不一樣了,它是共享的,所有實(shí)例會(huì)共享這個(gè)變量。

方法

再接著定義了一個(gè)返回值為空,只有一個(gè)入?yún)⒌姆椒?eatBread(int num) ,方法入?yún)?num 作為局部變量參與了內(nèi)部的運(yùn)算,通過(guò)和它的運(yùn)算,靜態(tài)變量breadNum 的值得到了更新,并打印了一行操作信息。方法的語(yǔ)法結(jié)構(gòu)如下:

  1. 修飾符 返回類型 方法名(方法參數(shù)列表) { 
  2.     方法語(yǔ)句; 
  3.     return 方法返回值; 

另外定義了 Person 的構(gòu)造方法,你會(huì)發(fā)現(xiàn)構(gòu)造方法和實(shí)例方法的區(qū)別就在于它是沒(méi)有返回值的,因?yàn)樗哪康暮芗兇?,就是用?lái)初始化對(duì)象實(shí)例的,和 new 搭配使用,所以它的方法名就是類名,它的入?yún)⒁捕己统蓡T變量有關(guān)。

到這里,你會(huì)發(fā)現(xiàn) Java 方法的返回值并不是那么重要,甚至沒(méi)有都可以!是的,Java 方法簽名只包括名稱和參數(shù)列表,它們是 JVM 標(biāo)識(shí)方法的唯一索引,是不包含返回值的,更不包括各種修飾符或者異常類型。

請(qǐng)注意,任何 class 都是有構(gòu)造方法的,即便你代碼里不寫,Java 也會(huì)在編譯 class 文件的時(shí)候,默認(rèn)生成一個(gè)無(wú)參構(gòu)造方法。但是只要你手動(dòng)定義了構(gòu)造方法,編譯器就不會(huì)再生成。也就是說(shuō)如果你僅定義了一個(gè)有參的構(gòu)造方法,那么編譯后的 class 是不會(huì)有無(wú)參構(gòu)造方法的。

最后就是靜態(tài)方法了,名字叫testStaticMethod ,方法內(nèi)部我們先用 new 的語(yǔ)法調(diào)用構(gòu)造方法,初始化了蝸牛和小白的Person 對(duì)象。這兩個(gè)對(duì)象就是 Person 這個(gè)類的實(shí)例,這兩個(gè)實(shí)例都有獨(dú)立空間,name 這個(gè)成員變量也只能在被實(shí)例化后使用,可以通過(guò) 對(duì)象.成員變量名 訪問(wèn)。

接著我們通過(guò) Person.breadNum 也就是 類名.靜態(tài)變量名 的方式,更新了面包數(shù)量這個(gè)值。你會(huì)發(fā)現(xiàn) breadNum 這個(gè)靜態(tài)變量無(wú)需實(shí)例化就能使用,因?yàn)榫瓦@個(gè)變量而言,Person 的每個(gè)實(shí)例都會(huì)共享同一個(gè)空間。這意味著,每個(gè)實(shí)例的修改,都會(huì)影響到這個(gè)變量值的變化。

然后我們通過(guò)調(diào)用方法 eatBread 并傳參的方式,影響到了面包數(shù)的值。

  1. package cn.java4u.oo; 
  2.  
  3. /** 
  4.  * @author 蝸牛 
  5.  * @from 公眾號(hào):蝸?;ヂ?lián)網(wǎng) 
  6.  */ 
  7. public class MainTest { 
  8.  
  9.     public static void main(String[] args) { 
  10.  
  11.  
  12.         // 靜態(tài)方法,通過(guò) 類名.靜態(tài)方法名 訪問(wèn) 
  13.         Person.testStaticMethod(); 
  14.     } 

最后我們新定義一個(gè)觸發(fā)調(diào)用的入口函數(shù),通過(guò) Person.testStaticMethod() 這樣 類名.靜態(tài)方法名 的方式就能訪問(wèn)到靜態(tài)方法了。

抽象類與接口

抽象類顧名思義,就是會(huì)對(duì)同類事物做抽象,通常包括抽象方法、實(shí)例方法和成員變量。被抽象類和抽象類之間是 is-a 關(guān)系,這種關(guān)系要符合里氏替換原則,即抽象類的所有行為都適用于被抽象類,比如大象是一種動(dòng)物,動(dòng)物能做的事,大象都能做。代碼定義也很簡(jiǎn)單,就是在 class 和抽象方法上加 abstract 修飾符。

  1. package cn.java4u.oo; 
  2.  
  3. /** 
  4.  * 抽象類 
  5.  * 
  6.  * @author 蝸牛 
  7.  * @from 公眾號(hào):蝸?;ヂ?lián)網(wǎng) 
  8.  */ 
  9. public abstract class AbstractClass { 
  10.  
  11.     String name
  12.  
  13.     /** 
  14.      * 實(shí)例方法 
  15.      * 
  16.      * @return name 
  17.      */ 
  18.     public String getName() { 
  19.         return name
  20.     } 
  21.  
  22.     /** 
  23.      * 抽象方法-操作 
  24.      * 
  25.      * @return 結(jié)果 
  26.      */ 
  27.     public abstract String operate(); 

如果一個(gè)抽象類只有一個(gè)抽象方法,那它就等于一個(gè)接口。接口是要求被普通類實(shí)現(xiàn)的,接口在被實(shí)現(xiàn)時(shí)體現(xiàn)的是 can-do 關(guān)系,它表達(dá)了對(duì)象具備的能力。鳥(niǎo)有飛的能力,宇宙飛船也有飛的能力,那么可以把飛的能力抽出來(lái),有單獨(dú)的一個(gè)抽象方法。代碼定義也比較簡(jiǎn)單,class 的關(guān)鍵字用 interface 來(lái)替換。

  1. package cn.java4u.oo; 
  2.  
  3. /** 
  4.  * 可飛翔 
  5.  * 
  6.  * @author 蝸牛 
  7.  * @from 公眾號(hào):蝸?;ヂ?lián)網(wǎng) 
  8.  */ 
  9. public interface Flyable { 
  10.  
  11.  
  12.     /** 
  13.      * 飛 
  14.      */ 
  15.     void fly(); 

內(nèi)部類

在 Java 源代碼文件中,只能定義一個(gè)類目與文件名完全一致的公開(kāi)類。如果想在一個(gè)文件里定義另外一個(gè)類,在面向?qū)ο罄镆彩侵С值?,那就是?nèi)部類。

內(nèi)部類分為以下四種:

  • 靜態(tài)內(nèi)部類:static class StaticInnerClass {}
  • 成員內(nèi)部類:private class InstanceInnerClass {}
  • 局部?jī)?nèi)部類:class MethodClass {} ,定義在方法或者表達(dá)式內(nèi)部
  • 匿名內(nèi)部類:(new Thread() {}).start();

示例代碼如下:

  1. package cn.java4u.oo.innerclass; 
  2.  
  3. /** 
  4.  * 內(nèi)部類演示 
  5.  * 
  6.  * @author 蝸牛 
  7.  * @from 公眾號(hào):蝸?;ヂ?lián)網(wǎng) 
  8.  */ 
  9. public class InnerClassDemo { 
  10.  
  11.     /** 
  12.      * 成員內(nèi)部類 
  13.      */ 
  14.     private class InstanceInnerClass {} 
  15.  
  16.     /** 
  17.      * 靜態(tài)內(nèi)部類 
  18.      */ 
  19.     static class StaticInnerClass {} 
  20.  
  21.     public static void main(String[] args) { 
  22.  
  23.         // 兩個(gè)匿名內(nèi)部類 
  24.         (new Thread() {}).start(); 
  25.         (new Thread() {}).start(); 
  26.  
  27.         // 方法內(nèi)部類 
  28.         class MethodClass {} 
  29.  
  30.     } 

編譯后得到的 class 文件如下:

我們會(huì)發(fā)現(xiàn),無(wú)論什么類型的內(nèi)部類,都會(huì)編譯生成一個(gè)獨(dú)立的 .class 文件,只是內(nèi)部類文件的命名會(huì)通過(guò) $ 連接在外部類后面,如果是匿名內(nèi)部類,會(huì)使用編號(hào)來(lái)標(biāo)識(shí)。

類關(guān)系

關(guān)系是指事物之間有沒(méi)有單向或者相互作用或者影響的狀態(tài)。

類和類之間的關(guān)系分為 6 種:

  • 繼承:extends(is-a)
  • 實(shí)現(xiàn):implements(can-do)
  • 組合:類是成員變量(contains-a)
  • 聚合:類是成員變量(has-a)
  • 依賴:?jiǎn)蜗蛉蹶P(guān)系(使用類屬性,類方法、作為方法入?yún)?、作為方法出?
  • 關(guān)聯(lián):互相平等的依賴關(guān)系(links-a)

序列化

內(nèi)存中的數(shù)據(jù)對(duì)象只有轉(zhuǎn)換為二進(jìn)制流才可以進(jìn)行數(shù)據(jù)持久化和網(wǎng)絡(luò)傳輸。

將數(shù)據(jù)對(duì)象轉(zhuǎn)換成二進(jìn)制流的過(guò)程稱為對(duì)象的序列化(Serialization)。

將二進(jìn)制流恢復(fù)為數(shù)據(jù)對(duì)象的過(guò)程稱為反序列化(Deserialization)。

常見(jiàn)的序列化使用場(chǎng)景是 RPC 框架的數(shù)據(jù)傳輸。

常見(jiàn)的序列化方式有三種:

  • Java 原生序列化。特點(diǎn)是兼容性好,不支持跨語(yǔ)言,性能一般。
  • Hessian 序列化。特點(diǎn)是支持跨語(yǔ)言,性能高效。
  • JSON 序列化。特點(diǎn)是可讀性好,但有安全風(fēng)險(xiǎn)。

封裝

封裝是在抽象基礎(chǔ)上決定信息是否公開(kāi),以及公開(kāi)等級(jí),核心問(wèn)題是以什么樣的方式暴露哪些信息。

抽象是要找到成員和行為的共性,成員是行為的基本生產(chǎn)資料,具有一定的敏感性,不能直接對(duì)外暴露。封裝的主要任務(wù)是對(duì)成員、數(shù)據(jù)、部分內(nèi)部敏感行為實(shí)現(xiàn)隱藏。

對(duì)成員的訪問(wèn)與修改必須通過(guò)定義公共的接口來(lái)進(jìn)行,另外某些敏感方法或者外部不需要感知的復(fù)雜邏輯處理,一般也會(huì)進(jìn)行封裝。

像智能音箱,與用戶交互的唯一接口就是語(yǔ)音輸入,封裝了內(nèi)部的實(shí)現(xiàn)細(xì)節(jié)和相關(guān)數(shù)據(jù)。

設(shè)計(jì)模式七大原則之一的迪米特法則也說(shuō)明了封裝的要求,A 接口使用 B 接口,對(duì) B 知道的要盡可能少。

包(package)這個(gè)名稱就很明顯體現(xiàn)了封裝的含義,它能起到把一個(gè)模塊封裝到一起,并由幾個(gè)接口開(kāi)放給使用方。使用方只能看到接口信息,而看不到接口實(shí)現(xiàn)。另外包解決重名問(wèn)題,相同類名在相同路徑下是不允許的,切換包路徑就可以起相同的類名。

訪問(wèn)權(quán)限控制

我們編寫的程序要想讓使用方,能看到一些信息,又不能看到另外一些信息,這就涉及到信息隱藏了。

信息隱藏是面向?qū)ο蟪绦蛟O(shè)計(jì)的重要特點(diǎn)之一,它可以防止類的使用者意外損壞數(shù)據(jù),對(duì)任何實(shí)現(xiàn)細(xì)節(jié)所作的修改不會(huì)影響到使用該類的其它代碼,也使類更易于使用。

那在 Java 里,實(shí)現(xiàn)信息隱藏的就是訪問(wèn)權(quán)限控制機(jī)制了。Java 的訪問(wèn)權(quán)限控制有 4 個(gè)訪問(wèn)修飾符:public 、protected 、private 和缺省??梢允褂眠@四個(gè)訪問(wèn)修飾符修飾類的成員,它們?cè)诓煌恢玫目稍L問(wèn)性如下表所示。

位置\訪問(wèn)修飾符 public protected 缺省 private
本類 可以 可以 可以 可以
本包 可以 可以 可以 不可以
子類 可以 可以 不可以 不可以
所有 可以 不可以 不可以 不可以

你會(huì)發(fā)現(xiàn) public 不受任何限制,本類和非本類都可以隨意訪問(wèn)(全局友好)。protected 本類及其子類可以訪問(wèn)(父子友好),同一個(gè)包中的其它類也可以訪問(wèn)(包內(nèi)友好)。而缺省的時(shí)候,只有相同包中的類可以訪問(wèn)(包內(nèi)友好)。private 只有本類可以訪問(wèn),其余都不可以(類內(nèi)友好)。

除了為類成員添加訪問(wèn)權(quán)限控制外,也可以在定義類的時(shí)候,為類添加訪問(wèn)修飾符,對(duì)類進(jìn)行訪問(wèn)權(quán)限控制。不過(guò)對(duì)類使用的訪問(wèn)修飾符只有 public 和缺省兩種,訪問(wèn)范圍也分別是全局友好和包內(nèi)友好。

getter 與 setter

為了讓類成員不對(duì)外直接暴露,我們經(jīng)常把成員變量的訪問(wèn)權(quán)限設(shè)置成 private,而成員值的訪問(wèn)與修改使用相應(yīng)的 getter/setter 方法。而不是對(duì) public 的成員進(jìn)行讀取和修改。

  1. package cn.java4u.oo.packagedemo; 
  2.  
  3. /** 
  4.  * getter 和 setter 演示 
  5.  * @author 蝸牛 
  6.  * @from 公眾號(hào):蝸牛互聯(lián)網(wǎng) 
  7.  */ 
  8. public class GetterSetterDemo { 
  9.  
  10.     /** 
  11.      * 成員變量私有化 
  12.      */ 
  13.     private String name
  14.  
  15.     /** 
  16.      * 公開(kāi)方法獲取成員變量值 
  17.      * 
  18.      * @return 名稱 
  19.      */ 
  20.     public String getName() { 
  21.         return name
  22.     } 
  23.  
  24.     /** 
  25.      * 公開(kāi)方法設(shè)置成員變量值 
  26.      * 
  27.      * @param name 名稱 
  28.      */ 
  29.     public void setName(String name) { 
  30.         this.name = name
  31.     } 

繼承

類繼承

class 了解之后,我們考慮一個(gè)問(wèn)題。如果兩個(gè) class,它們的變量和方法基本相同,僅僅是其中一個(gè) class 會(huì)有一些自己特有的變量和方法,那么相同的那些變量和方法真的需要在兩個(gè) class 里都寫一遍么?

比如一個(gè)表示學(xué)生的 class Student ,它相對(duì)于 class Person 只是多了一個(gè)分?jǐn)?shù) score 的成員變量,那還需要像下面這樣,把 name 字段也定義一下么?

  1. /** 
  2.  * 學(xué)生 
  3.  * 
  4.  * @author 蝸牛 
  5.  * @from 公眾號(hào):蝸?;ヂ?lián)網(wǎng) 
  6.  */ 
  7. public class Student { 
  8.  
  9.     /** 
  10.      * 名字 
  11.      */ 
  12.     String name
  13.  
  14.     /** 
  15.      * 分?jǐn)?shù) 
  16.      */ 
  17.     int score; 
  18.  

這很明顯帶來(lái)了代碼重復(fù)使用的問(wèn)題!那能不能在 Student 中不寫重復(fù)代碼?

Java 里的繼承這時(shí)候就派上用場(chǎng)了,繼承是面向?qū)ο缶幊痰囊环N強(qiáng)大機(jī)制,能夠讓子類繼承父類的特征和行為,使得子類對(duì)象能夠具有父類的實(shí)例變量和方法。

子類繼承父類,父類派生子類。父類也叫基類,子類也叫派生類。

通常來(lái)講,類的層次劃分總是下一層比上一層更具體,并且包含上一層的特征,這樣下層的類就能自動(dòng)享有上層類的特點(diǎn)和性質(zhì)。繼承就是派生類自動(dòng)地共享基類中成員變量和成員方法的機(jī)制。

在 Java 中,通過(guò) extends 關(guān)鍵字實(shí)現(xiàn)繼承,并且所有的類都是繼承于 java.lang.Object ,所以這就是萬(wàn)物皆對(duì)象在 Java 里的真實(shí)寫照。你可能會(huì)疑惑,自定義的類并沒(méi)有 extends 關(guān)鍵字為什么還能繼承 Object 呢?這是因?yàn)檫@個(gè)類在 java.lang 包里,Java 已經(jīng)默認(rèn)支持了。

  1. package cn.java4u.oo; 
  2.  
  3. /** 
  4.  * 學(xué)生 
  5.  * 
  6.  * @author 蝸牛 
  7.  * @from 公眾號(hào):蝸?;ヂ?lián)網(wǎng) 
  8.  */ 
  9. public class Student extends Person { 
  10.  
  11.     /** 
  12.      * 分?jǐn)?shù) 
  13.      */ 
  14.     int score; 
  15.  

知道了繼承的基礎(chǔ)概念后,我們看下繼承有啥作用?

首先,繼承是能夠自動(dòng)傳播代碼和重用代碼的有力工具。它能在已有類上擴(kuò)充新類,減少代碼的重復(fù)冗余,也因?yàn)槿哂喽冉档?,一致性就得到了增?qiáng),從而提升了程序的可維護(hù)性。

其次,繼承可以清晰體現(xiàn)出類與類之間的層次結(jié)構(gòu)關(guān)系,提升了代碼的可讀性。

另外,繼承是單方向的,即派生類可以繼承和訪問(wèn)基類成員,但反過(guò)來(lái)就不行。而且 Java 只允許單一繼承,也就是一個(gè)派生類不能同時(shí)繼承多個(gè)基類,這和 C++ 是不同的。

在使用繼承的時(shí)候,還要考慮到基類成員的訪問(wèn)控制權(quán)限??梢詤⒖挤庋b那塊內(nèi)容的訪問(wèn)權(quán)限控制介紹。

子類實(shí)例化過(guò)程

特別要說(shuō)明的是,父類的構(gòu)造方法是不能被子類繼承的,即便它是 public 的。父類的構(gòu)造方法負(fù)責(zé)初始化屬于它的成員變量,而子類的構(gòu)造方法只需考慮自己特有的成員變量即可,不必關(guān)注父類狀況。

  1. package cn.java4u.oo.inherit; 
  2.  
  3. /** 
  4.  * 定義父類 
  5.  * 
  6.  * @author 蝸牛 
  7.  * @from 公眾號(hào):蝸?;ヂ?lián)網(wǎng) 
  8.  */ 
  9. public class Parent { 
  10.  
  11.  
  12.     /** 
  13.      * 構(gòu)造方法 
  14.      */ 
  15.     public Parent() { 
  16.  
  17.         System.out.println("這是父類 Parent 的構(gòu)造方法"); 
  18.     } 
  19.  
  20. package cn.java4u.oo.inherit; 
  21.  
  22. /** 
  23.  * 定義子類 
  24.  * 
  25.  * @author 蝸牛 
  26.  * @from 公眾號(hào):蝸牛互聯(lián)網(wǎng) 
  27.  */ 
  28. public class Child extends Parent { 
  29.  
  30.     /** 
  31.      * 構(gòu)造方法 
  32.      */ 
  33.     public Child() { 
  34.  
  35.         System.out.println("這是子類 Child 的構(gòu)造方法"); 
  36.  
  37.     } 
  38.  
  39. package cn.java4u.test; 
  40.  
  41. import cn.java4u.oo.inherit.Child; 
  42.  
  43. /** 
  44.  * @author 蝸牛 
  45.  * @from 公眾號(hào):蝸?;ヂ?lián)網(wǎng) 
  46.  */ 
  47. public class InheritTest { 
  48.  
  49.     public static void main(String[] args) { 
  50.  
  51.         Child child = new Child(); 
  52.     } 

因此,在實(shí)例化子類的對(duì)象時(shí),Java 先是執(zhí)行父類的構(gòu)造方法,然后執(zhí)行子類的構(gòu)造方法。如果父類還有更上級(jí)的父類,就會(huì)先調(diào)用更高父類的構(gòu)造方法,再逐個(gè)依次地將所有繼承關(guān)系的父類構(gòu)造方法全部執(zhí)行。如果父類的構(gòu)造方法執(zhí)行失敗,則子類的對(duì)象也將無(wú)法實(shí)例化。

上邊的代碼運(yùn)行后,會(huì)輸出:

  1. 這是父類 Parent 的構(gòu)造方法 
  2. 這是子類 Child 的構(gòu)造方法 

this 與 super

如果調(diào)用父類構(gòu)造方法涉及到有參構(gòu)造方法,可以使用 super 關(guān)鍵字來(lái)調(diào)用父類構(gòu)造方法并傳遞參數(shù)。

說(shuō)的 super,它還有一個(gè)能力,就是父類和子類的成員如果同名了,子類中默認(rèn)只能訪問(wèn)自己的那個(gè)成員,想要訪問(wèn)父類成員,就可以通過(guò) super.成員名 的語(yǔ)法實(shí)現(xiàn)。但這有個(gè)前提,就是父類的這個(gè)成員不能是 private 的。

與 super 相對(duì)的關(guān)鍵字是 this ,super 是指向當(dāng)前對(duì)象的父類,而 this 是指向當(dāng)前對(duì)象自己。this 常用來(lái)區(qū)別成員變量和局部變量,比如下面這段代碼,我加了個(gè)有參構(gòu)造方法。

  1. public class Parent { 
  2.  
  3.     int a; 
  4.  
  5.     /** 
  6.      * 構(gòu)造方法 
  7.      */ 
  8.     public Parent() { 
  9.  
  10.         System.out.println("這是父類 Parent 的構(gòu)造方法"); 
  11.     } 
  12.  
  13.     public Parent(int a) { 
  14.         this.a = a; 
  15.     } 
  16.  
  17.  

多態(tài)

說(shuō)完繼承,我們?cè)賮?lái)聊聊多態(tài)!

多態(tài)字面上解釋,就是程序可以有多個(gè)運(yùn)行狀態(tài)。

既然是運(yùn)行狀態(tài),那其實(shí)更多的是強(qiáng)調(diào)方法的使用。

重載與覆寫

方法在兩種情況下使用會(huì)比較特別,一種是 overload(重載),overload 方法是本類內(nèi)的新方法,方法名一樣,但是參數(shù)的類型或數(shù)量不同。這種方法沒(méi)有特殊的標(biāo)識(shí),通過(guò)類內(nèi)方法是否重名判定。

另外一種就是 override(覆寫),override 方法是繼承關(guān)系下子類的新方法,方法簽名和父類完全相同。這種方法都會(huì)有 @Override 注解的標(biāo)識(shí)。

  1. package cn.java4u.oo.polymorphism; 
  2.  
  3. /** 
  4.  * 動(dòng)物 
  5.  * 
  6.  * @author 蝸牛 
  7.  * @from 公眾號(hào):蝸牛互聯(lián)網(wǎng) 
  8.  */ 
  9. public class Animal { 
  10.  
  11.  
  12.     /** 
  13.      * 與 eat(String food) 重載 
  14.      */ 
  15.     public void eat() { 
  16.         System.out.println("Animal.eat"); 
  17.     } 
  18.  
  19.     /** 
  20.      * 與 eat() 重載 
  21.      * 
  22.      * @param food 食物 
  23.      */ 
  24.     public void eat(String food) { 
  25.         System.out.println("Animal.eat: " + food); 
  26.     } 
  27.  
  28.     /** 
  29.      * 覆寫 
  30.      * 
  31.      * @return 字符串 
  32.      * @see java.lang.Object#toString 
  33.      */ 
  34.     @Override 
  35.     public String toString() { 
  36.         return "Animal " + super.toString(); 
  37.     } 

舉個(gè)例子,Animal 類里兩個(gè) eat 方法就互為重載方法,toString 方法就是相對(duì)于父類方法 java.lang.Object#toString 的覆寫方法。

多態(tài)就發(fā)生在覆寫這種場(chǎng)景下。針對(duì)某個(gè)類型的方法調(diào)用,它真正執(zhí)行的方法取決于運(yùn)行時(shí)期實(shí)際類型的方法。比如下面這段代碼,當(dāng)聲明類型為 Object ,初始化類型為 Animal 時(shí),你覺(jué)得輸出的是 Animal 的 toString 方法,還是 Object 的 toString 方法?

  1. package cn.java4u.oo.polymorphism; 
  2.  
  3. /** 
  4.  * @author 蝸牛 
  5.  * @from 公眾號(hào):蝸?;ヂ?lián)網(wǎng) 
  6.  */ 
  7. public class PolymorphismTest { 
  8.  
  9.     /** 
  10.      * 打印對(duì)象 
  11.      * 
  12.      * @param scene 打印場(chǎng)景 
  13.      * @param obj   obj 
  14.      */ 
  15.     public static void printObjectString(String scene, Object obj) { 
  16.  
  17.         System.out.println(scene + ": " + obj.toString()); 
  18.  
  19.     } 
  20.  
  21.     public static void main(String[] args) { 
  22.  
  23.         // 父類引用初始化父類對(duì)象并打印 
  24.         Object rootObj = new Object(); 
  25.         printObjectString("父類引用初始化父類對(duì)象", rootObj); 
  26.  
  27.         // 子類引用初始化子類對(duì)象并打印 
  28.         Animal animal = new Animal(); 
  29.         printObjectString("子類引用初始化子類對(duì)象", animal); 
  30.  
  31.  
  32.         // 父類引用初始化子類對(duì)象并打印 
  33.         Object animalWhenParentRef = new Animal(); 
  34.         printObjectString("父類引用初始化子類對(duì)象", animal); 
  35.          
  36.     } 

答案是子類 Animal 的 toString 方法!

  1. 父類引用初始化父類對(duì)象: java.lang.Object@60e53b93 
  2. 子類引用初始化子類對(duì)象: Animal cn.java4u.oo.polymorphism.Animal@5e2de80c 
  3. 父類引用初始化子類對(duì)象: Animal cn.java4u.oo.polymorphism.Animal@5e2de80c 

實(shí)際類型為 Animal 引用類型為 Object ,調(diào)用 toString 方法時(shí),實(shí)際上是子類的。因此我們可以得出結(jié)論:Java 的實(shí)例方法調(diào)用是基于運(yùn)行時(shí)的實(shí)際類型的動(dòng)態(tài)調(diào)用,而非變量的聲明類型。這種特性就是多態(tài)!

你會(huì)發(fā)現(xiàn) printObjectString 方法的第二個(gè)參數(shù),即便聲明的是 Object ,實(shí)際運(yùn)行的時(shí)候,卻可以是它的子類覆寫方法。

至此,我們也理出了 Java 實(shí)現(xiàn)多態(tài)三要素,那就是 繼承、覆寫和向上轉(zhuǎn)型。即兩個(gè)類之間有繼承關(guān)系,某個(gè)類覆寫了父類的某個(gè)方法,方法的引用會(huì)指向子類的實(shí)現(xiàn)處。

總結(jié)

本文從 Java 的視角出發(fā),分析了 Java 的語(yǔ)言特點(diǎn),并和 C++ 進(jìn)行了比較。針對(duì)這門典型的面向?qū)ο笳Z(yǔ)言,我們又分析了面向?qū)ο蟮母拍詈退枷搿=又诿嫦驅(qū)ο蟮奶卣鳎撼橄?、封裝、繼承和多態(tài),我們又詳細(xì)的分析了在 Java 中的體現(xiàn)方式,并伴有很多樣例代碼輔助學(xué)習(xí)。看完這篇文章,想必你對(duì)面向?qū)ο筮@個(gè)東西會(huì)有更全面的了解。

好啦,本期的分享就到這里,如果各位喜歡我的分享,請(qǐng)務(wù)必三連,點(diǎn)贊,在看,收藏,關(guān)注我,這會(huì)對(duì)我有非常大的幫助。

 

我們下期再見(jiàn)。

 

責(zé)任編輯:武曉燕 來(lái)源: 蝸?;ヂ?lián)網(wǎng)
相關(guān)推薦

2009-07-09 17:40:26

Java運(yùn)算符

2009-03-03 20:44:06

桌面虛擬化Xendesktop虛擬化

2010-10-14 16:55:00

MySQL聯(lián)結(jié)查詢

2017-03-06 16:34:12

虛擬個(gè)人助理

2020-02-17 15:29:00

石墨文檔

2010-11-15 09:55:35

Oracle轉(zhuǎn)換函數(shù)

2023-11-08 07:45:47

Spring微服務(wù)

2011-01-11 09:53:28

linux進(jìn)程

2011-01-11 10:06:14

linux進(jìn)程

2019-04-26 14:21:34

手機(jī)色彩蘋果

2009-06-22 17:20:33

J2EE框架

2014-08-11 11:19:53

Java虛擬機(jī)Java

2010-10-21 15:40:05

SQL Server服

2017-09-06 08:12:43

OpenStack功能模塊

2011-05-26 10:27:37

Fedora 15

2017-04-25 17:29:24

2012-02-20 09:06:20

JVM

2024-02-05 14:18:07

自然語(yǔ)言處理

2010-09-14 13:13:12

sql server備

2009-09-28 10:58:31

Google新搜索特性
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)