Scala
本文为个人的学习笔记,只记录了Scala中和Java不同之处,对于一些和Java相同的语言特点就不再赘述
一、Scala 语言特点
1.1 JVM和Scala的关系
一般来说,学 Scala 的人,都会 Java,而Scala 是基于 Java 的,因此我们需要将 Scala和 Java 以及 JVM 之间的关系搞清楚。
Scala是一门以Java虚拟机(JVM)为运行环境并将面向对象和函数式编程的最佳特性结合在一起的静态类型编程语言(静态语言需要提前编译的如:Java、c、c++等,动态语言如:js)。
1)Scala是一门多范式的编程语言,Scala支持面向对象和函数式编程。(多范式,就是多种编程方法的意思。有面向过程、面向对象、泛型、函数式四种程序设计方法。)
2)Scala源代码(.scala)会被编译成Java字节码(.class),然后运行于JVM之上,并可以调用现有的Java类库,实现两种语言的无缝对接。
1.2 类(class)和对象(object)的关系
Scala源于Java却不同于Java,下边简单介绍scala的简单应用:
- scala中没有public关键字,如果不声明访问权限(private),默认是public。
- scala没有static静态语法,但是使用了另一种方法模仿静态语法。
- 声明方法: def 方法名(参sd数列表) : 返回值类型。
- scala 采用Unit代替void
1.3 Scala程序的反编译
一个Scala文件经过编译后会产生一个.class文件和$.class文件,使用反编译软件Decompiler查看其中的内容:
Hello.class类作为编程入口,调用了Hello$.class类的一个静态对象,下边对其进行解析:
public final class Hello${
public static final MODULE$; #1.创建一个公开的并且是静态的MODULE$属性
static{ 2.通过静态代码块创建了一个当前对象
new();
}
public void main(String[] args){
Predef..MODULE$.println("hello world");
}
private HelloScala$(){
MODULE$ = this; #2.通过构造方法将创建的当前对象通过this关键字赋值给MODULE$属性
}
}
Object Hello称为:伴生对象
Hello.class称为:Obejct Hello(伴生对象)的伴生类
Hello$.class称为:Hello.class(伴生类)的所属类
Scala语言是完全面向对象的语言,所以并没有静态的操作(即在Scala中没有静态的概念)。但是为了能够和Java语言交互(因为Java中有静态概念),就产生了一种特殊的对象来模拟类对象,该对象为单例对象。若单例对象名与类名一致,则称该单例对象这个类的伴生对象,这个类的所有“静态”内容都可以放置在它的伴生对象中声明。
结合Hello.scala源码,我们通过Obejct声明一个Hello伴生对象,我们在编译时发现其使用一个同名的伴生类,而该伴生类又调用了一个所属类,创建了一个公开静态的对象,所以一开始就声明了一个唯一的静态对象(单例设计式)供使用。
附:该篇是学习笔记,有可能理解不当,可参考文章
//object 声明一个单例对象
//main方法: 从外部可以直接调用执行的方法
// def 方法名称(参数名: 参数类型):返回值类型 = {方法体}
object HelloWorld {
def main(args: Array[String]): Unit = {
println("hello world");
System.out.print("hello world");#Scala中能够调用java类库的
}
}
二、数据类型
变量和常量:(重点)
常量:在程序执行的过程中,其值不会被改变的变量
1)基本语法
var 变量名 [: 变量类型] = 初始值 var i:Int = 10
val 常量名 [: 常量类型] = 初始值 val j:Int = 20
注意:
(0)能用常量的地方不用变量
(1)声明变量时,类型可以省略,编译器自动推导,即类型推导
(2)类型确定后,就不能修改,说明 Scala 是强数据类型语言。
(3)变量声明时,必须要有初始值
(4)在声明/定义一个变量时,可以使用 var 或者 val 来修饰,var 修饰的变量可改变,val 修饰的变量不可改。(使用Val修饰的变量指向一个对象时,其指向的对象地址不能变,但对象里边的var修饰的属性是可以改变的)
标识符的命名规范:
Scala 对各种变量、方法、函数等命名时使用的字符序列称为标识符。即:凡是自己可以起名字的地方都叫标识符
Scala 中的标识符声明,基本和 Java 是一致的,但是细节上会有所变化,有以下三种规则:
(1)以字母或者下划线开头,后接字母、数字、下划线 尚硅谷大数据技术之 Scala
(2)以操作符开头,且只包含操作符(+ - * / # !等)
(3)用反引号....
包括的任意字符串,即使是 Scala 关键字(39 个)也可以
数据类型:
1)Scala中一切数据都是对象,都是Any的子类。
2)Scala中数据类型分为两大类:数值类型(AnyVal)、引用类型(AnyRef),不管是值类型还是引用类型都是对象。
3)Scala数据类型仍然遵守,低精度的值类型向高精度值类型,自动转换(隐式转换)
4)Scala中的StringOps是对Java中的String增强
5)Unit:对应Java中的void,用于方法返回值的位置,表示方法没有返回值。Unit是 一个数据类型,只有一个对象就是()。Void不是数据类型,只是一个关键字
6)Null是一个类型,只有一个对象就是null。Null是所有引用类型(AnyRef)的子类。
7)Nothing是所有数据类型的子类,主要用在一个函数没有明确返回值时使用,因为这样我们可以把抛出的返回值,返回给任何的变量或者函数。
2.1 Unit 类型、Null 类型和 Nothing 类型(重点)
数据类型 | 描述 |
---|---|
Unit | 表示无值,和其他语言中 void 等同。用作不返回任何结果的方法的结果类型。Unit 只有一个实例值,写成()。 |
Null | null , Null 类型只有一个实例值 null |
Nothing | Nothing 类型在 Scala 的类层级最低端;它是任何其他类型的子类型。当一个函数,我们确定没有正常的返回值,可以用 Nothing 来指定返回类型,这样有一个好处,就是我们可以把返回的值(异常)赋给其它的函数或者变量(兼容性) |
(1)Unit 类型用来标识过程,也就是没有明确返回值的函数。由此可见,Unit 类似于 Java 里的 void。Unit 只有一个实例——( ),这个实例也没有实质意义
object TestSpecialType {
def main(args: Array[String]): Unit = {
def sayOk : Unit = {// unit 表示没有返回值,即 void
}
val a = sayOk()
println(a) //()空值
}
}
(2)Null 类只有一个实例对象,Null 类似于 Java 中的 null 引用。Null 可以赋值给任意引用类型(AnyRef),但是不能赋值给值类型(AnyVal)
object TestDataType {
def main(args: Array[String]): Unit = {
//null 可以赋值给任意引用类型(AnyRef),但是不能赋值给值类型(AnyVal)
var cat = new Cat();
cat = null // 正确
var n1: Int = null // 错误
println("n1:" + n1) //null
}
}
class Cat {
}
(3)Nothing,可以作为没有正常返回值的方法的返回类型,非常直观的告诉你这个方法不会正常返回,而且由于 Nothing 是其他任意类型的子类,他还能跟要求返回值的方法兼容。
object TestSpecialType {
def main(args: Array[String]): Unit = {
def test1() : Nothing={
throw new Exception()
}
//使用方法
def test2(n:Int) : Nothing ={ //Nothing是所有数值类型的子类
if( n == 0 )
throw new NullPointerException
else
return n
}
}
}
2.2 数值类型的自动转换和强制转换
自动类型转换
当 Scala 程序在进行赋值或者运算时,精度小的类型自动转换为精度大的数值类型,这个就是自动类型转换(隐式转换)。数据类型按精度(容量)大小排序为:
Byte->Short->Int->Long->Float->Double(Char类型直接转换为Int)
(1)自动提升原则:有多种类型的数据混合运算时,系统首先自动将所有数据转换成精度大的那种数据类型,然后再进行计算。
(2)把精度大的数值类型赋值给精度小的数值类型时,就会报错,反之就会进行自动类型转换。
(3)(byte,short)和 char 之间不会相互自动转换。
(4)byte,short,char 他们三者可以计算,在计算时首先转换为 int 类型。
注意:自动类型转换的逆过程,将精度大的数值类型转换为精度小的数值类型。使用时要加上强制转函数,但可能造成精度降低或溢出,格外要注意。
例如:var n1: Int = 2.5.toInt //将数据从高精度转换到低精度,存在精度损失
强制类型转换:
(1)将数据由高精度转换为低精度,需要使用强制类型转换
val n1:Int = -2.9.toInt #-2
(2)强转符号只针对最近的操作数有效,往往会使用小括号提升优先级
val n2:Int = 2.6.toInt+3.7.toInt #5
val n3:Int = (2.6+3.7).toInt #6
(3)数值类型和String类型的转换
(1)数值转String
val n:Int = 37
val s:String = n+ "" or val s:String = n.toString
(2)String转数值
val m:Int = "12".toInt
val f:Float = "12.3".toFloat
val f2:Int = "12.3".toDouble.toInt
字符转的比较运算:
val s1:String = "hello";
val s2:String = new String("hello“)
println(s1 == s2) #true java中==比较的是引用地址值,Scala中能直接比较字符串数值
print(s1.equals(s2))# true 比较的还是字符串数值(能够重写)
println(s1.eq(s2))# false scala使用.eq()对引用对象的地址值进行比较
三、运算符和流程控制
3.1 运算符
Scala中不在保留Java的++,–操作符,但可以使用+=,-=来实现同样的效果。需要注意的是+=,-=操作符不再像java那样提供自动的类型强转。
注意:在 Scala 中其实是没有运算符的,所有运算符都是方法
1)当调用对象的方法时,点.可以省略
2)如果函数参数只有一个,或者没有参数,()可以省略
val n1:Int = 12
val n2:Int = 37
println(n1.+(n2))
print(7.5 toInt)# 7
3.2流程控制
Scala 中 if else 表达式其实是有返回值的,具体返回值取决于满足条件的代码体的最后一行内容
object TestIfElse {
def main(args: Array[String]): Unit = {
println("input age")
var age = StdIn.readInt() #Scala中的输入(Java中的Scanner)
val res :String = if (age < 18){
"童年"
}else if(age>=18 && age<30){
"中年"
}else{
"老年"
}
println(res)
}
#另一种更简便的写法
val res = if(age>=18) ”成年“ else "未成年"
}
for循环和嵌套循环:
for(i<-1 to 10 by 2){}#遍历1到10,步长为2[1,10]
#如果步长设置为-数,需要start大于end,步长不能为0
for(i<-1 until 10){}#遍历1直到10,[1,10)
for(i:Int<-1.to(10)){}#遍历1到10,[1,10]
for(i<-Range(1,10)){}#遍历1到10,[1,10]
#嵌套循环
for(i <- 1 to 3; j = 4 - i) {
println("i=" + i + " j=" + j)
}
循环守卫:循环保护式(也称作条件判断式,守卫)。保护式为true则进入循环体内部,为false则跳过,类似continue
for(i<-1 to 10){
if(i != 5){
print()
}
}
#Scala使用循环守卫能够直接跳过,优化资源使用
for(i<-1 to 10 if i != 5){
print()
}
循环返回值:
val ints: immutable.IndexedSeq[Int] = for (i <- 1 to 10) yield i
println(ints)#Vector(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)
yield关键字的理解可以参考Python中的yield和Scala中的yield
while循环和break对象的使用:
import scala.util.control.Breaks_
def main(args: Array[String]): Unit = {
breakable(
for (elem <- 1 to 10) {
println(elem)
if (elem == 5)
break() #break终止循环
}
)
println("正常结束循环")
}
(1)循环条件是返回一个布尔值的表达式
(2)while 循环是先判断再执行语句
(3)与 for 语句不同,while 语句没有返回值,即整个 while 语句的结果是 Unit 类型()
(4)因为 while 中没有返回值,所以当要用该语句来计算并返回结果时,就不可避免的使用变量,而变量需要声明在 while 循环的外部,那么就等同于循环的内部对外部的变量造成了影响,所以不推荐使用,而是推荐使用 for 循环。