Scala之父——马丁.奥德斯基
Scala将面向对象编程、函数式编程和强大的类型系统结合起来,让人能写出优雅、简洁的代码。
表达式、语句和代码块与Java一样,还有类、包和引用的语法。除语法之外,Scala还采用了Java的其他元素,如它的基本类型、类库和它的执行模式。函数式编程借鉴了SML,OCaml,和F#为代表的ML家族语言很接近,Scala的隐式参数灵感来自Haskell,基于actor的并发库来自EeLang的思想。
面向对象:Scala中的每一个值都是一个对象,包括基本数据类型在内,连函数也是对象。
函数式编程:Scala是一种函数式语言,函数也能当成值来使用。Scala提供了轻量级的语法以定义匿名函数,支持高阶函数,允许嵌套多次函数,并支持柯里化。Scala的case class(样例类)及其内置的模式匹配相当于函数式编程语言中常用的代数类型。
静态类型:Scala具备类型系统。
类型系统支持以下特性:
扩展性:Scala提供了许多独特的语言机制,可以以库的形式轻易无缝添加新的语言结构:
动态性:Scala使用Actor作为其并发模型,Actor是类似线程的实体,通过邮箱收发消息。Actor可以复用线程,因此可以在程序中可以使用数百万个Actor,而线程只能创建数千个。在2.10之后的版本中,使用Akka作为其默认Actor实现。
函数式编程是一种“编程范式”
函数式编程的一个特点是,函数也是值,同允许把函数本身作为参数传入另一函数,还允许返回一个函数!
静态编程语言:实现声明变量类型,类型不能改变,编译时检查
动态编程语言:不用事先声明类型,随时可以赋值为其他类型,编程时不知道什么类型,很难推断
强类型语言:不同类型之间操作,必须强制类型转换为同⼀类型
弱类型语言:不同类型间可以操作,自动隐式转换
Scala解释器也被称为REPL交互式编码环境
REPL:Read(取值)-> Evaluation(求值)-> Print(打印)-> Loop(循环)。
符号$
在Scala中可以看作字母,避免使用$
开始的标识符,以免造成冲突。
val修饰的变量,相当于Java中final修饰的变量;
val s1 = "1"//定义常量s1,使⽤字符串"1"赋值,⾃动推断为String类型,值不可变
val s2:String = "2"//定义常量s2,⼿动指定类型为String,此时需要保证所赋值类型匹配
//使⽤val定义基本数据类型时,值不可变,可以使⽤val重新定义
变量声明一定要初始化
含义:声明变量时,可以不指定变量类型,编译器会根据赋值内容自动推断当前变量的类型。
var i1 = 1
print(i1.isInstanceOf[Int])//定义变量a1,使⽤1赋值,⾃动推断为Int类型
i1 = 1.1类型确定后,就不能再赋值的类型
val(a,b,c) = (1,2,"a")//Scala中的多个变量的初始化
var i3,i4 = 10;//定义变量i3,i4,同时赋值为10
使用val声明⼀个常量或值:val修饰的变量是不可变的,注意不可变的是引用,而不是内容 ,val修饰的变量在编译后,等同于加上final。
使用var 声明⼀个变量:var修饰的变量,内容和引用都可变
只有val修饰的变量才能被lazy修饰,使用lazy定义变量后,只有在调用该变量时才会实例化这个变量的值,类似方法,先声明,后调用。
scala> val a = 10
a: Int = 10
scala> lazy val b = 100
b: Int = <lazy>
scala> b
res2: Int = 100
使用val OR var?
官方推荐val,使用val的好处:
Any:所有类型的父类
它定义了⼀些通用的方法如equals、hashCode和toString。Any有两个直接子类:AnyVal和AnyRef。
AnyVal:所有值类型的父类
有9个预定义的非空的值类型分别是:Double、Float、Long、Int、Short、Byte、Char、Unit和Boolean。Unit是不带任何意义的值类型,它仅有⼀个实例可以像这样声明:(),和Java不同的是 ,Scala没有基本类型与包装类型之分,这些类型都是类,有自己的属性和方法。
AnyRef:所有引用类型的父类
AnyRef代表引用类型。所有非值类型都被定义为引用类型。包括List、String以及自定义类等。
Nothing:所有类型的子类
常见的应用如:抛出异常、程序exit,无限循环等,返回值为Nothing类型。
Nothing没有对象,但是可以用来定义类型。
自动类型转换
//自动类型转换属于Scala中隐式转换
val b:Byte = 10;
val s:Short = b;
val i:Int = s;
val l:Long = i;
//Scala中允许将小数据类型范围数据赋值给大范围数据类型变量存储
当Char转整型时:
//当Char类型转换为整数时需要注意:
val b1:Byte = 'a'; //英文字母是1个字节
/*
Error:(11, 21) type mismatch; found : Char('我')
required: Byte val b2:Byte = '我';
中文是2个字节所以无法存在Byte中
*/
//val b2:Byte = '我';
//提供大于等于2个字节数据类型是可以存的
var s1:Short = '我'
小结:
低位数可以向高位数数转换。如Byte->Short
字符可以转整形或浮点型,不同长度字符应使用不同位数的整型
整型可以转浮点型,允许精度丢失。如Long->Float
强制类型转换
//Scala中提供一个 toXXX 的方法 XXX就是值类型 就可以完成强制类型转换
var i1:Int = 10;
var b3:Byte = i1.toByte;
b3.isInstanceOf[Byte] //检查b3是否属于Byte类型【得到结果true/false】
var d:Double = 19.9;
var i2:Int = d.asInstanceOf[Int]; //相当于d强制类型转换为int类型存储在i2变量中
数值类型转换为字符串类型
//将1.23和1转换为字符串类型
val str1 = 1.23+"";
val srr2 = 1.toString;
字符串类型转数值类型
val d2 = str1.toDouble
val i3 = str2.toInt
实际上Scala中没有操作符
a + b 等价于 a.+(b)
通过Int类型变量a调用了Int中提供+方法,方法参数是b求和操作
在Scala中 + 是一个方法 所以Scala中就允许使用 数据类型对应数据进行方法调用操作
Scala没有++,-- 可以用+=,-=代替
操作符都是方法的重载,是方法的调用
表达式:一段可以被求值的代码,在Scala中一切都是表达式
ps:Scala不会像Java一样单纯使用运算符和变量或常量组成表达式
//表达式与语句的区别
//Scala中的表达式与Java中的表达式共同共享一个概念【常量、变量、运算符组成】
var sum = 1+2;//ps:是一行语句
/*
在Scala中一切都是表达式
ps:提供块表达式的说明{}中提供一行或多行代码总体称之为块表达式
*/
val bool = if(true){
true
}else{
false
}
块表达式
//块表达式只需提供{},在{}中提供代码
var i = { //最后一个表达式即为返回值,不需要使用return
0 //等价于var i= 0 这个0是返回赋值给i这个变量
}
//添加return语句,除非自定义方法、函数中,使用return作为返回值使用,不建议单独使用
// var sum:Int ={
// return 1+2
// }
var sum :Int ={
1+2
}
//简化:var sum = 1+2
//块表达式中可以提供一条或多条语句【一个计算逻辑,需要得到结果】
var num1=1
var sum1 ={
num1 = num1+1
num1
}
println("sum1="+sum1)
/*
等价于
var num1 = 1
num1= num1+1
var sum = num1
*/
var sum2 = {
num1 = num1 + 1
}
println("sum="+sum2)
/*
scala中的分支只有if分支
没有switch...case,Scala中提供强大的“匹配模式”
基础应用效果就是和Java一样提供分支语句判断
*/
//判断是不是闰年
val year = 2000
if((year%4==0&&year%100!=0)||(year%400==0)){
println("闰年")
}else{
println("不是闰年")
}
//Scala中一切皆为表达式
val res:Boolean = if((year%4==0&&year%100!=0)||(year%400==0)) true else false
//scala分支语句返回值特点1:
//使用if并进行返回值接收
val x = 1
/*
if 单分支语句缺少false判断结果
通用语句-->if(判断条件)返回值else()
*/
val res2:AnyVal = if(x>2) 1
/*
如果判断语句返回语句中有引用类型AnyRef和值类型AnyVal-->得到返回值类型就是Any
*/
val res3 = if(x.isInstanceOf[Int])"11" else 0
//scala分支语句返回值特点2:
//使用if- else 语句或 if-else if语句,返回值类型都是一样,确定一个返回值类型
val res4:Int = if(x>2) 1 else -1
/*
如果返回值不是引用数据类型【即数据类型不一致】
如果确定与值类型有关即存在Unit-->AnyVal 如果是 引用类型和值类型-->Any
*/
val res5:Double = if(x>2) 1 else 1.3234//不需要Anyval double就可以
/*
PS:Scala中 if分支语句不仅具有分支语句特点(选择性执行)
而且也是Scala中表达式 即 可以得到Scala中计算结果
*/
/*
以Scala中if分支条件表达式来进行演示,分支语执行流程
参考Java中分支语句即可
*/
// if单分支
val x = 1;
val res = if( x > 2) 1;
/*
如果使用 if 单分支 作为 条件表达式计算时
如果语句缺少为false时的结果,那么Scala会将if单分支判断看做
if(x>2) 1 else ()
if单分支是缺少else Scala会进行默认 不全 返回的是
() 即Unit类型
所以此时res的数据类型就是 AnyVal
如果if提供返回数据是引用类型(AnyRef) 最终结果数据类型就是Any
*/
//因为默认不全之后是Any或AnyVal类型所以建议得到数据之后进行转换操作以保证数据可以正确执行
val res1 = if(res.isInstanceOf[Int]) res.asInstanceOf[Int] else 0;
println(res1)
//当条件表达式成立,即执行代码块1,否则执行代码块2.
//if-else 分支
val x1 = 2;
/*
因为显示的补全了else操作 所以这个类型是可控
*/
val res2 = if (x > 0) 2 else 1;
val x2 = 2;
val res3 = if (x > 0) 2 else if(x >=1) 1 else 0;
与Java中基本for循环是不一样的和foreach循环相似,Scala独有
//for循环
/*
语法:
for(i <- 范围|集合|数组){
ps:i这个变量就会获得 范围|集合|数组 中每一个元素进行操作
}
*/
//提供范围,简化为 to (包含)until(不包含)
//根据range提供范围执行循环操作
for(i <- Range(1,6)){
println(i)
}
//如果需要精准控制循环范围 可以使用 to和until(不包含)
for(i <- 1 to 5){
println(i)
}
for(i <- 1 until 5){
println(i)
}
//嵌套循环--》标准循环嵌套
for(i <- 1 to 9){
for(j <- 1 to i){
print(j+"x"+i+"="+i*j+"\t")
}
println()
}
//for循环提供嵌套,写到同一个小括号使用【;】
for(i <- 1 to 9;j <- 1 to i){
print(j+"x"+i+"="+i*j+"\t")
}
//scala中允许在for循环小括号中添加“守卫” 就是添加循环判断条件
/*
循环守卫:循环时 可以增加条件判断来决定是否执行循环,这个判断条件就是守卫
*/
//循环1-5之间的数据,如果当数据不为3的时候打印结果
for(i <- 1 to 5){
if(i!=3){
print(i)
}
}
//scala将if判断改变语句到小括号中称之为“守卫”
for(j <- 1 to 5;if(j!=3)){
print(j)
}
//yield 被称为for循环推导式 可以将for结果通过yield关键字后面提供 表达式计算 得到数组
//ps:只有循环使用yield关键字之后 我们才会进行循环返回值的接收
val ints = for (i <- 1 to 5) yield i * 2
for(i <- ints){
println(i)
}
//1 to 5 每次自增效果都是1,2,3,4,5 控制增长 步长
//Range 设置第三参数 Range(开始位置,到达位置,步长)
for(i <- Range(1,5,2)){
println(i)
}
//to 模式 使用by关键字的后面添加步长
for(i <- 1 to 5 by 2){
println(i)
}
//Scala中while循环和do-while循环和Java中是一样的
//使用while循环打印 1~10之间数据
var i = 1;
while(i<=10){
println(i);
i+=1; //Scala中没有++运算符 需要使用是+=
}
//使用do-while循环打印10~1之间的数据
var j = 10;
do{
println(j);
j-=1;//Scala中没有--运算符 需要使用是-=
}while(j>=1);
//循环停止的三种方式
object BreakLookDemo {
//方式1:
def add():Unit = {
for(i <- 1 to 10){
if(i == 7){
return
}
println(i)
}
}
def main(args: Array[String]): Unit = {
//方式2:
var flag = true
var n = 0
while (flag){
n += 1
println(n)
if(n == 10){
flag = false
}
}
//for循环
var flag1 = true
for(i <- 0 to 10 if flag){
println(i)
if(i == 7){
flag1 = false
}
}
//方式3:
import scala.util.control.Breaks._
breakable{
for(i <- 1 to 10){
break
}
}
}
}
方法与函数是两个不同的概念,函数是一个对象,可以赋值给一个变量
类中声明的函数称之为方法
函数没有重载、重写的概念
/*
一般在函数内部,针对方法赋值的位置
提供函数体实现的时候,不提供返回值类型,由Scala自动推断
*/
//说明:f1 函数名 ()=>{} 具体实现(包含参数定义,实现函数逻辑)
val f1= (x:Int,y:Int)=>{x*y}//数据类型+返回值类型=函数类型
//调用函数
f1(1,2)
//函数实现只有一行代码,可以省大括号
val f2 = () => 2+3
//还有一种形态
//val函数名:(数据类型) => 返回值类型 = {(参数名) => 函数的实现}
val f3:(Int) => Int = {(i) => i*100}
//一行代码 简化
val f4:(Int) => Int = i => i*100
//无参无返回值
val f5:() => Unit = () => print("你好")
//定义方法
def method:Unit = print("方法")
//不是函数
val f6=println("输出")
//在源码中提供方法,使用大量函数数据类型作为方法的参数存在
def method2(a:Int):Int=a*a
//将方法手动转化为函数
val f7=method2 _//方法转函数
//调用某个方法,对参数进行传递,传递的是一个方法【Scala自动转】
/*
定义方法:
def 方法名(参数列表):返回值类型 = {
提供方法体
}
ps:方法 函数有区分 类中定义写方法,方法内部实现 写函数
scala中的方法 不仅可以定义在类中 而且可以写方法内容
*/
//在类中定义一个 无返回值无参数的方法
def method1():Unit ={
println("这是一个方法")
}
def main(args: Array[String]): Unit = {
//在类中调用方法【ps:Scala中没有static方法之分】
//调用方法名(有参数提供,无参数则不提供--》需要返回值就接收 不需要接收直接调用即可)
method1()
//定义一个无参有返回值的方法
def method2():String = {
return "kaige"
}
val str:String = method2()
println(method2())
//有参无返回值-->
def method3(username:String,password:String):Unit={
println(username+password)
}
}
//有参有返回值
def method5(username:String):String={
println(username)
return username
}
//方法参数赋值 或默认
def methodShow(message:String="get kaige"):Unit = {
print(message)
}
//使用默认值
methodShow()
methodShow("kaige age is 50")
//默认参数可以给多个,并且对位置没有要求
def methodShow2(a:Int=1,b:Int,c:Int=3):Unit={
println(a+b+c)
}
//调用方法是对参数有要求
//不可以空参调用
//methodShow2(2) 不可以这样赋值 Scala是顺序赋值
methodShow2(b=2)
//同时修改abc
methodShow2(3,4,5)
//顺序赋值针对b 必须对a也进行修改
methodShow2(1,3)
//可变参数--》允许同时传入多个参数
//可变参数必须在所有参数的最后一个位置,赋值时可以是一个数组()但是需要转化
def methodShow3(a:Int*):Unit={//方法中可以当作数组处理
for(i<- a){
println(i)
}
}
//传参的时候,可以什么都没有,可以有多个 可以是数组
methodShow3()
methodShow3(1,23,44,5,6,6)
val arr = Array(1,2,3)
methodShow3(arr:_*)
传名调用和传值调用
先计算表达式的值,再应用到函数内部
将未计算的表达式
//传值
def addValue(a: Int, b: Int): Int = {
return a + b
}
//传名调用
def addName(a: Int, b: => Int): Int = {
return a + b
}
//计算完毕传入方法内部
val sum = addValue(1,2+2)
//return 2+4
println("传值调用:"+sum)
//进入函数内部之前,传递方法、函数的引用,传递要使用方法内部进行执行调用
val sum2 = addName(2,2+2)
//return 2+(2+2)
println("传名调用:"+sum2)
//提供至简
//提供一个方法,方法返回值是String类型
def method1():String={
//块表达式
//不写reutrn
"kaige"
}
//如果提供方法只有一句代码值,不写大括号
def method2():String="kaige"
//利用推到,利用最后一句代码自动推断 返回类型
def mehtod3()="kaige"
//没有参数列表 小括号都可以不写
def method4 = "kaige"
//没有参数的调用,需要区分是否存在小括号
mehtod3()
//method3
method4
def method5():String={
return "kaige"
}
//unit,省返回类型和等于号
def method6(): Unit ={
println("11111")
}
//提供直接实现方法的方式
()=>{
println("这是一个方法")
}
方法和函数的定义方式完全不同
函数可以作为方法参数或返回值存储
方法名是方法调用,而函数名只是代表函数对象本身
方法使用def而函数使用val
方法可以转换为函数,所以一般也不必严格区分
两种方式将方法转换成函数:
1、把方法作为参数传给另一个方法或者函数的时候,方法自动被转化成函数。
2、手动使用神奇的下划线_
将方法被转化成函数:
//自动转换方法到函数
//这种常量多是定义方法被传递到另外一个方的参数中,会被Scala自动转换
def m1(a:Int) = a*2;
val arr = Array(1,2,3,4,5,6);
arr.map(m1); //此时m1的方法会传递到map方法中会自动转换为函数
//手动方法到函数
val f7 = m1 _ // 【方法名 _】 把方法转换为函数
//函数时可以赋值给变量,但是不允许将方法名赋值给变量
// val f8 = m1;