1、什么是scala?
Scala是一门以Java虚拟机为运行环境并将面向对象和函数式编程的最佳特性结合在一起的静态类型编程语言。
2、scala的特点
函数是头等公民;
作为一门语言非常简洁高效,是一门多范式的编程语言,支持面向对象和函数式编程。编译成成Java字节码,然后运行于JVM之上,并可以调用现有的Java类库,实现两种语言的无缝对接。将函数式编程语言的特点融合到JAVA中。
3、scala和java有什么区?
-
变量的声明
变量var,常量val;scala支持自动类型推测 scala更多的是采用常量,而不是变量来解决问题,这样带来的好处是可以减少多线程并发安全问题,特别适合用于多并发分布式的场景;
-
函数的声明
关键字def,Scala函数没有返回值用Unit,相当于Java的void Scala支持函数式编程,可以使用高阶函数,函数是一等公民;
-
基本类型
其实可以认为scala中并没有真正意义上的基本类型,他的类型都是类;
-
静态
Java中静态static是违背Java面向对象的编程思想和封装特性的,scala取消了静态的概念,使用了单例对象Object来实现;
-
对字符串的支持
Scala支持使用字符串插值的方式对字符串进行格式化,使用$开头进行取值,另外支持使用三引号将其中的内容直接包起来,其中可以包括任何字符而不需要转义;
-
类
Scala类中的字段自动带有getter和setter方法,另外可以使用@BeanProperty注解来生成Java中的Get/Set方法,Scala中的每个类都有一个主构造器,这个构造器和类定义交织在一起,类的参数直接成为类的字段,主构造器执行类体中所有的语句;
-
Scala中不支持Break
使用return替代,在循环中用if和布尔类型变量进行守卫,导入java中支持break的包;
-
访问范围问题
java中外部看不到内部,内部能看到外部,scala中外部看不到内部,内部看不到外部;
-
通配符
Java使用*进行通配,Scala使用_进行通配;
-
默认导入的类
scala默认导入java.lang包、scala包、scala.Predef类。 java默认导入java.lang包;
-
特质 trait
可以类比java中的接口,但是又和接口非常不一样;
java中称为类实现了接口,scala中称为混入了特质,和java中的接口不同,scala中的特质可以包含,带有方法体的方法。
4、val和var的区别?
val是常量 var是变量
5、scala的数据类型?
- Any是所有类型地超类型,也称为顶级类型。它定义了一些通过地方法如:equals,hashCode和toString。Any有两个直接子类:AnyVal和AnyRef。(Object)
- AnyVal代表值类型。有9个预定义地非空地值类型分别是:Double,Float,Long,Int,Short,Byte,Char,Unit和Boolean。Unit是不带任何意义地值类型。它仅有一个实例可以像这样声明:()。所有地函数必须有返回,所以说有时候Unit也是有用返回类型(基本数据类型)
- AnyRef代表引用类型。所有非值类型都被定义为引用类型。在Scala中,每个用户自定义地类型都是ANyRef地子类型。如果Scala被应用在java地运行环境中,AnyRef相当于java.lang.Object。
6、scala运算符
算术运算符、关系运算符、逻辑运算符、赋值运算符、位运算符
7、scala块语句有什么特点?
最后一行的值或者方法体作为返回值
8、scala break使用?
需要先引入类import util.control.Breaks._
9、scala函数和方法的区别?
-
函数=后带>
为完成某一功能的程序指令(语句)的集合,称为函数
-
方法=后不带>
类中的函数称之方法
-
案例实操
- Scala语言的语法非常灵活,可以在任何的语法结构中声明任何的语法
- 函数没有重载和重写的概念,方法可以进行重载和重写
- scala中函数可以嵌套定义
-
Scala 有方法与函数,二者在语义上的区别很小。Scala方法是类的一部分,而函数是一个对象可以赋值给一个变量。换句话来说在类中定义的函数即是方法。
-
Scala 中的方法跟Java的类似,方法是组成类的一部分。
-
Scala 中的函数则是一个完整的对象,Scala中的函数其实就是继承了Trait的类的对象。
-
Scala 中使用val语句可以定义函数,def语句定义方法。
10、scala函数的参数?
可变参数:如果参数列表中存在多个参数,那么可变参数一般放置在最后
参数默认值:带名参数
11、scala方法的几种写法?
- 函数1:无参,无返回值
- 函数2:无参,有返回值
- 函数3:有参,无返回值
- 函数4:有参,有返回值
- 函数5:多参,无返回值
12、scala函数的写法
13、scala闭包?
就是一个函数和与其相关的引用环境(变量)组合的一个整体(实体)
14、scala柯里化?
将一个接收多个参数的函数转化成一个接受一个参数的函数过程,可以简单的理解为一种特殊的参数列表声明方式
15、scala函数至简原则?
- return可以省略,Scala会使用函数体的最后一行代码作为返回值
- 返回值类型如果能够推断出来,那么可以省略
- 如果函数体只有一行代码,可以省略花括号
- 如果函数无参,则可以省略小括号;若定义函数时省略小括号,则调用该函数时,也需省略小括号;若定时函数时未省略,则调用时,可省可不省
- 如果函数明确声明Unit,那么即使函数体中使用return关键字也不起作用
- Scala如果想要自动推断无返回值,可以省略等号
- 如果不关心名称,只关系逻辑处理,那么函数名(def)可以省略
- 如果函数明确使用return关键字,那么函数返回就不能使用自行推断了,需要声明返回值类型
16、scala lazy?
当函数返回值被声明为lazy时,函数的执行将被推迟,直到我们首次对此取值,该函数才会执行;这种函数我们称之为惰性函数
17、Scala包的三大作用
Scala包的三大作用(和Java一样)
- 区分相同名字的类
- 当类很多时,可以很好的管理类
- 控制访问范围
18、scala中的三个默认导入包?
- import java.lang._
- import scala._
- import scala.Predef._
19、scala访问修饰符,及作用域?
在Java中,访问权限分为:public,private,protected和默认。在Scala中,可以通过类似的修饰符达到同样的效果;但是使用上有区别:
- scala中属性和方法的默认访问权限为public,但scala中无public关键字
- private:私有权限,只在类的内部和伴生对象中可用
- protected:受保护权限,Scala中受保护权限比Java中更严格,同类、子类可以访问,同包无法访问
- private[包名]:增加包访问权限,包名下的其他类也可以使用
20、scala中@BeanProperty属性的特点?
Bean属性(@BeanPropetry),可以自动生成规范的setXxx / getXxx方法
21、scala中var和val修饰的对象有什么区别?
- val修饰对象,不能改变对象的引用(即:内存地址),可以改变对象属性的值
- var修饰对象,可以修改对象的引用和修改对象的属性值
22、scala的构造器?
和Java一样,Scala构造对象也需要调用构造方法,并且可以有任意多个构造方法。
Scala类的构造器包括:主构造器和辅助构造器
- 辅助构造器,函数的名称this,可以有多个,编译器通过参数的个数来区分
- 辅助构造方法不能直接构建对象,必须直接或者间接调用主构造方法
如果主构造器无参数,小括号可省略,构建对象时调用的构造方法的小括号也可以省略。
Scala类的主构造器函数的形参包括三种类型:未用任何修饰、var修饰、val修饰
- 未用任何修饰符修饰,这个参数就是一个局部变量
- var修饰参数,作为类的成员属性使用,可以修改
- val修饰参数,作为类只读属性使用,不能修改
23、scala的继承?
class 子类名 extends 父类名 {类体}
- 子类继承父类的属性和方法
- scala是单继承
- 继承的调用顺序:父类构造器 --> 子类构造器
24、scala的抽象类?
-
定义抽象类:通过abstract关键字标记抽象类
abstract class Person {}
-
定义抽象属性:一个属性没有初始化,就是抽象属性
val/var name: String
-
定义抽象方法:只声明而没有实现的方法,就是抽象方法
def hello(): String
25、scala的内部类访问的方法?
- 通过new 外部类对象.内部类 创建内部类对象
- 通过外部类暴露一个内部类对象的方法
26、scala object特点?
对象都是单例静态的
27、scala伴生类和伴生对象?
Scala语言是完全面向对象的语言,所以并没有静态的操作(即在Scala中没有静态的概念)。但是为了能够和Java语言交互(因为Java中有静态概念),就产生了一种特殊的对象来模拟类对象,该对象为单例对象。若单例对象名与类名一致,则称该单例对象这个类的伴生对象,这个类的所有静态内容都可以放置在它的伴生对象中声明。
- 单例对象采用object关键字声明
- 单例对象对应的类称之为伴生类,伴生对象的名称应该和伴生类名一致
- 单例对象中的属性和方法都可以通过伴生对象名(类名)直接调用访问
28、scala的apply?
- 通过伴生对象的apply方法,实现不使用new方法创建对象
- 如果想让主构造器变成私有的,可以在()之前加上private
- apply方法可以重载
- Scala中obj(arg)的语句实际是在调用该对象的apply方法,即obj.apply(arg)。用以同一面向对象编程和函数式编程的风格
29、scala的特质trait?
Scala语言中,采用特质trait(特征)来代替接口的概念,也就是说,多个类具有相同的特征(特征)时,就可以将这个特质(特征)独立出来,采用关键字trait声明
Scala中的trait中即可以有抽象属性和方法,也可以有具体的属性和方法,一个类可以混入(mixin)多个特质
Scala引入trait特征,第一可以替代Java的接口,第二个也是对单继承机制的一种补充
30、scala的类型检查和类型转换?
-
判断对象是否为某个类型的实例
obj.isInstanceOf[T]
-
将对象转换为某个类型的实例
obj.asInstanceOf[T]
-
获取对象的类名
classOf
31、scala集合
不可变集合:scala.collection.immutable(默认)
可变集合:scala.collection.mutable
31.1 scala 数组
31.2 scala list
数据有顺序(插入、取出有序),可重复
31.3 scala set
数据无序,不可重复
31.4 scala map
和Java类似,也是一个散列表
31.5 scala元组
Map中的兼职对其实就是元组,只不过元组的元素个数为2,称之为对偶
31.6 scala集合常用函数
val list1 = List(1, -4, 7, 13)
val list2 = List(4, 7, 45, 1)
val list3 = List(List(1, 2, 3), List(4, 5, 6), List(7, 8, 9))
val list4 = List("hello world", "hello scala", "hello")
定义 | 代码 | 结果 |
---|---|---|
获取集合长度 | list1.length | 4 |
获取集合大小 | list1.size | 4 |
循环遍历 | list1.foreach(println) | 1 \n -4 \n 7 \n 13 |
生成字符串 | list1.mkString(",") | 1,-4,7,13 |
迭代器 | for(elem <- list1.iterator) {print(elem + “,”)} | 1,-4,7,13 |
是否包含 | list1.contains(3) | false |
获取集合的头 | list1.head | 1 |
获取除第一个数据所有 | list1.tail | List(-4,7,13) |
获取最后一个 | list1.last | 13 |
获取除最后一个数据所有 | list1.init | List(1,-4,7) |
反转 | list1.reverse | List(13,7,-4,1) |
取前(后)n个元素 | list1.take(3) | List(1,-4,7) |
list1.takaRight(3) | List(-4,7,13) | |
去掉前(后)n个元素 | list1.drop(3) | List(13) |
list1.dropRight(3) | List(1) | |
求和 | list1.sum | 17 |
求乘积 | list1.product | -364 |
求最大值 | list1.max | 13 |
求最小值 | list1.min | -4 |
滑窗 | list1.sliding(3, 2).foreach(println(_)) | List(1,-4,7) |
滑动窗口长度是3,步长是2(滑动间隔) | List(7,13) | |
排序:默认式升序 | list1.sortBy(num => num) | List(-4,1,7,13) |
按绝对值大小排序 | list1.sortBy(x => x.abs) | List(1,-4,7,13) |
按元素大小(升)排序 | list1.sortWith((x, y) => x < y) | List(-4,1,7,13) |
按元素大小(降)排序 | list1.sortWith((x, y) => x > y) | List(13,7,1,-4) |
过滤:返回为true都保留 | list1.filter(x => x % 2 == 0) | List(-4) |
转换/映射 | list1.map(x => x + 1) | List(2,-3,8,14) |
分组 | list1.groupBy(x => x % 2) | Map(1 -> List(1,7,13), 0 -> List(-4)) |
并集 | list1.union(list2) | List(1,-4,7,13,4,7,45,1) |
交集 | list1.intersect(list2) | List(1,7) |
差集 | list1.diff(list2) | List(-4,13) |
拉链 | list1.zip(list2) | List((1,4),(-4,7),(7,45),(13,1)) |
扁平化flatten | list3.flatten | List(1,2,3,4,5,6,7,8,9) |
扁平化+映射flatMap | list4.flatMap(x => x.split(" ")) | List((hello, world, hello, scala, hello)) |
Reduce方法 | list1.reduce((x, y) => x + y) | 17 |
list1.reduceLeft((x, y) => x + y) | 17 | |
Fold折叠 | list1.foldLeft(0)(_ + _) | 17 |
list1.foldRight(0)(_ - _) | -1 |
拉链:如果两个集合的元素个数不相等,那么会将同等数量的数据进行拉链,多余的数据省略不用
Reduce方法:通过指定的逻辑将集合中的数据进行聚合,从而减少数据,最终获取结果
Fold折叠:化简得一种特殊情况,使用了函数柯里化,存在两个参数列表,fold底层是foldLeft
两个集合合并:
val map1 = mutable.Map("a"->1, "b"->2, "c"->3)
val map2 = mutable.Map("a"->4, "b"->5, "d"->6)
val map3 = map1.foldLeft(map2) {
(map, kv) => {
val k = kv._1
val v = kv._2
map(k) = map.getOrElse(k, 0) + v
map
}
}
println(map3)
打印结果:
Map(b->7, d->6, a->5, c->3)
32、scala的队列?
scala也提供了队列(Queue)的数据结构,队列的特点就是先进先出
进队和出队的方法分别为enqueue和dequeue
33、scala的并行集合?
Scala为了充分使用多核CPU,提供了并行集合(有别于前面的串行集合),用于多核环境的并行计算
34、scala模式匹配?
Scala中的模式匹配类似于Java中的switch语法,但是更加强大
- 如果所有case都不匹配,那么会执行case_ 分支,类似于Java中default语句,若没有case _ 分支,那么会抛出MatchError
- 每个case中,不用break语句,自动中断case
- match case语句可以匹配任何类型,而不只是字面量
- => 后面的代码块,是作为一个整体执行,可以使用{}括起来,也可以不括
35、scala样例类特点?
case class Person (name: String, age: Int)
- 样例类仍然是类,和普通类相比,只是其自动生成了伴生对象,并且伴生对象中自动提供了一些常用的方法,如apply、unapply、toString、equals、hashCode和copy
- 样例类是为模式匹配而优化的类,因为其默认提供了unapply方法,因此,样例类可以直接使用模式匹配,而无需自己实现unapply方法
- 构造器中的每一个参数都成为val,除非它被显式地声明为var(不建议这样做)
case中对象的unapply方法(提取器)返回Some,且所有属性均一致,才算匹配成功,属性不一致,或返回None,则匹配失败
- 若只提取对象的一个属性,则提取器为
unapply(obj: Obj): Option[T]
- 若提取对象的多个属性,则提取器为
unapply(obj: Obj): Option[(T1, T2, T3…)]
- 若提取对象的可变个属性,则提取器为
unapplySeq(obj: Obj): Option[Seq[T]]
36、scala偏函数?
偏函数也是函数的一种,通过偏函数我们可以方便的对输入参数做更精确的检查。例如该偏函数的输入类型为List[Int],而我们需要的是第一个元素是0的集合,这就是通过模式匹配实现的
注:该偏函数的功能是返回输入的List集合的第二个元素
37、scala异常?
- 将可疑代码封装在try块中。在try块之后使用了一个catch处理程序来捕获异常。如果发生任何异常,catch处理程序将处理它,程序将不会异常终止
- Scala的异常的工作机制和Java一样,但是Scala没有checked(编译期)异常,即Scala没有编译异常这个概念,异常都是在运行的时候捕获处理
- 异常捕捉的机制与其他语言中一样,如果有异常发生,catch子句是按次序捕捉的。因此,在catch子句中,越具体的异常越要靠前,越普遍的异常越靠后,如果把越普遍的异常写在前,把具体的异常写在后,在Scala中也不会报错,但这样是非常不好的编程风格
- finally子句用于执行不管是正常处理还是有异常发生时都需要执行的步骤,一般用于对象的清理工作,这点和Java一样
- 用throw关键字,抛出一个异常对象。所有异常都是Throwable的子类型。throw表达式是有类型的,就是Nothing,因为Nothing是所有类型的子类型,所以throw表达式可以用在需要类型的地方
- Scala提供了throws关键字来声明异常。可以使用方法定义声明异常。它向调用者函数提供了此方法可能引发此异常的信息。它有助于调用函数处理并将该代码包含在try-catch块中,以避免程序异常终止。在Scala中,可以使用throws注释来声明异常
38、scala隐式转换那些,有什么特点?
隐式转换可以再不需改任何代码的情况下,扩展某个类的功能
-
隐式参数
普通方法或者函数可以通过implicit关键字声明隐式参数,调用该方法时,就可以传入该参数,编译器会再相应的作用域寻找符合条件的隐式值
- 同一个作用域中,相同类型的隐式值只能有一个
- 编译器按照隐式参数的类型去寻找对应类型的隐式值,与隐式值的名称无关
- 隐式参数优先于默认参数
-
隐式类
在Scala2.10后提供了隐式类,可以使用implicit声明类,隐式类的非常强大,同样可以扩展类的功能,在集合中隐式类会发挥重要的作用
- 其所带的构造参数有且只能有一个
- 隐式类必须被定义在类或伴生对象或包对象里,即隐式类不能是顶级的
-
隐式解析机制
- 首先会在当前代码作用域下查找隐式实体(隐式方法、隐式类、隐式对象)(一般是这种情况)
- 如果第一条规则查找隐式实体失败,会继续在隐式参数的类型的作用域里查找。类型的作用域是指与该类型相关联的全部伴生对象以及该类型所在包的包对象
39、scala的泛型?
-
协变和逆变
-
协变:Son是Father的子类,则MyList[Son] 也作为MyList[Father]的子类
class MyList[+T] {}
-
逆变:Son是Father的子类,则MyList[Son]作为MyList[Father]的父类
class MyList[-T] {}
-
不变:Son是Father的子类,则MyList[Father]与MyList[Son]无父子关系
class MyList[T] {}
-
-
泛型上下界
泛型的上下限的作用是对传入的泛型进行限定
-
泛型上限
Class PersonList[T <: Person] {}
-
泛型下限
Class PersonList[T >: Person] {}
-
40、scala 的上下限定?
上下问限定是将泛型和隐式转换的结合产物,以下两者功能相同,使用上下文限定[A : Ordering]之后,方法内无法使用隐式参数名调用隐式参数,需要通过implicitly[Ordering[A]]获取隐式变量
def f[A: B](a: A) = println(a)
等同于
def f[A](a:A) (implicit arg: B[A]) = println(a)
41、WordCount
/**
* 单词计数:将集合中出现的相同的单词,进行计数,取计数排名前二的结果
*/
object WordCount {
def main(args: Array[String]): Unit = {
val stringList = List("hello scala hbase", "hello scala", "hello")
// 1.将每一个字符串转换成一个一个的单词
val wordList:List[String] = stringList.flatMap(str => str.split(" "))
println(wordList)
// 2.将相同的单词放置在一起
// 在map中,如果传进来什么就返回什么,不要用_省略
val wordsMap:Map[String, List[String]] = wordList.groupBy(word => word)
println(wordsMap)
// 3.对相同的单词进行计数
// (word, list) => (word, count)
val wordCount:Map[String, Int] = wordsMap.map(tuple => (tuple._1, tuple._2.size))
println(wordCount)
// 4.对计数完成后的结果进行排序(降序)
val sortList:List[(String, Int)] = wordCount.toList.sortWith((left, right) => left._2 > right._2)
println(sortList)
// 5.对排名后的结果取前2名
val resultList:List[(String, Int)] = sortList.take(2)
println(resultList)
// 以上所有代码可以简化成
stringList.flatMap(_.split(" ")).groupBy(word => word).map(tuple =>(tuple._1, tuple._2.size)).toList.sortBy(_._2).reverse.take(2).foreach(println(_))
println("-----------------------------")
// 复杂类型
val tupleList = List(("Hello Scala World ", 3), ("Hello Scala", 2), ("Hello", 1))
val stringList1:List[String] = tupleList.map(t => t._1 * t._2)
println(stringList1)
val words:List[String] = stringList1.flatMap(_.split(" "))
println(words)
}
}