java 关注公众号没有调接口_深入理解Java继承、封装、多态的实现原理
點(diǎn)擊關(guān)注上方“Java技術(shù)江湖”,設(shè)為“置頂或星標(biāo)”,第一時(shí)間送達(dá)技術(shù)干貨。
作者:黃小斜
文章來(lái)源:微信公眾號(hào)【Java技術(shù)江湖】
目錄
- 從JVM結(jié)構(gòu)開始談多態(tài) 
- JVM 的結(jié)構(gòu) 
- Java 的方法調(diào)用方式 
- 常量池(constant pool) - 圖 2. 常量池各表的關(guān)系 
 
- 方法表與方法調(diào)用 - 清單 1 
 
- 接口調(diào)用 - 圖 5.Dancer 的方法表(查看大圖) 
 
- 繼承的實(shí)現(xiàn)原理 
- 重載和重寫的實(shí)現(xiàn)原理 
- 參考文章 
- 微信公眾號(hào) 
- Java技術(shù)江湖 
- 個(gè)人公眾號(hào):黃小斜 
多態(tài)
本系列文章將整理到我在GitHub上的《Java面試指南》倉(cāng)庫(kù),更多精彩內(nèi)容請(qǐng)到我的倉(cāng)庫(kù)里查看
https://github.com/h2pl/Java-Tutorial
喜歡的話麻煩點(diǎn)下Star哈
文章首發(fā)于我的個(gè)人博客:
www.how2playlife.com
本文是微信公眾號(hào)【Java技術(shù)江湖】的《夯實(shí)Java基礎(chǔ)系列博文》其中一篇,本文部分內(nèi)容來(lái)源于網(wǎng)絡(luò),為了把本文主題講得清晰透徹,也整合了很多我認(rèn)為不錯(cuò)的技術(shù)博客內(nèi)容,引用其中了一些比較好的博客文章,如有侵權(quán),請(qǐng)聯(lián)系作者。該系列博文會(huì)告訴你如何從入門到進(jìn)階,一步步地學(xué)習(xí)Java基礎(chǔ)知識(shí),并上手進(jìn)行實(shí)戰(zhàn),接著了解每個(gè)Java知識(shí)點(diǎn)背后的實(shí)現(xiàn)原理,更完整地了解整個(gè)Java技術(shù)體系,形成自己的知識(shí)框架。為了更好地總結(jié)和檢驗(yàn)?zāi)愕膶W(xué)習(xí)成果,本系列文章也會(huì)提供每個(gè)知識(shí)點(diǎn)對(duì)應(yīng)的面試題以及參考答案。
如果對(duì)本系列文章有什么建議,或者是有什么疑問的話,也可以關(guān)注公眾號(hào)【Java技術(shù)江湖】聯(lián)系作者,歡迎你參與本系列博文的創(chuàng)作和修訂。
從JVM結(jié)構(gòu)開始談多態(tài)
Java 對(duì)于方法調(diào)用動(dòng)態(tài)綁定的實(shí)現(xiàn)主要依賴于方法表,但通過類引用調(diào)用和接口引用調(diào)用的實(shí)現(xiàn)則有所不同??傮w而言,當(dāng)某個(gè)方法被調(diào)用時(shí),JVM 首先要查找相應(yīng)的常量池,得到方法的符號(hào)引用,并查找調(diào)用類的方法表以確定該方法的直接引用,最后才真正調(diào)用該方法。以下分別對(duì)該過程中涉及到的相關(guān)部分做詳細(xì)介紹。
JVM 的結(jié)構(gòu)
JVM結(jié)構(gòu)中,我們只探討和本文密切相關(guān)的方法區(qū) (method area)。當(dāng)程序運(yùn)行需要某個(gè)類的定義時(shí),載入子系統(tǒng) (class loader subsystem) 裝入所需的 class 文件,并在內(nèi)部建立該類的類型信息,這個(gè)類型信息就存貯在方法區(qū)。類型信息一般包括該類的方法代碼、類變量、成員變量的定義等等??梢哉f,類型信息就是類的 Java 文件在運(yùn)行時(shí)的內(nèi)部結(jié)構(gòu),包含了改類的所有在 Java 文件中定義的信息。
注意到,該類型信息和 class 對(duì)象是不同的。class 對(duì)象是 JVM 在載入某個(gè)類后于堆 (heap) 中創(chuàng)建的代表該類的對(duì)象,可以通過該 class 對(duì)象訪問到該類型信息。比如最典型的應(yīng)用,在 Java 反射中應(yīng)用 class 對(duì)象訪問到該類支持的所有方法,定義的成員變量等等??梢韵胂?#xff0c;JVM 在類型信息和 class 對(duì)象中維護(hù)著它們彼此的引用以便互相訪問。兩者的關(guān)系可以類比于進(jìn)程對(duì)象與真正的進(jìn)程之間的關(guān)系。
Java 的方法調(diào)用方式
Java 的方法調(diào)用有兩類,動(dòng)態(tài)方法調(diào)用與靜態(tài)方法調(diào)用。靜態(tài)方法調(diào)用是指對(duì)于類的靜態(tài)方法的調(diào)用方式,是靜態(tài)綁定的;而動(dòng)態(tài)方法調(diào)用需要有方法調(diào)用所作用的對(duì)象,是動(dòng)態(tài)綁定的。類調(diào)用 (invokestatic) 是在編譯時(shí)刻就已經(jīng)確定好具體調(diào)用方法的情況,而實(shí)例調(diào)用 (invokevirtual) 則是在調(diào)用的時(shí)候才確定具體的調(diào)用方法,這就是動(dòng)態(tài)綁定,也是多態(tài)要解決的核心問題。
JVM 的方法調(diào)用指令有四個(gè),分別是 invokestatic,invokespecial,invokesvirtual 和 invokeinterface。前兩個(gè)是靜態(tài)綁定,后兩個(gè)是動(dòng)態(tài)綁定的。本文也可以說是對(duì)于 JVM 后兩種調(diào)用實(shí)現(xiàn)的考察。
常量池(constant pool)
常量池中保存的是一個(gè) Java 類引用的一些常量信息,包含一些字符串常量及對(duì)于類的符號(hào)引用信息等。Java 代碼編譯生成的類文件中的常量池是靜態(tài)常量池,當(dāng)類被載入到虛擬機(jī)內(nèi)部的時(shí)候,在內(nèi)存中產(chǎn)生類的常量池叫運(yùn)行時(shí)常量池。
常量池在邏輯上可以分成多個(gè)表,每個(gè)表包含一類的常量信息,本文只探討對(duì)于 Java 調(diào)用相關(guān)的常量池表。
CONSTANTUtf8info
字符串常量表,該表包含該類所使用的所有字符串常量,比如代碼中的字符串引用、引用的類名、方法的名字、其他引用的類與方法的字符串描述等等。其余常量池表中所涉及到的任何常量字符串都被索引至該表。
CONSTANTClassinfo
類信息表,包含任何被引用的類或接口的符號(hào)引用,每一個(gè)條目主要包含一個(gè)索引,指向 CONSTANTUtf8info 表,表示該類或接口的全限定名。
CONSTANTNameAndTypeinfo
名字類型表,包含引用的任意方法或字段的名稱和描述符信息在字符串常量表中的索引。
CONSTANTInterfaceMethodrefinfo
接口方法引用表,包含引用的任何接口方法的描述信息,主要包括類信息索引和名字類型索引。
CONSTANTMethodrefinfo
類方法引用表,包含引用的任何類型方法的描述信息,主要包括類信息索引和名字類型索引。
可以看到,給定任意一個(gè)方法的索引,在常量池中找到對(duì)應(yīng)的條目后,可以得到該方法的類索引(classindex)和名字類型索引 (nameandtypeindex), 進(jìn)而得到該方法所屬的類型信息和名稱及描述符信息(參數(shù),返回值等)。注意到所有的常量字符串都是存儲(chǔ)在 CONSTANTUtf8info 中供其他表索引的。
方法表與方法調(diào)用
方法表是動(dòng)態(tài)調(diào)用的核心,也是 Java 實(shí)現(xiàn)動(dòng)態(tài)調(diào)用的主要方式。它被存儲(chǔ)于方法區(qū)中的類型信息,包含有該類型所定義的所有方法及指向這些方法代碼的指針,注意這些具體的方法代碼可能是被覆寫的方法,也可能是繼承自基類的方法。
如有類定義 Person, Girl, Boy,
清單 1
class Person {public String toString(){
return "I'm a person.";
}
public void eat(){}
public void speak(){}
}
class Boy extends Person{
public String toString(){
return "I'm a boy";
}
public void speak(){}
public void fight(){}
}
class Girl extends Person{
public String toString(){
return "I'm a girl";
}
public void speak(){}
public void sing(){}
}
當(dāng)這三個(gè)類被載入到 Java 虛擬機(jī)之后,方法區(qū)中就包含了各自的類的信息。Girl 和 Boy 在方法區(qū)中的方法表可表示如下:
圖 3.Boy 和 Girl 的方法表
可以看到,Girl 和 Boy 的方法表包含繼承自 Object 的方法,繼承自直接父類 Person 的方法及各自新定義的方法。注意方法表?xiàng)l目指向的具體的方法地址,如 Girl 的繼承自 Object 的方法中,只有 toString() 指向自己的實(shí)現(xiàn)(Girl 的方法代碼),其余皆指向 Object 的方法代碼;其繼承自于 Person 的方法 eat() 和 speak() 分別指向 Person 的方法實(shí)現(xiàn)和本身的實(shí)現(xiàn)。
Person 或 Object 的任意一個(gè)方法,在它們的方法表和其子類 Girl 和 Boy 的方法表中的位置 (index) 是一樣的。這樣 JVM 在調(diào)用實(shí)例方法其實(shí)只需要指定調(diào)用方法表中的第幾個(gè)方法即可。
如調(diào)用如下:
清單 2
class Party{…
void happyHour(){
Person girl = new Girl();
girl.speak();
…
}
}
當(dāng)編譯 Party 類的時(shí)候,生成 girl.speak()的方法調(diào)用假設(shè)為:
Invokevirtual #12
設(shè)該調(diào)用代碼對(duì)應(yīng)著 girl.speak(); #12 是 Party 類的常量池的索引。JVM 執(zhí)行該調(diào)用指令的過程如下所示:
圖 4. 解析調(diào)用過程
JVM 首先查看 Party 的常量池索引為 12 的條目(應(yīng)為 CONSTANTMethodrefinfo 類型,可視為方法調(diào)用的符號(hào)引用),進(jìn)一步查看常量池(CONSTANTClassinfo,CONSTANTNameAndTypeinfo ,CONSTANTUtf8info)可得出要調(diào)用的方法是 Person 的 speak 方法(注意引用 girl 是其基類 Person 類型),查看 Person 的方法表,得出 speak 方法在該方法表中的偏移量 15(offset),這就是該方法調(diào)用的直接引用。
當(dāng)解析出方法調(diào)用的直接引用后(方法表偏移量 15),JVM 執(zhí)行真正的方法調(diào)用:根據(jù)實(shí)例方法調(diào)用的參數(shù) this 得到具體的對(duì)象(即 girl 所指向的位于堆中的對(duì)象),據(jù)此得到該對(duì)象對(duì)應(yīng)的方法表 (Girl 的方法表 ),進(jìn)而調(diào)用方法表中的某個(gè)偏移量所指向的方法(Girl 的 speak() 方法的實(shí)現(xiàn))。
接口調(diào)用
因?yàn)?Java 類是可以同時(shí)實(shí)現(xiàn)多個(gè)接口的,而當(dāng)用接口引用調(diào)用某個(gè)方法的時(shí)候,情況就有所不同了。Java 允許一個(gè)類實(shí)現(xiàn)多個(gè)接口,從某種意義上來(lái)說相當(dāng)于多繼承,這樣同樣的方法在基類和派生類的方法表的位置就可能不一樣了。
清單 3
interface IDance{void dance();
}
class Person {
public String toString(){
return "I'm a person.";
}
public void eat(){}
public void speak(){}
}
class Dancer extends Person
implements IDance {
public String toString(){
return "I'm a dancer.";
}
public void dance(){}
}
class Snake implements IDance{
public String toString(){
return "A snake.";
}
public void dance(){
//snake dance
}
}
圖 5.Dancer 的方法表(查看大圖)
可以看到,由于接口的介入,繼承自于接口 IDance 的方法 dance()在類 Dancer 和 Snake 的方法表中的位置已經(jīng)不一樣了,顯然我們無(wú)法通過給出方法表的偏移量來(lái)正確調(diào)用 Dancer 和 Snake 的這個(gè)方法。這也是 Java 中調(diào)用接口方法有其專有的調(diào)用指令(invokeinterface)的原因。
Java 對(duì)于接口方法的調(diào)用是采用搜索方法表的方式,對(duì)如下的方法調(diào)用
invokeinterface #13
JVM 首先查看常量池,確定方法調(diào)用的符號(hào)引用(名稱、返回值等等),然后利用 this 指向的實(shí)例得到該實(shí)例的方法表,進(jìn)而搜索方法表來(lái)找到合適的方法地址。
因?yàn)槊看谓涌谡{(diào)用都要搜索方法表,所以從效率上來(lái)說,接口方法的調(diào)用總是慢于類方法的調(diào)用的。
執(zhí)行結(jié)果如下:可以看到 System.out.println(dancer);?調(diào)用的是Person的toString方法。
繼承的實(shí)現(xiàn)原理
Java 的繼承機(jī)制是一種復(fù)用類的技術(shù),從原理上來(lái)說,是更好的使用了組合技術(shù),因此要理解繼承,首先需要了解類的組合技術(shù)是如何實(shí)現(xiàn)類的復(fù)用的。
使用組合技術(shù)復(fù)用類 假設(shè)現(xiàn)在的需求是要?jiǎng)?chuàng)建一個(gè)具有基本類型,String 類型以及一個(gè)其他非基本類型的對(duì)象。該如何處理呢?
對(duì)于基本類型的變量,在新類中成員變量處直接定義即可,但對(duì)于非基本類型變量,不僅需要在類中聲明其引用,并且還需要手動(dòng)初始化這個(gè)對(duì)象。
這里需要注意的是,編譯器并不會(huì)默認(rèn)將所有的引用都創(chuàng)建對(duì)象,因?yàn)檫@樣的話在很多情況下會(huì)增加不必要的負(fù)擔(dān),因此,在合適的時(shí)機(jī)初始化合適的對(duì)象,可以通過以下幾個(gè)位置做初始化操作:
在定義對(duì)象的地方,先于構(gòu)造方法執(zhí)行。在構(gòu)造方法中。在正要使用之前,這個(gè)被稱為惰性初始化。使用實(shí)例初始化。
class Soap {
private String s;
Soap() {
System.out.println("Soap()");
s = "Constructed";
}
public String tiString(){
return s;
}
}
public class Bath {
// s1 初始化先于構(gòu)造函數(shù)
private String s1 = "Happy", s2 = "Happy", s3, s4;
private Soap soap;
private int i;
private float f;
public Both() {
System.out.println("inSide Both");
s3 = "Joy";
f = 3.14f;
soap = new Soap();
}
{
i = 88;
}
public String toString() {
if(s4 == null){
s4 = "Joy"
}
return "s1 = " + s1 +"\n" +
"s2 = " + s2 +"\n" +
"s3 = " + s3 +"\n" +
"s4 = " + s4 +"\n" +
"i = " + i +"\n" +
"f = " + f +"\n" +
"soap = " + soap;
}
}
繼承 Java 中的繼承由 extend 關(guān)鍵字實(shí)現(xiàn),組合的語(yǔ)法比較平實(shí),而繼承是一種特殊的語(yǔ)法。當(dāng)一個(gè)類繼承自另一個(gè)類時(shí),那么這個(gè)類就可以擁有另一個(gè)類的域和方法。
class Cleanser{
private String s = "Cleanser";
public void append(String a){
s += a;
}
public void apply(){
append("apply");
}
public void scrub(){
append("scrub");
}
public String toString(){
return s;
}
public static void main(String args){
Cleanser c = new Cleanser();
c.apply();
System.out.println(c);
}
}
public class Deter extends Cleanser{
public void apply(){
append("Deter.apply");
super.scrub();
}
public void foam(){
append("foam");
}
public static void main(String args){
Deter d = new Deter();
d.apply();
d.scrub();
d.foam();
System.out.println(d);
Cleanser.main(args);
}
}
上面的代碼中,展示了繼承語(yǔ)法中的一些特性:
子類可以直接使用父類中公共的方法和成員變量(通常為了保護(hù)數(shù)據(jù)域,成員變量均為私有) 子類中可以覆蓋父類中的方法,也就是子類重寫了父類的方法,此時(shí)若還需要調(diào)用被覆蓋的父類的方法,則需要用到 super 來(lái)指定是調(diào)用父類中的方法。子類中可以自定義父類中沒有的方法??梢园l(fā)現(xiàn)上面兩個(gè)類中均有 main 方法,命令行中調(diào)用的哪個(gè)類就執(zhí)行哪個(gè)類的 main 方法,例如:java Deter。繼承語(yǔ)法的原理 接下來(lái)我們將通過創(chuàng)建子類對(duì)象來(lái)分析繼承語(yǔ)法在我們看不到的地方做了什么樣的操作。
可以先思考一下,如何理解使用子類創(chuàng)建的對(duì)象呢,首先這個(gè)對(duì)象中包含子類的所有信息,但是也包含父類的所有公共的信息。
下面來(lái)看一段代碼,觀察一下子類在創(chuàng)建對(duì)象初始化的時(shí)候,會(huì)不會(huì)用到父類相關(guān)的方法。
class Art{
Art() {
System.out.println("Art Construct");
}
}
class Drawing extends Art {
Drawing() {
System.out.println("Drawing Construct");
}
}
public class Cartoon extends Drawing {
public Cartoon() {
System.out.println("Cartoon construct");
}
public void static main(String args) {
Cartoon c = new Cartoon();
}
}
/*output:
Art Construct
Drawing Construct
Cartoon construct
*/
通過觀察代碼可以發(fā)現(xiàn),在實(shí)例化Cartoon時(shí),事實(shí)上是從最頂層的父類開始向下逐個(gè)實(shí)例化,也就是最終實(shí)例化了三個(gè)對(duì)象。編譯器會(huì)默認(rèn)在子類的構(gòu)造方法中增加調(diào)用父類默認(rèn)構(gòu)造方法的代碼。
因此,繼承可以理解為編譯器幫我們完成了類的特殊組合技術(shù),即在子類中存在一個(gè)父類的對(duì)象,使得我們可以用子類對(duì)象調(diào)用父類的方法。而在開發(fā)者看來(lái)只不過是使用了一個(gè)關(guān)鍵字。
注意:雖然繼承很接近組合技術(shù),但是繼承擁有其他更多的區(qū)別于組合的特性,例如父類的對(duì)象我們是不可見的,對(duì)于父類中的方法也做了相應(yīng)的權(quán)限校驗(yàn)等。
那么,如果類中的構(gòu)造方法是帶參的,該如何操作呢?(使用super關(guān)鍵字顯示調(diào)用)
見代碼:
class Game {
Game(int i){
System.out.println("Game Construct");
}
}
class BoardGame extends Game {
BoardGame(int j){
super(j);
System.out.println("BoardGame Construct");
}
}
public class Chess extends BoardGame{
Chess(){
super(99);
System.out.println("Chess construct");
}
public static void main(String args) {
Chess c = new Chess();
}
}
/*output:
Game Construct
BoardGame Construct
Chess construc
*/
重載和重寫的實(shí)現(xiàn)原理
??? 剛開始學(xué)習(xí)Java的時(shí)候,就了解了Java這個(gè)比較有意思的特性:重寫 和 重載。開始的有時(shí)候從名字上還總是容易弄混。我相信熟悉Java這門語(yǔ)言的同學(xué)都應(yīng)該了解這兩個(gè)特性,可能只是從語(yǔ)言層面上了解這種寫法,但是jvm是如何實(shí)現(xiàn)他們的呢 ?
重載官方給出的介紹:
一.? overload: The Java programming language supports overloading methods, and Java can distinguish between methods with different method signatures. This means that methods within a class can have the same name if they have different parameter lists .
Overloaded methods are differentiated by the number and the type of the arguments passed into the method.
You cannot declare more than one method with the same name and the same number and type of arguments, because the compiler cannot tell them apart.
The compiler does not consider return type when differentiating methods, so you cannot declare two methods with the same signature even if they have a different return type.
首先看一段代碼,來(lái)看看代碼的執(zhí)行結(jié)果:
public class OverrideTest {
class Father{}
class Sun extends Father {}
public void doSomething(Father father){
System.out.println("Father do something");
}
public void doSomething(Sun father){
System.out.println("Sun do something");
}
public static void main(String [] args){
OverrideTest overrideTest = new OverrideTest();
Father sun = overrideTest.new Sun();
Father father = overrideTest.new Father();
overrideTest.doSomething(father);
overrideTest.doSomething(sun);
}
}
看下這段代碼的執(zhí)行結(jié)果,最后會(huì)打印:
Father do something Father do something
為什么會(huì)打印出這樣的結(jié)果呢?首先要介紹兩個(gè)概念:靜態(tài)分派和動(dòng)態(tài)分派
靜態(tài)分派:依賴靜態(tài)類型來(lái)定位方法執(zhí)行版本的分派動(dòng)作稱為靜態(tài)分派
動(dòng)態(tài)分派:運(yùn)行期根據(jù)實(shí)際類型確定方法執(zhí)行版本的分派過程。
他們的區(qū)別是:
靜態(tài)分派發(fā)生在編譯期,動(dòng)態(tài)分派發(fā)生在運(yùn)行期;
private,static,final 方法發(fā)生在編譯期,并且不能被重寫,一旦發(fā)生了重寫,將會(huì)在運(yùn)行期處理。
重載是靜態(tài)分派,重寫是動(dòng)態(tài)分派
回到上面的問題,因?yàn)橹剌d是發(fā)生在編譯期,所以在編譯期已經(jīng)確定兩次 doSomething 方法的參數(shù)都是Father類型,在class文件中已經(jīng)指向了Father類的符號(hào)引用,所以最后會(huì)打印兩次Father do something。
二. override: An instance method in a subclass with the same signature (name, plus the number and the type of its parameters) and return type as an instance method in the superclass?overrides?the superclass's method.
The ability of a subclass to override a method allows a class to inherit from a superclass whose behavior is "close enough" and then to modify behavior as needed. The overriding method has the same name, number and type of parameters, and return type as the method that it overrides. An overriding method can also return a subtype of the type returned by the overridden method. This subtype is called a?covariant return type.
還是上面那個(gè)代碼,稍微改動(dòng)下
public class OverrideTest {
class Father{}
class Sun extends Father {}
public void doSomething(){
System.out.println("Father do something");
}
public void doSomething(){
System.out.println("Sun do something");
}
public static void main(String [] args){
OverrideTest overrideTest = new OverrideTest();
Father sun = overrideTest.new Sun();
Father father = overrideTest.new Father();
overrideTest.doSomething();
overrideTest.doSomething();
}
}
最后會(huì)打印:
Father do something
Sun do something
相信大家都會(huì)知道這個(gè)結(jié)果,那么這個(gè)結(jié)果jvm是怎么實(shí)現(xiàn)的呢?
在編譯期,只會(huì)識(shí)別到是調(diào)用Father類的doSomething方法,到運(yùn)行期才會(huì)真正找到對(duì)象的實(shí)際類型。
首先該方法的執(zhí)行,jvm會(huì)調(diào)用invokevirtual指令,該指令會(huì)找棧頂?shù)谝粋€(gè)元素所指向的對(duì)象的實(shí)際類型,如果該類型存在調(diào)用的方法,則會(huì)走驗(yàn)證流程,否則繼續(xù)找其父類。這也是為什么子類可以直接調(diào)用父類具有訪問權(quán)限的方法的原因。簡(jiǎn)而言之,就是在運(yùn)行期才會(huì)去確定對(duì)象的實(shí)際類型,根據(jù)這個(gè)實(shí)際類型確定方法執(zhí)行版本,這個(gè)過程稱為動(dòng)態(tài)分派。override 的實(shí)現(xiàn)依賴jvm的動(dòng)態(tài)分派。
參考文章
https://blog.csdn.net/djdengjian/article/details/80811348 https://blog.csdn.net/chenssy/article/details/12757911 https://blog.csdn.net/fan2012huan/article/details/51007517 https://blog.csdn.net/fan2012huan/article/details/50999777 https://www.cnblogs.com/serendipity-fly/p/9469289.html https://blog.csdn.net/m037264516/article/details/86709537
微信公眾號(hào)
Java技術(shù)江湖
如果大家想要實(shí)時(shí)關(guān)注我更新的文章以及分享的干貨的話,可以關(guān)注我的公眾號(hào)【Java技術(shù)江湖】一位阿里 Java 工程師的技術(shù)小站,作者黃小斜,專注 Java 相關(guān)技術(shù):SSM、SpringBoot、MySQL、分布式、中間件、集群、Linux、網(wǎng)絡(luò)、多線程,偶爾講點(diǎn)Docker、ELK,同時(shí)也分享技術(shù)干貨和學(xué)習(xí)經(jīng)驗(yàn),致力于Java全棧開發(fā)!
Java工程師必備學(xué)習(xí)資源: 一些Java工程師常用學(xué)習(xí)資源,關(guān)注公眾號(hào)后,后臺(tái)回復(fù)關(guān)鍵字 “Java” 即可免費(fèi)無(wú)套路獲取。
創(chuàng)作挑戰(zhàn)賽新人創(chuàng)作獎(jiǎng)勵(lì)來(lái)咯,堅(jiān)持創(chuàng)作打卡瓜分現(xiàn)金大獎(jiǎng)總結(jié)
以上是生活随笔為你收集整理的java 关注公众号没有调接口_深入理解Java继承、封装、多态的实现原理的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
 
                            
                        - 上一篇: python实例变量不存在_python
- 下一篇: synchronized锁升级_sync
