小男孩‘自慰网亚洲一区二区,亚洲一级在线播放毛片,亚洲中文字幕av每天更新,黄aⅴ永久免费无码,91成人午夜在线精品,色网站免费在线观看,亚洲欧洲wwwww在线观看

分享

學(xué)習(xí)Scala:從HelloWorld開始

 看風(fēng)景D人 2016-10-07


前言


最近在學(xué)習(xí)Scala語言,雖然還沒有完全學(xué)通, 但是隱約可以體會(huì)到Scala的簡(jiǎn)潔和強(qiáng)大。 它既能讓程序員使用函數(shù)式編程, 也提供了全面的面向?qū)ο缶幊獭?在剛剛開始讀《Scala編程》的時(shí)候, 剛讀了幾頁, 我就被Scala語言吸引住了, 所以就一直讀下去。 在學(xué)習(xí)的過程中, 也會(huì)有一些感悟, 對(duì)于一些原理, 也會(huì)盡量搞明白。 所以打算一邊學(xué)習(xí), 一邊寫博客, 雖然目前還沒有深入, 但是還是有很多東西值得寫下來。


我們知道, Scala也是一種運(yùn)行于Java虛擬機(jī)上的語言, 既然能夠運(yùn)行于虛擬機(jī)之上, 那么它必然可以編譯成class文件, 因?yàn)樘摂M機(jī)只認(rèn)class文件。 所以, scalac編譯器將.scala源文件, 編譯成class文件, 然后這些class文件被虛擬機(jī)加載并執(zhí)行。



所以, 如果你對(duì)class文件格式和java虛擬機(jī)足夠了解的話, 那么學(xué)習(xí)scala語言就會(huì)相對(duì)簡(jiǎn)單。Java文件編譯成class文件, 而Scala源文件也是編譯成class文件, 雖然他們語法大相徑庭, 但是最后殊途同歸。  如果我們能基于class文件分析scala的行為, 有助于理解scala語言。有人說scala的語法很多很難, 其實(shí)語法終歸是寫法, class文件的格式是不變的, 可以把scala的語法看成java語法的高級(jí)語法糖。


本系列博客基于分析class文件, 來分析scala的語法。 如果你對(duì)class文件格式不熟悉, 建議讀一下我的專欄, 該專欄是專門分析class文件和JVM行為的。 專欄地址:


http://blog.csdn.NET/column/details/zhangjg-java-blog.html



Scala的HelloWorld


按照IT界的傳統(tǒng), 下面我們就從HelloWorld開始分析。 下面是scala版的HelloWorld源碼:
[plain] view plain copy
在CODE上查看代碼片派生到我的代碼片
  1. object HelloWorld{  
  2.     def main(args : Array[String]){  
  3.         println("HelloWorld")  
  4.     }  
  5. }  

如果對(duì)scala的語法不是很熟悉, 并且對(duì)scala比較感興趣, 建議先熟悉一下scala的基本語法。 這里簡(jiǎn)單說兩以下幾點(diǎn):

1   以object關(guān)鍵字修飾一個(gè)類名, 這種語法叫做孤立對(duì)象,這個(gè)對(duì)象是單例的。 相當(dāng)于將單例類和單例對(duì)象同時(shí)定義。

2   方法聲明以def開頭, 然后是方法名, 參數(shù)列表, 返回值, 等號(hào), 方法體 。如下:
[plain] view plain copy
在CODE上查看代碼片派生到我的代碼片
  1. def doSomeThing(x : Int) : Int = {  
  2.     x += 1  
  3. }  

 如果沒有返回值, 可以省略等號(hào), 直接寫方法體。

3 Array[String]是scala的一種數(shù)據(jù)類型, 可以理解為字符串?dāng)?shù)組。

這篇博客的目的不是詳細(xì)的講解語法, 而是基于class文件來分析scala語法的實(shí)現(xiàn)方式, 所以對(duì)于語法只簡(jiǎn)單提一下 。 



反編譯scala HelloWorld


我們所說的反編譯, 是指使用javap工具反編譯class文件, 所以, 在反編譯之前, 要先使用scalac編譯器編譯該源文件:

[plain] view plain copy
在CODE上查看代碼片派生到我的代碼片
  1. scalac HelloWorld.scala  

命令執(zhí)行完成后, 可以看到HelloWorld.scala所在的目錄中多出兩個(gè)class文件:



其中有一個(gè)是和HelloWorld.scala對(duì)應(yīng)的HelloWorld.class 。 那么HelloWorld$.class是什么呢?難道一個(gè)scala類可以生成多個(gè)class嗎? 下面通過反編譯來找到答案。

首先反編譯HelloWorld.class : 

[plain] view plain copy
在CODE上查看代碼片派生到我的代碼片
  1. javap -c -v -classpath . HelloWorld  

反編譯結(jié)果如下: (為了便于講述, 給出了所有的輸出, 會(huì)有些長(zhǎng))

  1. Classfile /D:/Workspace/scala/scala-test/HelloWorld/HelloWorld.class  
  2.   Last modified 2014-4-1; size 586 bytes  
  3.   MD5 checksum 2ce2089f345445003ec6b4ef4ed4c6d1  
  4.   Compiled from "HelloWorld.scala"  
  5. public final class HelloWorld  
  6.   SourceFile: "HelloWorld.scala"  
  7.   RuntimeVisibleAnnotations:  
  8.     0: #6(#7=s#8)  
  9.     ScalaSig: length = 0x3  
  10.      05 00 00  
  11.   minor version: 0  
  12.   major version: 50  
  13.   flags: ACC_PUBLIC, ACC_FINAL, ACC_SUPER  
  14. Constant pool:  
  15.    #1 = Utf8               HelloWorld  
  16.    #2 = Class              #1             //  HelloWorld  
  17.    #3 = Utf8               java/lang/Object  
  18.    #4 = Class              #3             //  java/lang/Object  
  19.    #5 = Utf8               HelloWorld.scala  
  20.    #6 = Utf8               Lscala/reflect/ScalaSignature;  
  21.    #7 = Utf8               bytes  
  22.    #8 = Utf8               :Q!\t\t!S3mY><vN7ea ...  
  23.    #9 = Utf8               main  
  24.   #10 = Utf8               ([Ljava/lang/String;)V  
  25.   #11 = Utf8               HelloWorld$  
  26.   #12 = Class              #11            //  HelloWorld$  
  27.   #13 = Utf8               MODULE$  
  28.   #14 = Utf8               LHelloWorld$;  
  29.   #15 = NameAndType        #13:#14        //  MODULE$:LHelloWorld$;  
  30.   #16 = Fieldref           #12.#15        //  HelloWorld$.MODULE$:LHelloWorld$;  
  31.   #17 = NameAndType        #9:#10         //  main:([Ljava/lang/String;)V  
  32.   #18 = Methodref          #12.#17        //  HelloWorld$.main:([Ljava/lang/String;)V  
  33.   #19 = Utf8               Code  
  34.   #20 = Utf8               SourceFile  
  35.   #21 = Utf8               RuntimeVisibleAnnotations  
  36.   #22 = Utf8               ScalaSig  
  37. {  
  38.   public static void main(java.lang.String[]);  
  39.     flags: ACC_PUBLIC, ACC_STATIC  
  40.     Code:  
  41.       stack=2, locals=1, args_size=1  
  42.          0: getstatic     #16                 // Field HelloWorld$.MODULE$:LHelloWorld$;  
  43.          3: aload_0  
  44.          4: invokevirtual #18                 // Method HelloWorld$.main:([Ljava/lang/String;)V  
  45.          7: return  
  46. }  

從輸出結(jié)果可以看到, 這個(gè)類確實(shí)有傳統(tǒng)意義上的main方法。 這個(gè)main方法中的字節(jié)碼指令大概是這樣:

1 getstatic訪問一個(gè)靜態(tài)字段, 這個(gè)靜態(tài)字段是定義在HelloWorld$類中的MODULE$字段, 這個(gè)字段的類型是HelloWorld$ 。 講到這里, 大概出現(xiàn)了單例的影子。 我們并沒有定義這個(gè)類, 所以這個(gè)類是scala編譯器自動(dòng)生成的, 用來輔佐HelloWorld類。

2 然后使用這個(gè)靜態(tài)對(duì)象調(diào)用main方法, 這個(gè)main方法是HelloWorld$類中的, 而不是當(dāng)前HelloWorld中的。 它不是靜態(tài)的, 而是成員方法。 


下面反編譯HelloWorld$類: 
  1. javap -c -v -classpath . HelloWorld$  

反編譯結(jié)果如下:
  1. Classfile /D:/Workspace/scala/scala-test/HelloWorld/HelloWorld$.class  
  2.   Last modified 2014-4-1; size 596 bytes  
  3.   MD5 checksum 7b3e40952539579da28edc84f370ab9b  
  4.   Compiled from "HelloWorld.scala"  
  5. public final class HelloWorld$  
  6.   SourceFile: "HelloWorld.scala"  
  7.     Scala: length = 0x0  
  8.   
  9.   minor version: 0  
  10.   major version: 50  
  11.   flags: ACC_PUBLIC, ACC_FINAL, ACC_SUPER  
  12. Constant pool:  
  13.    #1 = Utf8               HelloWorld$  
  14.    #2 = Class              #1             //  HelloWorld$  
  15.    #3 = Utf8               java/lang/Object  
  16.    #4 = Class              #3             //  java/lang/Object  
  17.    #5 = Utf8               HelloWorld.scala  
  18.    #6 = Utf8               MODULE$  
  19.    #7 = Utf8               LHelloWorld$;  
  20.    #8 = Utf8               <clinit>  
  21.    #9 = Utf8               ()V  
  22.   #10 = Utf8               <init>  
  23.   #11 = NameAndType        #10:#9         //  "<init>":()V  
  24.   #12 = Methodref          #2.#11         //  HelloWorld$."<init>":()V  
  25.   #13 = Utf8               main  
  26.   #14 = Utf8               ([Ljava/lang/String;)V  
  27.   #15 = Utf8               scala/Predef$  
  28.   #16 = Class              #15            //  scala/Predef$  
  29.   #17 = Utf8               Lscala/Predef$;  
  30.   #18 = NameAndType        #6:#17         //  MODULE$:Lscala/Predef$;  
  31.   #19 = Fieldref           #16.#18        //  scala/Predef$.MODULE$:Lscala/Predef$;  
  32.   #20 = Utf8               HelloWorld  
  33.   #21 = String             #20            //  HelloWorld  
  34.   #22 = Utf8               println  
  35.   #23 = Utf8               (Ljava/lang/Object;)V  
  36.   #24 = NameAndType        #22:#23        //  println:(Ljava/lang/Object;)V  
  37.   #25 = Methodref          #16.#24        //  scala/Predef$.println:(Ljava/lang/Object;)V  
  38.   #26 = Utf8               this  
  39.   #27 = Utf8               args  
  40.   #28 = Utf8               [Ljava/lang/String;  
  41.   #29 = Methodref          #4.#11         //  java/lang/Object."<init>":()V  
  42.   #30 = NameAndType        #6:#7          //  MODULE$:LHelloWorld$;  
  43.   #31 = Fieldref           #2.#30         //  HelloWorld$.MODULE$:LHelloWorld$;  
  44.   #32 = Utf8               Code  
  45.   #33 = Utf8               LocalVariableTable  
  46.   #34 = Utf8               LineNumberTable  
  47.   #35 = Utf8               SourceFile  
  48.   #36 = Utf8               Scala  
  49. {  
  50.   public static final HelloWorld$ MODULE$;  
  51.     flags: ACC_PUBLIC, ACC_STATIC, ACC_FINAL  
  52.   
  53.   
  54.   public static {};  
  55.     flags: ACC_PUBLIC, ACC_STATIC  
  56.     Code:  
  57.       stack=1, locals=0, args_size=0  
  58.          0: new           #2                  // class HelloWorld$  
  59.          3: invokespecial #12                 // Method "<init>":()V  
  60.          6: return  
  61.   
  62.   public void main(java.lang.String[]);  
  63.     flags: ACC_PUBLIC  
  64.     Code:  
  65.       stack=2, locals=2, args_size=2  
  66.          0: getstatic     #19                 // Field scala/Predef$.MODULE$:Lscala/Predef$;  
  67.          3: ldc           #21                 // String HelloWorld  
  68.          5: invokevirtual #25                 // Method scala/Predef$.println:(Ljava/lang/Object;)V  
  69.          8: return  
  70.       LocalVariableTable:  
  71.         Start  Length  Slot  Name   Signature  
  72.                0       9     0  this   LHelloWorld$;  
  73.                0       9     1  args   [Ljava/lang/String;  
  74.       LineNumberTable:  
  75.         line 5: 0  
  76. }  

從輸出結(jié)果可以知道:


 HelloWorld$類有一個(gè)靜態(tài)字段
  1. public static final HelloWorld$ MODULE$;  
  2.   flags: ACC_PUBLIC, ACC_STATIC, ACC_FINAL  

它的訪問修飾符是 public static final   , 類型是HelloWorld$ , 字段名是 MODULE$ 。

 
 HelloWorld$類還有一個(gè)靜態(tài)初始化方法:
  1. public static {};  
  2.     flags: ACC_PUBLIC, ACC_STATIC  
  3.     Code:  
  4.       stack=1, locals=0, args_size=0  
  5.          0: new           #2                  // class HelloWorld$  
  6.          3: invokespecial #12                 // Method "<init>":()V  
  7.          6: return  

在這個(gè)靜態(tài)初始化方法中, 使用new指令創(chuàng)建了一個(gè)HelloWorld$對(duì)象, 并且調(diào)用該對(duì)象的構(gòu)造方法<init>初始化這個(gè)對(duì)象。 
實(shí)際上就是對(duì)靜態(tài)字段MODULE$ 的賦值。

 HelloWorld$類還有一個(gè)main方法:
  1. public void main(java.lang.String[]);  
  2.    flags: ACC_PUBLIC  
  3.    Code:  
  4.      stack=2, locals=2, args_size=2  
  5.         0: getstatic     #19                 // Field scala/Predef$.MODULE$:Lscala/Predef$;  
  6.         3: ldc           #21                 // String HelloWorld  
  7.         5: invokevirtual #25                 // Method scala/Predef$.println:(Ljava/lang/Object;)V  
  8.         8: return  
  9.      LocalVariableTable:  
  10.        Start  Length  Slot  Name   Signature  
  11.               0       9     0  this   LHelloWorld$;  
  12.               0       9     1  args   [Ljava/lang/String;  
  13.      LineNumberTable:  
  14.        line 5: 0  

這個(gè)main方法不是靜態(tài)的, 是一個(gè)實(shí)例方法, 從它的字節(jié)碼指令可以看出, 實(shí)現(xiàn)的是打印字符串HelloWorld的邏輯。


HelloWorld的實(shí)現(xiàn)方式總結(jié)


從上面的講述中, 我們可知, scalac編譯器使用兩個(gè)class文件, 實(shí)現(xiàn)HelloWorld.scala源文件中的邏輯, 除了生成HelloWorld.class外, 還生產(chǎn)一個(gè)HelloWorld$.class 。實(shí)現(xiàn)邏輯如下:


1 傳統(tǒng)意義上的入口main方法被編譯在HelloWorld.class中

2  在HelloWorld.class中的main方法中, 會(huì)訪問HelloWorld$.class中的靜態(tài)字段MODULE$  (這個(gè)字段的類型就是HelloWorld$) , 并使用這個(gè)字段調(diào)用HelloWorld$中的main方法。

HelloWorld中的邏輯有點(diǎn)像下面這樣(以下偽代碼旨在說明原理, 并不符合java或scala的語法):

  1. public class HelloWorld{  
  2.       
  3.     public static void main(String[] args){  
  4.   
  5.   
  6.       HelloWorld$.MODULE$.main(args);  
  7.     }     
  8. }  

3 真正打印字符串“HelloWorld”的邏輯在HelloWorld$中。 這個(gè)類有一個(gè)main實(shí)例方法, 來處理打印字符串的邏輯, 并且該類中有一個(gè)HelloWorld$類型的靜態(tài)字段MODULE$ 。 上面的HelloWorld類中的入口main方法, 正是通過這個(gè)字段調(diào)用的HelloWorld$的main實(shí)例方法來打印"HelloWorld" 。

HelloWorld$中的代碼有點(diǎn)像這樣(以下偽代碼旨在說明原理, 并不符合java或scala的語法):

  1. public final class HelloWorld${  
  2.   
  3.     public static final HelloWorld$ MODULE$ = new HelloWorld$();  
  4.   
  5.     public void main(String[] args){  
  6.         println("HelloWorld");  
  7.     }  
  8. }  





本文基于分析class字節(jié)碼來分析scala, 對(duì)class文件格式不熟悉的同學(xué), 可以參考我的專欄: 深入理解Java語言 。




    本站是提供個(gè)人知識(shí)管理的網(wǎng)絡(luò)存儲(chǔ)空間,所有內(nèi)容均由用戶發(fā)布,不代表本站觀點(diǎn)。請(qǐng)注意甄別內(nèi)容中的聯(lián)系方式、誘導(dǎo)購(gòu)買等信息,謹(jǐn)防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請(qǐng)點(diǎn)擊一鍵舉報(bào)。
    轉(zhuǎn)藏 分享 獻(xiàn)花(0

    0條評(píng)論

    發(fā)表

    請(qǐng)遵守用戶 評(píng)論公約

    類似文章 更多