scala和java的關聯以及區別

從Hello World說起

編寫一段最基本的helloworld代碼,然後我們對生產的class文件進行反編譯,可以看到生成了兩個class文件 一個是HelloWorld,一個是HelloWorld$

object HelloWorld {
  def main(args: Array[String]): Unit = {
    println("Hello World!")
  }
}

仔細查看兩端代碼,發現它的底層是生成了一個HelloWorld$類對 代碼進行包裝 ,生成一個靜態實例對象,然後通過HelloWorld通過調用這個對象的main方法打印HelloWorld,這兩個類其實是一個伴生關係,一個爲伴生類一個爲伴生對象,這個我們後面講到。舉這個簡單的例子只是想說明scala 和java具有互通性,sacla的代碼進行反編譯以後底層都可以轉換成java代碼,它們的本質都是將.java或者.scala文件編譯成.class的字節碼文件到JVM中運行,由於scala的範圍比較大,用scala的指令設置可以對一些java文件進行編譯

//HelloWorld
public final class HelloWorld {
  public static void main(String[] paramArrayOfString) {
    //調用靜態實例方法
     HelloWorld$.MODULE$.main(paramArrayOfString); 
}
}

//HelloWorld$
public final class HelloWorld$ {
//創建靜態對象
  public static final HelloWorld$ MODULE$;
  //main方法
  public void main(String[] args) { Predef$.MODULE$.println("Hello World!"); } static  {
  
  }
//私有構造器 
private HelloWorld$() { MODULE$ = this; }
}

scala和java關係圖

scala指令運行java

數據類型對比

java 數據類型分爲基本類型和引用類型,而scala則是遵從一切皆爲對象的原則所有數據類型都爲對象,甚至將null也封裝成了一個Null。scala所有類的超類爲Any,類似於java中Object類。AnyVal繼承了Any,繼承Any的類中與JAVA的基本數據類型幾乎一一對應,此外String對應String, Unit對應的void(scala將無返回值也封裝成了一個類), AnyRef的繼承類可以理解成java的引用類型,Null是所有AnyRef子類,它只有一個值。Nothing是所有類的子類。圖中的虛線標是可以隱式轉換的類,隱式轉換是scala的一個特色,這個後面會說到。

變量創建

scala創建變量和java也有所不同,他可以指定類型,也可以自動推導

object HelloWorld {
  def main(args: Array[String]): Unit = {
    //不指定類型,自動推導成Int類型
    var num1 = 10
    println(num1.isInstanceOf[Double])
    //指定Double類型
    var num2 :Double=10
    println(num2.isInstanceOf[Double])
  }
}

var修飾符創建的變量爲可變變量,val創建的爲不可變變量,類似於final,這樣也是爲了提升運行效率,一般創建對象實例推薦直用val

修改變量值提示錯誤

所有的變量都要初始化

屬性

scala 類的屬性直接通過var或者val修飾符創建,必須賦值,_標是爲默認值。在scala的底層默認私有屬性,並且爲我們創建了get set方法。如果在創建屬性是用了private修飾則表明 set和get方法是私有的,下面我們來舉一個例子然後反編譯一下看下底層的java代碼是什麼

object Test {
  def main(args: Array[String]): Unit = {
    println("")
  }
}


class User{
    //創建常量
  val username:String = "stanley"
    //創建可變屬性
  var nickname:String = _
   // 創建私有方法

  private var password:String = _
}

反編譯結果

public class User
{
  private String nickname;
  private String password;
  private final String username = "stanley"; 
//username爲不可變屬性,只讀方法
  public String username() { return this.username; }
//nickname爲可變屬性,提供公開的讀寫方法
  public String nickname() { return this.nickname; } public void nickname_$eq(String x$1) { this.nickname = x$1; }
//password 只提供私有的讀寫方法
  private String password() { return this.password; } private void password_$eq(String x$1) { this.password = x$1; }
}

伴生

前面我們講到了伴生類和伴生對象,伴生對象是有object修飾,伴生類是由class修飾。java的類是將所有的靜態非靜態 屬性方法都放在一個類下面,scala則是將靜態方法屬性放到object,非靜態的方法屬性放到class中,目的是爲了取消static這個修飾符。下面我們創建一個伴生類和對象,調用伴生對象的屬性和方法,發現直接通過類名調用就能實現。由此我們就不難理解爲什麼程序入口要創建一個object,因爲main方法本身就是一個靜態方法

object Test {
  def main(args: Array[String]): Unit = {
    //創建一個user對象
    val user =new User
    //調用靜態屬性
    println("2019年"+user.username+"年齡爲:"+User.age)
    //調用靜態方法
    User.addAge()
    //再次調用靜態屬性
    println("2020年"+user.username+"年齡爲:"+User.age)
  }
}


class User{
  val username:String = "stanley"
  var nickname:String = _
  private var password:String = _
}

object User{
  var age:Int = 18
  def addAge(): Unit ={
    this.age+=1
  }
}

運行一下看下結果

構造器

java構造器有無參構造和有參構造,有參構造可以通過參數類型和數量不同進行重載。scala的構造器分爲主構造器和副構造器,在創建類是最外層就是一個構造器,副構造器在類的內部,而且必須通過this關鍵字繼承主構造器,也就是說通過副構造器創建實例時,必須先執行主構造器的方法

object ContructorTest {
  def main(args: Array[String]): Unit = {
    val user1 = new User01("stanley","hello")
    println("*****************")
    val user2 = new User01("stanley","hello","123456")
  }
}




class User01(){
  println("這是主構造器...")
  def this(username:String, nickname:String){
    this
    println("這是兩個參數副構造器...")
  }


  def this(username:String, nickname:String,password:String){
    this("stanley","hello")
    println("這是三個參數副構造器...")


  }
}

通過副構造器創建實例,都會調用父構造器,並最終調用主構造器

接口抽象類和特質

java是通過將類的共同特性抽象出來編寫成接口或者抽象類讓子類去實現或者繼承,scala也有類似的特性叫做trait,trait同時包含接口和抽象類。下面我們編寫一個接口來反編譯一下看下它的底層java代碼是什麼樣子

object TraitTest {
  def main(args: Array[String]): Unit = {
    println("")


  }


}


//第一個特質有一個抽象方法
trait Trait01{
  def show()
}


//第二個特質有一個屬性和一個方法
trait Trait02{
  val name:String = "stanley"
  def show(): Unit ={
    println("我是trait02")
  }
}

Trait01就是一個普通的java接口

Trait02 生成了兩個class文件,Trait02.class和Trait01.class一樣有兩個抽象方法 

Trait02$class  則是一個抽象類 實現了接口的抽象方法

由此可見trait同時擁有接口和抽象類的特性。

繼承

與java同的是,scala繼承特質可以動態混入,他可以創建對象的時候動態繼承特質,構造方法是從左往右執行,繼承方法是從右往左執行。

object TraitTest {
  def main(args: Array[String]): Unit = {
    val obj = new TraitTest() with Trait01 with Trait02{
       override def show(): Unit ={
         super.show()
       }
    }
    obj.show()
  }
}




trait Trait01{
  println("我是trait01構造方法")
  def show(): Unit ={
    println("我是trait01")
  }
}




trait Trait02{
  println("我是trait02構造方法")
  def show(): Unit ={
    println("我是trait02")
  }
}


class TraitTest{
  println("我是traittest構造方法")




}

首先從左往右執行構造方法,show()方法從右往左尋找,找到就執行

模式匹配

scala 取消了swith case的語句,換成了更爲強大的match匹配模式,它不但能實現switch的功能,還可以匹配類型,對象提取,偏函數,樣例類匹配,下面舉兩個最普通的例子來看一下

object MatchTest {
  def main(args: Array[String]): Unit = {
    var str:String = "abc"
    println("普通匹配")
    str match{
      case "abc" => println("我是abc")
      case _ =>println("我不是abc")
    }
    println("類型匹配:")
    str match{
      case a:String => println("我是STRING")
      case _ =>println("我不是STRING")
    }
  }
}

隱式轉換

scala的隱式轉換前面那種數據類型的圖裏有體現,什麼是隱式轉換呢,我們來舉一個最簡單的例子,下面是一個隱式轉換方法,在創建a變量的時候自動調用d2i的方法
object ImpTest {
  def main(args: Array[String]): Unit = {
    //隱式轉換方法講double類型轉換成int類型
    implicit  def d2i (d:Double): Int ={
      d.toInt
    }
    var a:Int =3.5
    println("我是隱式轉換後的值:"+a)
  }
}

隱式轉換類,創建一個隱式轉換類,可以讓AA類調用BB隱式類的方法

object ImpTest {
  def main(args: Array[String]): Unit = {


    //創建隱式類
    implicit class BB(val aa: AA) {
      def show01(): Unit = {
        println("我是BB的方法")
      }
    }
    val aa: AA = new AA
    aa.show01()
  }
}


class AA {
  def show(): Unit = {
    println("我是AA的方法")
  }
}

隱式值,設定一個隱式值,調用有參函數時,沒有傳入值默認使用隱式值,它的優先級大於默認值

小結

java和scala除了區別外,scala還有很多特有的特性,比如元組,包對象,可變集合和不可變集合的數據類型也各有不同等等。由於當下最火的兩個大數據實時框架flink和spark都是由scala編寫,所以學好scala對日後的源碼研究以及二次開發都有很大的幫助
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章