Scala编程基础

Scala编程基础

Scala之父——马丁.奥德斯基

Scala将面向对象编程函数式编程和强大的类型系统结合起来,让人能写出优雅、简洁的代码。

表达式、语句和代码块与Java一样,还有类、包和引用的语法。除语法之外,Scala还采用了Java的其他元素,如它的基本类型、类库和它的执行模式。函数式编程借鉴了SML,OCaml,和F#为代表的ML家族语言很接近,Scala的隐式参数灵感来自Haskell,基于actor的并发库来自EeLang的思想。

Scala特点

面向对象:Scala中的每一个值都是一个对象,包括基本数据类型在内,连函数也是对象。

函数式编程:Scala是一种函数式语言,函数也能当成值来使用。Scala提供了轻量级的语法以定义匿名函数,支持高阶函数,允许嵌套多次函数,并支持柯里化。Scala的case class(样例类)及其内置的模式匹配相当于函数式编程语言中常用的代数类型。

静态类型:Scala具备类型系统。
类型系统支持以下特性:

  • 泛型类
  • 协变和逆变
  • 标注
  • 类型参数的上下限约束
  • 把类型和抽象类型作为对象成员
  • 复合类型
  • 引用自己时显示指定类型
  • 视图
  • 多态方法

扩展性:Scala提供了许多独特的语言机制,可以以库的形式轻易无缝添加新的语言结构:

  • 任何方法可用作前缀或后缀操作符
  • 可以根据预期类型自动构造闭包

动态性:Scala使用Actor作为其并发模型,Actor是类似线程的实体,通过邮箱收发消息。Actor可以复用线程,因此可以在程序中可以使用数百万个Actor,而线程只能创建数千个。在2.10之后的版本中,使用Akka作为其默认Actor实现。

函数式编程

函数式编程是一种“编程范式”

函数式编程的特点
  • 函数是⼀等公民:指的是函数与其他数据类型⼀样,处于平等地位,可以赋值给其他变量,也可以作为参数,传入另一个函数,或者作为别的函数的返回值。
  • 以表达式为中心
  • 无副作用:⼀个函数在程序执行过程中除了根据输⼊参数给出运算结果外,没有其他的影响,就称为没有副作用的。
  • 不修改状态:状态不能保存在变量中
  • 引用透明:不依赖外部变量或状态
函数式编程的优势
  • 代码简洁
  • 接近自然语言
  • 易于代码管理
  • 适合并发编程
  • 适用于热升级

函数式编程的一个特点是,函数也是值,同允许把函数本身作为参数传入另一函数,还允许返回一个函数!

静态、动态语言

静态编程语言:实现声明变量类型,类型不能改变,编译时检查

动态编程语言:不用事先声明类型,随时可以赋值为其他类型,编程时不知道什么类型,很难推断

强、弱类型语言

强类型语言:不同类型之间操作,必须强制类型转换为同⼀类型

弱类型语言:不同类型间可以操作,自动隐式转换

Scala解释器

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
var和val的区别
是否可变

使用val声明⼀个常量或值:val修饰的变量是不可变的,注意不可变的是引用,而不是内容 ,val修饰的变量在编译后,等同于加上final。

使用var 声明⼀个变量:var修饰的变量,内容和引用都可变

是否可以有lazy修饰:延迟加载

只有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的好处:

  • 更安全
  • 代码可读性高
  • 资源回收更快,方法执行完,val所定义的变量即回收
类型层级关系

Scala编程基础_第1张图片

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编程基础_第2张图片

      //自动类型转换属于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)
if分支条件表达式
	/*
    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;
循环表达式
for循环

与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)
    }
while循环和do…while循环
//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

方法可以转换为函数,所以一般也不必严格区分

  • 自动转换:在需要函数的地方,如果传递一个方法,Scala能够自动把方法转换为函数
  • 手动转换 方法名 _

两种方式将方法转换成函数

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;

你可能感兴趣的:(Scala,scala,java,开发语言)