scala和java的关联以及区别

5 篇文章 0 订阅
4 篇文章 0 订阅

从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对日后的源码研究以及二次开发都有很大的帮助
  • 3
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值