從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
數據類型對比
變量創建
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")
}
}
}
隱式轉換
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的方法")
}
}
隱式值,設定一個隱式值,調用有參函數時,沒有傳入值默認使用隱式值,它的優先級大於默認值