1. 程式人生 > >夯實Java基礎系列23:一文讀懂繼承、封裝、多型的底層實現原理

夯實Java基礎系列23:一文讀懂繼承、封裝、多型的底層實現原理

本系列文章將整理到我在GitHub上的《Java面試指南》倉庫,更多精彩內容請到我的倉庫裡檢視

https://github.com/h2pl/Java-Tutorial

喜歡的話麻煩點下Star哈

文章首發於我的個人部落格:

www.how2playlife.com

從JVM結構開始談多型

Java 對於方法呼叫動態繫結的實現主要依賴於方法表,但通過類引用呼叫和介面引用呼叫的實現則有所不同。總體而言,當某個方法被呼叫時,JVM 首先要查詢相應的常量池,得到方法的符號引用,並查詢呼叫類的方法表以確定該方法的直接引用,最後才真正呼叫該方法。以下分別對該過程中涉及到的相關部分做詳細介紹。

JVM 的結構

典型的 Java 虛擬機器的執行時結構如下圖所示

圖 1.JVM 執行時結構

此結構中,我們只探討和本文密切相關的方法區 (method area)。當程式執行需要某個類的定義時,載入子系統 (class loader subsystem) 裝入所需的 class 檔案,並在內部建立該類的型別資訊,這個型別資訊就存貯在方法區。型別資訊一般包括該類的方法程式碼、類變數、成員變數的定義等等。可以說,型別資訊就是類的 Java 檔案在執行時的內部結構,包含了改類的所有在 Java 檔案中定義的資訊。

注意到,該型別資訊和 class 物件是不同的。class 物件是 JVM 在載入某個類後於堆 (heap) 中建立的代表該類的物件,可以通過該 class 物件訪問到該型別資訊。比如最典型的應用,在 Java 反射中應用 class 物件訪問到該類支援的所有方法,定義的成員變數等等。可以想象,JVM 在型別資訊和 class 物件中維護著它們彼此的引用以便互相訪問。兩者的關係可以類比於程序物件與真正的程序之間的關係。

Java 的方法呼叫方式

Java 的方法呼叫有兩類,動態方法呼叫與靜態方法呼叫。靜態方法呼叫是指對於類的靜態方法的呼叫方式,是靜態繫結的;而動態方法呼叫需要有方法呼叫所作用的物件,是動態繫結的。類呼叫 (invokestatic) 是在編譯時刻就已經確定好具體呼叫方法的情況,而例項呼叫 (invokevirtual) 則是在呼叫的時候才確定具體的呼叫方法,這就是動態繫結,也是多型要解決的核心問題。

JVM 的方法呼叫指令有四個,分別是 invokestatic,invokespecial,invokesvirtual 和 invokeinterface。前兩個是靜態繫結,後兩個是動態繫結的。本文也可以說是對於 JVM 後兩種呼叫實現的考察。

常量池(constant pool)

常量池中儲存的是一個 Java 類引用的一些常量資訊,包含一些字串常量及對於類的符號引用資訊等。Java 程式碼編譯生成的類檔案中的常量池是靜態常量池,當類被載入到虛擬機器內部的時候,在記憶體中產生類的常量池叫執行時常量池。

常量池在邏輯上可以分成多個表,每個表包含一類的常量資訊,本文只探討對於 Java 呼叫相關的常量池表。

CONSTANT_Utf8_info

字串常量表,該表包含該類所使用的所有字串常量,比如程式碼中的字串引用、引用的類名、方法的名字、其他引用的類與方法的字串描述等等。其餘常量池表中所涉及到的任何常量字串都被索引至該表。

CONSTANT_Class_info

類資訊表,包含任何被引用的類或介面的符號引用,每一個條目主要包含一個索引,指向 CONSTANT_Utf8_info 表,表示該類或介面的全限定名。

CONSTANT_NameAndType_info

名字型別表,包含引用的任意方法或欄位的名稱和描述符資訊在字串常量表中的索引。

CONSTANT_InterfaceMethodref_info

介面方法引用表,包含引用的任何介面方法的描述資訊,主要包括類資訊索引和名字型別索引。

CONSTANT_Methodref_info

類方法引用表,包含引用的任何型別方法的描述資訊,主要包括類資訊索引和名字型別索引。

圖 2. 常量池各表的關係

可以看到,給定任意一個方法的索引,在常量池中找到對應的條目後,可以得到該方法的類索引(class_index)和名字型別索引 (name_and_type_index), 進而得到該方法所屬的型別資訊和名稱及描述符資訊(引數,返回值等)。注意到所有的常量字串都是儲存在 CONSTANT_Utf8_info 中供其他表索引的。

方法表與方法呼叫

方法表是動態呼叫的核心,也是 Java 實現動態呼叫的主要方式。它被儲存於方法區中的型別資訊,包含有該型別所定義的所有方法及指向這些方法程式碼的指標,注意這些具體的方法程式碼可能是被覆寫的方法,也可能是繼承自基類的方法。

如有類定義 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(){} 
 }

當這三個類被載入到 Java 虛擬機器之後,方法區中就包含了各自的類的資訊。Girl 和 Boy 在方法區中的方法表可表示如下:

圖 3.Boy 和 Girl 的方法表

可以看到,Girl 和 Boy 的方法表包含繼承自 Object 的方法,繼承自直接父類 Person 的方法及各自新定義的方法。注意方法表條目指向的具體的方法地址,如 Girl 的繼承自 Object 的方法中,只有 toString() 指向自己的實現(Girl 的方法程式碼),其餘皆指向 Object 的方法程式碼;其繼承自於 Person 的方法 eat() 和 speak() 分別指向 Person 的方法實現和本身的實現。

Person 或 Object 的任意一個方法,在它們的方法表和其子類 Girl 和 Boy 的方法表中的位置 (index) 是一樣的。這樣 JVM 在呼叫例項方法其實只需要指定呼叫方法表中的第幾個方法即可。

如呼叫如下:

清單 2

 class Party{ 
…
 void happyHour(){ 
 Person girl = new Girl(); 
 girl.speak(); 
…
     } 
 }

當編譯 Party 類的時候,生成 girl.speak()的方法呼叫假設為:

Invokevirtual #12

設該呼叫程式碼對應著 girl.speak(); #12 是 Party 類的常量池的索引。JVM 執行該呼叫指令的過程如下所示:

圖 4. 解析呼叫過程

JVM 首先檢視 Party 的常量池索引為 12 的條目(應為 CONSTANT_Methodref_info 型別,可視為方法呼叫的符號引用),進一步檢視常量池(CONSTANT_Class_info,CONSTANT_NameAndType_info ,CONSTANT_Utf8_info)可得出要呼叫的方法是 Person 的 speak 方法(注意引用 girl 是其基類 Person 型別),檢視 Person 的方法表,得出 speak 方法在該方法表中的偏移量 15(offset),這就是該方法呼叫的直接引用。

當解析出方法呼叫的直接引用後(方法表偏移量 15),JVM 執行真正的方法呼叫:根據例項方法呼叫的引數 this 得到具體的物件(即 girl 所指向的位於堆中的物件),據此得到該物件對應的方法表 (Girl 的方法表 ),進而呼叫方法表中的某個偏移量所指向的方法(Girl 的 speak() 方法的實現)。

介面呼叫

因為 Java 類是可以同時實現多個介面的,而當用介面引用呼叫某個方法的時候,情況就有所不同了。Java 允許一個類實現多個介面,從某種意義上來說相當於多繼承,這樣同樣的方法在基類和派生類的方法表的位置就可能不一樣了。

清單 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 的方法表中的位置已經不一樣了,顯然我們無法通過給出方法表的偏移量來正確呼叫 Dancer 和 Snake 的這個方法。這也是 Java 中呼叫介面方法有其專有的呼叫指令(invokeinterface)的原因。

Java 對於介面方法的呼叫是採用搜尋方法表的方式,對如下的方法呼叫

invokeinterface #13

JVM 首先檢視常量池,確定方法呼叫的符號引用(名稱、返回值等等),然後利用 this 指向的例項得到該例項的方法表,進而搜尋方法表來找到合適的方法地址。

因為每次介面呼叫都要搜尋方法表,所以從效率上來說,介面方法的呼叫總是慢於類方法的呼叫的。

執行結果如下:

可以看到System.out.println(dancer); 呼叫的是Person的toString方法。

繼承的實現原理

Java 的繼承機制是一種複用類的技術,從原理上來說,是更好的使用了組合技術,因此要理解繼承,首先需要了解類的組合技術是如何實現類的複用的。

使用組合技術複用類
假設現在的需求是要建立一個具有基本型別,String 型別以及一個其他非基本型別的物件。該如何處理呢?

對於基本型別的變數,在新類中成員變數處直接定義即可,但對於非基本型別變數,不僅需要在類中宣告其引用,並且還需要手動初始化這個物件。

這裡需要注意的是,編譯器並不會預設將所有的引用都建立物件,因為這樣的話在很多情況下會增加不必要的負擔,因此,在合適的時機初始化合適的物件,可以通過以下幾個位置做初始化操作:

在定義物件的地方,先於構造方法執行。
在構造方法中。
在正要使用之前,這個被稱為惰性初始化。
使用例項初始化。

class Soap {
    private String s;
    Soap() {
        System.out.println("Soap()");
        s = "Constructed";
    }
    public String tiString(){
        return s;
    }
}

public class Bath {
    // s1 初始化先於建構函式
    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 關鍵字實現,組合的語法比較平實,而繼承是一種特殊的語法。當一個類繼承自另一個類時,那麼這個類就可以擁有另一個類的域和方法。

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);
    }
}

上面的程式碼中,展示了繼承語法中的一些特性:

子類可以直接使用父類中公共的方法和成員變數(通常為了保護資料域,成員變數均為私有)
子類中可以覆蓋父類中的方法,也就是子類重寫了父類的方法,此時若還需要呼叫被覆蓋的父類的方法,則需要用到 super 來指定是呼叫父類中的方法。
子類中可以自定義父類中沒有的方法。
可以發現上面兩個類中均有 main 方法,命令列中呼叫的哪個類就執行哪個類的 main 方法,例如:java Deter。
繼承語法的原理
接下來我們將通過建立子類物件來分析繼承語法在我們看不到的地方做了什麼樣的操作。

可以先思考一下,如何理解使用子類建立的物件呢,首先這個物件中包含子類的所有資訊,但是也包含父類的所有公共的資訊。

下面來看一段程式碼,觀察一下子類在建立物件初始化的時候,會不會用到父類相關的方法。

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
*/

通過觀察程式碼可以發現,在例項化Cartoon時,事實上是從最頂層的父類開始向下逐個例項化,也就是最終例項化了三個物件。編譯器會預設在子類的構造方法中增加呼叫父類預設構造方法的程式碼。

因此,繼承可以理解為編譯器幫我們完成了類的特殊組合技術,即在子類中存在一個父類的物件,使得我們可以用子類物件呼叫父類的方法。而在開發者看來只不過是使用了一個關鍵字。

注意:雖然繼承很接近組合技術,但是繼承擁有其他更多的區別於組合的特性,例如父類的物件我們是不可見的,對於父類中的方法也做了相應的許可權校驗等。

那麼,如果類中的構造方法是帶參的,該如何操作呢?(使用super關鍵字顯示呼叫)

見程式碼:

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
*/

過載和重寫的實現原理

    剛開始學習Java的時候,就瞭解了Java這個比較有意思的特性:重寫 和 過載。開始的有時候從名字上還總是容易弄混。我相信熟悉Java這門語言的同學都應該瞭解這兩個特性,可能只是從語言層面上了解這種寫法,但是jvm是如何實現他們的呢 ?

過載官方給出的介紹:

一.  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.

首先看一段程式碼,來看看程式碼的執行結果:

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);
    }
}

看下這段程式碼的執行結果,最後會列印:

Father do something
Father do something

為什麼會打印出這樣的結果呢? 首先要介紹兩個概念:靜態分派和動態分派

靜態分派:依賴靜態型別來定位方法執行版本的分派動作稱為靜態分派

動態分派:執行期根據實際型別確定方法執行版本的分派過程。

他們的區別是:

1.  靜態分派發生在編譯期,動態分派發生在執行期;

2.  private,static,final 方法發生在編譯期,並且不能被重寫,一旦發生了重寫,將會在執行期處理。

3.  過載是靜態分派,重寫是動態分派

回到上面的問題,因為過載是發生在編譯期,所以在編譯期已經確定兩次 doSomething 方法的引數都是Father型別,在class檔案中已經指向了Father類的符號引用,所以最後會列印兩次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.

還是上面那個程式碼,稍微改動下

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();
    }
}


最後會列印:

Father do something

Sun do something

 

相信大家都會知道這個結果,那麼這個結果jvm是怎麼實現的呢?

在編譯期,只會識別到是呼叫Father類的doSomething方法,到執行期才會真正找到物件的實際型別。

首先該方法的執行,jvm會呼叫invokevirtual指令,該指令會找棧頂第一個元素所指向的物件的實際型別,如果該型別存在呼叫的方法,則會走驗證流程,否則繼續找其父類。這也是為什麼子類可以直接呼叫父類具有訪問許可權的方法的原因。簡而言之,就是在執行期才會去確定物件的實際型別,根據這個實際型別確定方法執行版本,這個過程稱為動態分派。override 的實現依賴jvm的動態分派。

參考文章

https://blog.csdn.net/dj_dengjian/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/m0_37264516/article/details/86709537

微信公眾號

Java技術江湖

如果大家想要實時關注我更新的文章以及分享的乾貨的話,可以關注我的公眾號【Java技術江湖】一位阿里 Java 工程師的技術小站,作者黃小斜,專注 Java 相關技術:SSM、SpringBoot、MySQL、分散式、中介軟體、叢集、Linux、網路、多執行緒,偶爾講點Docker、ELK,同時也分享技術乾貨和學習經驗,致力於Java全棧開發!

Java工程師必備學習資源: 一些Java工程師常用學習資源,關注公眾號後,後臺回覆關鍵字 “Java” 即可免費無套路獲取。

個人公眾號:黃小斜

作者是跨考軟體工程的 985 碩士,自學 Java 兩年,拿到了 BAT 等近十家大廠 offer,從技術小白成長為阿里工程師。

作者專注於 JAVA 後端技術棧,熱衷於分享程式設計師乾貨、學習經驗、求職心得和程式人生,目前黃小斜的CSDN部落格有百萬+訪問量,知乎粉絲2W+,全網已有10W+讀者。

黃小斜是一個斜槓青年,堅持學習和寫作,相信終身學習的力量,希望和更多的程式設計師交朋友,一起進步和成長!

關注公眾號【黃小斜】後回覆【原創電子書】即可領取我原創的電子書《菜鳥程式設計師修煉手冊:從技術小白到阿里巴巴Java工程師》

程式設計師3T技術學習資源: 一些程式設計師學習技術的資源大禮包,關注公眾號後,後臺回覆關鍵字 “資料” 即可免費無套路獲取。

相關推薦

夯實Java基礎系列23繼承封裝底層實現原理

本系列文章將整理到我在GitHub上的《Java面試指南》倉庫,更多精彩內容請到我的倉庫裡檢視 https://github.com/h2pl/Java-Tutorial 喜歡的話麻煩點下Star哈 文章首發於我的個人部落格: www.how2playlife.com 從JVM結構開始談多型 Jav

夯實Java基礎系列7Java 程式碼塊和執行順序

目錄 Java中的構造方法 構造方法簡介 構造方法例項 例 1 例 2 Java中的幾種構造方法詳解 普通構造方法 預設構造方法 過載構造方法 java子類構造方法呼叫父類構造方法 Java中的程式碼塊簡介 Java程式碼塊使用 區域性程式碼塊 構造程式碼塊 靜態程式碼塊 Java程式碼塊、

夯實Java基礎系列22Java序列化和反序列化

本系列文章將整理到我在GitHub上的《Java面試指南》倉庫,更多精彩內容請到我的倉庫裡檢視 https://github.com/h2pl/Java-Tutorial 喜歡的話麻煩點下Star哈 文章首發於我的個人部落格: www.how2playlife.com 本文參考 http://www

夯實Java基礎系列6抽象類和介面,從基礎到面試題,揭祕其本質區別!

目錄 抽象類介紹 為什麼要用抽象類 一個抽象類小故事 一個抽象類小遊戲 介面介紹 介面與類相似點: 介面與類的區別: 介面特性 抽象類和介面的區別 介面的使用: 介面最佳實踐:設計模式中的工廠模式 介面與抽象類的本質區別是什麼? 基本語法區別 設計思想區別 如何回答面試題:介面和抽象類的區別?

夯實Java基礎系列19Java集合類框架,以及常見面試題

本系列文章將整理到我在GitHub上的《Java面試指南》倉庫,更多精彩內容請到我的倉庫裡檢視 https://github.com/h2pl/Java-Tutorial 喜歡的話麻煩點下Star哈 文章首發於我的個人部落格: www.how2playlife.com 本文參考 https://ww

夯實Java基礎系列4了解final關鍵字的特性使用方法,以及實現原理

目錄 final使用 final變數 final修飾基本資料型別變數和引用 final類 final關鍵字的知識點 final關鍵字的最佳實踐 final的用法 關於空白final final記憶體分配 使用final修飾方法會提高速度和效率嗎 使用final修飾變數會讓變數的值不能被改變嗎; 如何保

夯實Java基礎系列5Java檔案和Java包結構

目錄 Java中的包概念 包的作用 package 的目錄結構 設定 CLASSPATH 系統變數 常用jar包 java軟體包的型別 dt.jar rt.jar *.java檔案的奧祕 *.Java檔案簡介 為什麼一個java原始檔中只能有一個public類? Main方法 外部類的訪問許可權

夯實Java基礎系列9深入理解Class類和Object類

目錄 Java中Class類及用法 Class類原理 如何獲得一個Class類物件 使用Class類的物件來生成目標類的例項 Object類 類構造器public Object(); registerNatives()方法; Clone()方法實現淺拷貝 getClass()方法 equals()方法

夯實Java基礎系列10深入理解Java中的異常體系

目錄 為什麼要使用異常 異常基本定義 異常體系 初識異常 異常和錯誤 異常的處理方式 "不負責任"的throws 糾結的finally throw : JRE也使用的關鍵字 異常呼叫鏈 自定義異常 異常的注意事項 當finally遇上return JAVA異常常見面試題 參考文章 微信公眾號 Java技術

夯實Java基礎系列11深入理解Java中的回撥機制

目錄 模組間的呼叫 多執行緒中的“回撥” Java回撥機制實戰 例項一 : 同步呼叫 例項二:由淺入深 例項三:Tom做題 參考文章

夯實Java基礎系列12深入理解Java中的反射機制

本系列文章將整理到我在GitHub上的《Java面試指南》倉庫,更多精彩內容請到我的倉庫裡檢視 https://github.com/h2pl/Java-Tutorial 喜歡的話麻煩點下Star哈 文章首發於我的個人部落格: www.how2playlife.com 列舉(enum)型別是Java

夯實Java基礎系列21Java8新特性終極指南

本系列文章將整理到我在GitHub上的《Java面試指南》倉庫,更多精彩內容請到我的倉庫裡檢視 https://github.com/h2pl/Java-Tutorial 喜歡的話麻煩點下Star哈 文章首發於我的個人部落格: www.how2playlife.com 這是一個Java8新增特性的總

從HTTP/0.9到HTTP/2HTTP協議的歷史演變和設計思路

eight 結果 key 視頻 this sso單點登陸 會有 研究 patch 本文原作者阮一峰,作者博客:ruanyifeng.com。 1、引言 HTTP 協議是最重要的互聯網基礎協議之一,它從最初的僅為瀏覽網頁的目的進化到現在,已經是短連接通信的事實工業標準,最新版

生死時刻Facebook資料洩密始末

上週末Facebook捲入了一宗醜聞,媒體揭露稱一家服務特朗普競選團隊的資料分析公司Cambri

超級乾貨 資料視覺化

前言資料視覺化,是指將相對晦澀的的資料通過可視的、互動的方式進行展示,從而形象、直觀地表達資料蘊

[轉]機器學習科普文章機器學習,大資料/自然語言處理/演算法全有了”

       在本篇文章中,我將對機器學習做個概要的介紹。本文的目的是能讓即便完全不瞭解機器學習的人也能瞭解機器學習,並且上手相關的實 踐。這篇文件也算是EasyPR開發的番外篇,從這裡開始,必須對機器學習瞭解才能進一步介紹EasyPR的核心。當然,本文也面對一般讀者,不會對

深度 | 生成對抗網路初學入門GAN的基本原理(附資源)

選自 Sigmoidal 作者:Roman Trusov 機器之心編譯 參與:Panda 生成對抗網路是現在人工智慧領域的當紅技術之一。近日,Sigmoidal.io 的部落格發表了一篇入門級介紹文章,對 GAN 的原理進行了解釋說明。另外,在該文章的最後還附帶了一些能幫助初學者自己

推薦 R中的探索性資料分析(附R程式碼)

作者:Pablo Casas;翻譯:蔣雨暢;校對:車前子;本文約1500字,建議閱讀7分鐘。本文

資料變金礦序列模型(附用例)

作者:TAVISH SRIVASTAVA翻譯:王雨桐校對:丁楠雅本文約3000字,建議閱讀15分

「05」迴歸的誘惑線性迴歸

前言 從這一篇文章開始,就正式進入「美團」演算法工程師帶你入門機器學習系列的正文了,之前的幾篇算是導讀和預熱,想必大家看的並不過癮。從這裡開始,我們將會以線性迴歸為起點,貫通迴歸方法在機器學習演算法中所扮演的角色、具有的功能和使用的方法。 說起迴歸,它是我們在高中時就接觸過的內容。具體的,迴歸(Regress