Scala介绍
java和scala关系
- java — JVM
xxx.java —javac— yyy.class ----- java
真正在jvm中能够运行的是.class字节码文件
Scala之父:Martin Odersky 马丁·奥德斯基 - Scala也是JVM系的语言。
xxx.scala —scalac ---- yyy.class ---- scala
scala和java是可以混合编程。 - scala底层用到的还是java,只是在java上套了一层壳,由于scala最终被编译为.class 所以其实本质上还是java 所以在scala中可以任意的调用java的api
scala的多范式编程
- Scala将面向对象和函数式编程结合成一种简洁的高级语言。
- Scala的静态类型有助于避免复杂应用程序中的错误,java也是静态类型的。
静态类型:指的就是类型一旦确定就不能更改
int a = 10;//a是int类型的
a = "java";//error
面向对象编程
- 面向对象:不断创建对象、使用对象完成功能
- java不是纯面向对象的。基本数据类型、静态
- scala是纯面向对象的。一切皆对象。没有基本数据类型
函数式编程
函数式编程:将函数提升成“一等公民”,函数也是对象,函数就可以当作参数和返回值。
Scala下载和安装
下载地址:https://www.scala-lang.org/download/all.html,我安装的是kafka_2.11-1.0.2.tgz
安装的前提条件:
- 确保安装了JDK8。
- 安装路径不允许出现中文和特殊字符。
- 安装完成后配置SCALA_HOME的环境变量
表达式
表达式是可计算的语句。
scala> 1 + 1
res1: Int = 2
数据类型
在Scala中,所有的值都有类型,包括数值和函数。下图 阐述了类型层次结构的一个子集。
Scala类型层次结构
Any
- Any是所有类型的超类型,也称为顶级类型。
- 它定义了一些通用的方法如equals、hashCode和toString。
- Any有两个直接子类:AnyVal和AnyRef。
AnyVal
- AnyVal代表值类型。
- 有9个预定义的非空的值类型分别是:Double、Float、Long、Int、Short、Byte、Char、Unit和Boolean。
- Unit是不带任何意义的值类型,它仅有一个实例可以像这样声明:()。
- 所有的函数必须有返回,所以说有时候Unit也是有用的返回类型。
AnyRef
- AnyRef代表引用类型。
- Scala中除了AnyVal外,都是AnyRef类型
- 在Scala中,每个用户自定义的类型都是AnyRef的子类型。如果Scala被应用在Java的运行环境中,AnyRef相当于java.lang.Object。
Nothing
- Nothing是所有类型的子类型,也称为底部类型。
- 没有一个值是Nothing类型的。它的用途之一是给出非正常终止的信号,如抛出异常、程序退出或者一个无限循环(可以理解为它是一个不对值进行定义的表达式的类型,或者是一个不能正常返回的方法)。
Null
- Null是所有引用类型的子类型(即AnyRef的任意子类型)。
- 它有一个单例值由关键字null所定义。
- Null主要是使得Scala满足和其他JVM语言的互操作性,但是几乎不应该在Scala代码中使用。
类型转换
变量和常量
scala中使用var修饰变量;使用val修饰常量
-
java中定义变量的格式:
数据类型 变量名;
-
java中变量的初始化:
数据类型 变量名 = 初始值; 或: 数据类型 变量名; 变量名 = 初始值;
-
scala中定义变量及初始化
// 方式一:使用了类型推断 var 变量名 = 值 scala> var age = 10 age: Int = 10 // 方式二:显式指定数据类型 var 变量名:数据类型 = 值 scala> var name:String ="zhangsan" name: String = zhangsan // 方式三:显式指定数据类型 var 变量名 = 值:数据类型 scala> var sum = 20:Double sum: Double = 20.0
注意:scala语句后面可以不要分号;除非一行上有 多条语句。
-
scala中定义常量
常量只能赋值一次。
格式和变量一样,仅仅是将var变成val即可。
类型推断
scala定义变量的时候可以省略类型,scala编译器可以根据变量的值推断出变量的数据类型。但是在有些情况下类型是不能省略的。
IDEA中编写Scala代码
-
在创建的module的main下创建一个scala文件夹
-
右键scala – Mark directory as — Source root
-
在创建好的module上右键-- add framework support – 勾选scala
注意:如果没有出现scala sdk,选择create — browse— scala安装目录添加即可。
代码块(Blocks)
用{}包围起来。我们称之为代码块(block)。
代码块中最后一个表达式的结果就是整个代码块的结果。
println({
val x = 1 + 1
x + 1
}) // 3
运算符
scala> var a = 1
a: Int = 1
scala> var b = a + 3
b: Int = 4
scala> var b = a.+(3)
b: Int = 4
在scala中一切皆对象,所以上面代码中的1是对象,a也 是对象。按照我们面向对象的知识,对象访问的是属性或方法。
所以,在scala中的运算符(=除外)其实是方法。
-
*运算符
var name:String = "java" println(name * 2) // javajava
-
scala中没有++和–
-
==
var s1:String = new String("java") var s2:String = new String("java") println(s1 == s2) //true println(s1.equals(s2)) //true
-
+=、-=
var a:Byte = 2 a += 3 println(a) //error //scala中+=等符号没有做类型转换
流程控制
if语句
在scala中if语句是有返回值的。
返回值就是if代码块最后一条语句的结果
注意: scala中没有switch语句。但是提供了比switch更加强大的模式匹配。
for循环
scala> 1 to 10
res4: scala.collection.immutable.Range.Inclusive = Range(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)
scala> 1 until 10
res7: scala.collection.immutable.Range = Range(1, 2, 3, 4, 5, 6, 7, 8, 9)
-
循环输出1-10的所有数
//循环输出1-10的所有数 for(i <- 1 to 10){ println(i) } println("-------------------") //循环输出10 - 1的所有数 for(i <- 10.to(1,-1) ){ println(i) }
for循环守卫
//这里的if叫做for循环的守卫
for (i <- 1 to 10 if(i % 2 == 0)){
println(i)
}
for…yield
作用:就是遍历每一个元素,执行yield中的逻辑,将得到的结果存在集合中
//得到1-10的所有数乘以2的结果
/**
* for...yield
* 作用:就是遍历每一个元素,执行yield中的逻辑,将得到的结果存在集合中
*/
val ints: immutable.IndexedSeq[Int] = for (i <- 1 to 10) yield i * 2
for循环嵌套
for(i <- 1 to 9;j <- 1 to i){
print(j + "*" + i + "=" + (i * j) +"\t")
if(i == j){
println()
}
}
while循环
def main(args: Array[String]): Unit = {
//循环输出1-10的数字
var i = 1
while(i <= 10){
println(i)
i+=1
}
}
do…while循环
def main(args: Array[String]): Unit = {
//循环输出1-10的数字
var i = 1
do {
println(i)
i+=1
}while(i <= 10)
}
循环中断
scala中没有break、continue关键字
-
scala实现java中break的效果:
def main(args: Array[String]): Unit = { //循环输出1-10 //当数据等于5的时候中断循环 val loop = new Breaks() loop.breakable{ //捕获异常 for(i <- 1 to 10){ if( i == 5) { loop.break() //抛出异常 } println(i) } } }
-
scala实现java中continue的效果:
//使用for循环守卫 for(i <- 1 to 10 if(i != 5)){ println(i) }
也可以用break实现
def main(args: Array[String]): Unit = { //循环输出1-10 //当数据等于5的时候中断循环 val loop = new Breaks() for(i <- 1 to 10){ loop.breakable{ //捕获异常 if( i == 5) { loop.break() //抛出异常 } println(i) } } }
方法
在Java中方法和函数一回事。但是在scala中方法是方 法,函数是函数,方法和函数有区别。
- scala中方法使用def定义
- scala中方法是类的一部分
- scala中函数使用val修饰
- scala中函数是对象
格式
def 方法名(参数名:参数类型):返回值类型={
方法体
}
// 入口
def main(args: Array[String]): Unit = {}
//定义一个方法,输出hello, scala
/**
* :返回值数据类型 省略,scala使用类型推断
* 递归方法不能省略 :返回值数据类型
*
* :返回值数据类型= 返回值类型就是Unit
*/
def m01() = {
println("hello, scala")
}
Scala支持方法重载
Scala方法可以嵌套
Scala无参方法调用的时候可以省略()
def main(args: Array[String]): Unit = {
println(add(2,3))
def add(a:Int,b:Int):Int={
a + b
}
}
可变参数
参数名:数据类型 *
def main(args: Array[String]): Unit = {
//:_* 得到集合中的所有元素
val sum = add(1 to 10 :_*)
println(sum)
}
//可变参数
def add(a:Int *):Int={
var sum = 0
for(i <- a){
sum += i
}
sum
}
字符串插值器
https://docs.scala-lang.org/zh-cn/overviews/core/string-interpolation.html
自2.10.0版本开始,Scala提供了一种新的机制来根据数据生成字符串:字符串插值。字符串插值允许使用者将变量引用直接插入处理过的字面字符中。
val name="James"
println(s"Hello,$name")//Hello,James
Scala 提供了三种创新的字符串插值方法:s,f 和 raw.
s 字符串插值器
在任何字符串前加上s,就可以直接在串中使用变量了
字符串插值器也可以处理任意的表达式。例如:
println(s"1+1=${1+1}") 将会输出字符串1+1=2。任何表达式都可以嵌入到${}中。
f 插值器
在任何字符串字面前加上 f,就可以生成简单的格式化串,功能相似于其他语言中的 printf 函数。当使用 f 插值器的时候,所有的变量引用都应当后跟一个printfstyle格式的字符串,如%d。
val height=1.9d
val name="James"
println(f"$name%s is $height%2.2f meters tall")//James is 1.90 meters tall f 插值器是类型安全的。如果试图向只支持 int 的格式化串传入一个double 值,编译器则会报错。例如:
val height:Double=1.9d
scala>f"$height%4d"
<console>:9: error: type mismatch;
found : Double
required: Int
f"$height%4d"
^ f 插值器利用了java中的字符串数据格式。这种以%开头的格式在 [Formatter javadoc] 中有相关概述。如果在具体变量后没有%,则格式化程序默认使用 %s(串型)格式。
raw 插值器
除了对字面值中的字符不做编码外,raw 插值器与 s 插值器在功能上是相同的。如下是个被处理过的字符串:
scala>s"a\nb"
res0:String=
a
b //这里,s 插值器用回车代替了\n。而raw插值器却不会如此处理。
scala>raw"a\nb"
res1:String=a\nb 当不想输入\n被转换为回车的时候,raw 插值器是非常实用的。
默认参数值
Scala具备给参数提供默认值的能力,这样调用者就可以忽略这些具有默认值的参数。
def log(message: String, level: String = "INFO") = println(s"$level: $message")
log("System starting") // prints INFO: System starting
log("User not found", "WARNING") // prints WARNING: User not found
命名参数
- 当调用方法时,实际参数可以通过其对应的形式参数的名称来标记:
- 注意使用命名参数时,顺序是可以重新排列的。 但是,如果某些参数被命名了,而其他参数没有,则未命名的参数要按照其方法签名中的参数顺序放在前面。
- 注意调用 Java 方法时不能使用命名参数。
def printName(first: String, last: String): Unit = {
println(first + " " + last)
}
printName("John", "Smith") // Prints "John Smith"
printName(first = "John", last = "Smith") // Prints "John Smith"
printName(last = "Smith", first = "John") // Prints "John Smith"
def main(args: Array[String]): Unit = {
m03(age=20, name="zhangsan",sex="male")
m03( "zhangsan",age=20,"male")
}
def m03(name:String,age:Int,sex:String)={
println(s"$name,$age,$sex")
}
函数
Scala中函数也是对象。
Scala中的函数是继承了Function0—Function22特质的对象
函数定义
// ()=>函数体
scala> ()=>println("scala") //匿名函数,无参,返回值是Unit
res10: () => Unit = <function0> // 函数的数据类型
// () => Unit 该函数具体的数据类型
//function0 该函数具体数据类型的特质
def main(args: Array[String]): Unit = {
//val age = 10
val f01 = () => println("hello")
//val age:Int = 10
val f02:()=>Unit = () => println("hello")
//val age = 10:Int
val f03 = (() => println("hello")):()=>Unit
f01 //获取的是函数的信息
f01()//调用函数
}
注意:
1、无参方法调用的时候可以省略()
2、无参函数调用的时候不能省略()
def main(args: Array[String]): Unit = {
val f01 = (s:String) => println(s)
// val f02 = (a:Int,b:Int) => a + b
// val f02:(Int,Int)=>Int = (a:Int,b:Int) => a + b
// val f02:(Int,Int)=>Int = (a,b) => a + b
//如果函数参数在函数体中只使用了一次,可以使用_替换(暂时这么记忆,这个不完全正确)
val f02:(Int,Int)=>Int = _ + _
//val f02 = _ + 2
// val f02:Int=>Int = _+2
// val f02:Function2[Int,Int,Int] = (a,b) => a + b
println(f02)
println(f02(1,2))
}
函数作为参数
/**
* 定义一个方法实现两个Int数据的任意运算
* 也就是说这个方法既可以做加法、也可以减法、乘法等其他运算
*
* 分析:
* 1、参数的定义
* 接收任意两个Int数据的参数:a,b
* 这个方法需要满足做任意运算,那么在定义方
法的时候需要定义一个参数来接收任意的运算
* 运算不是单一的值,而是一个表达式。在Java中运算都是定义方法
* 在Scala中运算可以是方法、可以是函数
* 所以在这个需要作为参数传递,我们定义一个参数接收函数
*/
object FunctionDemo02 {
def main(args: Array[String]): Unit ={
// val f = (a:Int,b:Int)=>a + b
// println(m01(1,2,f))
// println(m01(1,2,(a:Int,b:Int)=>a + b))
// println(m01(1,2,(a,b)=>a + b))
println(m01(1,2,_+_))
println(m01(1,2,_-_))
println(m01(1,2,_*_))
println(m01(1,2,_/_))
println(m01(1,2,_%_))
}
def m01(a:Int,b:Int,f:(Int,Int)=>Int):Int ={
f(a,b)
}
}
方法转函数
object FunctionDemo04 {
def main(args: Array[String]): Unit = {
//add方法 自动转成了函数
println( m01(1,2,add))
//add方法 手动转函数
// println( m01(1,2,add _))
val f01:(Int,Int)=>Int = add
println(m01(2,3,f01))
}
def m01(a:Int,b:Int,f:(Int,Int)=>Int):Int ={
f(a,b)
}
def add(a:Int,b:Int):Int={
a + b
}
}
函数作为返回值
package com.bigdata.scala02
object FunctionDemo05 {
def main(args: Array[String]): Unit ={
// val f = m01
// val f = m01()
// println(f(1,2))
// val f01 = m02(1,2)
// (Int,Int) => ()=>Int
val f01:(Int,Int) =>()=>Int = m02
println(f01(1,2)())
// val f01:()=>Int = m02(1,2)
// println(f01())
}
def m01():(Int,Int)=>Int ={
_ + _
}
def m02(a:Int,b:Int):()=>Int ={
val f:()=>Int = ()=> a + b
f
}
}
惰性变量
被lazy修饰的val变量就是惰性变量。
比如数据库的连接就可以使用惰性变量。
//懒加载
object LazyVarDemo {
def main(args: Array[String]): Unit ={
lazy val num = m01()
println(num)
}
def m01():Int={
println("m01------------")
10
}
}
type
在scala中可以使用type给类、特质取别名
type String = java.lang.String
type xx = Int //取别名
var num:xx = 20
神奇的下划线_
-
:_*
获取集合一个一个的元素_package com.bigdata.scala01 object MethodDemo02 { def main(args: Array[String]): Unit = { //:_* 得到集合中的所有元素 val sum = add(1 to 10 :_*) println(sum) } //可变参数 def add(a:Int *):Int={ var sum = 0 for(i <- a){ sum += i } sum } }
-
_
如果函数参数在函数体中只使用了一次,可以使用_替换 -
方法转函数: 方法名 _
package com.bigdata.scala02 /** * 定义一个方法实现两个Int数据的任意运算 * 也就是说这个方法既可以做加法、也可以减法、乘法等其他运算 * * 分析: * 1、参数的定义 * 接收任意两个Int数据的参数:a,b * 这个方法需要满足做任意运算,那么在定义方法的时候需要定义一个参数来接收任意的运算 * 运算不是单一的值,而是一个表达式。在Java中运算都是定义方法 * 在Scala中运算可以是方法、可以是函数 * 所以在这个需要作为参数传递,我们定义一个参数接收函数 * * */ object FunctionDemo04 { def main(args: Array[String]): Unit = { //add方法 自动转成了函数 println( m01(1,2,add)) //add方法 手动转函数 // println( m01(1,2,add _)) val f01:(Int,Int)=>Int = add println(m01(2,3,f01)) } def m01(a:Int,b:Int,f:(Int,Int)=>Int):Int ={ f(a,b) } def add(a:Int,b:Int):Int={ a + b } }
-
成员变量初始化的时候作为占位符
class Student { //成员变量 var name:String = _ var age:Int = _ var sex:String = _ }
-
导入包下所有的内容,使用_
// _相当于Java中的*,scala可以导入类的属性和方法 import com.bigdata.demo01._
_的注意事项
def main(args: Array[String]): Unit = {
// val f01:(Int,Int)=>Unit = (a,b)=>println(a + b)
//_单独使用就是一个值,不作为表达式
//_运算的时候是需要进行函数展开的,遇见()也会进行函数展开
val f01:(Int,Int)=>Int = _ + _ + 3
// _ + _ + 3 == (a:Int,b:Int)=>a + b + 3
//(_ + _) + 3 == ((a:Int,b:Int)=>a + b) + 3
println(f01(1,2))
}