Scala学习

安装JDK

因为Scala是运行在JVM平台上的,所以安装Scala之前要安装JDK。

Windows安装Scala编译器

访问Scala官网http://www.scala-lang.org/下载Scala编译器安装包,目前最新版本是2.12.x,下载scala-2.11.8.zip,解压后配置上环境变量就可以了。

scala环境变量.png

Linux安装Scala编译器

下载Scala地址https://www.scala-lang.org/download/2.11.8.html
然后解压Scala到指定目录
tar -zxvf scala-2.11.8.tgz -C /usr/java
配置环境变量,将scala加入到PATH中

vi /etc/profile
export JAVA_HOME=/usr/java/jdk1.8
export PATH=$PATH:$JAVA_HOME/bin:/usr/java/scala-2.11.8/bin

Scala开发工具安装

目前Scala的开发工具主要有两种:Eclipse和IDEA,这两个开发工具都有相应的Scala插件,如果使用Eclipse,直接到Scala官网下载即可http://scala-ide.org/download/sdk.html
由于IDEA的Scala插件更优秀,大多数Scala程序员都选择IDEA,可以到http://www.jetbrains.com/idea/download/下载,点击下一步安装即可,安装时如果有网络可以选择在线安装Scala插件。
这里我们使用离线安装Scala插件:
1.安装IDEA,点击下一步即可。
2.下载IEDA的scala插件
插件地址: https://plugins.jetbrains.com/plugin/1347-scala
3.安装Scala插件:Configure -> Plugins -> Install plugin from disk -> 选择Scala插件 -> OK -> 重启IDEA

scala插件1.png

scala插件2.png


scala插件3.png

scala插件4.png

scala插件5.png

scala插件6.png

创建工程支持scala代码开发

第一步:idea当中创建创建普通maven工程

File ==> New ==> Project


scala开发1.png
scala开发2.png
scala开发3.png

第二步:修改pom.xml添加scala的版本以及打包插件

<dependencies>
        <dependency>
            <groupId>org.scala-lang</groupId>
            <artifactId>scala-library</artifactId>
            <version>2.11.8</version>
            <!-- 如果想要用java -jar 来运行我们打包之后的jar包,则下面这个配置必须注释掉 -->
           <!-- <scope>provided</scope>-->
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <!-- 限制jdk版本插件 -->
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.0</version>
                <configuration>
                    <source>1.8</source>
                    <target>1.8</target>
                    <encoding>UTF-8</encoding>
                </configuration>
            </plugin>
        <!-- 编译scala需要用到的插件 -->
            <plugin>
                <groupId>net.alchim31.maven</groupId>
                <artifactId>scala-maven-plugin</artifactId>
                <version>3.2.2</version>
                <executions>
                    <execution>
                        <goals>
                            <goal>compile</goal>
                            <goal>testCompile</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>
            <!-- 项目打包用到的插件 -->
            <plugin>
                <artifactId>maven-assembly-plugin</artifactId>
                <configuration>
                    <descriptorRefs>
                        <!--将所有依赖的jar包都打进去-->
                        <descriptorRef>jar-with-dependencies</descriptorRef>
                    </descriptorRefs>
                    <archive>
                        <manifest>
                            <!--指定main程序所在的类-->
                            <mainClass>cn.itcast.scala.demo1.ScalaFirst</mainClass>
                        </manifest>
                    </archive>
                </configuration>
                <executions>
                    <execution>
                        <id>make-assembly</id>
                        <phase>package</phase>
                        <goals>
                            <goal>single</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>
        </plugins>
    </build>

第三步:创建scala代码保存的文件夹

src ==> main ==> new ==> Directory ==> scala


scala开发4.png

scala开发5.png

第四步:开发scala的代码并打包运行

scala开发6.png

scala开发7.png

开发代码如下:

object ScalaFirst {
  def main(args: Array[String]): Unit = {
    println("hello world")
  }
}

第五步:打包我们的scala代码并准备运行

将我们的代码打包,之后,进行运行
双击package之后,就会出现我们打好的jar包,然后选择下面这个就可以运行了


scal开发8.png

运行我们的代码一共可以有四个命令,两种是打包的时候选择了我们的main程序类的,两种是我们打包时候没有选择main程序类的
其中第一种和第三种,都是选定了我们的main程序类
第二种和第四种都是没有选定我们的main程序类
四种运行方式都可以用于运行我们的jar包

第一种运行方式:我们打包的时候指定了对应的main方法所在的程序类
scala scaladay01-1.0-SNAPSHOT-jar-with-dependencies.jar
第二种运行方式:不管打包的时候有没有指定main方法所在的程序类,都可以运行
scala -cp scaladay01-1.0-SNAPSHOT-jar-with-dependencies.jar cn.itcast.scala.demo1.ScalaFirst
第三种方式:我们打包的时候指定了对应的main方法所在的程序类
java -jar scaladay01-1.0-SNAPSHOT-jar-with-dependencies.jar
第四种方式:不管打包的时候有没有指定main方法所在的程序类,都可以运行
java -cp scaladay01-1.0-SNAPSHOT-jar-with-dependencies.jar cn.itcast.scala.demo1.ScalaFirst


scala开发8.png

我们可以看到我们的scala代码最终也编译成了class文件

Scala Class

新建Scala Class时包括三个选项,Class、Object、Trait
Class:类
Object:对象,可以认为Object中所有修饰的东西,都是静态的
Trait:特质

在Java中有静态和非静态之分

public class Hello{
    private static String username;//静态成员变量
    private Int age;//非静态成员变量
    public static String sayHello(){
        
    };//静态成员方法
    public String sayHhello2(){
        
    };//非静态成员方法
}

在java中,静态的属性和方法都是可以直接调用,而非静态的属性和方法都要通过new对象,然后进行调用

在Scala中,为了解决静态和非静态的区分,专门有一个对象叫Object ,Object里面所有的东西都是类似于java中的静态的static修饰的

1.在Scala中声明private变量,Scala编译器会自动生成get,set方法

2.在Scala中变量需要初始化

3.在Scala中没有静态修饰符,在object下的成员全部都是静态的,如果在类中声明了与该类相同的名字的object则该object是该类的”伴生对象”

可以理解为Scala把类中的static集中放到了object对象中,伴生对象和类文件必须是同一个源文件,可以用伴生对象做一些初始化操作.

4.在Java中可以通过interface实现多继承,在Scala中可以通过特征(trait)实现多重继承,但是与Java不同的是,它可以定义自己的属性和实现方法体

5.object不能提供构造器参数,也就是说object必须是无参的

Scala中object与class的区别
在Scala中,类名可以和对象名为同一个名字,该对象称为该类的伴生对象,类和伴生对象可以相互访问他们的私有属性,但是它们必须在同一个源文件中
类只会被编译,不能直接执行,类的声明和主构造器在一起被声明,在一个类中,主构造器只有一个.
类和它的伴生对象可以相互访问其私有成员
class和object的一个差别是,单例对象不带参数,而类可以.因为你不能用new关键字实例化一个单例对象,你没有机会传递给它参数

第一步:创建实例化对象

(1)进行搜索
例如:
Ctrl+Alt+Shift+N:StreamContxt
查看StreamContxt是类、接口、还是样例类
(2)如果是类(class):
(1)scala伴生类
查看半生类是否有构造器,初始化对象
根据构造器中的参数继续实例化对象

(2)scala伴生对象
查看是否apply方法,初始化方法

(3)如果是样例类(case class):

(1)scala伴生类
查看半生类是否有构造器,初始化对象
根据构造器中的参数继续实例化对象

(2)scala伴生对象
查看是否apply方法,初始化方法

(3)如果是抽象类

(1)子类

(2)自定义类继承抽象类

基础学习

1、scala当中申明值和变量

scala当中的变量申明可以使用两种方式,第一种使用val来申明变量。第二种使用var来申明变量。

//申明变量的基本语法
val/var 变量名 [:变量类型] = 变量值
//申明一个变量,变量名 name 变量类型是String 变量值是zhangsan

//注意,val修饰的变量,不可变,类似于java当中final锁定了

val name:String = "zhangsan"
println(name) 
//name="lisi"  报错,val不可以修改变量 

//使用var修改的变量可变  
var age:Int = 30 
println(age) 
age = 50 
println(age) 
//总结:在scala当中一般建议使用val来定义变量 

//类型推断,如果我们不定义变量的类型,scala会帮我们自动推断 
var address = 123f 
println(address)

2、块表达式

定义变量时用 {} 包含一系列表达式,其中块中的最后一个表达式的值就是块的值。

val block = {
  println("hello world")
  val a=1
  val b=2
  b-a
}
//结果:
hello world
block: Int = 1

//块表达式,返回值的类型;取决于我们最后一行代码返回值类型

val block = {
  println("hello world")
}
//结果:block: Unit = ()
//Unit类型,类似于java中void,用()表示;表示什么类型都没有

3、scala当中常用数据类型

Scala和Java一样,有7种数值类型Byte、Char、Short、Int、Long、Float、Double类型和1个Boolean类型。

字段 类型
Boolean true 或者 false
Byte 8位, 有符号
Short 16位, 有符号
Int 32位, 有符号
Long 64位, 有符号
Char 16位, 无符号
Float 32位 ,单精度浮点数
Double 64位, 双精度浮点数
String 其实就是由Char数组组成

与java当中不同,scala当中并不区分基本数据类型和引用数据类型,所有的这些类型全部都是对象,可以调用相对应的方法。scala当中的String,其实就是使用了java当中String,但是scala当中的string的方法更加丰富,scala当中给String扩展了很多方法,但是没有改String的源码,使用隐式转换来丰富了很多的方法

4、scala当中常用的类型结构图

Scala中,所有的值都是类对象,而所有的类,包括值类型,都最终继承自一个统一的根类型Any。
值类型与引用类型
值类型:一些数据值
引用类型:我们自己定义的一些scala的class类,一些java类等等
null是所有引用类型的子类
nothing是所有引用类型和值类型的子类

  1. Unit类型用来标识过程,也就是没有明确返回值的函数。 由此可见,Unit类似于Java里的void。Unit只有一个实例,(),这个实例也没有实质的意义。

5、算数操作符的重载

scala当中也有 + - * / 的操作,但是 + - * / 都是方法名

val age2 :Int = 30 
//注意,这个  -  号是一个方法 println(age2 - 10) 
//如果减号是一个方法名  那么我们可以通过  .方法名去调用
println(age2.- (10))  
//总结  scala当中调用方法
//第一种  空格 直接写方法名
//第二种  .方法名(方法的参数)

注意:Scala中没有++、--操作符,需要通过+=、-=来实现同样的效果。

6、流程控制语句以及方法和函数

1、if else表达式

scala中没有三目运算符,因为根本不需要。scala中if else表达式是有返回值的,如果if或者else返回的类型不一样,就返回Any类型(所有类型的公共超类型)。

例如:if else返回类型一样

val a = 20
val aResult = if(a > 10){
  10
}else{
  30
}
println(aResult)
//结果:
aResult: Int = 10

if else 返回类型不一样

val b = 20
val bResult = if(b > 10){
  "hello"
}else{
  30
}
println(aResult)
//结果:
bResult: Any = hello world

2、whie循环

//while循环没有任何返回值类型,关注的是执行的过程
var e  = 1 
val f = while(e <= 10){
    e += 1 
} 
println(f) 
//结果:
c: Int = 1
d: Unit = ()

//循环终止
/*
scala当中while循环的contine和break:注意:scala当中并没有提供类似于java的continue和break操作,如果需要终止循环,我们可以有以下几种方式
1、使用Boolean标识来进行终端
2、使用嵌套函数,从函数中直接return
3、使用Breaks对象当中的break方法
*/
var g = 10 
val loop = new Breaks 
loop.breakable{   
    val h =  while(g <=20){
        g +=1    
        println(g)     
        if(g == 15){       
            loop.break()     
        }   
    }
 println(h)
}

3、for循环

//使用to实现左右两边均为闭合访问
for(i <- 1 to 3;j <-  1 to 5){
  println(i * j )
}
//1 2 3 4 5 2 4 6 8 10 3 6 9 12 15

//使用util实现左右两边分别为前闭后开的访问
for(i <- 1 until 5; j <- 1 until 4){
  println(i * j )
}
//1 2 3 4 2 4 6 8

//引入保护模式(也称条件判断式)在for循环条件里加入判断表达式.如果满足则进入for循环,如果不满足则不进入for循环.相当于java中的continue
for(i <- 1 to 3 if(i != 2)){
  println("hello")
}
//hello hello

//引入变量
for(i <- 1 to 3 ; j = i-1){
  println(i * j )
}
//0 2 6

//将遍历过程中处理的结果返回一个变量,使用yield关键字接收
val for5 = for(i <- 1 to 5) yield  i
println(for5)
Vector(1, 2, 3, 4, 5)
//Vector是一个集合,将for循环中的数据取出放置到集合中

//使用大括号代替小括号
for{
  i <- 1 to 5
  j = 5-i
}
  println( i* j +"myij")

7、scala当中调用函数与方法(重点)

scala是一个面向函数式的编程语言,同时兼容面向对象的编程的特性

调用函数,求平方根

//表示将scala下的math方法都导进来
import scala.math._
sqrt(100)

调用方法,静态方法(scala中没有静态方法这个概念,需要通过半生类对象来实现)
//生成一个数据的素数

BigInt.probablePrime(16, scala.util.Random)

//调用方法,非静态方法,使用对象调用
//去重

"Hello World".distinct
//结果:
Helo Wrd

// apply与update方法
//apply方法是调用时可以省略方法名的方法。用于构造和获取元素:
"hello"(4)
"hello".apply(4)
//结果:
res17: Char = o
res18: Char = o
//"hello"(4)等同于"hello".apply(4)

Array(1,2,3)
Array.apply(1,2,3)
//结果:
res19: Array[Int] = Array(1, 2, 3)
res20: Array[Int] = Array(1, 2, 3)
//Array(1,2,3)等同于Array.apply(1,2,3)

//update方法也是调用时可以省略方法名的方法,用于元素的更新:

val arr1 = new Array[Int](5)

//默认是Array(0,0,0,0,0)
arr1(1) = 2
println(arr1.mkString(","))//0,2,0,0,0  
arr1.update(1, 4)
println(arr1.mkString(","))//0,4,0,0,0  

8、scala当中函数与方法的定义 重点

1、定义方法

方法与函数的定义形式不一样。方法就是函数,函数也是对象

方法定义的形式:

def 方法名(参数1:参数类型1,参数2:参数类型2):返回值的类型 = {

方法体

}

需求:定义一个方法,方法名是hello,两个参数,一个参数int类型,一个参数是string,返回值int类相关

示例一:定义一个最标准的方法,且定义方法的返回值类型为Int类型

def hello(first:String,second:Int) :Int = {
  second
}
//因为设置了返回值类型为Int,所以在块中{}要设置Int类型的数据

示例二:定义一个方法,且不定义返回值

注意:如果定义的方法没有返回值,那么方法的返回值会做自动推断。根据我们方法的最后一个返回类型来推断我们的方法返回类型

def  hello2(first:Int , second:String) ={
  println(first)
  20
}
//结果:  Int

//调用hello2的方法,方法中需要输入参数(20,"abc");得到是方法体中的最后的数据,使用一般变量接受,最后输出结果
val hello2Result = hello2(20,"abc")
println( hello2Result)

示例三:定义一个方法,不定义返回值,可以通过自动推断,返回不同类型的值

def hello3(first:Int,second:String) ={
  if(first > 10){
    first
  }else{
    second
  }
}
val hello3Result = hello3(5,"helloworld")
//如果参数不按照顺序输入,则需要带上参数名
val hello3Result2 = hello3(second = "hello",first=10)
println(hello3Result)
//结果:
helloworld
//根据输入的参数和判断条件,进行判断输出结果

示例四:定义一个方法,参数给定默认值,如果不传入参数,就使用默认值来代替

def hello4(first:Int = 10,second:String)={
  println(first+"\t"+ second)
}
//注意我们在调用方法的时候我们可以通过参数名来指定我们的参数的值  
hello4(second="helloworld")
//结果:  10   helloworld

示例五:变长参数,方法的参数个数不定的,类似于java当中的方法的...可变参数

def hello5(first:Int * ):Int={
  for (i <- first){
    println(i)
  }
  20
}
hello5(1,2,3,4,5)
//结果:1 2 3 4 5

示例六:递归函数。我们可以定义一个方法,使得方法自己调用自己,形成一个递归函数,但是方法的返回值类型必须显示的手动指定

//scala当中的递归一定要定义返回值
//scala当中的递归如果需要退出,一定要满足一定的条件,判断条件不能太多,最好一个
def  hello6(first:Int):Int={
  if(first <= 1){
    1
  }else{
    first * hello6(first -1)
  }
}
//10*hello6(9)-->9*hello6(8)-->8*hello6(7)-->7*hello(6)-->....
val hello6Result = hello6(10)
println(hello6Result)

示例七:定义一个方法,没有显示的指定返回值,那么我们方法当中定义的等号可以省掉

注意:如果省掉了=号,那么这个方法强调的就是一个代码执行的过程

/**
  * 定义了一个方法,省掉方法定义的=号,如果省掉 = 号,
  * 那么这个方法强调的是一个过程,代码执行的过程,
  * 不会产生任何的返回值
  */
def hello7(first:Int){
  println(first)
  30
}
hello7(20)
res4: Unit = ()

示例八:直接通过def定义一个方法

//定义方法
def hello8() = {10}
def hello8() = 10
def hello8=10;
//定义变量
val hello8Result = hello8
println(hello8Result)

示例九:如果方法体当中只有一行代码,我们也可以省掉大括号

def hello10(first:Int,second:Int) = first+second
val hello10Result = hello10(10,20)
println(hello10Result)

定义方法使用的关键字

2、定义函数

关键字 =>

简单的认为,看到def就是方法,看到 => 就是一个函数

函数定义的两种形式

第一种形式:

val 函数名 = (参数名1:参数类型1,参数名2:参数类型2) => {函数体}

第二种形式:

val 函数名 :(参数类型1,参数类型2) => (返回类型) = {

函数体

}

示例一:定义一个标准函数,使用 =>来进行定义

val func1 =(x:Int,y:Int) =>{
  x+y
}
func1(2,8)

示例二:定义匿名函数。也就是我们可以定义一个没有名字的函数

定义一个匿名函数之后,这个函数就没法使用了

(x:Int,y:String) =>{x + y}
//结果:
Int = <function2>
function2:参数的个数

示例三:函数定义的另外一种形式,定义一个函数,参数只有一个且是Int类型,返回值也是Int类型

val func3:(Int,String)=>(String)=
{
  (x,y)=>y
}
//通过x,y接收变量,然后通过=>匿名函数对我们传入的参数进行操作,最后匿名函数的返回值,就是我们外层函数需要的返回值

示例四:定义一个函数,参数值是两个,分别是Int和String,返回值是一个元组,分别是String和Int

val func4:(Int,String) =>(String,Int) ={
  (x,y) => (y,x)
}
val func4Result = func4(10,"hello")
println(func4Result)
//结果:
func4Result: (String, Int) = (hello,10)
(hello,10)

3、方法与函数的区别,以及方法转换为函数

方法就是函数,函数也是对象

函数可以作为一个参数,传入到方法里面去

val myfunc=(参数名1,参数类型1)=>{函数体}
方法就是函数,函数也是一个对象
def hello(first:Int,myfunc):Int={方法体}
//Int、String等基本类型,在scala中参数就是对象,所以没有myfunc也是对象

val myfunc = (x:Int) => {
  x
}

val myfunc2 : (Int) => Int ={
  x=>x*x
}

def methodFunc (f:Int=>Int ):Int={
  f(100)
}
val myMethodResult=methodFunc(myfunc2)
println(myMethodResult)

方法可以自动转换成函数作为参数传递到方法里面去

def method2(x:Int) ={ x * x }
def methodFunc2(x:Int => Int):Int ={
  x(100)
}
//这里传入的是方法method2,方法是没办法作为方法参数的,必须要将方法转换为函数,
val methodFunc2Result = methodFunc2(method2)
println(methodFunc2Result)

通过 _ 自动将方法转换成函数
def  method3(x:Int,y:String)={
  x
}
println(method3 _)
//<function2>

懒值加载

//懒值加载,申明的值不会马上分配内存空间,等到要调用的时候再去计算
def init(): String = {
  println("init方法执行")
  "嘿嘿嘿,喵喵喵~"
}
//调用init,赋值给msg,这是msg是没有值的,只有println(msg)时,才会执行init方法
lazy val msg = init()
println("lazy方法没有执行")
println(msg)
//结果:
lazy方法没有执行
嘿嘿嘿,喵喵喵~

9、scala当中的数据结构

scala当中的不可变

1、数组的定义与使用

//定义定长数组
val  array = new Array[Int](10)
println(array)//默认填充;10个0
println(array(1))//0

array(1) = 10
array(2) = 20

println(array(1))//10

//通过伴生对象来创建数组,Array是个对象
val  array2 = Array(1,2,3,4,5)
println(array2(2))//3
 
//变长数组
val array3 = new ArrayBuffer[Int]()
array3.append(1,2,3,4,5)
println(array3.mkString(","))//1,2,3,4,5

//需求,一个定长的数组,需要多添加几个元素(append)
//将定长数组,转换成变长数组
val toBuffer: mutable.Buffer[Int] = array.toBuffer
toBuffer.append(20,30,40)
println(toBuffer)

//变长数组转换成为定长的数组
val toArray: Array[Int] = array3.toArray
//转换为定长数组后,不能再使用append添加字符

//多维数组
val dim = Array.ofDim[Double](3,4)
dim(1)(1) = 11.11
println(dim.mkString(","))

//数组遍历
val array5  = ArrayBuffer(1,2,3,4,5,6)
//单层for循环   1 to 5
for(x <- array5){
  println(x )
}


val array6 = Array(1,2,4,3,5,6)
array6.max

array6.min

array6.sum

array6.sorted

2、元组的创建与遍历

在scala当中提供元组tuple的数据类型,可以理解tuple为一个容器,可以存放各种不同的数据类型的数据,一个Tuple中既可以存放String类型数据同时也可以存放Int类型数据

注意:元组一旦创建之后,就是不可变的,也就是元组中不能添加和删除元素

(1)创建元组,元组里面可以放置各种类型数据

val tuple1 = ("helloworld","zhangsan",28,5.0f)

//数据的访问,通过 _下标来进行访问,下标是从1开始的

println(tuple1._1)

元组的遍历

for(y <- tuple1.productIterator){
  println(y)
}

tuple1.productIterator.foreach(x => println(x))

3、Map集合

map集合的操作

//不可变的Map
val  map1 = Map("hello" -> "zhagnsan","age" ->  28 ,"address" -> "地球村")

//可变的map
val map2 = scala.collection.mutable.Map("hello" -> "world","age" -> 28)
//添加元素
map2.+= ("name" -> "zhangsan")
println(map2)
//删除元素
map2.-= ("name")

//更新元素
//通过key的覆盖来更新值
map2 +=("hello" -> "scala")
println(map2)

//如果没有这个key,都会进行添加
//如果有这个key,都会进行覆盖
map2 +("hello" ->"china","phonNo" -> "13688886666")

map2 +=("sex" ->"女","hello" ->  "你好")


map2.get("name")//zhangsan
map2.get("nameabc")//none  没有值
//scala当中的option  some  none

//使用key来进行获取值,如果获取到了,那么就直接取出来,如果获取不到,那么就用后面的默认值
map2.getOrElse("name","120")
//遍历key和value
//表示遍历map2集合,将key赋值给x,将value赋值给y
for((x,y) <- map2){
  println(x )
  println(y)
}
//遍历所有的key
for(x <- map2.keys){
  println(x )
}

//遍历所有的value
for(x <- map2.values){
  println(x)

}

//打印key,value对
for(p <- map2){
  println(p)
}
//结果:
(address,nanjing)
(sex,nv)
(hello,lisi)

5.将对偶的数组转换成map
//("name","zhangsan")可以看做为一个元组tuple
val arrayMap = Array(("name","zhangsan"),("age",20))

//("name","zhangsan")也可以看成一个键值对
val toMap: Map[String, Any] = arrayMap.toMap
println(toMap)
Map(name -> zhangsan, age -> 20)

4、list集合

注意:列表当中的元素类型可以是不同的,与元组类似,但是列表当中的元素是可以删减的

1.创建一个列表
val list1 = List("hello",20,5.0f)
println(list1)//List(hello, 20, 5.0)
2.访问列表当中的元素
println(list1(0))//hello
3.添加元素
//加在末尾
val list2 = list1 :+ 100
//List(hello, 20, 5.0, 100)
//加在起始
val list3 = 100 +: list1
List(100, hello, 20, 5.0)
4.List的创建与追加元素
//尾部添加了Nil,那么就会把元素和List集合放在一个新的集合当中
val list4 = 1 ::2 ::3 :: list1 ::Nil
//List(1, 2, 3, List(hello, 20, 5.0))

//将元素放置集合当中
val list5 = 1::2 ::3 :: list1
//List(1, 2, 3, hello, 20, 5.0)


val list6 = list1 :: 1 ::2 :: 3 :: Nil
//List(List(hello, 20, 5.0), 1, 2, 3)
5.可变的列表list
val list6 = new ListBuffer[String]
list6.append("hello")
list6.append("abc")

5、set集合常用操作

set集合是不重复元素的集合,不保留顺序,默认是以hash集实现

//不可变集合
val set1 = Set("1","1",2,2)
println(set1.mkString(","))

import scala.collection.mutable.Set
//可变set
//添加元素
val set2 = Set (1,2,3,4)
set2.add(5)
val value :mutable.Set[Int]=set2.+=(6)
println(set2.mkString(","))
//1, 5, 2, 6, 3, 4

val ints:mutable.Set[Int]=set2.+(7,8)
println(ints)
//Set(1, 5, 2, 6, 3, 7, 4, 8);创建的是一个新的集合
println(set2.mkString(","))
//1,5,2,6,3,4;所以使用.+不会改变set2的元素

//删除元素
set2 -= (8)
set2.remove(7)


//set集合当中遍历元素
for(x <-  set2){
  println(x)
}

序号 方法 描述
1 def +(elem: A): Set[A] 为集合添加新元素,并创建一个新的集合,除非元素已存在
2 def -(elem: A): Set[A] 移除集合中的元素,并创建一个新的集合
3 def contains(elem: A): Boolean 如果元素在集合中存在,返回 true,否则返回 false。
4 def &(that: Set[A]): Set[A] 返回两个集合的交集
5 def &~(that: Set[A]): Set[A] 返回两个集合的差集
6 def ++(elems: A): Set[A] 合并两个集合
7 def drop(n: Int): Set[A]] 返回丢弃前n个元素新集合
8 def dropRight(n: Int): Set[A] 返回丢弃最后n个元素新集合
9 def dropWhile(p: (A) => Boolean): Set[A] 从左向右丢弃元素,直到条件p不成立
10 def max: A 查找最大元素
11 def min: A 查找最小元素
12 def take(n: Int): Set[A] 返回前 n 个元素

6、集合元素与函数的映射

使用map方法,传入一个函数;然后将这个函数作用在集合当中的每一个元素上

//集合当中的元素与函数的映射
val list = List("zhangsan","lisi","wangwu")
val list1 = list.map(x => x+"  ni hao")
println(list1)
val list2 = list1.map(_.toUpperCase)
println(list2)
//结果重新定义了一个集合


//在scala当中规定,如果变量x   在  =>右边只使用了一次,那么可以使用下划线代替
println(listFunc.map(x => x.toUpperCase()))
println(listFunc.map(_.toUpperCase()))
//scala当中的下划线 val  tuple2  = Tuple(1,2,3,)
//tuple2._2;



val listFunc2 = List("address")
//压平的操作
val flatten: List[Char] = listFunc2.flatten
//结果:字符串变为字符;a,d,d,r,e,s,s

listFunc2.flatMap(_ + "world")
//addressworld
//压平 -->  a,d,d,r,e,s,s,w,o,r,l,d

7、队列与折叠化简和扫描

#####1.创建队列
val queue1 = new  mutable.Queue[Int]()
println(queue1)
//结果:Queue()
2.队列当中添加元素
queue1 += 1
//队列当中添加集合
//添加集合,会将集合当中的元素全部取出来,放到队列里面去
queue1 ++= List(1,2,3)
println(queue1)
//Queue(1, 1, 2, 3)
3.按照进入队列顺序,删除队列当中的元素
val dequeue: Int = queue1.dequeue()
println(dequeue)
//结果:1
//结果:Queue(1, 2, 3)
4.向队列当中加入元素
queue1.enqueue(4,5,6,7,8)
println(queue1)
//Queue(1, 2, 3, 4, 5, 6, 7)
5.获取队列第一个与最后一个元素
println(queue1.head)
println(queue1.last)
6.折叠化简与扫描操作

1.折叠、化简、reduce操作

val reduceList = List(1,2,3,4,5)
//MapReduce当中  reduce方法
reduceList.reduceLeft((x,y) => x-y)
//Int = -13,默认值时0,将集合里面的值依次赋值给函数中

1.折叠、化简、folder操作

reduce的本质其实就是fold操作,只不过我们使用fold操作的时候,需要指定初始值��

val  foldList = List(1,9,2,8)
val foldResult = foldList.fold(10)((x,y) => x+y)
println(foldResult)
//Int=30  将集合中的元素依次赋值给函数,再和默认的值相加
//50-1-9-2-8 = 30
val foldLeftResult = foldList.foldLeft(50)((x,y) => x-y)
println(foldLeftResult)

8、拉链操作

/拉链操作
val  zipList1 = List("name","age","sex")
val zipList2 = List("zhangsan",28)
//zipList1.zip(zipList2)
val zipResult = zipList1 zip zipList2
println(zipResult)
//List((name,zhangsan), (age,28))  多余的删除掉

//将集合转换成map结合
val zipMap: Map[String, Any] = zipResult.toMap
println(zipMap)

9.迭代器

val listIterator = List(1,2,"zhangsan")
val iterator: Iterator[Any] = listIterator.iterator
while(iterator.hasNext){
  println( iterator.next())
}

10、高阶函数(重点)

使用函数作为参数的方法,叫做高阶函数

  def main(args: Array[String]): Unit = {
    //定义一个函数
    val muFunc=(x:Int) =>{x*x}   
    //map[B](f: A => B)  map中的参数为一个函数
    //val myArray = Array(1,2,3,4).map((x:Int) =>{x*x})
    //val myArray = Array(1,2,3,4).map(_*2)  如果=>右边参数只出现一次,可以使用_   
    val myArray = Array(1,2,3,4).map(muFunc)
    println(myArray.mkString(","))
  }

    //方法就是函数,函数也是对象
  def main(args: Array[String]): Unit = {
    //定义一个方法
    def myMethod (x:Int):Int={x*x}
    val myArray = Array(1,2,3,4).map(myMethod)
    println(myArray.mkString(","))
  }

//注意在java中不能在方法中定义方法,在scala中,可以把方法看做对象

匿名函数

匿名函数语法:=>左边是参数列表,右边是函数体;参数类型可省略

val func =(x:Int,y:Int) =>x+y
println(func)
//<function2>
println(func(1,2))
//3

val func3:(String,Int)=>(String)={
      (x,y)=>y+x
    }
println (x:Int,y:Int)=>{x*y}
//function2

    val func3:(String,Int)=>(String)={
      (x,y)=>y+x
    }

    def myMethod3 (func3:(String,Int)=>(String)):String={
      //{}中的方法体,需要的是符合返回值类型的元素
      val hellonew : String= func3("张三",20)
      hellonew
      //"hello"
    }
    //myMethod中要调用函数,并且已经定义好了要传入函数的框架,和返回值类型
    //而func3正好符合myMethod3的函数框架,而且还有具体的函数体
    val method : String = myMethod3(func3)
    println(method)
2、高阶函数同样可以返回一个函数类型
//def func(参数名:参数类型)={方法体}-->如果只有一个参数,{}可以省略  
def myFunc4(x:Int) = (y:String) => y
//方法的返回值是一个匿名函数
  println(myFunc4(50))
//结果:<function1>

参数类型推断

val array  = Array(1,2,3,4,5,6,7,8,9)
  //map当中需要传入一个函数,我们可以直接定义一个函数
  array.map((x:Int) => x * 2 )
  //进一步化简 传入的是int类型,参数推断省去类型信息
  array.map((x) => x * 2 )
  //进一步化简  单个参数可以省去括号
  array.map( x => x * 2 )
  //进一步化简  如果变量只在=>右边只出现一次,可以用_来代替
  array.map( 2 * _ )

闭包和柯里化(了解)

柯里化是把接受多个参数的函数变换成接受一个单一参数的函数

新的函数返回一个以原有第二个参数为参数的函数。

  //柯里化的定义形式
 def main(args: Array[String]): Unit = {
  //柯里化的定义形式
  def kery(x:Int)(y:Int):Int={
    x + y
  }
  println(kery(3)(5))
  //柯里化的推导过程,注意方法的返回值不要定义任何的返回值类型
     /*
     val keryResult = (x:Int) => Unit{(y:Int) => {x + y}}
     (x:Int)为参数
     (y:Int) => {x + y}为函数体,函数体又是一个匿名函数,匿名函数可以作为方法的参数,就可以个匿名参数赋值
     输入参数x20  -->(y:Int)=>(20+y),执行后得到还是一个函数,函数还可以作为参数传入方法当参数,就可以输入参数y
     最终转换成为柯里化的形式
     */
val keryResult = (x:Int) => (y:Int) => {x + y}

  def keryMethod(x:Int) ={
      //注意,方法当中的函数,调用了方法的参数,就叫做闭包
    (y:Int) => x+ y
  }
  println(keryMethod(20))
  println(keryMethod(20)(10))  
}

11、scala当中的类

1、类的定义与创建

创建一个scala class来定义我们的一个类。类当中可以定义各种属性或者方法,或者函数都可以

class Person {
  //定义一个属性,叫做name的,使用val不可变量来进行修饰
  // 用val修饰的变量是可读属性,有getter但没有setter(相当与Java中用final修饰的变量)
  val name:String ="zhangsan"
  //定义一个属性,叫做age的,使用var可变量来进行修饰
  //用var修饰的变量都既有getter,又有setter
  var age:Int = 28

  //类私有字段,只能在类的内部使用或者伴生对象中访问
  private val  address:String = "地球上"

  //类私有字段,访问权限更加严格的,该字段在当前类中被访问
  //在伴生对象里面也不可以访问
  this:表示当前类
  private[this] var pet = "小强"

  //在类当中定义了一个方法,
  def hello(first:Int,second:String):Int ={
    println(first+"\t"+second)
    250
  }
    
  // 定义了一个函数
  val func1 =(x:Int,y:Int) =>{
    x+y
  }
}
2、类的实例化以及使用

如果想要使用类的话,那么REPL就满足不了我们的要求了,我们重新创建一个对应的Object的scala文件

object ScalaClass {
  def main(args: Array[String]): Unit = {
    //创建对象两种方式。这里都是使用的无参构造来进行创建对象的
    val person = new Person
    val person1 = new Person()
    //注意,我们可以使用对象的属性加上_= 给var修饰的属性进行重新赋值
    //其实就是调用set方法,方法名叫做 age_=    
    person.age_= (50)
    //直接调用类的属性,其实就是调用get方法
    println(person.age)
    println(person.hello(50,"helloworld"))
    val func = person.func1(10,20)
    println(func)
  }
}
3、属性的getter和setter方法

对于scala类中的每一个属性,编译后,会有一个私有的字段和相应的getter、setter方法生成

val只生成getter方法

//getter方法
println(person age)
//setter方法
println(person .age_= (18))
//getter方法
println(person.age)
4、类的构造器

scala当中类的构造器分为两种:主构造器和辅助构造器

scala当中规定,所有的辅助构造器,最后都必须调用另外一个构造器,另外一个构造器可以是辅助构造器,也可以是主构造器

//主构造器,直接定义在类上面
class Dog (name:String,age:Int){

  //在scala当中,可以直接将代码写在class当中,而在java当中,代码必须包含在方法当中。
  //其实在scala当中,虽然你把代码写在了Class类当中,经过编译之后,class类的代码都进入到了主构造器方法当中去了
    /*
    class Dog{
    public Dog(String name,int age)
     println(name)
     println(age)
    }
    */
  println(name)
  println(age)

  var gender:String = "";
    //使用this关键字定义构造器
   def this(name:String,age:Int,gender:String){
    //每个辅助构造器,都必须以其他辅助构造器,或者主构造器的调用作为第一句
    this(name:String,age:Int)
    this.gender = gender
  }

  var color ="";

//我们也可以通过private来进行修饰我们的构造器,

  private def this(name:String,age:Int,color:String,gender:String){
    this(name:String,age:Int)
    this.color = color
  }
}

12、scala当中的对象

1、scala当中的Object

在scala当中,没有类似于像java当中的static修饰的静态属性或者静态方法或者静态代码块之类的,但是我们可以通过scala当中的Object来实现类似的功能。可以理解为scala当中的Object里面的属性或者方法都是静态的,可以直接调用

定义一个class类,然后在class类当中定义一个Object的对象。object对象当中的所有属性或者方法都是静态的

class Session {
  def hello(first:Int):Int={
    println(first)
    first
  }
}
object SessionFactory{
  val session = new Session
  def getSession():Session ={
    session
  }
  def main(args: Array[String]): Unit = {

    for(x <- 1 to 10){
      //通过直接调用,产生的对象都是单列的
      val session = SessionFactory.getSession()
      println(session)
    }
  }
}
2、伴生类与伴生对象

如果有一个class文件,还有一个与class同名的object文件,那么就称这个object是class的伴生对象,class是object的伴生类;

伴生类和伴生对象必须存放在一个.scala文件中;

伴生类和伴生对象的最大特点是,可以相互访问;

举例说明:

class ClassObject {
  val id = 1
  private var name = "itcast"
  def printName(): Unit ={
    //这里是直接调用对象的静态属性
    println(ClassObject.CONSTANT + name )
  }
}

object ClassObject{
  //伴生对象中的私有属性
  private val CONSTANT = "汪汪汪 : "
  def main(args: Array[String]) {
      //这里是new类中的对象
    val p = new ClassObject
    //访问私有的字段name
    p.name = "123"
    p.printName()
  }
}
3、scala当中的apply方法

object 中非常重要的一个特殊方法,就是apply方法;

apply方法通常是在伴生对象中实现的,其目的是,通过伴生类的构造函数功能,来实现伴生对象的构造函数功能;

通常我们会在类的伴生对象中定义apply方法,当遇到类名(参数1,...参数n)时apply方法会被调用;

在创建伴生对象或伴生类的对象时,通常不会使用new class/class() 的方式,而是直接使用 class(),隐式的调用伴生对象的 apply 方法,这样会让对象创建的更加简洁;

举例说明:

//定义一个class类,主构造器传入一个参数
class ApplyObjectClass (name:String){
  println(name)
}

object ApplyObjectClass{
  //复写apply方法,
  def apply(name:String): ApplyObjectClass = {
    new ApplyObjectClass(name)
  }
  def main(args: Array[String]): Unit = {
    //通过半生对象,直接调用apply来获取我们的一个半生类,就可以直接调用类中的属性和方法
    val applyObjectClass = ApplyObjectClass("lisi")
    //调用的是new  Class来创建对象
    val applyObjectClass2 =new ApplyObjectClass("wangwu")
      
      //直返回一个Array,底层调用的是apply方法,new Array[Int](5)
      val myArray = Array(1,2,3,4,5)
  }
}
4、scala当中的main方法
object MainDemo1 {
  def main(args: Array[String]): Unit = {
    println("我是main方法")
  }
}
//继承了App的特质,代码最后输出也是调用源文件中的main方法,可以连续多次打印输出
object  MainDemo2 extends App{
  println("我是第一行代码")
  println("我是第二行代码")
}

13、scala当中的继承

1、Scala中继承(extends)的概念

Scala 中,让子类继承父类,与 Java 一样,也是使用 extends 关键字;

继承就代表,子类可继承父类的 field 和 method ,然后子类还可以在自己的内部实现父类没有的,子类特有的 field 和method,使用继承可以有效复用代码;

子类可以覆盖父类的 field 和 method,但是如果父类用 final 修饰,或者 field 和 method 用 final 修饰,则该类是无法被继承的,或者 field 和 method 是无法被覆盖的。

private 修饰的 field 和 method 不可以被子类继承,只能在类的内部使用;

field 必须要被定义成 val 的形式才能被继承,并且还要使用 override 关键字。 因为 var 修饰的 field 是可变的,在子类中可直接引用被赋值,不需要被继承;即 val 修饰的才允许被继承,var 修饰的只允许被引用。继承就是改变、覆盖的意思。

Java 中的访问控制权限,同样适用于 Scala

        类内部     本包      子类      外部包 

public √ √ √ √

protected √ √ √ ×

default √ √ × ×

private √ × × ×

class Person {
  val name="zhangsan"
  def getName=this.name
}
class Student extends Person{
  //通过override,重写name属性覆盖父类的field或method
  override
  val name="lisi"
  override def getName: String = {
    "wangwu"
      
  //子类可以定义自己的field和method
  val score="A"
  def getScore=this.score
}
2、Scala中override 和 super 关键字

如果在子类中要调用父类中被覆盖的方法,则必须要使用 super 关键字,显示的指出要调用的父类方法。
举例说明:

class Person1 {
  private val name = "leo"
  val age=50
  def getName = this.name
}
class Student1 extends Person1{
  private val score = "A"
  //子类可以覆盖父类的 val field,使用override关键字
  override
  val age=30
  def getScore = this.score
  //覆盖父类非抽象方法,必须要使用 override 关键字
  //同时调用父类的方法,使用super关键字
  override def getName = "your name is " + super.getName
}
3、Scala中isInstanceOf 和 asInstanceOf

java:
Class B extends Class A
Class C
B instanceOf A => 如果判断 B是A的子类 (A)B
C instanceOf A ==> 如果判断 C不是A的子类 不能强转
java当中只提供了子类判断是否属于父类形 instanceOf,没有提供判断一个类是否是另外一个类的父类的方法

scala:(可以判断A是B的父类)
isInstanceOf和asInstanceOf:
如果判断 A isInstanceOf B 为true
可用调用A asInstanceOf B 做类型转换

class Person {}
class Student extends Person
object Student{
    def main (args: Array[String] ) {
    val p: Student = new Student
    var s: Student = null
    //如果对象是 null,则 isInstanceOf 一定返回 false
    println (s.isInstanceOf[Student3])
    
    // 判断 p 是否为 Student3 对象的实例
  if (p.isInstanceOf[Student3] ) {
    //把 p 转换成 Student3 对象的实例
      s = p.asInstanceOf[Student3]
  }
  println (s.isInstanceOf[Student3] )
  }
4、Scala中getClass 和 classOf

isInstanceOf 只能判断出对象是否为指定类以及其子类的对象,而不能精确的判断出,对象就是指定类的对象;

如果要求精确地判断出对象就是指定类的对象,那么就只能使用 getClass 和 classOf 了;

p.getClass 可以精确地获取对象的类,classOf[XX] 可以精确的获取类,然后使用 == 操作符即可判断;

举例说明:

class Person4 {}
class Student4 extends Person4
class Student5 extends Person4
//类与类型:
//类就是具体到某一个指定的Class里面去
//类型:可以这带很多,包括子类:Person、Student4、Student5
object Student4{
  def main(args: Array[String]) {
    val p:Person4=new Student4
    //判断p是否为Person4类的实例
    println(p.isInstanceOf[Person4])//true
    //判断p的类型是否为Person4类
    println(p.getClass == classOf[Person4])//false
      //其底层比较的是.class字节码,Student的字节码不等于Person的字节码
    //判断p的类型是否为Student4类
    println(p.getClass == classOf[Student4])//true
  }
5、Scala中使用模式匹配进行类型判断
class Person5 {}
class Student5 extends Person5
class Student6
object Student5{
  def main(args: Array[String]) {
    val p1:Person5=new Student5
      
    val p2:Student6 = new Student6
      
    p1/p2 match {
      // 匹配是否为Person类或其子类对象
      case per:Person5 => println("This is a Person5's Object!")
      // 匹配所有剩余情况
      case _  =>println("Unknown type!")
    }
  }
    //p1:判断该对象为子类对象
    //p2:判断该对象不为子类对象
6、Scala中protected

跟 Java 一样,Scala 中同样可使用 protected 关键字来修饰 field 和 method。在子类中,可直接问父类的 field 和 method,而不需要使用 super 关键字;

还可以使用 protected[this] 关键字, 访问权限的保护范围:只允许在当前子类中访问父类的 field 和 method,不允许通过其他子类对象访问父类的 field 和 method。

举例说明:

class Person6{
  protected var name:String="tom"
  protected[this] var hobby:String ="game"
  protected def sayBye=println("再见...")
}
class Student6 extends Person6{
  //父类使用protected 关键字来修饰 field可以直接访问
  def  sayHello =println("Hello "+name)
  //父类使用protected 关键字来修饰method可以直接访问
  def  sayByeBye=sayBye
  def makeFriends(s:Student6)={
    println("My hobby is "+hobby+", your hobby is UnKnown")
  }
}
object Student6{
  def main(args: Array[String]) {
    val s:Student6=new Student6
    s.sayHello
    s.makeFriends(s)
    s.sayByeBye
  }
7、Scala中调用父类的constructor

Scala中,每个类都可以有一个主constructor和任意多个辅助constructor,而且每个辅助constructor的第一行都必须调用其他辅助constructor或者主constructor代码;因此子类的辅助constructor是一定不可能直接调用父类的constructor的;

只能在子类的主constructor中调用父类的constructor。

如果父类的构造函数已经定义过的 field,比如name和age,子类再使用时,就不要用 val 或 var 来修饰了,否则会被认为,子类要覆盖父类的field,且要求一定要使用 override 关键字。

举例说明:

class Person7(name:String ,age:Int) {
  var score:Double = 0.0

  //定义一个辅助构造器,调用我们的主构造器
  def this(name:String,score:Double){
    this(name,30)
    this.score = score
  }
//再定义一个辅助构造器,
  def this(name:String,address:String){
    this(name,100.0)
    this.address = address
  }
}

//如果我们继承的class类没有空的构造器,那么我们在继承的时候,必须将参数给传过去
//子类的主构造器,可以调用父类的主构造器,或者父类的辅助构造器
class Student7(name:String,score:Double) extends Person7("zhangsan",23 ){
    var  myaddress = "上海"

  //注意:子类的辅助构造器,只能够调用子类的主构造器,或者子类的辅助构造器
  //不能够调用父类的辅助构造器或者,或者父类的主构造器
    def  this(name:String,myaddress:String){
      //this(name,"tianjin")
    this(name,23.0)
    }
}

object  Student7{
  def main(args: Array[String]): Unit = {
    val student = new Student7("zhangsan","lisi")
    println(student)
  }
}
8、Scala中抽象类(了解)

如果在父类中,有某些方法无法立即实现,而需要依赖不同的子类来覆盖,重写实现不同的方法。此时,可以将父类中的这些方法编写成只含有方法签名,不含方法体的形式,这种形式就叫做抽象方法;

一个类中,如果含有一个抽象方法或抽象field,就必须使用abstract将类声明为抽象类,该类是不可以被实例化的;
在子类中覆盖抽象类的抽象方法时,可以不加override关键字;
举例说明:

abstract class Person9(val name:String) {
  //必须指出返回类型,不然默认返回为Unit
  def sayHello:String
  def sayBye:String
}
//Student9不是抽象类,继承抽象类必须实现抽象类的方法
class Student9(name:String) extends Person9(name) {
  //必须指出返回类型,不然默认
  def sayHello: String = "Hello," + name

  def sayBye: String = "Bye," + name
}
object Student9{
  def main(args: Array[String]): Unit = {
  val name = new Student9("李四")
    val hello: String = name.sayHello
    val bye: String = name.sayBye
    println(hello+","+bye)
  }
}

14、scala当中的特质trait

1、将trait作为接口使用
Ø Scala中的trait是一种特殊的概念;
Ø 首先先将trait作为接口使用,此时的trait就与Java中的接口 (interface)非常类似;
Ø 在trait中可以定义抽象方法,就像抽象类中的抽象方法一样,只要不给出方法的方法体即可;
Ø 类可以使用extends关键字继承trait,注意,这里不是 implement,而是extends ,在Scala中没有 implement 的概念,无论继承类还是trait,统一都是 extends;
Ø 类继承后,必须实现其中的抽象方法,实现时,不需要使用 override 关键字;
Ø Scala不支持对类进行多继承,但是支持多重继承 trait,使用 with 关键字即可。
Ø 举例说明:

//将特质当做接口来使用,我们子类通过extends来继承特质,并重写特质当中的方法
trait HelloTrait {
  def sayHello:Unit
}

trait MakeFriends{
  def makeFriends(ch:Children):Unit
}

class Children(val name:String) extends HelloTrait with MakeFriends with Cloneable with Serializable{
  override def sayHello: Unit = println("hello")
  override def makeFriends(c: Children): Unit = println("Hello, my name is " + this.name + ", your name is " + c.name)
}
 
object  Children{
  def main(args: Array[String]): Unit = {
    val zhangsan = new Children("zhangsan")
    val hello: Unit = zhangsan.sayHello
    val friends: Unit = zhangsan.makeFriends(zhangsan)
  }
}

特质当中的方法如果实现了方法体,在子类当中就不用覆写了,直接可以拿过来调用

/**
  * 比如 trait 中可以包含很多子类都通用的方法,例如打印日志或其他工具方法等等。
  * spark就使用trait定义了通用的日志打印方法;
  */
trait Logger {
  def log(message: String): Unit = println(message)
}
class PersonForLog(val name: String) extends Logger {
  def makeFriends(other: PersonForLog) = {
    println("Hello, " + other.name + "! My name is " + this.name + ", I miss you!!")
    this.log("makeFriends method is invoked with parameter PersonForLog[name = " + other.name + "]")
  }
}
object PersonForLog{
  def main(args: Array[String]) {
    val p1=new PersonForLog("jack")
    val p2=new PersonForLog("rose")
    p1.makeFriends(p2)
    //Hello, rose! My name is jack, I miss you!!
    //makeFriens method is invoked with parameter PersonForLog[name = rose]
  }
}

如果trait当中已经定义好了一个字段,且字段赋值了,在子类当中可以直接使用

trait PersonForField {
  val  age:Int=50
}

//继承 trait 获取的field直接被添加到子类中
class StudentForField(val name: String) extends PersonForField {
  def sayHello = println("Hi, I'm " + this.name + ", my  age  is "+ age)
}

object StudentForField{
  def main(args: Array[String]) {
    val s=new StudentForField("tom")
    s.sayHello
  }
}

如果特制当中的字段没有实现,那么我们必须在子类当中给实现了ٜ��

trait SayHelloTrait {
  //注意,这个字段,没有实现
  val msg:String
  def sayHello(name: String) = println(msg + ", " + name)
}

class PersonForAbstractField(val name: String) extends SayHelloTrait {
  //必须覆盖抽象 field
  val msg = "Hello"
  def makeFriends(other: PersonForAbstractField) = {
    this.sayHello(other.name)
    println("I'm " + this.name + ", I want to make friends with you!!")
  }
}
object PersonForAbstractField{
  def main(args: Array[String]) {
    val p1=new PersonForAbstractField("Tom")
    val p2=new PersonForAbstractField("Rose")
    p1.makeFriends(p2)
  }
}

在对象当中混入某个特质o

trait LoggedTrait {
  // 该方法为实现的具体方法
  def log(msg: String) = {}
}
trait MyLogger extends LoggedTrait{
  // 覆盖 log() 方法
  override def log(msg: String) = println("log: " + msg)
}

trait MyLogger2 {
  def MyNewMethod(name:String) ={
    println("hahhaha")
  }
}


class PersonForMixTraitMethod(val name: String) extends LoggedTrait {
  def sayHello = {
    println("Hi, I'm " + this.name)
    log("sayHello method is invoked!")
  }
}

/*class PersonForMixTraitMethod(val name: String) extends MyLogger {
  def sayHello = {
    println("Hi, I'm " + this.name)
    log("sayHello method is invoked!")
  }
}*/



object PersonForMixTraitMethod{
  def main(args: Array[String]) {
    val tom= new PersonForMixTraitMethod("Tom").sayHello //结果为:Hi, I'm Tom
    // 使用 with 关键字,指定混入MyLogger trait
    val rose = new PersonForMixTraitMethod("Rose") with MyLogger
    rose.sayHello
    val rose2 = new PersonForMixTraitMethod("Rose") with MyLogger2
    // 结果为:     Hi, I'm Rose
    // 结果为:     log: sayHello method is invoked!
  }
}

scala当中的特质:

1、 特质可以作为一个接口使用,里面定义的方法没有方法体
2、 特质里面的字段Field,如果不实现,就是一个抽象字段,子类一定要实现
3、 特质里面定义好的已经实现了的方法,子类可以拿过来直接使用
4、 特质里面定义好的field,已经赋值了,子类可以拿过来直接使用
5、 在实例对象中混入某个trait,扩展一些功能
6、 Trait的调用链 从右往左边调用
7、 Trait的构造器 从左往右边进行实例化
8、Trait继承class类

15、模式匹配和样例类

java中的swicth
switch (Type){
    case "1"
        sout("星期一")
        break;
    case "2"
        sout("星期二")
        break;
        .....
    defult
        sout("日期格式错误")
        break;
}

1、字符匹配

def main(args: Array[String]): Unit = {
  val charStr = '6'
  charStr match {
    case '+' => println("匹配上了加号")
    case '-' => println("匹配上了减号")
    case '*' => println("匹配上了乘号")
    case '/' => println("匹配上了除号")
    //注意:所有的模式匹配都必须最终匹配上一个值,如果没有匹配上任何值,就会报错
    case _  => println("都没有匹配上,我是默认值")
  }
}

2.字符串匹配

val myArray= Array ("hadoop","zookeeper","spark","hive")
//随机获得集合当中的字符串
val array: String = myArray(Random.nextInt(myArray.length))
    array match {
      case "hadoop" => println("hadoop")
      case "zookeeper" => println("zookeeper")
      case "spark" => println("spark")
      case "hive" => println("没有匹配值")
    }

3、守卫模式

    var ch ="500"
    var sign = 0
    ch match {
      case "+" => sign =1
      case "-" => sign =2
      case _ if ch.equals("300") => sign=3
      case _ => sign =4
    }
println(ch +" "+sign)

4、匹配类型

    val a = 3
    val obj = if(a == 1) 1
    else if(a == 2) "2"
    else if(a == 3) BigInt(3)
    else if(a == 4) Map("aa" -> 1)
    else if(a == 5) Map(1 -> "aa")
    else if(a == 6) Array(1, 2, 3)
    else if(a == 7) Array("aa", 1)
    else if(a == 8) Array("aa")
   val matchResult= obj match {
     case x:Int => x
     case s:String => s
     case BigInt => 3//没有定义参数名,不能匹配
     //泛型的擦除,如果匹配的类型,除了Array类型,其他的集合类型都会擦除
     //输入4时,Map的类型是String,Int,但是泛型会被擦掉Map(Any,Any)会匹配到m1Map[String,Int]
     //输入5时,Map(Any,Any)到m1时就会被拦截
     case m1:Map[String,Int] => "map1"
     case m2:Map[Int,String] => "map2"
     case a1:Array[Int] => Array(1)
     case a2:Array[Any] => "array[Any]"
     case a3:Array[String] => "array[String]"
     case _ => "没有任何匹配"
   }
    println(matchResult)


val myArray = Array(0,3,4)
myArray match {
    //缺少集合中的元素,不能够匹配
    case Array(0,2)  => println(x)
    //集合中固定元素不对应,不能匹配
    case Array (1,x,y) => println (x+y)
    
    //打印上述两个结果,会因被穿透而报错
}


    val list = List (0,1)
    list match{
      case 0::Nil => println("匹配方式1")//元素个数对应不上,不能匹配
      case 0::1::Nil => println("匹配方式2")
      case x::y::Nil => println(s"$x$y")//scala当中字符串特有一种拼接方式
      case 0::tail => println("匹配方式4")//只要第一个元素能够匹配上,就可以匹配
    }


val myTuple = (1,3,5)
myTuple match {
    case (1,x,y) => println (s"$x$y")
    case (_,z,5) => print(z)
}

5.样例类

case class SubmitTask(id:String,name:String)
case class HeattBeat(time:Long)
case object CheckTimeOutTask

object Chapter13 {
  def main(args: Array[String]): Unit = {
    val array = Array(SubmitTask("1","zhangsan"),HeattBeat(123),CheckTimeOutTask)
    val myArray = array(Random.nextInt(array.length))
    myArray match {
      case SubmitTask(x,y) => println(s"$x+$y")
      case HeattBeat(z) => println("HeattBeat")
      case CheckTimeOutTask => println("CheckTimeOutTask")
    }
  }
}

16、scala当中的Actor并发编程

第一个例子
怎么实现actor并发编程:
1、定义一个class或者是object继承Actor特质,注意导包import scala.actors.Actor
2、重写对应的act方法
3、调用Actor的start方法执行Actor
4、当act方法执行完成,整个程序运行结束

//定义一个类
class Actor1 extends Actor {
  override def act(): Unit = {
    for (i <- 1 to 10 ){
      println("actor1==="+i)
    }
  }
}
//定义一个对象
object Actor2 extends Actor {
  override def act(): Unit = {
    for (j <- 1 to 10 ){
      println("actor2==="+j)
    }
  }
}
//入口类
object Actor3 {
  def main(args: Array[String]): Unit = {
      //Actor1是一个类
    val actor = new Actor1
    actor.act()
      //Actor2相当于已经静态类
    Actor2.act()
  }
}

说明:上面分别调用了两个单例对象的start()方法,他们的act()方法会被执行,相同与在java中开启了两个线程,线程的run()方法会被执行

注意:这两个Actor是并行执行的,act()方法中的for循环执行完成后actor程序就退出

第二个例子
怎么实现actor发送、接受消息
1、定义一个class或者是object继承Actor特质,注意导包import scala.actors.Actor
2、重写对应的act方法
3、调用Actor的start方法执行Actor
4、通过不同发送消息的方式对actor发送消息
5、act方法中通过receive方法接受消息并进行相应的处理
6、act方法执行完成之后,程序退出

class MyActor2 extends Actor{
  override def act(): Unit = {
    receive{
      case "start" => println("start!!!")
    }
  }
}
object MyActor2{
  def main(args: Array[String]): Unit = {
    val actor = new MyActor2
    actor.start()
    actor ! "start"
  }
}

第三个例子
怎么实现actor可以不断地接受消息:
在act方法中可以使用while(true)的方式,不断的接受消息。

class MyActor3 extends Actor{
  override def act(): Unit = {
    while(true){
      //每次来一条消息都会启动一个线程去执行,线程开销太大,可以使用react方法来代替,react会复用线程,不会反复创建线程
/*      receive{
        case "start" => println("start!!!")
        case "end" => println("end!!!")
      }*/
      react{
        case "start" => println("start!!!")
        case "end" => println("end!!!")
      }
    }
  }
}
object MyActor3 {
  def main(args: Array[String]): Unit = {
    val actor = new MyActor3
    actor.start()
    actor ! "start"
    actor ! "end"
  }
}

第四个例子
结合case class样例类发送消息和接受消息
1、将消息封装在一个样例类中
2、通过匹配不同的样例类去执行不同的操作
3、Actor可以返回消息给发送方。通过sender方法向当前消息发送方返回消息

import scala.actors.{Actor, Future}
//异步消息
case class AsyncMessage(id:String,name:String)
//同步消息
case class SyncMessage(id:String,name:String)
//返回消息
case class ReplyMessage(id:String,name:String)
class MyActor4 extends Actor{
  override def act(): Unit = {
    loop{
      react{
        case AsyncMessage(x,y)=> println(s"$x$y")
          sender ! ReplyMessage ("4","异步返回消息")
        case SyncMessage(x,y) => println(s"$x$y")
          sender ! ReplyMessage ("5","同步且等待消息")
      }
    }
  }
}
object MyActor4{
  def main(args: Array[String]): Unit = {
    val actor = new MyActor4
    actor.start()
    val unit: Unit = actor ! AsyncMessage("1","异步无返回值消息")
      //1异步无返回值消息
    val asyncMessage : Future[Any] = actor !! AsyncMessage("2","异步有返回值消息")
      //异步有返回值,使用apply()接受返回的参数
    val apply: Any = asyncMessage.apply()
    println(apply)
      //2异步有返回值消息
      //ReplyMessage(4,异步返回消息)
      
    //同步阻塞消息
    val syncMessage: Any = actor !? SyncMessage("3","同步阻塞消息")
    println(syncMessage)
      //3同步阻塞消息
      //ReplyMessage(5,同步且等待消息)
  }
}
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容

  • 1. 特点: Scala是一门多范式编程语言,集成了面向对象编程和函数式编程等多种特性。 Scala运行在虚拟机上...
    EmmaQin阅读 1,343评论 0 0
  • scala文档 包含内容 1基础 2控制结构和函数 3数组相关操作 4映射和元组 5类 6对象 7.包和引入(x)...
    zlcook阅读 1,019评论 0 3
  • scala学习笔记 第2章 变量和数据类型 基本数据 scala的核心数据为四种 :字面量、值、变量、类型 值使...
    485b1aca799e阅读 2,160评论 0 1
  • 教材:快学Scalamarkdown阅读:https://www.zybuluo.com/mdeditor cha...
    hakase_nano阅读 1,051评论 0 1
  • 大数据学院_腾讯大数据http://data.qq.com/academySpark是一个通用的并行计算框架,立足...
    葡萄喃喃呓语阅读 654评论 0 1