關(guān)于Java的private帘睦,你可能不知道的事

最近學習JVM躺盛,學習過程中坤候,對某些基礎(chǔ)的知識又有了新的認識提陶,故得此博文儡蔓。

private

盡管private看起來已經(jīng)那么熟悉外厂,實際上還是有很多地方?jīng)]有遇到過也沒有思考過泥畅,這里先引用Thinking in Java中對它的描述:

The private keyword means that no one can access that member except the class that contains that member, inside methods of that class. Other classes in the same package cannot access private members, so it’s as if you’re even insulating the class against yourself.

關(guān)鍵有兩點:

  • 只能在包含該成員的類中去訪問它料扰,例如在包含該成員的類的方法
  • 其余類中一律不能訪問凭豪,不管以何種方式

按理來講,這兩條算是說的清清楚楚晒杈,但是由于對某些概念迷迷糊糊嫂伞,導(dǎo)致在實際過程中,還是可能犯糊涂拯钻。

示例1

設(shè)有一下代碼:

// Person.java
public class Person {
  private int pri;
}
// Man.java
public class Man extends Person {
  public static void main(String[] args) {
    Person person = new Person();
    // 以下代碼會出現(xiàn)語法錯誤
    person.pri = 5;
  }
}

這個例子可能大部分人都能看出錯誤的原因了帖努,大部分人也幾乎不會犯這樣的錯誤,不過這里引出了一些需要注意的東西粪般,所以還是提一下拼余。錯誤的原因,是對The private keyword means that no one can access that member except the class that contains that member有誤解亩歹,認為匙监,person是擁有pri的,應(yīng)當是可以訪問的捆憎。當然若仔細看前面提到的那兩條舅柜,就會發(fā)現(xiàn),實際上這個錯誤非常明顯躲惰。首先致份,第一點后面還有一句inside methods of that class,這句話表明础拨,你應(yīng)該在包含該變量的類的方法中去訪問氮块,此處是在Man中的方法绍载,而不是Person的方法,故有誤滔蝉。第二條更加直接击儡,不能再其它的類訪問,故這兩條均說明了上面那樣訪問是有問題的蝠引。出現(xiàn)這樣的錯誤阳谍,應(yīng)該是對含有該變量的類和含有該變量的對象產(chǎn)生了混淆,前面說的是:private修飾的變量螃概,應(yīng)該在包含該變量的類里面去訪問矫夯;而對象,僅僅是類的實例(實際上吊洼,在運行時训貌,對象中的數(shù)據(jù)并不包含方法數(shù)據(jù),方法的數(shù)據(jù)都是保存在類中冒窍,也就是方法區(qū)中)递沪,不存在”包含該變量的對象里面去訪問“的說法。

這里有幾點需要提一下综液,首先是款慨,擁有該變量的的類(或者更準確的,聲明該變量的類)意乓,這里樱调,擁有該pri的類就是Person,然后是届良,訪問代碼所在的類笆凌,例如前面的person.pri所在的類,就是Man士葫;最后是訪問該變量的對象所對應(yīng)的類乞而,還是以person.pri為例,訪問pri的變量的對象person所對應(yīng)的類就是Person慢显。

實際上爪模,想要訪問private修飾的變量,必須要前面所提到的三個類都是相同的荚藻。

示例2

// Person.java
public class Person {
  private int pri;
  public static void main(String[] args) {
    Person person = new Person();
    
    person.pri = 5; // 如前面所介紹的屋灌,此處訪問是沒有問題的
    Man man = new Man();
    
    man.pri = 10;  // 根據(jù)前面介紹的,man所對應(yīng)的類是Man而不是Person应狱,因此是不能這樣訪問的
    ((Person) man).pri = 2; // ?
  } 
}
// Man.java
public class Man extends Person {
}

代碼的共郭?所標記的地方,其實是可以這樣訪問的,由于強制類型轉(zhuǎn)化除嘹,將man對應(yīng)的類變成了Person写半,所以符合前面的規(guī)則。

動態(tài)類型和靜態(tài)類型

對JVM有一定了解的人(或者熟悉多態(tài)的人)都知道所謂的對象的動態(tài)類型和靜態(tài)類型尉咕。靜態(tài)類型叠蝇,就是指編譯時期確定的類型,動態(tài)類型就是指在運行時期年缎,對象實際的類型悔捶。最典型的就是多態(tài):

// Person.java
public class Person {
  public void fun() {
    System.out.println("fun in Person");
  }
}

// Man.java
public class Man extends Person {
  public void fun() {
    System.out.println("fun in Man");
  }
  public static void main(String[] args) {
    Person man = new Man();
  }
}

以上代碼是很常見的,尤其對于面向接口編程的時候晦款,尤為廣泛炎功。man在編譯時候能確定其類型為Person(嚴格來講,應(yīng)該是引用類型缓溅,該引用指向Person類,此處這么講只是為了方便理解)赁温,而實際上在運行時期坛怪,它是一個Man類型(同樣,嚴格來講是個引用類型股囊,這里這么說是為了方便理解)袜匿。我們還可以通過強制類型轉(zhuǎn)換來轉(zhuǎn)換靜態(tài)類型:man = (Man)man。動態(tài)類型并不會因為強轉(zhuǎn)而改變稚疹,例如居灯,我們將Man的main函數(shù)改為下面的代碼:

public static void main(String[] args) {
  Man man = new Man();
  Person person = (Person) man;
  man.fun();
  person.fun();
}

運行代碼你會發(fā)現(xiàn)輸出為:

fun in Man
fun in Man

這說明,man的動態(tài)類型沒有改變内狗,更嚴格的來講怪嫌,man和person均是一個引用類型(類符號引用),man應(yīng)當指向一個Man類實例(就是前面講的柳沙,man的靜態(tài)類型為Man)岩灭,person應(yīng)當指向一個Person類的實例(就是指person的靜態(tài)類型為Person),而實際上赂鲤,man和person是指向了同一個實例噪径,即Man的實例。因此数初,強制類型轉(zhuǎn)換找爱,只是改變了變量應(yīng)當指向某種類型的實例,但不能改變變量所指向類實例的真實類型(用前面的話來講泡孩,強制類型轉(zhuǎn)換车摄,只是改變了靜態(tài)類型,而無法改變動態(tài)類型)。

再看示例2

// Person.java
public class Person {
  private int pri;
  public static void main(String[] args) {
    Person person = new Person();
    
    person.pri = 5; // 如前面所介紹的练般,此處訪問是沒有問題的
    Man man = new Man();
    
    man.pri = 10;  // 根據(jù)前面介紹的矗漾,man所對應(yīng)的類是Man而不是Person,因此是不能這樣訪問的
    ((Person) man).pri = 2; // ?
  } 
}
// Man.java
public class Man extends Person {
}

我們已經(jīng)知道(Pewrson)man表明man應(yīng)當指向一個Person類的實例薄料。然而實際上敞贡,man還是指向了Man的實例,那么問題來了摄职,既然還是Man的實例誊役,為什么能夠訪問父類Person的private 變量呢?private變量不是不會被繼承嗎谷市?雖然前面已經(jīng)通過規(guī)則說明這樣訪問是沒有問題的蛔垢,但是,這里引出了一些問題:創(chuàng)建子類對象迫悠,調(diào)用父類的默認構(gòu)造器究竟做了什么鹏漆?父類的private變量真的不能繼承嗎?

首先回答第一個問題创泄,調(diào)用父類的默認構(gòu)造器究竟做了什么艺玲?

很明顯,并沒有創(chuàng)建對象鞠抑,創(chuàng)建普通對象(特指非數(shù)組對象)需要虛擬機的new指令(注意和Java的new關(guān)鍵字區(qū)分開)饭聚,創(chuàng)建子類對象時,僅僅會調(diào)用父類的默認構(gòu)造器搁拙。

那么秒梳,什么是父類的默認構(gòu)造器呢?

默認構(gòu)造器就是無參構(gòu)造器箕速,若一個類沒有提供任何構(gòu)造器酪碘,則編譯器會生成一個無參構(gòu)造器,若提供了一個構(gòu)造器弧满,則編譯器便不會生成無參構(gòu)造器婆跑,除非自己寫了一個無參構(gòu)造器,否則該類就沒有默認構(gòu)造器庭呜,對于沒有默認構(gòu)造器的類滑进,其子類必須顯示調(diào)用父類構(gòu)造器;對有默認構(gòu)造器的類募谎,子類的構(gòu)造器可以不顯示調(diào)用父類的構(gòu)造器扶关,但是編譯器實際上會在構(gòu)造器前面加上super();以保證調(diào)用了父類的構(gòu)造器。

因此数冬,嚴格來講节槐,創(chuàng)建子類對象搀庶,其實不一定是調(diào)用父類的默認構(gòu)造器(畢竟父類可能沒有默認構(gòu)造器),但一定調(diào)用了父類的某個構(gòu)造器铜异,而父類的構(gòu)造器是用來進行初始化父類的變量(非靜態(tài)變量)的哥倔,但是調(diào)用父類構(gòu)造器,并沒有創(chuàng)建對象揍庄,那么那些非靜態(tài)變量放在哪里呢咆蒿?

只可能是子類實例里面了,那么蚂子,父類的構(gòu)造器若對private變量進行初始化沃测,表明這個private修飾的變量也應(yīng)該存在!那么實際上食茎,在子類實例里面蒂破,還是有父類的private修飾的變量的。所以别渔,實際上附迷,private變量也被“繼承”了。但是基于前面所講的規(guī)則钠糊,是無法在子類的方法里面訪問父類的private成員變量的挟秤,于是問題2也答案也同時明確了〕椋看起來,private成員變量也是被“繼承”了管宵,只不過這沒什么用截珍,不能讀不能寫,放在那里還占內(nèi)存箩朴。不過至少有一個好處岗喉,那就是Java虛擬機實現(xiàn)上變簡單了,不用考慮父類成員變量是否是private的炸庞,一股腦的全部放過來就是了钱床,只需要在訪問時,進行權(quán)限檢查就行了埠居。而權(quán)限檢查是很常見的查牌,各種調(diào)用方法和訪問變量的時候都需要進行檢查,因此滥壕,并沒有為把父類的private變量“繼承”之后進行單獨的檢查纸颜,所以實現(xiàn)上確實要比不“繼承”來的方便,更加關(guān)鍵的绎橘,對于例子2胁孙,我們通過((Person) man).pri就成功訪問了父類的private變量,若沒有把這個變量繼承,反而會造成其他的問題涮较。

小結(jié)

  • 一個類若沒有顯示提供構(gòu)造器稠鼻,則編譯器會提供一個無參的構(gòu)造器
  • 一個類若提供了構(gòu)造器,則編譯器不會提供構(gòu)造器
  • 無參構(gòu)造器就是默認構(gòu)造器狂票,一個類有構(gòu)造器候齿,要么該類沒有顯示提供任何構(gòu)造器,要么顯示提供了無參構(gòu)造器
  • 父類的成員變量(包括private修飾的)會在子類的實例中苫亦。
  • 子類的成員變量若和父類的成員變量重名也沒有什么關(guān)系毛肋,雖然都放在子類實例中,但是放在了不同的地方

對于最后一點可以看看如下代碼:

public class Parent {
  int a;
}
public class Child extends Parent {
  int a;
  
  public void fun() {
    a = 10;
    super.a = 20;
    System.out.println("a in child: " + a);
    System.out.println("a in parent: " + super.a);
  }
  public static void main(String[] args) {
    Child child = new Child();
    child.fun();
  }
}

運行代碼便知屋剑。同樣的润匙,父類方法和子類方法重名時也和這種情況類似。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末唉匾,一起剝皮案震驚了整個濱河市孕讳,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌巍膘,老刑警劉巖厂财,帶你破解...
    沈念sama閱讀 206,311評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異峡懈,居然都是意外死亡璃饱,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,339評論 2 382
  • 文/潘曉璐 我一進店門肪康,熙熙樓的掌柜王于貴愁眉苦臉地迎上來荚恶,“玉大人,你說我怎么就攤上這事磷支≮撕常” “怎么了?”我有些...
    開封第一講書人閱讀 152,671評論 0 342
  • 文/不壞的土叔 我叫張陵雾狈,是天一觀的道長廓潜。 經(jīng)常有香客問我,道長善榛,這世上最難降的妖魔是什么辩蛋? 我笑而不...
    開封第一講書人閱讀 55,252評論 1 279
  • 正文 為了忘掉前任,我火速辦了婚禮锭弊,結(jié)果婚禮上堪澎,老公的妹妹穿的比我還像新娘。我一直安慰自己味滞,他們只是感情好樱蛤,可當我...
    茶點故事閱讀 64,253評論 5 371
  • 文/花漫 我一把揭開白布钮呀。 她就那樣靜靜地躺著,像睡著了一般昨凡。 火紅的嫁衣襯著肌膚如雪爽醋。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,031評論 1 285
  • 那天便脊,我揣著相機與錄音蚂四,去河邊找鬼。 笑死哪痰,一個胖子當著我的面吹牛遂赠,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播晌杰,決...
    沈念sama閱讀 38,340評論 3 399
  • 文/蒼蘭香墨 我猛地睜開眼跷睦,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了肋演?” 一聲冷哼從身側(cè)響起抑诸,我...
    開封第一講書人閱讀 36,973評論 0 259
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎爹殊,沒想到半個月后蜕乡,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,466評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡梗夸,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 35,937評論 2 323
  • 正文 我和宋清朗相戀三年层玲,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片反症。...
    茶點故事閱讀 38,039評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡称簿,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出惰帽,到底是詐尸還是另有隱情,我是刑警寧澤父虑,帶...
    沈念sama閱讀 33,701評論 4 323
  • 正文 年R本政府宣布该酗,位于F島的核電站,受9級特大地震影響士嚎,放射性物質(zhì)發(fā)生泄漏呜魄。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 39,254評論 3 307
  • 文/蒙蒙 一莱衩、第九天 我趴在偏房一處隱蔽的房頂上張望爵嗅。 院中可真熱鬧,春花似錦笨蚁、人聲如沸睹晒。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,259評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽伪很。三九已至戚啥,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間锉试,已是汗流浹背猫十。 一陣腳步聲響...
    開封第一講書人閱讀 31,485評論 1 262
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留呆盖,地道東北人拖云。 一個月前我還...
    沈念sama閱讀 45,497評論 2 354
  • 正文 我出身青樓,卻偏偏與公主長得像应又,于是被迫代替她去往敵國和親宙项。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 42,786評論 2 345

推薦閱讀更多精彩內(nèi)容

  • 1. Java基礎(chǔ)部分 基礎(chǔ)部分的順序:基本語法丁频,類相關(guān)的語法杉允,內(nèi)部類的語法,繼承相關(guān)的語法席里,異常的語法叔磷,線程的語...
    子非魚_t_閱讀 31,581評論 18 399
  • 20- 枚舉,枚舉原始值,枚舉相關(guān)值,switch提取枚舉關(guān)聯(lián)值 Swift枚舉: Swift中的枚舉比OC中的枚...
    iOS_恒仔閱讀 2,242評論 1 6
  • 一:java概述:1,JDK:Java Development Kit奖磁,java的開發(fā)和運行環(huán)境改基,java的開發(fā)工...
    ZaneInTheSun閱讀 2,629評論 0 11
  • Spring Cloud為開發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見模式的工具(例如配置管理,服務(wù)發(fā)現(xiàn)咖为,斷路器秕狰,智...
    卡卡羅2017閱讀 134,599評論 18 139
  • 總得來說 MVP 模式可分為五大模塊: 我們先以實現(xiàn)一個登錄界面的實例,來大致看一下MVP模式的幾大模塊 bean...
    好爛的筆頭閱讀 281評論 0 0