Scala学习宝典(保姆级)

前言

为什么要学习Scala

分布式高并发语言Go、R、Erlang等等为何选择Scala?
Spark是大数据处理的核心方式,用scala语言编写!
Kafka分布式发布订阅消息系统,由LinkedIn捐给Apache,以极高的吞吐量著称,是目前最火爆的MQ,用scala语言编写!
Flink最新一代分布式海量数据计算框架,Alibaba收购并开源,自己开发Blink分支,Scala语言编写!

学前寄语

在我个人看来,Scala是一门非常优雅的语言,但优雅的背后要付出的辛苦也很多,比如学习Scala的人都会说,Scala语法非常简洁,但这也意味着抽象级别比较高,对初学者而言不好理解。也会有人说,Scala语法非常灵活,一个功能可以有非常多的实现方法,可以说条条大路通罗马,那么代价就是对于初学者来说,路多了反而不好选择。
所以在这里我对初学者的忠告是:在学习Scala前期,先走通一条路,屏蔽掉多余的干扰项,可能我们第一次使用Scala实现的项目看起来是非常笨拙的,但是没关系,任何的学习都是循序渐进的,不要追求一蹴而就。走通之后再回头,去寻找更好的替代方案。这样才能享受学习。

Scala简介

Scala学习宝典(保姆级)_第1张图片
Scala combines object-oriented and functional programming in one concise, high-level language. Scala’s static types help avoid bugs in complex applications, and its JVM and JavaScript runtimes let you build high-performance systems with easy access to huge ecosystems of libraries.
Scala是一个将面向对象和函数式编程结合在一起的简洁的高级语言。Scala的静态类型有助于避免复杂应用程序中的错误,其可以运行在JVM和JavaScript的特点使您可以轻松访问庞大的生态系统库来构建高性能系统。

Scala的诞生

Scala学习宝典(保姆级)_第2张图片
Martin OrderSky马丁 奥德斯基 是JVM开发团队核心成员,是JDK1.5泛型,增强for循环,自动类型转换,JDK1.8Lambda等重要特性的作者.
在对JAVA的维护过程中,马丁非常推崇JAVA的面向对象及垃圾回收,是James Gosling的小迷弟. 但作为编辑器开发的狂热爱好者的他,又觉得JAVA的语法过于繁琐.所以相继自己开发了另外两种语言Pizza和Scala.其中Scala被用于实验室科研后推广开源.在上边提到JDK1.5和1.8的两个版本中的新特性就是从Pizza和Scala中迁移而来.
Scala诞生于2001年,但真正被人们所熟知并广泛应用于大数据开发是在十多年之后.可以说是Spark和Kafka带火了Scala.
James Gosling曾在采访中说,如果让他选择“今天使用的语言不是Java,那就是Scala”。

函数式编程

C:面向过程编程
Java:面向对象编程
Scala:面向函数编程
函数式编程:将所有复杂的问题的解决拆分为若干函数的处理,每一个函数可以去实现一部分功能,利用很多次函数的处理最终解决问题。函数式编程相对于面向对象编程更加的抽象,好处是代码可以非常的简洁,更多的采用常量而不是变量来解决问题,这样额外带来的好处是在线程并发时可以减少甚至杜绝多线程并发安全问题,特别适合于应用在处理高并发场景、分布式场景下的问题。函数式编程可以使用高阶函数,函数在scala中是一等公民,可以更加灵活的进行程序的编写。
函数式编程并不是面向对象编程的发展,而是另外一种解决问题的思路,两者之间也并没有绝对的好坏之分,在不同的场景中各有各的优缺点。
总结:Scala是一个面向对象的函数式编程语言.

Scala和java的关系

java是由C语言开发,scala基于java,现在看来大部分情况下,青出于蓝而胜于蓝!
分布式开发中会面对很多棘手的问题,如面临服务器之间的通信、远程调用、序列化、反序列化等等。但有了scala这一切变得轻松,它全部都做了内部实现。让我们访问分布式集群环境就和访问单机一样简单。
同时Scala无缝集成Java,Scala编译器会先把源文件编译成.class文件再运行。Scala可以调用几乎所有的Java类库,也可以从Java应用程序中调用Scala的代码。它也可以访问现存的数之不尽的Java类库,这让用户(潜在地)迁移到Scala更加容易。
Java => 编译 => .class => JVM
Scala => 编译 => .class => JVM
Scala => JVM(支持,但不推荐,不是解释执行)
扩展:groovy、clojure(storm)都可以编译成.class,利用JVM。

Scala的特点

Scala学习宝典(保姆级)_第3张图片

Scala环境搭建

SDK下载安装及测试

提示:Scala基于Java,需要提前安装JDK并配置环境变量
官网:https://www.scala-lang.org/
Scala学习宝典(保姆级)_第4张图片
说明: 编写本篇教程时,Scala的最新版本为2.13.3,但是基于后续Spark\Kafka\Flink三个主流框架的版本及对Scala的版本要求.我们最终选择Scala-2.12.7这个版本作为基础环境. 2.11版本之后差别并不大,所以该文所有代码预计在2.11\2.12\2.13版本环境中都可以正常运行.
1.访问Scala官网,找到所需安装包进行下载.https://www.scala-lang.org/download/all.html
在这里,我们选择的是2.12.7这个版本进行下载.Scala学习宝典(保姆级)_第5张图片
2.进入2.12.7版本下载页后,在尾部找到对应系统安装包进行下载.
Scala学习宝典(保姆级)_第6张图片
3.安装Scala,双击msi包,一路下一步即可.注意中间在安装目录选择时,路径不要有中文或空格.

Scala学习宝典(保姆级)_第7张图片
Scala学习宝典(保姆级)_第8张图片
选择合适的目录
Scala学习宝典(保姆级)_第9张图片
Scala学习宝典(保姆级)_第10张图片
Scala学习宝典(保姆级)_第11张图片
4.检查环境
Scala学习宝典(保姆级)_第12张图片
Scala学习宝典(保姆级)_第13张图片
5.Hello World
Scala学习宝典(保姆级)_第14张图片

IDEA插件安装

在IDEA的File菜单中打开Settings窗口Scala学习宝典(保姆级)_第15张图片
选择Plugins在Marketplace中搜索Scala,找到Scala插件后点击Install.Scala学习宝典(保姆级)_第16张图片
等待插件下载完成,点击重启IEDA
Scala学习宝典(保姆级)_第17张图片
如果网络环境不佳无法自动下载插件.此时需要手动导入,具体导入方法可以参考我的另一篇文章: https://blog.csdn.net/dcc15011003101/article/details/107805938

第一个Scala工程

创建一个新的工程
Scala学习宝典(保姆级)_第18张图片
Scala学习宝典(保姆级)_第19张图片
Scala学习宝典(保姆级)_第20张图片
创建一个Object(至于什么是Object之后会详细解释)
Scala学习宝典(保姆级)_第21张图片
命名为: cn.tedu.scalabasic.HelloWorld
选择Object选项
Scala学习宝典(保姆级)_第22张图片
编写代码

package cn.tedu.scalabasic

/**
 * 这是我们的第一个Scala程序!
 */
object HelloWorld {
     
  def main(args: Array[String]): Unit = {
     
    print("Hello World!!!")
  }
}

右键运行
Scala学习宝典(保姆级)_第23张图片
结果
Scala学习宝典(保姆级)_第24张图片
主方法
Scala学习宝典(保姆级)_第25张图片

Scala基础语法

注释

Scala中注释的写法与Java一致

package cn.tedu.scalabasic
/**
 * Scala基础语法
 */
object ScalaGrammar {
     
  def main(args: Array[String]): Unit = {
     
    /*
    1.注释
    Scala中的注释形式与Java中一致.包括文档注释,多行注释,单行注释
     */
    /**
     * 这是一个文档注释
     */
    /*
    这是一个多行注释
     */
    //这是一个单行注释
  }
}

代码分隔

在Scala代码中每行代码的结尾不需要分号";"作为结束符(自动推断),也可以加分号.

package cn.tedu.scalabasic
/**
 * Scala基础语法
 */
object ScalaGrammar {
     
  def main(args: Array[String]): Unit = {
     
    /*
    2.代码分隔符
    在Scala中,代码行与行之间的分隔符与Java相同,使用分号;,但是一般不需要写,Scala实现了自动推断.
     */
    println("再见,刘沛霞");
    println("你好,张慎政")
    println("么么哒,董长春")
  }
}

变量和常量定义

在Scala中变量和常量分别使用var和val来定义.可以声明类型但不必要(类型自动推断).
其中,官方建议尽量不要使用var而是使用val来定义,有利于内存回收.和线程的安全.

package cn.tedu.scalabasic
/**
 * Scala基础语法
 */
object ScalaGrammar {
     
  def main(args: Array[String]): Unit = {
     
    /*
    3.常量变量
    在Scala中,变量使用var来定义,常量使用val定义
    并且在定义变量和常量时一般都不写类型.Scala按照上下文可以自动推断
    **重要**:在Scala中我们更推荐大家尽可能的使用常量来定义属性,这更利于内存回收.
     */
    var a = 12
    val b = 10
    val c: Int = 11
    a += 1
    //    b += 2  //编译报错,常量不可变
    println(a)
    println(b)
  }
}

标识符

Scala 可以使用两种形式的标志符,字符数字和符号。
字符数字使用字母或是下划线开头,后面可以接字母或是数字,符号"$“在 Scala 中也看作为字母。然而以”$“开头的标识符为保留的 Scala 编译器产生的标志符使用,应用程序应该避免使用”$"开始的标识符,以免造成冲突。
同时,Scala与Java相同的是同样建议使用驼峰规则命名.

package cn.tedu.scalabasic
/**
 * Scala基础语法
 */
object ScalaGrammar {
     
  def main(args: Array[String]): Unit = {
     
    /*
    4.标识符
    Scala标识符规则与Java大致相同.不能以符号和数字开头(_和$可以).
    其中以$例外一般不作为标识符的开头使用,暂时不必深究.
    命名格式推荐使用驼峰命名法:AaaBbb,aaaBbb,AAA_BBB
    不能使用关键字和保留字命名
     */
    val userName = "董长春"
    //    val if = 12 //编译报错,关键字和保留字不能用作标识符

  }
}

关键字保留字

Scala学习宝典(保姆级)_第26张图片

方法和操作符

在Scala中有这样一句话:操作符即方法,方法即操作符
意思是方法可以按照操作符的使用规则来使用,操作符也可以按照方法的方式使用

package cn.tedu.scalabasic
/**
 * Scala基础语法
 */
object ScalaGrammar {
     
  def main(args: Array[String]): Unit = {
     
    /*
    5.操作符和方法
    在Scala中有这样一句话:操作符即方法,方法即操作符
    意思是方法可以按照操作符的使用规则来使用,操作符也可以按照方法的方式使用
     */
    val str = "123_456_789"
    val strs = str split "_" //这里以操作符的形式(1 + 2)使用split方法
    for (s <- strs) {
     
      println(s)
    }

    println(2.*(4)) //这里以方法的形式(a.split("_"))使用+操作符

    //对于没有参数的方法,也可以不写括号
    val num: Int = 123
    println(num.toString())
    println(num.toString)
  }
}

Scala数据类型

在Java中,分为基本数据类型(8种) 和 引用类型(继承自Object).其中基本数据类型直接以字面量的形式赋值,没有属性和方法.
在Scala中数据类型与Java的设计类似,并且针对面向对象和函数式编程做了更进一步的优化.
Scala学习宝典(保姆级)_第27张图片

其中:

  1. Scala中有两种类型,AnyVal(值类型)和AnyRef(引用类型)都继承自Any(顶级父类).
  2. AnyVal类型也是以"对象"形式存在,有属性和方法.声明时以字面量的形式声明.其中包含了类似Java中的8大基本类型:Byte\Short\Int\Long\Float\Double\Boolean\Char.除此之外还有一个特殊的Unit类型,表示空值(类似Java中的void).
  3. AnyRef类型中包括所有的Scala内置对象和自定义的Scala对象,集合等,以及所有引用的Java对象.还定义了一个Null类型来表示空对象,他的默认值为null.
  4. 在Scala中还有一个非常特殊的Nothing类型.他是所有Scala类型的子类.被定义为abstract final class Nothing extends Any.不可被继承,不可被实现.
    官方图谱:
    Scala学习宝典(保姆级)_第28张图片

AnyVal

这里我们先对AnyVal做详细讲解
值类型的定义

package cn.tedu.scalabasic

import scala.collection.immutable.StringOps

/**
 * AnyVal值类型的声明
 */
object DataTypes {
     

  def main(args: Array[String]): Unit = {
     
    val a: Byte = 123
    println(a,a.getClass)

    val b: Short = 123
    println(b,b.getClass)

    val c: Int = 123
    println(c,c.getClass)

    val d: Long = 123L  //此处加不加L都可以
    println(d,d.getClass)

    val e: Float = 1.23f  //为区别Double,Float类型后加f
    println(e,e.getClass)

    val f: Double = 1.23
    println(f,f.getClass)

    val g: Char = '@'
    println(g,g.getClass)

    val h: Boolean = false
    println(h,h.getClass)

    val i: Unit = "123" //不管这里赋值什么,他的值始终是()这是Unit的默认值.
    println(i,i.getClass)

    /*
      特殊说明
      虽然String是java.lang.String.在Scala中属于AnyRef类型,
      但是在实际操作是会被隐式转换为StringOps类型属于AnyVal
      通过以下实验可以证明
     */
    val j: String = "123"
    println(j,j.getClass) //(123,class java.lang.String)

    val k: StringOps = "123"
    println(k,k.getClass) //(123,class scala.collection.immutable.StringOps)
  }
}

值类型的转换

自动类型转换

在Scala中类型的转换包括两种,一种是值类型转换,另外一种是引用类型转换,这里我们先重点讲解值类型的转换.
值类型可以通过以下方式进行转换:
Scala学习宝典(保姆级)_第29张图片
其转换方向取决于长度,只能由范围小的类型自动转换为范围大的,不能大变小.

package cn.tedu.scalabasic

/**
 * 类型转换
 */
object TypeTrans {
     
  def main(args: Array[String]): Unit = {
     
    /*
    自动类型转换
    在计算的过程中,值类型会进行自动转换,根据范围由小到大
    Byte->Short->Int->Long->Float->Double
                  ↑
                 Char
     */
    var a: Int = 12
    var b: Byte = 1
    var c: Char = 'a'
    var d: Long = 12
    var e: Double = 11.1
    var f: Short = 3
    var g: Float = 3.2f
    println(a+b,(a+b).getClass)
    println(a+c,(a+c).getClass)
    println(a+d,(a+d).getClass)
    println(a+e,(a+e).getClass)
    println(a+f,(a+f).getClass)
    println(d+g,(d+g).getClass)

  }
}

Scala学习宝典(保姆级)_第30张图片
说明:浮点数32位的范围大于Long的64位,由转换公式决定.

强制类型转换

范围大的数据类型通过 .toxxx 实现向范围小的数据类型强制转换,注意可能会造成精度的缺失.

package cn.tedu.scalabasic

/**
 * 类型转换
 */
object TypeTrans {
     
  def main(args: Array[String]): Unit = {
     
    /*
    强制类型转换
    范围大的数据类型通过 .toxxx 实现向范围小的数据类型强制转换,注意可能会造成精度的缺失.
     */
    val h: Int = 12
    val i: Double = 2.12
    val j: Int = (h + i).toInt
    println(j)
  }
}

值类型和String类型之间的相互转换

  1. 值类型转换为String类型有两种方式,分别是a+"" 和 a.toString
  2. String类型数据转换为值类型的前提是可转,使用.toXxx
package cn.tedu.scalabasic

/**
 * 类型转换
 */
object TypeTrans {
     
  def main(args: Array[String]): Unit = {
     
    /*
    值类型和String类型之间的相互转换
     */
    //值类型转换为String类型有两种方式,分别是a+"" 和 a.toString
    val k:Int = 12
    println(k+"",(k+"").getClass)

    val l:Int = 12
    println(l.toString,(k.toString).getClass)

    //String类型数据转换为值类型的前提是可转,使用.toXxx
    val m:String = "123"
//    val m:String = "12a"  //报错:java.lang.NumberFormatException
    println(m.toInt,m.toInt.getClass)
  }
}

运算符

算数运算符

Scala中的算数运算符与Java基本一致,唯一不同是没有++和–.
Scala学习宝典(保姆级)_第31张图片

package cn.tedu.scalabasic

/**
 * Scala中的操作符
 */
object ScalaOperator {
     
  def main(args: Array[String]): Unit = {
     
    /*
    算数运算符
     */
    val a = 13
    val b = 3
    println(a + b)
    println(a - b)
    println(a * b)
    println(a / b)
    println(a / 3.0)  //如果在除法运算时需要精确值,需要有至少一个浮点型
    println(a % b)
  }
}

赋值运算符

Scala中的赋值运算符与Java基本一致

package cn.tedu.scalabasic

/**
 * Scala中的操作符
 */
object ScalaOperator {
     
  def main(args: Array[String]): Unit = {
     
    /*
    赋值运算符
     */
    var c = 3 //将3这个值赋值给变量a
    c += 3
    println(c)
    c -= 3
    println(c)
    c *= 3
    println(c)
    c /= 3
    println(c)
    c %= 3
    println(c)
  }
}

关系运算符

Scala中的关系运算符与Java基本一致
Scala学习宝典(保姆级)_第32张图片

package cn.tedu.scalabasic

/**
 * Scala中的操作符
 */
object ScalaOperator {
     
  def main(args: Array[String]): Unit = {
     
    /*
    关系运算符
     */
    println(a>b)
    println(a>=b)
    println(a<b)
    println(a<=b)
    println(a==b)
    println(a!=b)
  }
}

逻辑运算符

Scala中的逻辑运算符与Java基本一致,但是在取反操作时不能像Java一样多层取反.
Scala学习宝典(保姆级)_第33张图片

package cn.tedu.scalabasic

/**
 * Scala中的操作符
 */
object ScalaOperator {
     
  def main(args: Array[String]): Unit = {
     
    /*
    逻辑运算符
     */
    var bool1 = true
    var bool2 = false
    println(bool1 && bool2)
    println(bool1 || bool2)
    println(!bool2) //Scala不支持多层取反(!!!!!true)
  }
}

位运算符

Scala中的位运算符与Java基本一致,了解即可.
Scala学习宝典(保姆级)_第34张图片

流程控制

程序的执行顺序需要根据需求进行相应的控制,比如执行的顺序,执行条件,执行次数等.

顺序结构

Scala像大部分语言一样,代码一般按照从上到下的顺序执行.
一行代码中安装从左到右的顺序执行,同一行内如果有括号,先算括号里.
数学计算中遵循先乘除后加减.

package cn.tedu.scalabasic

/**
 * 流程控制
 */
object ProcessControl {
     
  def main(args: Array[String]): Unit = {
     
    /*
    顺序结构
     */
    println("Hello World")
    println(1+2+"Hello World"+3+2*2+(4+5))
  }
}

分支结构

在Scala分支结构中,只有if…else if…else 没有Java中的switch…case(模式匹配)
需要注意的是if分支结构是可以有返回值的.所以可以使用if语句替代Scala中没有的三元运算符.
与Java一致的是,分支结构可以多层嵌套.

package cn.tedu.scalabasic

/**
 * 流程控制
 */
object ProcessControl {
     
  def main(args: Array[String]): Unit = {
     
    /*
    分支结构
     */
    val age = 19
    if (age >= 18) {
     
      println("你已经成年了")
    } else if (age >= 500) {
     
      println("你已经成精了")
    } else {
     
      println("你还是个孩子")
    }
    //在Scala中没有三元运算符 a?b:c,使用if...else代替
    var result = if (age >= 18) "你已经成年了" else "你还是个孩子"
    println(result)
  }
}

循环结构

for循环

在Scala中for循环用于控制程序执行次数,与Java一致,同时具备了更加丰富的功能.

  1. for循环表达式可以同时定义多个.
  2. for循环内可以加条件.
  3. for循环可以有返回值.
  4. for循环有返回值,返回每次执行的结果.
package cn.tedu.scalabasic

/**
 * 流程控制
 */
object ProcessControl {
     
  def main(args: Array[String]): Unit = {
     
    /*
    循环结构-for
     */
    for (i <- 1 to 10) {
     
      println(i)
    }
    //九九乘法表
	for (i <- 1 to 9) {
     
      for (j <- 1 to i) {
     
		//print(j + "*" + i + "=" + i * j + "\t")
        //在Scala中变量和字符串的拼接可以简化为占位符
        print(s"${j} * ${i} = ${i*j}\t")
      }
      println()
    }

    //简写
    for (i <- 1 to 9; j <- 1 to i) {
     
      print(s"${j} * ${i} = ${i*j}\t")
      if (i == j) println()
    }

    //循环条件,守卫,步长
    for(i <- 1 to 10 if i % 2 == 0) println(i)

    //推导式:生成一个2到10的偶数集合
    var nums = for(i <- 1 to 10 if i % 2 == 0) yield i
    println(nums)
  }
}

while循环

Scala中while与Java中写法和用法都一致.但是更推荐使用for,因为for更加简洁.

package cn.tedu.scalabasic

/**
 * 流程控制
 */
object ProcessControl {
     
  def main(args: Array[String]): Unit = {
     
    /*
    循环结构-while
     */
    var num1 = 0
    while (num1 <= 10) {
     
      num1 += 1
    }
    println(num1)

    var num2 = 0
    do {
     
      num2 += 1
    } while (num2 <= 10)
    println(num2)
  }
}

break()和breakable

在Scala中没有break和continue想要使用类似功能只能用scala.util.control.Breaks._工具

package cn.tedu.scalabasic

/**
 * 流程控制
 */
object ProcessControl {
     
  def main(args: Array[String]): Unit = {
     
    /*
    break()和breakable
     */
     //导包
    import scala.util.control.Breaks._
    //break
    breakable {
     
      for (i <- 1 to 10) {
     
        println(i)
        if (i == 6) break()
      }
    }

    //continue
    for (i <- 1 to 10) {
     
      breakable {
     
        if (i % 2 == 0) break()
        println(i)
      }
    }
  }
}

代码块

在Java中我们学过静态代码块,在Scala中也有代码块的概念,就是用大括号括起来的一段代码.并且在Scala中代码块是可以有返回值的.

package cn.tedu.scalabasic

/**
 * 流程控制
 */
object ProcessControl {
     
  def main(args: Array[String]): Unit = {
     
    /*
    代码块
    */
    val block = {
     
      println("代码")
      val v1 = 12
      val v2 = 14
      v1 + v2  //最后一行的表达式为返回值
    }
    println(block)
  }
}

方法和函数

在Scala中方法和函数是不一样的.一般情况下在使用时并不需要特殊区分.

方法

Scala学习宝典(保姆级)_第35张图片

Scala中的方法定义与Java不同更加简洁.
一般情况下返回值类型可以省略.递归方法除外.
return可以省略,默认方法体中最后一行被返回.
只有一行代码的时候大括号也可以省略.
当返回值类型为Unit(没有返回值)时,=也可以省略不写,也就是说如果不写等号该方法不会返回任何数据.这种形式在Scala中叫做"过程"

package cn.tedu.scalafunction

/**
 * 方法
 */
object Method {
     
  def main(args: Array[String]): Unit = {
     
    /*
    方法的定义
     */
    def getSum1(a:Int,b:Int):Int = {
     
      return a+b
    }
    //返回值类型可以省略(自动推断)
    //return可以省略(默认返回最后一行)
    //{}可以省略(只有一行代码)
    def getSum2(a:Int,b:Int)= a + b
    
    val sum1 = getSum1(1,2)
    val sum2 = getSum2(3,4)
    
    println(sum1)
    println(sum2)

    //没有返回值时,=等号可以不写.这种形式在Scala中叫做:过程
    def sayHello() {
     
      println("Hello,World")
      return 1  //即使写了返回1,因为没有等号= 方法也不会返回任何数据.
    }
    val hello = sayHello()
    println(hello)
  }
}

递归

方法内部调用自己,即为递归
递归方法定义时不能省略返回值类型

package cn.tedu.scalafunction

/**
 * 方法
 */
object Method {
     
  def main(args: Array[String]): Unit = {
     
    //递归方法定义时必须声明返回值
    def getFactorial(n: Int): Int = {
     
      if (n == 1) n else n * getFactorial(n - 1)
    }

    val factorial = getFactorial(5)

    println(factorial)
  }
}

方法的参数

默认参数

在方法的定义时,我们有时会遇到这样的需求: 某些参数通常是不变的数据只有少数情况下才会变化.如果大多数情况下都需要手动传入同样值不符合编程中复用的原则.默认参数就可以解决这类问题.

package cn.tedu.scalafunction

/**
 * 方法
 */
object Method {
     
  def main(args: Array[String]): Unit = {
     
    /*
    默认参数
     */
    def getUserInfo(name:String,age:Int,addr:String = "张家口"):String = {
     
      s"${name}来自${addr},今年${age}岁了"
    }
    val info1 = getUserInfo("董长春",12)
    val info2 = getUserInfo("马梦诗",12)
    val info3 = getUserInfo("刘沛霞",12,"天津")

    println(info1)
    println(info2)
    println(info3)
  }
}

指定参数

指定参数时指,在调用方法是通过指定参数名来改变参数传递的前后顺序.

package cn.tedu.scalafunction

/**
 * 方法
 */
object Method {
     
  def main(args: Array[String]): Unit = {
     
    /*
    指定参数
     */

    def getProduct(a: Int = 5, b: Int = 10) = (a + 3) * b
    //改变参数传入的顺序
    println(getProduct(b = 4, a = 3))
    //避免在参数有默认值时错传
    println(getProduct(b = 1))
  }
}

可变参数

当方法的参数列表中有多个不确定的参数时,可以是用可变参数,与Java类似.
可变参数用数组来保存,可以直接调用数组的方法.
如果参数列表中既有普通参数,也有可变参数,可变参数必须写在最后.

package cn.tedu.scalafunction

/**
 * 方法
 */
object Method {
     
  def main(args: Array[String]): Unit = {
     
    /*
    可变参数
     */
    def getSum4(nums:Int*) = nums.sum //可变参数是用数组保存的,可以直接使用数组的方法.
    //需要注意的是,如果参数列表中既有普通参数,也有可变参数,可变参数需要写在最后.
    //def getLogs(logs:String*,id:Int) = {} //编译错误:*-parameter must come last
  }
}

方法的调用

在Scala中方法的调用也是非常灵活的,我们来总结一下.

后缀调用

最普通的调用方式就是后缀调用,这与Java中是一致的.都是对象.methodName(参数)的形式.

中缀调用

中缀调用其实在前边已经接触过,就是方法即运算法,运算符及方法的体现.
比如:1 to 10.就是1.to(10)

大括号调用

当参数只有一个时,也可以将小括号替换为大括号.比如: 对象.methodName{参数},用的不多.

无括号调用

当没有参数时,方法的调用可以不写括号,比如:array.sum

惰性加载

为了解决资源推迟加载,优化启动速度,区别加载顺序等问题,Scala中设计了一个惰性加载的概念.
所有被lazy修饰的val所指向的资源不会直接加载,而是延迟到使用时才会加载.
需要注意的是,lazy只能修饰val 不能修饰var
当用于接收方法返回值得val被lazy修饰是,这个方法会惰性加载.叫做惰性方法.
这种惰性加载的形式在Java中也有,只不过Java没有原生支持,而是通过代码逻辑来实现,叫做懒加载,比如单例中的懒汉式.在Spring等对象管理框架中也有懒加载的概念.

package cn.tedu.scalafunction

/**
 * 方法
 */
object Method {
     
  def main(args: Array[String]): Unit = {
     
    /*
    惰性方法(类似Java中的懒加载)
     */
    def getSum3(a:Int,b:Int) = {
     
      println("getSum3已经加载")
      a + b
    }
//    val sum3 = getSum3(1,2)
    lazy val sum3 = getSum3(1,2)
    println(sum3)
  }
}

函数

在Scala中函数与方法是不同的.方法依附于对象,而函数本身就是对象.
函数的定义方式也与方法不同.

object Function {
     
  def main(args: Array[String]): Unit = {
     
    /*
    函数的定义
    1.函数本身是一个对象.
    2.函数参数列表和函数体之间是 => 来链接
    3.函数不需要写返回值类型.
    4.函数定义时的变量名就是函数名,也就是这个对象的引用,调用时使用函数名(参数)的形式调用.
    5.函数可以直接用lazy修饰作为惰性函数.
     */
    val getSum = (a:Int,b:Int) => {
     a + b}
    println(getSum(1,2))

    val printMsg = () => {
     println("Hello")}
    printMsg()

    lazy val getMax = (v1:Int,v2:Int) => {
     if (v1>v2) v1 else v2}
    println(getMax(2, 3))
    
    /*
    方法转函数
    有时候我们需要将一段逻辑作为参数传递,但是方法不支持传递,可以将其转化为函数.
    通过本案例的输出结果,我们也可验证,函数是对象,而方法不是.
     */
    
    def getInfo() = {
     println("我叫董长春")}
    println(getInfo)
    val toFunction = getInfo _
    println(toFunction)
  }
}

类和对象

回顾面向对象

在Java的学习过程中,面向对象无疑是重中之重.而Scala是一门面向对象的函数式编程语言,所以面向对象的概念或者思想在Scala学习过程中也是不能忽视的.

什么是面向对象

面向对象是一种编程思想,是相对于面向过程的又一层高级抽象.
我们将一系列事物的特性或者功能抽象出来并对其进行封装,这个封装结果就是类,而属于这个类的所有个体就是这个类的对象.

面向对象的特点

封装: 保证安全性
继承: 保证复用性
多态: 保证拓展性

类的定义和对象创建

Scala中简单的类定义与Java相同,内部可以定义成员属性和成员方法.

package cn.tedu.scalaoop

/**
 * 定义一个类
 */
class Person {
     
  //初始化属性有三种方式
  var name:String = ""  //姓名
  var age:Int = _       //年龄
  var addr = ""         //地址

  def sayHello(){
     println(s"大家好我是${name},今年${age}岁了,来自${addr}.")}
}

对象的创建方式与Java也类似,通过new关键字实现对象的创建
在使用空参构造时,类名后的括号可以省略.
对象可以访问类的公共属性和方法.

package cn.tedu.scalaoop

/**
 * 创建对象
 */
object PersonObject {
     
  def main(args: Array[String]): Unit = {
     
    val p1 = new Person()
    //构造方法为空时,括号可以省略
    val p2 = new Person
    p2.name = "董长春"
    p2.age = 18
    p2.addr = "张家口"
    p2.sayHello()
  }
}

访问权限修饰符

Scala中访问权限修饰符与Java稍有不同,但是功能是一致的.
Scala中共有四种访问权限修饰符
包括:private \ private[this] \ protected \ 默认

修饰符 权限
private[this] 作用域私有的,只能在范围内中访问,对于其他私有
private 类私有的,只能在本类访问
protected 受保护的,只能在本类及其子类中访问
默认 公共的(public)全局可访问

一般情况下,开发中更多的使用到默认的,和private私有的.

package cn.tedu.scalaoop

/**
 * 定义一个动物类
 */
class Animal {
     
  var name = ""
  private var age = 0
  protected var color = ""
  private[this] var eat = ""

  def sayHello(){
     println(name,age,color,eat)}
}
package cn.tedu.scalaoop

object AnimalObject {
     
  def main(args: Array[String]): Unit = {
     
    val animal = new Animal
    animal.name = "驴"
    //非公有不能访问
//    animal.age
//    animal.color
//    animal.eat

  }
  class Donkey extends Animal{
     
    var animal = new Animal
    animal.name
//    animal.age  //私有不能访问
    animal.color  //子类可以访问protected修饰的属性
//    animal.eat  //私有不能访问
  }
}

构造器

像Java创建对象一样,Scala在创建对象的时候也是调用构造器,而Scala中构造器的写法与Java不用,直接定义在类名之后的小括号里.

单例对象

伴生类和伴生对象

private[this]权限修饰

apply()方法

继承

特质Trait

包和样例

容器

数组

元组

列表

集合

映射

迭代器

函数式编程

模式匹配

IO

高阶函数

隐式转换

泛型

集合

Actor

Akka

你可能感兴趣的:(大数据,Scala,scala)