Scala

第一章入门

0、为什么学Scala

Spark新一代内存及级大数据计算框架,是大数据的重要内容

Spark是适用Scala编写的。因此为了更好的学习Spark,需要掌握Scala这门语言

Spark的兴起,带动了Scala的发展

1、Scala和Java关系

Java运行原理
    -先编译,再解释
    - .java源文件--->编译器(javac)--->.class字节码文件--->JVM(java 不同平台)--->机器指令


Scala运行原理
    -先编译,再解释
    - .scala源文件--->编译器(scalac)--->.class字节码文件--->JVM(scala 不同平台)--->机器指令
 

Scala_第1张图片

Scala_第2张图片

2、Scala环境搭建

Scala环境搭建_asd623444055的博客-CSDN博客

3、HelloScala

(1)java编译过程

1)新建文件HelloJava.java

Scala_第3张图片

 2)编写代码

public class HelloJava{
 public static void main(String[] args) {
		System.out.println("HelloJava");
	}
}

3)使用编译器编写

Scala_第4张图片

4)生成字节码文件 

Scala_第5张图片

5)执行字节码文件

 Scala_第6张图片

 (2)Scala编译过程

1)新建文件HelloScala.scala

Scala_第7张图片

 2)编写代码

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

3)使用编译器编写

 4)生成字节码文件 

Scala_第8张图片

 5)执行字节码文件

(3)使用java 编译Scala文件

Scala_第9张图片

 Java不支持执行Scala命令

 (4)使用Scala 编译Java文件(不建议

 Scala命令可以执行部分Java程序:兼容部分语法和代码。不建议

4、创建IDEA项目工程

1.引入Scala框架

Scala_第10张图片

 Scala_第11张图片

Scala_第12张图片

2.创建Scala类

Scala_第13张图片

 3.HelloIWorld

package com.scala.chapter01

/**
 * 这是第一个HelloScala程序
 *
 * -object 名称
 *  从语法角度讲,上面语法声明了一个伴生对象
 *  Scala是纯面向对象,去除了Java中的static语法,通过伴生对象来模拟static效果
 *
 *
 * -伴生对象
 *  伴随类产生的对象
 *  当我们对源文件进行编译之后,默认会生成两个字节码文件,一个是伴生类,另一个是伴生对象所属类
 *  其实真正的伴生对象是 伴生对象所属类中创建的单例对象
 *
 *  如果不想默认生成伴生类,可以手动生成,要求伴生类名称和伴生对象名称一致
 *
 *  注意:以后在Scala语言中,如果要定义类似Java的Static内容,都应该放到伴生对象中声明
 *
 *  main方法名
 *  小括号表示参数列表
 *    参数声明方式:java -> 类型参数名
 *               scala-> 参数名:类型
 *  public修饰符:scala中没有public关键字,如果不声明访问权限,那么就是公共的
 *  static修饰符:scala中没有静态语法,所有没有static关键字
 *  void关键字:表示返回值,但是不遵循面向对象语法,所有scala中没有,但是有Unit类,表示没有返回值
 *  scala中:方法名(参数列表):返回值类型
 *  scala中声明方法必须采用关键字def声明
 *  scala中方法实现赋值给方法声明,所以中间需要等号连接
 *
 *  Scala是一个完全面向对象的语言,所以没有静态语法,为了能调用静态语法(模仿静态语法),采用了伴生对象单例的方式调用方法
 *
 *  >object
 *  关键字,表示声明一个伴生对象
 *  >Scala01_HelloWorld
 *  伴生对象的名字,取名的时候需要符合标识符命名规则
 *  >def
 *  关键字  标识声明一个方法
 *  >main
 *  方法的名称
 *  >(args: Array[String])
 *  &args 参数名称
 *  &Array[String]参数类型,在Scala语言中,[]表示泛型
 *  &声明参数的时候,名称在前,类型在后,名称和类型之间用冒号分隔
 *  >Unit
 *  &返回值类型为空,相当于java语言中的void关键字
 *  &Unit是一个类型,当前类型只有一个实例()
 *  &参数列表和返回值类型之间,用冒号进行分隔
 *  &返回值类型和函数体之间用等号进行连接
 *  > println("HelloScala")
 *  向控制台打印输出内容
 *  在Scala语言中,语句结束不需要加分号
 *  伴生对象
 *  -从字面意思来讲,伴生对象就是伴随类产生的对象
 *  -在scala中,没有static关键字,通过伴生对象模拟static关键字的实现
 *  -当通过object名称定义一个伴生对象的时候,默认底层会编译生成两个字节码文件,一个是伴生类,
 *  另一个是伴生对象所属类。实际我们所说的伴生对象是伴生对象所属类中定义的一个单例对象,然后
 *  通过对象 .的方式访问属性或者方法等
 *
 *  -以后在scala程序中,如果要想实现static效果,那么我们应该将属性以及方法定义在伴生对象中
 */
object Scala01_HelloWorld {
  def main(args: Array[String]): Unit = {
    println("HelloWorld")
  }
}

//伴生对象
object Student{
  var master:String = "John"
}

//伴生类
class Student{
 var name:String = _
 var age:Int = _

}

 反编译

  伴生对象所属类

package com.scala.chapter01;

import scala.reflect.ScalaSignature;

@ScalaSignature(bytes="\006\001!:Q!\001\002\t\002%\t!cU2bY\006\004\024g\030%fY2|wk\034:mI*\0211\001B\001\nG\"\f\007\017^3saER!!\002\004\002\013M\034\027\r\\1\013\003\035\t1aY8n\007\001\001\"AC\006\016\003\t1Q\001\004\002\t\0025\021!cU2bY\006\004\024g\030%fY2|wk\034:mIN\0211B\004\t\003\037Ei\021\001\005\006\002\013%\021!\003\005\002\007\003:L(+\0324\t\013QYA\021A\013\002\rqJg.\033;?)\005I\001\"B\f\f\t\003A\022\001B7bS:$\"!\007\017\021\005=Q\022BA\016\021\005\021)f.\033;\t\013u1\002\031\001\020\002\t\005\024xm\035\t\004\037}\t\023B\001\021\021\005\025\t%O]1z!\t\021SE\004\002\020G%\021A\005E\001\007!J,G-\0324\n\005\031:#AB*ue&twM\003\002%!\001")
public final class Scala01_HelloWorld
{
  public static void main(String[] paramArrayOfString)
  {
    Scala01_HelloWorld..MODULE$.main(paramArrayOfString);
  }
}

  伴生类

package com.scala.chapter01;

import scala.Predef.;

public final class Scala01_HelloWorld$
{
  public static final  MODULE$;

  static
  {
    new ();
  }

  public void main(String[] args)
  {
    Predef..MODULE$.println("HelloIDEAScala");
  }
  private Scala01_HelloWorld$() { MODULE$ = this; }

}

反编译

 伴生对象所属类

package com.scala.chapter01;

public final class Student$
{
  public static final  MODULE$;
  private String master;

  static
  {
    new ();
  }

  public String master()
  {
    return this.master; } 
  public void master_$eq(String x$1) { this.master = x$1; }

  private Student$()
  {
    MODULE$ = this;

    this.master = "John";
  }
}

 伴生类

package com.scala.chapter01;

import scala.reflect.ScalaSignature;

@ScalaSignature(bytes="\006\001\r;Q!\001\002\t\002%\tqa\025;vI\026tGO\003\002\004\t\005I1\r[1qi\026\024\b'\r\006\003\013\031\tQa]2bY\006T\021aB\001\004G>l7\001\001\t\003\025-i\021A\001\004\006\031\tA\t!\004\002\b'R,H-\0328u'\tYa\002\005\002\020#5\t\001CC\001\006\023\t\021\002C\001\004B]f\024VM\032\005\006)-!\t!F\001\007y%t\027\016\036 \025\003%AqaF\006A\002\023\005\001$\001\004nCN$XM]\013\0023A\021!$\b\b\003\037mI!\001\b\t\002\rA\023X\rZ3g\023\tqrD\001\004TiJLgn\032\006\0039AAq!I\006A\002\023\005!%\001\006nCN$XM]0%KF$\"a\t\024\021\005=!\023BA\023\021\005\021)f.\033;\t\017\035\002\023\021!a\0013\005\031\001\020J\031\t\r%Z\001\025)\003\032\003\035i\027m\035;fe\0022A\001\004\002\001WM\021!F\004\005\006))\"\t!\f\013\002]A\021!B\013\005\na)\002\r\0211A\005\002a\tAA\\1nK\"I!G\013a\001\002\004%\taM\001\t]\006lWm\030\023fcR\0211\005\016\005\bOE\n\t\0211\001\032\021\0311$\006)Q\0053\005)a.Y7fA!I\001H\013a\001\002\004%\t!O\001\004C\036,W#\001\036\021\005=Y\024B\001\037\021\005\rIe\016\036\005\n})\002\r\0211A\005\002}\nq!Y4f?\022*\027\017\006\002$\001\"9q%PA\001\002\004Q\004B\002\"+A\003&!(\001\003bO\026\004\003")
public class Student
{
  private String name;
  private int age;

  public static void master_$eq(String paramString)
  {
    Student..MODULE$.master_$eq(paramString);
  }

  public static String master()
  {
    return Student..MODULE$.master();
  }

  public String name()
  {
    return this.name; } 
  public void name_$eq(String x$1) { this.name = x$1; } 
  public int age() { return this.age; } 
  public void age_$eq(int x$1) { this.age = x$1; }

}

2章 变量和数据类型

1、变量和常量

package com.scala.chapter02

/**
 * 变量与常量
 * Java
 * --变量
 * 数据类型 变量名 = 值
 * int a = 10;
 * --常量
 * final 数据类型 变量名 = 值
 * final int a = 10;
 *
 * Scala
 * --变量
 * var 变量名:数据类型 = 值
 * var a:Int = 10
 * --常量
 * val 变量名:数据类型 = 值
 * val a:Int = 10
 */
object Scala02_var {
  /*  (0)声明一个整数类型的变量a,并给其赋值
        var a: Int = 10
        println(a)

   */
  /*  (1)声明变量时,类型可以省略,编译器自动推导,即类型推导
        var a = 10     等同于    var a: Int = 10
        println(a)
   */
  /*  (2)类型确定后,就不能修改,说明Scala是强数据类型语言。
        var a = 10
        a = "123"   ×
   */
  /*  (3)变量声明时,必须要有初始值。不同于Java,Scala在声明同时必须赋值
        var a:Int   ×
        println(a)
   */
  /*  (4)在声明/定义一个变量时,可以使用var或者val来修饰,var修饰的变量可改变
        var a = 10
        val b = 20
            a = 15
            b = 25       Reassignment to val            
   */

  /*  (5)var修饰的对象引用可以改变,val修饰的对象则不可改变,但对象的状态(值)却是可以改变的。(比如:自定义对象、数组、集合等等)
        var std1 = new Student()
        std1 = new Student

        val std2 = new Student()
        std2.name = "lisi"
        std2 = new Student        
   */
  /*  (6)在实际开发过程中,var和val优先选哪个
        val
  
   */
}
class Student{
  var name:String = "zhangsan"
}

2、标识符的命名规范

Scala对各种变量方法函数等命名时使用的字符序列称为标识符。即:凡是自己可以起名字的地方都叫标识符。

1)命名规则

Scala中的标识符声明,基本和Java是一致的,但是细节上会有所变化,有以下四种规则:

(1)以字母或者下划线开头,后接字母、数字、下划线

(2)以操作符开头,且只包含操作符(+ - * / # !等)

3)用反引号`....`包括的任意字符串,即使是Scala关键字(39个)也可以

  • package, import, class, object, trait, extends, with, type, for
  • private, protected, abstract, sealed, final, implicit, lazy, override
  • try, catch, finally, throw
  • if, else, match, case, do, while, for, return, yield
  • def, val, var
  • this, super
  • new
  • true, false, null

object TestName {

    def main(args: Array[String]): Unit = {

        // (1)以字母或者下划线开头,后接字母、数字、下划线
        var hello: String = "" // ok
        var Hello12: String = "" // ok
        var 1hello: String = "" // error 数字不能开头

        var h-b: String = "" // error   不能用-
        var x h: String = "" // error   不能有空格
        var h_4: String = "" // ok
        var _ab: String = "" // ok
        var Int: String = "" // ok 因为在Scala中Int是预定义的字符,不是关键字,但不推荐

        var _: String = "hello" // ok 单独一个下划线不可以作为标识符,因为_被认为是一个方法
        println(_)

        //(2)以操作符开头,且只包含操作符(+ - * / # !等)
        var +*-/#! : String = "" // ok
        var +*-/#!1 : String = "" // error 以操作符开头,必须都是操作符

        //(3)用反引号`....`包括的任意字符串,即使是Scala关键字(39个)也可以
        var if : String = "" // error 不能用关键字
        var `if` : String = "" // ok 用反引号`....`包括的任意字符串,包括关键字
    }
}

3、字符串输出

package com.scala.chapter02

/**
 * 字符串输出
 */
object Scala03_TestString {
  def main(args: Array[String]): Unit = {
    //(1)字符串,通过+号连接
    var name: String = "xiaoming"
    var age: Int = 12
    // println(age + "岁的" + name + "在学习")
    //(2)printf用法:字符串,通过%传值。
    printf("%d岁的%s在学习", age, name)
    printf(s"${age}岁的${name}在学习", age, name)
    //(3)字符串模板(插值字符串):通过$获取变量值
    //多行字符串,在Scala中,利用三个双引号包围多行字符串就可以实现。
    //输入的内容,带有空格、\t之类,导致每一行的开始位置不能整洁对齐。
    //应用scala的stripMargin方法,在scala中stripMargin默认是“|”作为连接符,
    //在多行换行的行头前面加一个“|”符号即可。
    var sql: String =
    
      s"""
        select
          *
        from
        	student
        where
        	name = ${name}
        and
        	age = ${age}
        """.stripMargin

    println(sql)
  }

}

4、键盘输入

在编程中,需要接收用户输入的数据,就可以使用键盘输入语句来获取。

1)基本语法

StdIn.readLine()、StdIn.readShort()、StdIn.readDouble()

package com.scala.chapter02

import scala.io.StdIn
/**
 * 获取用户键盘输入
 */
object Scala04_TestStdIn {
  def main(args: Array[String]): Unit = {
    println("请输入用户名")
    val name = StdIn.readLine()
    println("请输入年龄")
    val age = StdIn.readInt()

    println(s"欢迎${age}岁的${name}加入")
  }
}

5、数据类型

Java数据类型

Scala_第14张图片

 Scala数据类型

Scala_第15张图片

6、 整数类型(Byte、Short、Int、Long)

Scala的整数类型就是用于存放整数值的,比如12,30,3456等等。

1)整型分类

数据类型

描述

Byte [1]

8位有符号补码整数。数值区间为 -128 127

Short [2]

16位有符号补码整数。数值区间为 -32768 32767

Int [4]

32位有符号补码整数。数值区间为 -2147483648 2147483647

Long [8]

64位有符号补码整数。数值区间为 -9223372036854775808 9223372036854775807 = 2(64-1)次方-1

2)案例实操

(1)Scala各整数类型有固定的表示范围和字段长度,不受具体操作的影响,以保证Scala程序的可移植性。

object TestDataType {

    def main(args: Array[String]): Unit = {

        // 正确
        var n1:Byte = 127
        var n2:Byte = -128

        // 错误
        // var n3:Byte = 128
        // var n4:Byte = -129
    }
}

(2)Scala的整型,默认为Int型,声明Long型,须后加‘l’或‘L’

object TestDataType {

    def main(args: Array[String]): Unit = {

        var n5 = 10
        println(n5)

        var n6 = 9223372036854775807L
        println(n6)
    }
}

(3)Scala程序中变量常声明为Int型,除非不足以表示大数,才使用Long

 7、浮点类型(Float、Double) 

Scala的浮点类型可以表示一个小数,比如123.4f,7.8,0.12等等。

1)浮点型分类

数据类型

描述

Float [4]

32 , IEEE 754标准的单精度浮点数

Double [8]

  1. IEEE 754标准的双精度浮点数

2)案例实操

Scala的浮点型常量默认为Double型,声明Float型常量,须后加‘f’或‘F’。

object TestDataType {

    def main(args: Array[String]): Unit = {

        // 建议,在开发中需要高精度小数时,请选择Double
        var n7 = 2.2345678912f
        var n8 = 2.2345678912

        println("n7=" + n7)
        println("n8=" + n8)
    }
}

 //运行的结果

n7=2.2345679
n8=2.2345678912

8、字符类型(Char)

1)基本说明

字符类型可以表示单个字符,字符类型是Char。

2)案例实操

(1)字符常量是用单引号 ' ' 括起来的单个字符。

(2)\t :一个制表位,实现对齐的功能

(3)\n :换行符

(4)\\ :表示\

(5)\" :表示"

package com.scala.chapter02

/**
 * char字符类型
 */
object Scala07_CharType {
  def main(args: Array[String]): Unit = {
    //(1)字符常量是用单引号 ' ' 括起来的单个字符。
    var c1: Char = 'a'
    println("c1=" + c1)
    //注意:这里涉及自动类型提升,其实编译器可以自定判断是否超出范围,
    //不过idea提示报错
    var c2: Char = 'a' + 1
    println(c2)

    //(2)\t :一个制表位,实现对齐的功能
    println("姓名\t年龄")

    //(3)\n :换行符
    println("张三\n李四")

    //(4)\\ :表示\
    println("d:\\学习资料\\avi")

    //(5)\" :表示"
    println("张三:\"hello\"")
  }
}

9、布尔类型:Boolean

1)基本说明

(1)布尔类型也叫Boolean类型,Booolean类型数据只允许取值true和false

(2)boolean类型占1个字节。

2)案例实操

object TestBooleanType {

    def main(args: Array[String]): Unit = {
        
        var isResult : Boolean = false
        var isResult2 : Boolean = true
    }
}

10、Unit类型、Null类型和Nothing类型

1)基本说明

数据类型

描述

Unit

表示无值,和其他语言中void等同。用作不返回任何结果的方法的结果类型。Unit只有一个实例值,写成()

Null

null , Null 类型只有一个实例值null

Nothing

Nothing类型在Scala的类层级最低端;它是任何其他类型的子类型。

当一个函数,我们确定没有正常的返回值,可以用Nothing来指定返回类型,这样有一个好处,就是我们可以把返回的值(异常)赋给其它的函数或者变量(兼容性)

2)案例实操

(1)Unit类型用来标识过程,也就是没有明确返回值的函数。

由此可见,Unit类似于Java里的void。Unit只有一个实例——( ),这个实例也没有实质意义


    def main(args: Array[String]): Unit = {

        def sayOk : Unit = {// unit表示没有返回值,即void
            
        }
        println(sayOk)
    }
}

(2)Null类只有一个实例对象,Null类似于Java中的null引用。Null可以赋值给任意引用类型(AnyRef),但是不能赋值给值类型(AnyVal)

object TestDataType {

    def main(args: Array[String]): Unit = {

        //null可以赋值给任意引用类型(AnyRef),但是不能赋值给值类型(AnyVal)
        var cat = new Cat();
        cat = null	// 正确

        var n1: Int = null // 错误
        println("n1:" + n1)

    }
}

class Cat {

}

(3)Nothing,可以作为没有正常返回值的方法的返回类型,非常直观的告诉你这个方法不会正常返回,而且由于Nothing是其他任意类型的子类,他还能跟要求返回值的方法兼容。

object TestSpecialType {

    def main(args: Array[String]): Unit = {

        def test() : Nothing={
            throw new Exception()
        }
        test
    }
}

11、类型转换

1.Java中的隐式类型转换

package com.scala.chapter01;

public class Test {
    public static void main(String[] args) {
        byte b = 10;
       test(b); //默认调用同类型方法,无同类型寻找接近的类型,不会使用char
    }

    private static void test(byte b) {
        System.out.println("byte");
    }

    private static void test(short b) {
        System.out.println("short");
    }

    private static void test(char b) {
        System.out.println("char");
    }

    private static void test(int b) {
        System.out.println("int");
    }
}

2.当Scala程序在进行赋值或者运算时,精度小的类型自动转换为精度大的数值类型,这个就是自动类型转换(隐式转换)。数据类型按精度(容量)大小排序为:

Scala_第16张图片

1)基本说明

(1)自动提升原则:有多种类型的数据混合运算时,系统首先自动将所有数据转换成精度大的那种数据类型,然后再进行计算。

(2)把精度大的数值类型赋值给精度小的数值类型时,就会报错,反之就会进行自动类型转换。

(3)(byte,short)和char之间不会相互自动转换。

(4)byte,short,char他们三者可以计算,在计算时首先转换为int类型

package com.scala.chapter02

/**
 * 类型转换
 * 自动类型转换
 * 强转
 */
object Scala08_DataType {
  def main(args: Array[String]): Unit = {

    /*(1)自动提升原则:有多种类型的数据混合运算时,系统首先自动将所有数据转换成精度大的那种数据类型,然后再进行计算。
    var b1:Byte = 10
    var n:Long = 20L
    var res = n + b1

     */

    /*(2)把精度大的数值类型赋值给精度小的数值类型时,就会报错,反之就会进行自动类型转换。
    var b:Byte = 10
    var n:Int = b //将精度小的数据赋值给精度大的类型,会自动进行转换(隐式转换)
    var b2:Byte = n  //精度大的数值类型赋值给精度小的数值类型时,就会报错

     */

    /*(3)(byte,short)和char之间不会相互自动转换。
    var b:Byte = 10
    var c:Char = b
    var c1:Char = 96
    var n:Int = c1

     */


    //(4)byte,short,char他们三者可以计算,在计算时首先转换为int类型。
    var b: Byte = 10
    var s: Short = 20
    var c: Char = 30
    val res: Int = b + s + c
  }
}

3.强制类型转换

1)基本说明

自动类型转换的逆过程,将精度大的数值类型转换为精度小的数值类型。使用时要加上强制转函数,但可能造成精度降低或溢出,格外要注意。

Java  :

 int num = (int)2.5

Scala :  

var n: Int = 2.5.toInt

2)案例实操

(1)将数据由高精度转换为低精度,就需要使用到强制转换

(2)强转符号只针对于最近的操作数有效,往往会使用小括号提升优先级

  //(1)将数据由高精度转换为低精度,就需要使用到强制转换
  var n1: Int = 2.5.toInt // 这个存在精度损失

  //(2)强转符号只针对于最近的操作数有效,往往会使用小括号提升优先级
  var r1: Int = 10 * 3.5.toInt + 6 * 1.5.toInt  // 10 *3 + 6*1 = 36
  var r2: Int = (10 * 3.5 + 6 * 1.5).toInt  // 44.0.toInt = 44
  println("r1=" + r1 + " r2=" + r2)

3.数值类型和String类型间转换

1)基本说明

在程序开发中,我们经常需要将基本数值类型转成String类型。或者将String类型转成基本数值类型。

2)案例实操

(1)基本类型转String类型(语法:将基本类型的值+"" 即可)

(2)String类型转基本数值类型(语法:s1.toInt、s1.toFloat、s1.toDouble、s1.toByte、s1.toLong、s1.toShort)

  //(1)基本类型转String类型(语法:将基本类型的值+"" 空串即可)
        var str1 : String = true + ""
        var str2 : String = 4.5 + ""
        var str3 : String = 100 + ""

        //(2)String类型转基本数值类型(语法:调用相关API)
        var s1 : String = "12"

        var n1 : Byte = s1.toByte
        var n2 : Short = s1.toShort
        var n3 : Int = s1.toInt
        var n4 : Long = s1.toLong
    

(3)注意事项

在将String类型转成基本数值类型时,要确保String类型能够转成有效的数据,比如我们可以把"123",转成一个整数,但是不能把"hello"转成一个整数。

var n5:Int = "12.6".toInt会出现NumberFormatException异常。

/**
 *  var b:Byte = 128.toByte
 * 128在scala中默认是Int类型,应该是32位
 * 源码 00000000 00000000 00000000 10000000
 * 补码 00000000 00000000 00000000 10000000
 * 对Int数据进行截取,截取为Byte,剩下8位 ==》10000000 约定当前值为范围最小值-128
 *
 *  var b:Byte = 130.toByte
 *  130在scala中默认是Int类型,应该是32位
 *  源码 00000000 00000000 00000000 10000010
 *  补码 00000000 00000000 00000000 10000010
 *  Int数据进行截取,截取为Byte,剩下8位 负数补码10000010==》反码:11111101--》源码11111110
 *
 *   var b:Byte = -130.toByte
 * 源码 10000000 00000000 00000000 10000010
 * 反码 11111111 11111111 11111111 01111101
 * 补码 11111111 11111111 11111111 01111110
 * Int数据进行截取,截取为Byte,剩下8位 补码01111110==》反码:01111110--》源码01111110
 *
 */
object Scala_DataType {
  def main(args: Array[String]): Unit = {
    var b:Byte = 128.toByte
    println(b) //-128
  }
}

数据类型小结


    -Any
        *AnyVal(值类型)
            >Byte,Short,Int,Long,Float,Double,Boolean,Char
            >Unit
                表示返回值类型为空,相当于Java中的void关键字
            >StringOps
                对字符串功能的增强

        *AnyRef(引用类型)
            >所有Java语言中的类型
            >Scala语言中的类
            >集合

            >Null
                表示变量声明后,没有指向任何对象,相当于Java中null关键字
                是所有AnyRef的子类,所以不能将null赋值给AnyValue

        *Nothing  
            是所有类的子类,一般用于方法的返回值,表示当前方法没有正常返回的情况            

3章 运算符

Scala运算符的使用Java运算符的使用基本相同只有个别细节上不同

1、算术运算符

1)基本语法

运算符

运算

范例

结果

+

正号

+3

3

-

负号

b=4; -b

-4

+

5+5

10

-

6-4

2

*

3*4

12

/

5/5

1

%

取模(取余)

7%5

2

+

字符串相加

“He”+”llo”

“Hello”

(1)对于除号“/”,它的整数除和小数除是有区别的:整数之间做除法时,只保留整数部分而舍弃小数部分。

(2)对一个数取模a%b,和Java的取模规则一样。

 //-------------------------------算术运算符--------------------------------------
    //(1)对于除号“/”,它的整数除和小数除是有区别的:整数之间做除法时,只保留整数部分而舍弃小数部分。
    var r1: Int = 10 / 3 // 3
    println("r1=" + r1)

    var r2: Double = 10 / 3 // 3.0
    println("r2=" + r2)

    var r3: Double = 10.0 / 3 // 3.3333
    println("r3=" + r3)
    println("r3=" + r3.formatted("%.2f")) // 含义:保留小数点2位,使用四舍五入
  //(2)对一个数取模a%b,和Java的取模规则一样。
        var r4 = 10 % 3 // 1
        println("r4=" + r4)

2、关系运算符(比较运算符)

1)基本语法

运算符

运算                                                                          

范例

结果

==

相等于

4==3

false

!=

不等于

4!=3

true

<

小于

4<3

false

>

大于

4>3

true

<=

小于等于

4<=3

false

>=

大于等于

4>=3

true

2)案例实操

(1)需求1:

  // 测试:>、>=、<=、<、==、!=
        var a: Int = 2
        var b: Int = 1

        println(a > b)      // true
        println(a >= b)     // true
        println(a <= b)     // false
        println(a < b)      // false
        println("a==b" + (a == b))    // false
        println(a != b)     // true

(2)需求2:Java和Scala中关于==的区别

/**
     * Java
     * ==比较的是对象的内存地址
     * equals 默认是和 "==" 一样 , 也是比较的地址,String对quals方法进行了重写,字符串的equals比较的是内容
     *
     * Scala
     * ==和equals和功能一样,比较的是内容是否相同
     * 反编译
     * String s1 = new String("abc");
     * String s2 = "abc";
     * String str1 = s2;
     * String tmp163_157 = s1; tmp163_157; if (str1 != null) { if (!Predef..MODULE$.equals(str1)); } else { tmpTernaryOp = (tmp163_157 == null ? tmp163_157 : 1); break label189; }
     * }
     * eq 等同于Java中的equals,比较对象的内存地址
     *
     */
    var s1: String = new String("abc")
    var s2: String = "abc"
    println(s1 == s2)    //true
    println(s1.equals(s2)) //true

    println(s1.eq(s2)) //false

3、逻辑运算符

1)基本语法

用于连接多个条件(一般来讲就是关系表达式),最终的结果也是一个Boolean值。

假定:变量A为true,B为false

运算符

描述

实例

&&

逻辑与

(A && B) 运算结果为 false

||

逻辑或

(A || B) 运算结果为 true

!

逻辑非

!(A && B) 运算结果为 true

2)案例实操

  // 测试:&&、||、!
        var a = true
        var b = false

        println("a&&b=" + (a && b))     // a&&b=false
        println("a||b=" + (a || b))     // a||b=true
        println("!(a&&b)=" + (!(a && b))) // !(a&&b)=true
    }
}
//扩展避免逻辑与空指针异常
isNotEmpty(String s){
	//如果逻辑与,s为空,会发生空指针
	return s!=null && !"".equals(s.trim());
}

 4、赋值运算符

1)基本语法

赋值运算符就是将某个运算后的值,赋给指定的变量。

运算符

描述

实例

=

简单的赋值运算符,将一个表达式的值赋给一个左值

C = A + B 将 A + B 表达式结果赋值给 C

+=

相加后再赋值

C += A 等于 C = C + A

-=

相减后再赋值

C -= A 等于 C = C - A

*=

相乘后再赋值

C *= A 等于 C = C * A

/=

相除后再赋值

C /= A 等于 C = C / A

%=

求余后再赋值

C %= A 等于 C = C % A

<<=

左移后赋值

C <<= 2等于 C = C << 2

>>=

右移后赋值

C >>= 2 等于 C = C >> 2

&=

按位与后赋值

C &= 2  等于 C = C & 2

^=

按位异或后赋值

C ^= 2  等于 C = C ^ 2

|=

按位或后赋值

C |= 2  等于 C = C | 2

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

    //在java语言中,+=运算符可以自动进行强转,但是在scala语言中,+=底层不会自动进行强转
    // var b:Byte =10
    var b:Int =10
    b +=1
    println(b)

   def main(args: Array[String]): Unit = {
        
        var r1 = 10
        
        r1 += 1 // 没有++
        r1 -= 2 // 没有--
    }

 5.位运算符

1)基本语法

变量 a = 60,b = 13。

运算符

描述

实例

&

按位与运算符

(a & b) 输出结果 12 ,二进制解释: 0000 1100

|

按位或运算符

(a | b) 输出结果 61 ,二进制解释: 0011 1101

^

按位异或运算符

(a ^ b) 输出结果 49 ,二进制解释: 0011 0001

~

按位取反运算符

(~a ) 输出结果 -61 ,二进制解释: 1100 0011, 在一个有符号二进制数的补码形式。

<<

左移动运算符

a << 2 输出结果 240 ,二进制解释: 0011 0000

>>

右移动运算符

a >> 2 输出结果 15 ,二进制解释: 0000 1111

>>>

无符号右移

a >>>2 输出结果 15, 二进制解释: 0000 1111

6.Scala运算符本质 

在Scala中其实是没有运算符的,所有运算符都是方法。

1)当调用对象的方法时,.可以省略

2)如果函数参数只有一个,或者没有参数,()可以省略

    // 标准的加法运算
        val i:Int = 1.+(1)

        // (1)当调用对象的方法时,.可以省略
        val j:Int = 1 + (1)

        // (2)如果函数参数只有一个,或者没有参数,()可以省略
        val k:Int = 1 + 1

运算符小结


    -Java
        *==比较的是对象的内存地址
        *equals默认比较的也是对象的内存地址,但是一般我们会对其进行重写,比较对象的内容

    -Scala
        *==和equals比较的规则一样,都是比较对象的内容是否相等
        *eq比较的是对象的内存地址

4章 流程控制

1、分支控制if-else

让程序有选择的的执行,分支控制有三种:单分支、双分支、多分支

1. 单分支

1)基本语法

if  (条件表达式)  {

执行代码块

}

说明:当条件表达式为ture时,就会执行{ }的代码。

2)案例实操

需求:输入人的年龄,如果该同志的年龄小于18岁,则输出“童年”

    //-----------------单分支-------------------
    println("请输入年龄")
    val age: Int = StdIn.readInt()
    if (age < 18) {
      println("童年")
    }

2.双分支

1)基本语法

if (条件表达式) {

执行代码块1

} else {

执行代码块2

}

2)案例实操

需求:输入年龄,如果年龄小于18岁,则输出“童年”。否则,输出“成年”。

 //-----------------双分支------------------- 
   println("请输入年龄")
    val age: Int = StdIn.readInt()  
    if (age < 18){
      println("童年")
    }else{
      println("成年")
    }

3.多分支

1)基本语法

if (条件表达式1) {

执行代码块1

}

else if (条件表达式2) {

执行代码块2

}

   ……

else {

执行代码块n

}

2)案例实操

(1)需求1:需求:输入年龄,如果年龄小于18岁,则输出“童年”。如果年龄大于等于18且小于等于30,则输出“中年”,否则,输出“老年”。

   println("请输入年龄")
    val age: Int = StdIn.readInt()

    //-----------------多分支-------------------

    if (age < 18){
      println("童年")
    }else if(age>=18 && age<30){
      println("中年")
    }else{
      println("老年")
    }
  }

(2)需求2:Scala中if else表达式其实是有返回值的,具体返回值取决于满足条件的代码体的最后一行内容

 println("请输入年龄")
    val age: Int = StdIn.readInt()

    //-----------------多分支-------------------
    val res :String = if (age < 18){
      "童年"
    }else if(age>=18 && age<30){
      "中年"
    }else{
      "老年"
    }

    println(res)
  }

(3)需求3:Scala中返回值类型不一致,取它们共同的祖先类型。

 def main(args: Array[String]): Unit = {

        println("input age")
        var age = StdIn.readInt()

        val res:Any = if (age < 18){
            "童年"
        }else if(age>=18 && age<30){
            "中年"
        }else{
            100
        }

        println(res)
    }

(4)需求4:Java中的三元运算符可以用if else实现

如果大括号{}内的逻辑代码只有一行,大括号可以省略。如果省略大括号,if只对最近的一行逻辑代码起作用

  def main(args: Array[String]): Unit = {

        // Java
// int result = flag?1:0

        // Scala
        println("input age")
        var age = StdIn.readInt()
        val res:Any = if (age < 18)  "童年" else "成年"
"不起作用"

        println(res)
    }

2、嵌套分支

在一个分支结构中又完整的嵌套了另一个完整的分支结构,里面的分支的结构称为内层。分支外面的分支结构称为外层分支。嵌套分支不要超过3层。

1)基本语法

if(){
    if(){

    }else{

    }	
}

2)案例实操

需求:如果输入的年龄小于18,返回“童年”。如果输入的年龄大于等于18,需要再判断:如果年龄大于等于18且小于30,返回“中年”;如果其他,返回“老年”。

 def main(args: Array[String]): Unit = {

        println("input age")
        var age = StdIn.readInt()

        val res :String = if (age < 18){
            "童年"
        }else {
            if(age>=18 && age<30){
                "中年"
            }else{
                "老年"
            }
        }

        println(res)
    }

 3、For循环控制

Scala也为for循环这一常见的控制结构提供了非常多的特性,这些for循环的特性被称为for推导式或for表达式。

1.范围数据循环(To)

1)基本语法

for(i <- 1 to 3){

    print(i + " ")

}

println()

(1)i 表示循环的变量,<- 规定to

(2)i 将会从 1-3 循环,前后闭合

2)案例实操

 //对范围数据进行遍历 to

    //for (i:Int <- 1. to (10) ){
    //声明变量的时候,类型可以省略,因为编译器可以根据集合中的数据,推导出变量类型
     for (i <- 1 to 10 ){
      println(i)
    }

2.范围数据循环(Until

1)基本语法

for(i <- 1 until 3) {

    print(i + " ")

}

println()

(1)这种方式和前面的区别在于i是从1到3-1

(2)即使前闭合后开的范围

2)案例实操

 //对范围数据进行遍历 until
    for (i <- 1 until  10 ){
      println(i)
    }

3.循环守卫

1)基本语法

for(i <- 1 to 3 if i != 2) {

    print(i + " ")

}

println()

说明:

(1)循环守卫,即循环保护式(也称条件判断式,守卫)。保护式为true则进入循环体内部,为false则跳过,类似于continue。

(2)上面的代码等价

for (i <- 1 to 3){
	if (i != 2) {
		print(i + " ")
	}
}

2)案例实操

需求:输出1到5中,不等于3的值

 //在Scala语言中,没有continue和break关键字,通过if判断模拟continue跳出本次循环,提供方法调用模拟break跳出整个循环    
for (i <- 1 to 5 ; if i != 3){
        println(i)
    }

4.循环步长

1)基本语法

for (i <- 1 to 10 by 2) {

    println("i=" + i)

}

说明:by表示步长

2)案例实操

需求:输出1到10以内的所有奇数

 for (i <- 1 to 10 by 2) {
      println("i=" + i)
    }

输出结果

Scala_第17张图片

5.嵌套循环 

1)基本语法

for(i <- 1 to 3; j <- 1 to 3) {

    println(" i =" + i + " j = " + j)

}

说明:没有关键字,所以范围后一定要加来隔断逻辑

2)基本语法

上面的代码等价

for (i <- 1 to 3) {
    for (j <- 1 to 3) {
        println("i =" + i + " j=" + j)
    }
}

6.引入变量

1)基本语法

for(i <- 1 to 3; j = 4 - i) {

    println("i=" + i + " j=" + j)

}

说明:

(1)for推导式一行中有多个表达式时,所以要加 ; 来隔断逻辑

(2)for推导式有一个不成文的约定:当for推导式仅包含单一表达式时使用圆括号,当包含多个表达式时,一般每行一个表达式,并用花括号代替圆括号,如下

for {
    i <- 1 to 3

j = 4 - i

} {

    println("i=" + i + " j=" + j)

}

2)案例实操

上面的代码等价于

for (i <- 1 to 3) {
    var j = 4 - i
    println("i=" + i + " j=" + j)
}

练习:打印99乘法表

 for (i <- 1 to 9; j <- 1 to i ;val str = if (j == i) "\n" else "\t")print(j + "*" + i + "=" + j * i + str)

 练习:打印等腰三角

for( i <- 1 to 9 ) println(" " * (9 - i) + "*" * (2 * i - 1) )
for (i <- 1 to 18 by 2; j = (18 - i) / 2) println(" " * j + "*" * i)

7.循环返回值

1)基本语法

val res = for(i <- 1 to 10) yield i

println(res)

说明:将遍历过程中处理的结果返回到一个新Vector集合中,使用yield关键字。

注意:开发中很少使用。

2)案例实操

需求:将原数据中所有值乘以2,并把数据返回到一个新的集合中。

    val ints: immutable.IndexedSeq[Int] = for (i <- 1 to 10) yield i * 2
    println(ints)

Vector(2, 4, 6, 8, 10, 12, 14, 16, 18, 20)

8.倒序打印

1)说明:如果想倒序打印一组数据,可以用reverse。

2)案例实操:

需求:倒序打印10到1

 for (i <- 1 to 10 reverse) println(i)

4、While和do..While循环控制 

1.While循环控制

1)基本语法

循环变量初始化

while (循环条件) {

      循环体(语句)

      循环变量迭代

}

说明:

(1)循环条件是返回一个布尔值的表达式

(2)while循环是先判断再执行语句

(3)与for语句不同,while语句没有返回值,即整个while语句的结果是Unit类型()

(4)因为while中没有返回值,所以当要用该语句来计算并返回结果时,就不可避免的使用变量,而变量需要声明在while循环的外部,那么就等同于循环的内部对外部的变量造成了影响,所以不推荐使用,而是推荐使用for循环

2)案例实操

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

2.do..while循环控制

1)基本语法

  循环变量初始化;

   do{

       循环体(语句)

       循环变量迭代

   } while(循环条件)

说明

(1)循环条件是返回一个布尔值的表达式

(2)do..while循环是先执行,再判断

2)案例实操

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

5、循环中断

1)基本说明

Scala内置控制结构特地去掉了breakcontinue,是为了更好的适应函数式编程,推荐使用函数式的风格解决break和continue的功能,而不是一个关键字。Scala中使用breakable控制结构来实现break和continue功能。

2)案例实操

需求1:采用异常的方式退出循环

  try{
      for (i <- 1 to 5) {
      if (i == 3) {
        //跳出循环
        throw new RuntimeException
      }
        println(i)
    }

    }catch {
      //捕获异常,并对其进行处理
      case e:Exception=>
    }

需求2:采用Scala自带的函数,退出循环

  Breaks.breakable(
    for (i <- 1 to 5){
      if (i == 3) {
        //跳出循环
        Breaks.break()
      }
      println(i)
    }
    )
    println("这个是循环体外的代码")

需求3:对break进行省略

import scala.util.control.Breaks._

object Scala03_Break {
  def main(args: Array[String]): Unit = {
 
  breakable(
    for (i <- 1 to 5){
      if (i == 3) {
        //跳出循环
       break
      }
      println(i)
    }
    )
    println("这个是循环体外的代码")
  }
}
}

7、多重循环

1)基本说明

(1)将一个循环放在另一个循环体内,就形成了嵌套循环。其中,for,while,do…while均可以作为外层循环和内层循环。【建议一般使用两层,最多不要超过3层

(2)设外层循环次数为m次,内层为n次,则内层循环体实际上需要执行m*n次。

程序的流程控制小结


    -顺序流程
    -分支流程(选择流程)
        *单分支
            if(条件表达式){
                语句块
            }

        *双分支
            if(条件表达式){
                语句块1
            }else{
                语句块2
            }    

        *多分支
            if(条件表达式1){
                语句块1
            }else if(条件表达式2){
                语句块2
            }....else if可以有多个
            else{
                语句块3
            }
        *在scala语言中,分支是有返回值的,返回值是满足条件的分支块最后一行的执行结果;如果每一个分支块
            返回的数据类型不一致,那么就用它们共同的祖先类型作为返回值类型


    -循环流程
        *for
            >语法
                for(变量名:数据类型 <- 数组|集合){

                }
                数据类型可以省略,因为可以通过集合中的元素推断出来

            >范围数据循环    to  | until   
            
            >循环守卫
                在循环的过程中,加判断条件
                通过循环守卫或者在循环体中加if判断,可以实现类似continue效果

            >循环步长  by
                只针对于Range集合
                按照指定的步长对数据进行生成        

            >循环嵌套
                for(外层循环;内层循环){}

            >循环引入变量
                在循环的时候,定义新的变量,该变量的值受循环变量的影响

            
        *while    
            >while    
            >do...while

        *循环中断
            >在scala语言中,没有break关键字,通过抛出异常,并对异常进行处理的方式,来实现类似break的效果
            >Scala提供了Breaks类   break()  和breakable()
            >导入每一个类下的"静态"成员    
 

第5章 函数式编程

1)面向对象编程

解决问题,分解对象,行为,属性,然后通过对象的关系以及行为的调用来解决问题。

对象:用户

行为:登录、连接JDBC、读取数据库

属性:用户名、密码

Scala语言是一个完全面向对象编程语言。万物皆对象

对象的本质:对数据和行为的一个封装

2)函数式编程

解决问题时,将问题分解成一个一个的步骤,将每个步骤进行封装(函数),通过调用这些封装好的步骤,解决问题。

例如:请求->用户名、密码->连接JDBC->读取数据库

Scala语言是一个完全函数式编程语言。万物皆函数。

函数的本质:函数可以当做一个值进行传递

3)在Scala中函数式编程和面向对象编程完美融合在一起了。

1、函数基础

 1.函数基本语法

1)基本语法


  def sayHello(name:String):String={
      return "Hello" + name
    }

def 自定义函数的关键字
sayHello 函数名
name 参数名
String 参数类型 
String= 函数返回值类型
return "Hello" + name 函数体

2)案例实操

需求:定义一个函数,实现将传入的名称打印出来。

/**
 * 函数的基本用法
 */
object Scala01_TestFun {
  def main(args: Array[String]): Unit = {
    //定义一个函数
    def sayHello(name:String):String={
      return "Hello" + name
    }

    println(sayHello("小明"))
  }

2.函数和方法的区别

1)核心概念

(1)为完成某一功能的程序语句的集合,称为函数。

(2)类中的函数称之方法。

2)案例实操

(1)Scala语言可以在任何的语法结构中声明任何的语法

(2)函数没有重载和重写的概念;方法可以进行重载和重写

(3)Scala中函数可以嵌套定义

/**
 * 函数的基本用法
 *  --函数和方法
 *    -》函数
 *      完成某个特殊功能的代码块
 *    -》函数和方法定义的位置不一样
 *        -函数:定义在方法或者函数内部
 *        -方法:定义在类下的函数,叫方法
 *    -》方法支持重写和重载,但是函数不允许
 *        重载对调用者可以屏蔽因为方法参数不同而造成的差异
 */
object Scala01_TestFun {
  //定义一个方法
  def sayHello(name:String):String={
    return "Hello" + name
  }
  def sayHello(age:Int):String={
    return "Hello" + age
  }
  def main(args: Array[String]): Unit = {

    println(sayHello("小明"))
  }

3.函数定义

1)函数定义

(1)函数1:无参,无返回值

(2)函数2:无参,有返回值

(3)函数3:有参,无返回值

(4)函数4:有参,有返回值

(5)函数5:多参,无返回值

(6)函数6:多参,有返回值

2)案例实操

package com.scala.chapter04

/**
 * 函数定义形式
 */
object Scala02_TestFun {
  def main(args: Array[String]): Unit = {

    //(1)函数1:无参,无返回值
    def f1(): Unit ={
      println("无参,无返回值")
    }
    //(2)函数2:无参,有返回值
    def f2(): Int ={
      println("无参,有返回值")
      return 10
    }
    //(3)函数3:有参,无返回值
    def f3(name:String): Unit ={
      println("有参,无返回值")
    }
    f3("x")
    //(4)函数4:有参,有返回值
    def f4(name:String): String ={
      println("有参,有返回值")
      return  name
    }
    println(f4("x"))
    //(5)函数5:多参,无返回值
    def f5(name:String,name2:String): Unit ={
      println("多参,无返回值")
      println(s"$name and $name2 300kg")
    }
    println(f5("xiaoming", "xiaogang"))
    //(6)函数6:多参,有返回值
    def f6(int: Int,int2: Int): Int ={
      println("多参,有返回值")
      return int+int2
    }

    println(f6(1, 5))
  }
}

4.函数参数

1)案例实操

(1)可变参数

(2)如果参数列表中存在多个参数,那么可变参数一般放置在最后

(3)参数默认值,一般将有默认值的参数放置在参数列表的后面

(4)带名参数

package com.scala.chapter04

/**
 * 函数的参数
 */
object Scala03_TestFun {

  def main(args: Array[String]): Unit = {

    //(1)可变参数
    def function1(string: String*): Unit = {
      println(string)
    }

    function1("aaa", "sss", "ddd", "55")

    //(2)如果参数列表中存在多个参数,那么可变参数应该放置在最后
    //在一个参数列表中,至多有一个可变参数
    def function2(int: Int, string2: String*): Unit = {
      println(int, string2)
    }

    function2(2, "sss", "ddd", "hhh")

    //(3)参数默认值,一般将有默认值的参数放置在参数列表的后面
    def function3(int: Int, string: String = "dream"): Unit = {
      println(int, string)
    }

    function3(1)

    //(4)带名参数
    def function4(name: String = "xiaobai", sex: String): Unit = {
      println(name, sex);
    }

    function4(sex = "female")
  }
}

5.函数至简原则

函数至简原则:能省则省

1)至简原则细节

(1)return可以省略,Scala会使用函数体的最后一行代码作为返回值

(2)如果函数体只有一行代码,可以省略花括号

(3)返回值类型如果能够推断出来,那么可以省略(:和返回值类型一起省略)

(4)如果有return,则不能省略返回值类型,必须指定

(5)如果函数明确声明unit,那么即使函数体中使用return关键字也不起作用

(6)Scala如果期望是无返回值类型,可以省略等号

(7)如果函数无参,但是声明了参数列表,那么调用时,小括号,可加可不加

(8)如果函数没有参数列表,那么小括号可以省略,调用时小括号必须省略

(9)如果不关心名称,只关心逻辑处理,那么函数名(def)可以省略

package com.scala.chapter04

/**
 * 函数至简原则:
 * -》能省则省
 */
object Scala04_TestFun {
  def main(args: Array[String]): Unit = {
    /*函数正常定义与调用
    def say(name:String): String ={
      return name
    }
    println(sayHello("john"))

    */
    //(1)return可以省略,Scala会使用函数体的最后一行代码作为返回值
    def say1(name: String):String = {
       name
    }
    println(say1("john"))

    //(2)如果函数体只有一行代码,可以省略花括号
    def say2(name: String):String = name
    println(say2("john"))

    //(3)返回值类型如果能够推断出来,那么可以省略(:和返回值类型一起省略)
    def say3(name: String) = name
    println(say3("john"))

    //(4)如果有return,则不能省略返回值类型,必须指定
    def say4(name: String):String =  return name
    println(say4("john"))

    //(5)如果函数明确声明unit,那么即使函数体中使用return关键字也不起作用
    def say5(name: String):Unit =  return name
    println(say5("john"))

    //(6)Scala如果期望是无返回值类型,可以省略等号 这种写法称为过程
    def say6(name: String){}
    println(say6("john"))

    //(7)如果函数无参,但是声明了参数列表,那么调用时,小括号,可加可不加
    def say7(): Unit ={
      println("Hi")
    }
    say7()
    say7

    //(8)如果函数没有参数列表,那么小括号可以省略,调用时小括号必须省略
    def say8: Unit ={
      println("Hi")
    }
    say8
    //say8() error


    //(9)如果不关心名称,只关心逻辑处理,那么函数名(def)可以省略
    //函数类型  f:(参数列表声明)=>返回值
    /*
    def say10(string: String): Unit ={
      println(string)
    }
    say9(say10)
     */
    def say9(f: (String)=>Unit): Unit ={
      f("Clinton")
    }
      //匿名函数 通过Lambda表达式实现 (参数列表)->{函数体}
    //  say9((s:String)=>{println(s)})
    
    //(1)参数的类型可以省略,会根据形参进行自动的推导
    //say9((s)=>{println(s)})
    //(2)类型省略之后,发现只有一个参数,则圆括号可以省略;其他情况:没有参数和参数超过1的永远不能省略圆括号。
    //say9(s=>{println(s)})
    //(3)匿名函数如果只有一行,则大括号也可以省略
    //say9(s=>println(s))
    //(4)如果参数只出现一次,则参数省略且后面参数可以用_代替
    //say9(println(_))
    //(5)如果进行println输出的时候,可以推断出是一个函数,那么下划线可以省略
    say9(println)
  //定义一个函数,接收一个函数类型的参数,该函数类型有两个参数
    def f1(fun: (Int, Int) => Int): Int = {
      fun(1, 2)
    }

    //f1((x: Int, y: Int) => {x + y})
    println(f1(_ - _))
  }
}

函数式编程小结


    -函数的基本语法
        def 函数名(参数列表):返回值类型={
            函数体
        }

        函数类型:    (参数形式)=>返回值

    -函数的参数
        *可变长参数
            >在一个参数列表中,至多只能有一个可变长参数
            >如果一个参数列表中,有多个参数,应该将可变长参数放到最后

        *参数默认值
            >在声明函数的时候,函数的形参可以赋默认值
            >如果参数有默认值,那么在调用函数的时候,有默认值的参数可以不用传参,使用默认值作为参数
            >如果参数有默认值,那么在调用函数的时候,也可以传递参数,会用传递的参数替换默认值作为参数

        *带名参数
            在调用函数的时候,我们可以指定给哪个参数进行赋值

    -函数至简原则(能省则省)
        *return可以省略,Scala会使用函数体的最后一行代码作为返回值
        *如果函数体只有一行代码,可以省略花括号
        *返回值类型如果能够推断出来,那么可以省略(:和返回值类型一起省略)
        *如果有return,则不能省略返回值类型,必须指定
        *如果函数明确声明unit,那么即使函数体中使用return关键字也不起作用
        *Scala如果期望是无返回值类型,可以省略等号
        *如果函数无参,但是声明了参数列表,那么调用时,小括号,可加可不加
        *如果函数没有参数列表,那么小括号可以省略,调用时小括号必须省略
        *如果不关心名称,只关心逻辑处理,那么函数名(def)可以省略

    -匿名函数
        *参数的类型可以省略,会根据形参进行自动的推导
        *类型省略之后,发现只有一个参数,则圆括号可以省略;其他情况:没有参数和参数超过1的永远不能省略圆括号。
        *匿名函数如果只有一行,则大括号也可以省略
        *如果参数只出现一次,则参数省略且后面参数可以用_代替

2.函数高级

1.高阶函数

在Scala中,函数是一等公民。怎么体现的呢?

对于一个函数我们可以:定义函数、调用函数

object Scala06_TestFun_high {

  def main(args: Array[String]): Unit = {

    //函数正常的声明与调用
    def foo(): Int ={
    println("foo")
     1
        }
    }
}

但是其实函数还有更高阶的用法

1)函数可以作为值进行传递

object Scala06_TestFun_high {

    def main(args: Array[String]): Unit = {

    //将函数作为值进行传递 语法:在函数名称后面 + 空格 + 下划线
    //将foo函数作为一个和整体,赋值给f变量, f是函数类型 ()=> Int
    val f: () => Int = foo _
    //此时变量是一个变量,如果要运行f的化,需要添加括号

    def f1: () => Int = foo _


    var ff = foo //将函数执行结果赋值给ff   foo 1
    var ff = foo _ //将函数本身赋值给ff 

    //将函数本身作为值赋给ff 如果明确了变量类型,那么空格和下划线可以省略
    val ff: () =>Unit = foo
    println(ff)  //

    }
}

2)函数可以作为参数进行传递

def main(args: Array[String]): Unit = {
    
    // (1)定义一个函数,函数参数还是一个函数签名;f表示函数名称;(Int,Int)表示输入两个Int参数;Int表示函数返回值
    def f1(f: (Int, Int) => Int): Int = {
        f(2, 4)
    }
    
    // (2)定义一个函数,参数和返回值类型和f1的输入参数一致
    def add(a: Int, b: Int): Int = a + b
    
    // (3)将add函数作为参数传递给f1函数,如果能够推断出来不是调用,_可以省略
    println(f1(add))
println(f1(add _))
//可以传递匿名函数
}

    //函数可以作为参数,进行传递(多数情况,通过匿名函数的形式来实现)
    //定义一个函数calculator,该函数接收一个函数类型的参数op,op定义了两个整数要进行的操作

   def calculator(a:Int,b:Int,op:(Int,Int)=>Int):Int={
      op(a,b)
    }

    println(calculator(50, 20, _ * _))

package com.scala.chapter04


/**
 * 高价函数:函数可以作为参数进行传递 -》匿名函数
 * 优点:扩展函数的功能
 * 提高函数的灵活性
 */
object Scala07_TestFun_Non {
  def main(args: Array[String]): Unit = {
    //需求:提供一个函数,对数组中的原始进行处理,处理完毕后返回一个新的方式 目前的处理方式:元素+1
    //创建一个数组对象 val arr: Array[Int] = Array(1, 2, 3, 4)

    def operationArray(arr: Array[Int], op: Int => Int): Array[Int] = {
      //第一步 遍历数组元素 通过yield接收 使用op方法操作
      for (elem <- arr) yield op(elem)
    }
/*
    def op(elem: Int): Int = {
      elem + 1
    }
    //调用函数,实现元素+1的处理
    val newArr: Array[Int] = operationArray(Array(1, 2, 3, 4), op)

    //打印输出, mkString方法 用指定的字符串连接数组中的元素,形成一个新的字符串
    println(newArr.mkString(","))

 */
    //通过匿名函数调用,对数组元素 * 3

    //val newArr: Array[Int] = operationArray(Array(1, 2, 3, 4), (elem: Int) => {elem * 3})
    // println(newArr.mkString(","))

    //简化
    val newArr: Array[Int] = operationArray(Array(1, 2, 3, 4), _ * 3)
    println(newArr.mkString(","))
  }
}

3)函数可以作为函数返回值返回

def main(args: Array[String]): Unit = {
  //函数可以作为函数返回值返回--函数嵌套

    def f1() = {
      def f2(): Unit = {
        println("f2函数被调用")
      }
      //将f2作为返回值返回给f1
      //调用f2
      f2 _
    }

    //ff就是f2
    //var ff = f1()
    //ff()
    
    //简化f1()()调用f2

    f1()()
  }

package com.scala.chapter04

import scala.collection.immutable.Range

/**
 * 高阶函数-》函数作为返回值返回
 */
object Scala08_TestFun_return {
  def main(args: Array[String]): Unit = {
    def f1(): (Int) => Int = {
      var a: Int = 10

      def f2(b: Int): Int = {
        a + b
      }

      f2 _
    }

    //执行f1函数返回f2,将返回的f2赋值给ff变量
    val ff: Int => Int = f1()
    //调用ff函数,其实就是调用f2
    println(ff(12))

    //通过()()直接调用
    println(f1()(20))

  }

}

2.匿名函数

1)说明

没有名字的函数就是匿名函数。

(x:Int)=>{函数体}

x:表示输入参数类型;Int:表示输入参数类型;函数体:表示具体代码逻辑

2)案例实操

需求1:传递的函数有一个参数

传递匿名函数至简原则:

(1)参数的类型可以省略,会根据形参进行自动的推导

(2)类型省略之后,发现只有一个参数,则圆括号可以省略其他情况:没有参数和参数超过1的永远不能省略圆括号

(3)匿名函数如果只有一行,大括号也可以省略

(4)如果参数只出现一次,则参数省略且后面参数可以用_代替

package com.scala.chapter04


/**
 * 高价函数:函数可以作为参数进行传递 -》匿名函数
 * 优点:扩展函数的功能
 * 提高函数的灵活性
 */
object Scala07_TestFun_Non {
  def main(args: Array[String]): Unit = {
    //需求:提供一个函数,对数组中的原始进行处理,处理完毕后返回一个新的方式 目前的处理方式:元素+1
    //创建一个数组对象 val arr: Array[Int] = Array(1, 2, 3, 4)

    def operationArray(arr: Array[Int], op: Int => Int): Array[Int] = {
      //第一步 遍历数组元素 通过yield接收 使用op方法操作
      for (elem <- arr) yield op(elem)
    }
/*
    def op(elem: Int): Int = {
      elem + 1
    }
    //调用函数,实现元素+1的处理
    val newArr: Array[Int] = operationArray(Array(1, 2, 3, 4), op)

    //打印输出, mkString方法 用指定的字符串连接数组中的元素,形成一个新的字符串
    println(newArr.mkString(","))

 */
    //通过匿名函数调用,对数组元素 * 3

    //val newArr: Array[Int] = operationArray(Array(1, 2, 3, 4), (elem: Int) => {elem * 3})
    // println(newArr.mkString(","))

    //简化
    val newArr: Array[Int] = operationArray(Array(1, 2, 3, 4), _ * 3)
    println(newArr.mkString(","))
  }
}

3.函数柯里化&闭包

闭包:函数式编程的标配

1)说明

闭包:如果一个函数,访问到了它的外部(局部)变量的值,那么这个函数和他所处的环境,称为闭包

函数柯里化:把一个参数列表的多个参数,变成多个参数列表。

2)案例实操

(1)闭包

package com.scala.chapter04

import scala.collection.immutable.Range

/**
 * 高阶函数-》函数作为返回值返回
 */
object Scala08_TestFun_return {
  def main(args: Array[String]): Unit = {
    /*   def f1(): (Int) => Int = {
         var a: Int = 10

         def f2(b: Int): Int = {
           a + b
         }

         f2 _
       }

       //执行f1函数返回f2,将返回的f2赋值给ff变量
       val ff: Int => Int = f1()

       //闭包:内层函数f2要访问外层函数f1局部变量a,当外层函数f1执行结束之后,f1会释放栈内存,但是会自动的延迟f1函数的局部变量的生命周期
       //    和内层函数f2形成一个闭合的效果,这种效果称为闭包
       //闭包包含:外层函数的局部变量 + 内层函数
       //调用ff函数,其实就是调用f2
       println(ff(12))

       //通过()()直接调用
       println(f1()(20))

     */

    println("===========================================")
    //此代码可以转为如下代码

    /*  def f1(): (Int) => Int = {
        var a: Int = 10

        def f2(b: Int): Int = {
          a + b
        }
        f2 _
      }
      //此代码可以转为如下代码

      def f3() = {
        var a: Int = 10
        (b:Int) => a + b
      }
      */
    // def f4(参数列表)(参数列表):返回类型 ={函数体}
    // 函数柯里化,其实就是将复杂的参数逻辑变得简单化,函数柯里化一定存在闭包
    // 将一个函数的一个参数列表中的多个参数,拆分为多个参数列表
    def f4()(b: Int): Int = {
      var a: Int = 10
      a + b
    }
    println(f4()(20))

  }

}

4.递归

1)说明

一个函数/方法在函数/方法体内又调用了本身,我们称之为递归调用

2)案例实操

package com.scala.chapter04

/**
 * 通过递归求一个整数阶乘
 *  1) 方法调用自身
 *  2) 方法必须要有跳出的逻辑
 *  3) 方法调用自身时,传递的参数应该有规律
 *  4) scala中的递归必须声明函数返回值类型
 */
object Scala09_TestFun_Jc {
  def main(args: Array[String]): Unit = {

    println(jc(5))

    def jc(n: Int): Int = {
      if (n == 1) {
        return 1
      } else {
         n * jc(n - 1)
      }
    }
  }
}

5.控制抽象

1)值调用:把计算后的值传递过去

2)名调用:把代码传递过去

package com.scala.chapter04


/**
 * 控制抽象
 * 1)值调用:
 * 把函数计算后的值传递过去
 * 2)名调用:
 * 把代码块传递过去
 */
object Scala10_TestControlAbstract {
  def main(args: Array[String]): Unit = {
    // 值调用:把函数计算后的值传递过去
     def foo(a:Int): Unit ={
       println(a)
       println(a)
     }
     def f():Int={
       println("f.....")
       1
     }
     //f.....
     //10
     //10
 
     foo(f)
 
     

    //名调用:把代码块传递过去
    //数据类型  Int,String,函数 参数类型=>返回值,代码块 =>返回值类型

    //参数为Int类型代码块
    def foo(a: => Int): Unit = {
      println(a)
      println(a)
    }

    def f(): Int = {
      println("f.....")
      1
    }
    //f.....
    //1
    //f.....
    //1
    foo(f)

    def f1(aa: => Unit): Unit = {
      aa
      aa
    }

    f1(
      {
        println("aass")
      }
    )

    def f2(bb: => String): Unit = {
      println(bb)
      println(bb)
    }

    f2(
      {
        "ss"
      }
    )
    
  }
  
}

自定义一个While循环

package com.scala.chapter04

/**
 * 自定义函数,实现while循环
 */
object Scala11_TestMyWhile {
  def main(args: Array[String]): Unit = {
    //While循环
    /*var n = 1
    while (n <= 10){
      println(n)
      n += 1
    }
     */
    //while (循环条件)(循环体)


    // 使用柯里化实现 def myWhile(循环条件)(循环体){}
    // 使用柯里化实现 def myWhile(布尔代码块)(代码块){}
    // 重点 boolean: => Boolean 为代码块
   /* def myWhile(boolean: => Boolean)(op: => Unit): Unit = {
      if (boolean) {
        op
        myWhile(boolean)(op)
      }
    }
    */
    //使用普通函数,实现while循环
    /**柯里化优点
        将一个函数参数列表中的多个参数,拆分为多个参数列表,使得参数所表示的含义,清晰明确

     */

   /* def myWhile(boolean: => Boolean,op: => Unit):Unit={
      if (boolean){
        op
        myWhile(boolean,op)
      }
    }
    var i:Int = 1
    myWhile(i <= 10, {
      println(i)
      i += 1
    }
    )

    */
    //使用闭包,实现myWhile循环
    //柯里化优点2:简化闭包编写
    def myWhile(boolean: => Boolean):(=>Unit)=>Unit ={
     //内存函数参数表示循环体
     def ff(op: => Unit): Unit ={
       if (boolean){
         op
         myWhile(boolean)(op)
       }
     }
     ff _
   }
    var i = 1
    myWhile(i <= 10) {
      println(i)
      i += 1
    }


  }
}

6.惰性函数

1)说明

函数返回值被声明为lazy时,函数的执行将被推迟,直到我们首次对此取值,该函数才会执行。这种函数我们称之为惰性函数。

2)案例实操

未使用惰性加载执行

package com.scala.chapter04

/**
 * 惰性加载
 */
object Scala12_TestLazy {
  def main(args: Array[String]): Unit = {
    val res: Int = sum(34, 2)
    println("1============")
    println(res + "第2")

  }

  def sum(a: Int, b: Int): Int = {
    println("3:sum被执行")
    a + b
  }
}

 Scala_第18张图片

package com.scala.chapter04

/**
 * 惰性加载
 */
object Scala12_TestLazy {
  def main(args: Array[String]): Unit = {
    lazy val res: Int = sum(34, 2)
    println("1============")
    println(res + "第2")

  }

  def sum(a: Int, b: Int): Int = {
    println("3:sum被执行")
    a + b
  }
}

 Scala_第19张图片

高阶函数小结


    -函数更高级的用法
    -在Scala语言中,函数是一等公民
        *函数可以作为值进行传递
            >语法
                var f = 函数名 _
            >如果明确了变量的数据类型,那么下划线可以省略

        *函数可以作为参数进行传递   
            >通过匿名函数
            >扩展函数的功能
            >提高函数的灵活度

        *函数可以作为返回值进行返回 
            >函数的嵌套
            
            >函数链式调用,通过参数传递数据,在执行的过程中,函数始终占据栈内存,容易导致内存溢出
            
            >闭包
                内层函数访问外层函数的局部变量,会自动延长外层函数局部变量的生命周期,与内层函数形成一个
                闭合的效果,我们称之为闭包

            >柯里化
                &将一个参数列表中的多个参数,拆分为多个参数列表
                &好处1:每一个参数列表表示函数清晰明确
                &好处2:简化闭包代码的编写    

    -控制抽象
        *值调用
            传递的是函数执行后的结果

        *名调用
            传递的是代码块  
            =>代码块返回值类型

第6章 面向对象

Scala的面向对象思想和Java的面向对象思想和概念是一致的。

Scala中语法和Java不同,补充了更多的功能。

1、Scala

1)基本语法

package 包名.类名

2)Scala包的三大作用(和Java一样)

(1)区分相同名字的类

(2)当类很多时,可以很好的管理类

(3)控制访问范围

1.包的命名

1)命名规则

只能包含数字、字母、下划线、小圆点.,但不能用数字开头,也不要使用关键字。

2)案例实操

package demo.class.exec1  //错误,因为 class 关键字
demo.12a    //错误,数字开头

3)命名规范

一般是小写字母+小圆点

com.公司名.项目名.业务模块名

4)案例实操

com.sohu.bank.order

2.包说明(包语句

1)说明

Scala有两种包的管理风格,一种方式和Java的包管理风格相同,每个源文件一个包(包名和源文件所在路径不要求必须一致),包名用“.”进行分隔以表示包的层级关系,如com.xxx.scala。另一种风格,通过嵌套的风格表示层级关系,如下

package com{
  package xxx{
    package scala{

    }
  }
}

第二种风格有以下特点:

(1)一个源文件中可以声明多个package

(2)子包中的类可以直接访问父包中的内容,而无需导包

2)案例实操

package com {s

  import com.xxx.Inner //父包访问子包需要导包

  object Outer {
    val out: String = "out"

    def main(args: Array[String]): Unit = {
      println(Inner.in)
    }
  }

  package xxx {

    object Inner {
      val in: String = "in"

      def main(args: Array[String]): Unit = {
        println(Outer.out) //子包访问父包无需导包
      }
    }
  }
}

package other {

}

3.包对象

在Scala中可以为每个包定义一个同名的包对象,定义在包对象中的成员,作为其对应包下所有class和object的共享变量,可以被直接访问。

1)定义

package com.scala

package object chapter05 {
  val shareValue="share"
  def shareMethod()={}
}

说明

(1)若使用Java的包管理风格,则包对象一般定义在其对应包下的package.scala文件中,包对象名与包名保持一致。

(2)如采用嵌套方式管理包,则包对象可与包定义在同一文件中,但是要保证包对象与包声明在同一作用域中。

package com {

  object Outer {
    val out: String = "out"

    def main(args: Array[String]): Unit = {
      println(name)
    }
  }
}

package object com {
  val name: String = "com"
}

4.导包说明

1)和Java一样,可以在顶部使用import导入,在这个文件中的所有类都可以使用。

2)局部导入:什么时候使用,什么时候导入。在其作用范围内都可以使用

3)通配符导入:import java.util._

4)给类起名:import java.util.{ArrayList=>JL}

5)屏蔽类:import java.util.{ArrayList =>_,_}

6)导入相同包的多个类:import java.util.{HashSet, ArrayList}

7)导入包的绝对路径:new _root_.java.util.HashMap

package java {
    package util {
        class HashMap {

        }
    }
}

2)注意

Scala中的三个默认导入分别是

import java.lang._

import scala._

import scala.Predef._

2、类和对象

--在自然界中,只要是客观存在的都是对象(万物皆对象)

--对大量对象共性进行抽象,抽象类

>有什么         属性

>能做什么     方法

>属性和方法虽然定义在类中,但是属于每一个对象

--在Java/Scala语言中,类是创建对象的模板
--类是客观事物在人脑中的主观反应

1.定义类

1)回顾:Java中的类

如果类是public的,则必须和文件名一致。

一般,一个.java有一个public类

注意Scala中没有public,一个.scala可以多个类

1)基本语法

[修饰符] class 类名 {

    类体

}

说明

(1)Scala语法中,类并不声明为public,所有这些类都具有公有可见性(即默认就是public)

(2)一个Scala源文件可以包含多个类

2)案例实操

object Scala01_TestObject {
  def main(args: Array[String]): Unit = {
    //创建对象
    val std = new Student01
    println(std.name)
    std.study()
  }

}
//定义一个类
class Student01{
  var name:String = "Max"

  def study(): Unit ={
    println(s"${name}在学习")
  }
}

2.属性

属性是类的一个组成部分

1)基本语法

[修饰符] var|val 属性名称 [:类型] = 属性值

Bean属性(@BeanPropetry),可以自动生成规范的setXxx/getXxx方法

2)案例实操

package com.scala.chapter05

import scala.beans.BeanProperty
object Scala_TestField {
  def main(args: Array[String]): Unit = {
    //创建对象
    val std = new Student02()
    //通过对象.的方式访问属性
    //std.name= "john"
    //println(std.name)
  }

  class Student02 {

    //在Scala语言中,属性,方法,类默认的修饰是public
    //但是public不能显式加 类似java中的default
    //底层生成的字节码文件对属性用private进行修饰,提供了公开的获取设置属性值的方法
    //作用1:符合面向对象的封装特性

    private var name: String = "Mac"

    //如果想要生成符合JavaBean规范的get、set方法,需要加上@Beanproperty
    //作用2:对一些框架进行支持

    @BeanProperty
    var age:Int = 19

    //如果想给属性赋默认值,需要在声明的时候,赋_
    var sex:String = _





  }

}

3、封装

封装就是把抽象出的数据和对数据的操作封装在一起,数据被保护在内部,程序的其它部分只有通过被授权的操作(成员方法),才能对数据进行操作。Java封装操作如下,

(1)将属性进行私有化

(2)提供一个公共的set方法,用于对属性赋值

(3)提供一个公共的get方法,用于获取属性的值

Scala中的public属性,底层实际为private,并通过get方法(obj.field())和set方法(obj.field_=(value))对其进行操作。所以Scala并不推荐将属性设为private,再为其设置public的get和set方法的做法。但由于很多Java框架都利用反射调用getXXX和setXXX方法,有时候为了和这些框架兼容,也会为Scala的属性设置getXXX和setXXX方法(通过@BeanProperty注解实现)。

 1.访问权限

1)说明

在Java中,访问权限分为:public,private,protected和默认。在Scala中,你可以通过类似的修饰符达到同样的效果。但是使用上有区别。

(1)Scala 中属性和方法的默认访问权限为public,但Scala中无public关键字。

(2)private为私有权限,只在类的内部和伴生对象中可用。

(3)protected为受保护权限,Scala中受保护权限比Java中更严格,同类、子类可以访问,同包无法访问。

(4)private[包名]增加包访问权限,包名下的其他类也可以使用

2)案例实操

package com.scala.chapter05

/**
 * 访问权限
 * Java
 * -private                  只能在当前类中被访问
 * -default(默认)             当前类以及同包的其他类
 * -protected                当前类以及同包的其他类以及非同包的子类
 * -public                   所有类
 *
 * Scala
 * -在scala中,类、方法、属性默认就是public修饰,但是没有public关键字
 * -private      私有的,只能在当前类以及伴生对象中使用
 * -protected    受保护的,比Java中的权限设置更加严格,同类、子类可以访问,同包其它类不能访问
 * -private[包名]  包访问权限,在指定的包下的其它类也可以访问
 *
 */
object Scala02_TestPrivilege {

  class Person03 {
    private var idCard: String = "10000"
    protected var name: String = "banzhang"
    private[chapter06] var age: Int = 40
    var sex: String = "男"

    def m1(): Unit = {
      println(idCard)
      println(name)
      println(age)
      println(sex)
    }

  }

}
package com.scala.chapter05

import com.scala.chapter05.Scala02_TestPrivilege.{Person03, Person06}

/**
 * 访问权限测试
 */
object Scala_TestPrivilege {
  def main(args: Array[String]): Unit = {
    val p = new Person03
    println(p.age)
    println(p.sex)
    //println(p.idCard)私有的,只能在本类中被访问
    //println(p.name) 受保护的,只能在本类以及子类中被访问
  }


}
class Student04 extends Person03{
  def f1(): Unit ={
   // println(idCard)私有的,只能在本类中被访问
    println(name)
    println(age)
    println(sex)
  }
}

2.方法

1)基本语法

def 方法名(参数列表) [:返回值类型] = {

方法体

}

2)案例实操

class Person {

    def sum(n1:Int, n2:Int) : Int = {
        n1 + n2
    }
}

object Person {

    def main(args: Array[String]): Unit = {

        val person = new Person()

        println(person.sum(10, 20))
    }
}

 3.创建对象

1)基本语法

val | var 对象名 [:类型]  = new 类型()

2)案例实操

(1)val修饰对象,不能改变对象的引用(即:内存地址),可以改变对象属性的值。

(2)var修饰对象,可以修改对象的引用和修改对象的属性值

(3)自动推导变量类型不能多态,所以多态需要显示声明

class Person {

    var name: String = "canglaoshi"

}



object Person {



    def main(args: Array[String]): Unit = {

        //val修饰对象,不能改变对象的引用(即:内存地址),可以改变对象属性的值。

        val person = new Person()

        person.name = "bobo"



        // person = new Person()// 错误的



        println(person.name)

    }

}

4.构造器

和Java一样,Scala构造对象也需要调用构造方法,并且可以有任意多个构造方法。

Scala类的构造器包括:主构造器和辅助构造器

1)基本语法

class 类名(形参列表) {  // 主构造器

   // 类体

   def  this(形参列表) {  // 辅助构造器

   }

   def  this(形参列表) {  //辅助构造器可以有多个...

   }

} 

说明

(1)辅助构造器函数的名称this,可以有多,编译器通过参数的个数及类型来区分。

(2)辅助构造方法不能直接构建对象,必须直接或者间接调用主构造方法。

(3)构造器调用其他另外的构造器,要求被调用构造器必须提前声明。

2)案例实操

(1)如果主构造器无参数,小括号可省略,构建对象时调用的构造方法的小括号也可以省略。

package com.scala.chapter05

/**
 * 构造器
 *   -构造器(构造方法)
 *     >构造对象
 *     >初始化属性(给对象的属性赋值)
 *   -Java中构造器
 *     *方法名和类名保持一致
 *     *构造方法没有返回值类型
 *     *构造方法可以重载
 *   -Scala中构造器
 *      *主构造方法
 *         >在声明类的同时,定义了主构造方法
 *         >在一个类中个,主构造方法只能有一个
 *      *辅助构造方法
 *         >方法名必须叫this
 *         >在辅助构造方法开始(第一行)必须直接或者间接的调用主构造方法
 *         >辅助构造方法可以重载
 */
object Scala03_TestConstructor {
  def main(args: Array[String]): Unit = {
//    val std = new Student05()
    val std = new Student05("max",19)

  }

}
//(1)如果主构造器无参数,小括号可省略
class Student05(){
  println("Student05的主构造方法被调用了")
  var name:String= _
  var age:Int= _

  //什么辅助构造方法
  def this(name:String){
    //主键调用主构造方法
    this()
    println("Student05的辅助构造方法1被调用了")
    this.name = name

  }
  def this(name:String,age:Int){

    this(name)
    println("Student05的辅助构造方法2被调用了")
    this.age=age
  }

}

5.构造器参数

1)说明

Scala类的主构造器函数的形参包括三种类型:未用任何修饰、var修饰、val修饰

(1)未用任何修饰符修饰,这个参数就是一个局部变量

(2)var修饰参数,作为类的成员属性使用,可以修改

(3)val修饰参数,作为类只读属性使用,不能修改

2)案例实操

package com.scala.chapter05

object Scala04_TestConstructor {
  def main(args: Array[String]): Unit = {
    val std = new Student06("jean", 22)
    println(std.name)
    println(std.age)


  }

}
/*
在scala语言中,以下写法较少
class Student06(nameParam:String,ageParam:Int){
  var name:String = nameParam
  var age:Int = ageParam
}
 */
/*在声明主构造方法参数的时候,如果参数前什么也不加,那么参数就作为当前类的局部变量使用,但是不能作为类属性被访问
//如果需要将参数作为类属性被访问,需要在声明前加var、val关键字
class Student06(var name:String,var age:Int){
  def m1(): Unit ={
    println(name)
  }
}
 */
//同时提供主,辅构造方法
class Student06{
  var name:String = _
  var age:Int = _

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

4、继承

1)基本语法

class 子类名 extends 父类名  { 类体 }

(1)子类继承父类的属性方法

(2)scala是单继承

2)案例实操

(1)子类继承父类的属性方法

(2)继承的调用顺序:父类构造器->子类构造器

package com.scala.chapter05

object Scala05_TestExtends {
  def main(args: Array[String]): Unit = {
    //自动推导类型,不能推导出多态
    //val std = new student07() -->1,3
    //val std = new student07("zz",11,"1") //-->1,3,4
    val std = new student07("zz",11,"1") //-->1,2,3,4


  }
}

class Person3{
  println("1-父类主构造方法被调用")
  var name:String= _
  var age:Int= _
  def this(name:String,age:Int){
    this()
    println("2-父类的辅助构造方法被调用")
    this.name=name
    this.age=age
  }
}
class student07(name: String, age: Int) extends Person3(name, age) {
  println("3-子类主构造方法被调用")
  var stdNo: String = _

  def this(name: String, age: Int, stdNo: String) {
    this(name,age)
    println("4-子类的辅助构造方法被调用")
    this.stdNo = stdNo
  }
}

5、抽象属性和抽象方法

1.抽象属性和抽象方法

1)基本语法

(1)定义抽象类:abstract class Person{} //通过abstract关键字标记抽象类

(2)定义抽象属性:val|var name:String //一个属性没有初始化,就是抽象属性

(3)定义抽象方法:def  hello():String //只声明而没有实现的方法,就是抽象方法

package com.scala.chapter05

/**
 * 抽象
 * -模糊、不具体
 * -使用abstract关键字来定义抽象类
 * -抽象类一般和抽象属性和抽象方法配合属性
 * 》抽象属性
 * 属性只有声明,没有赋值
 * 》抽象方法
 * 方法只有声明,没有实现
 *
 * -在一个类中,如果存在抽象属性或者抽象方法,那么这个类一定是抽象类
 * -如果一个类是抽象类,那么它不一定包含抽象属性和抽象方法
 * -如果一个类存在抽象属性或抽象方法,那么具体的实现应该交给子类完成
 * -如果子类也不能实现抽象内容,那么子类也为抽象类
 */
class Scala06_TestAbstract {

}

//愚公移山
abstract class YG {
  //移山
  def move(): Unit
}

class YGSon extends YG {
  override def move(): Unit = {
    println("使用铁锹")
  }
}

abstract class YGDau extends YG {
  override def move(): Unit
}

class YGWS extends YGDau {
  override def move(): Unit = {
    println("使用挖掘机")
  }
}
abstract class Person {

    val name: String

    def hello(): Unit
}

class Teacher extends Person {

    val name: String = "teacher"

    def hello(): Unit = {
        println("hello teacher")
    }
}

object Scala07_TestAbstract {

}

abstract class Person12{
  //非抽象属性
  val name:String = "zhangsan"
  //抽象方法
  var age:Int

  //非抽象方法
  def eat(): Unit ={
    println("Person's eat")
  }

  //抽象方法
  def sleep():Unit
}
class Student12 extends Person12{
  //在Scala语言中,除了方法之外,属性也可以重写
  //子类中,如果重写的是抽象属性和方法,那么override关键字可以省略
  override var age: Int = 18

  override def sleep(): Unit = {
    println("jean  sleep")
  }

  //对父类的非抽象方法进行重写
  //override关键字不能省略
  override def eat(): Unit = {
    //可以通过super关键字,去调用父类的方法
    super.eat()
    println("Student's Eat")
  }
  //在子类中对父类非抽象属性重写
  //父类声明了抽象属性,子类对属性进行实现
  //父类声明了非抽象属性,子类对属性进行重写
  //如果子类需要对父类的属性进行重写,那么属性只能是val修饰的
  override val name:String = "zhang"
}

2)继承&重写

(1)如果父类为抽象类,那么子类需要将抽象的属性和方法实现,否则子类也需声明为抽象类

(2)重写非抽象方法需要用override修饰,重写抽象方法则可以不加override。

(3)子类中调用父类的方法使用super关键字

(4)子类对抽象属性进行实现,父类抽象属性可以用var修饰;

子类对非抽象属性重写,父类非抽象属性只支持val类型,而不支持var。

因为var修饰的为可变变量,子类继承之后就可以直接使用,没有必要重写  

(5)Scala中属性和方法都是动态绑定,而Java中只有方法为动态绑定。

案例实操(对比Java与Scala的重写)

Scala

class Person {
    val name: String = "person"

    def hello(): Unit = {
        println("hello person")
    }
}

class Teacher extends Person {

    override val name: String = "teacher"

    override def hello(): Unit = {
        println("hello teacher")
    }
}

object Test {
    def main(args: Array[String]): Unit = {
        val teacher: Teacher = new Teacher()
        println(teacher.name)
        teacher.hello()

        val teacher1:Person = new Teacher
        println(teacher1.name)
        teacher1.hello()
    }
}

Java

class Person {

    public String name = "person";
    public void hello() {
        System.out.println("hello person");
    }

}
class Teacher extends Person {

public String name = "teacher";

    @Override
    public void hello() {
        System.out.println("hello teacher");
    }

}
public class TestDynamic {
public static void main(String[] args) {

        Teacher teacher = new Teacher();
        Person teacher1 = new Teacher();

        System.out.println(teacher.name);
        teacher.hello();

        System.out.println(teacher1.name);
        teacher1.hello();
    }
}

结果对比

                       Scala                                                                                         Java

Scala_第20张图片

 2.匿名子类

1)说明

Java一样,可以通过包含带有定义或重写的代码块的方式创建一个匿名的子类。

2)案例实操

package com.scala.chapter05

/**
 * 匿名子类
 */

object Scala08_TestNon {
  def main(args: Array[String]): Unit = {
    //抽象类不能实例化
    //可以通过匿名子类的方式创建对象
    val person1: Person14 = new Person14 {
      override var name: String = "nns"
      override var m1: Unit = {
        println("xx")
      }
    }
    println(person1.name)
    person1.m1
  }

}
abstract class Person14{
  var name:String
  var m1:Unit

}

 6、单例对象(伴生对象)

        Scala语言是完全面向对象的语言,所以并没有静态的操作(即在Scala中没有静态的概念)。但是为了能够和Java语言交互(因为Java中有静态概念),就产生了一种特殊的对象来模拟类对象,该对象为单例对象。若单例对象名与类名一致,则称该单例对象这个类的伴生对象,这个类的所有“静态”内容都可以放置在它的伴生对象中声明。

1.单例对象语法

1)基本语法


object Person{
	val country:String="China"
}

2)说明

(1)单例对象采用object关键字声明

(2)单例对象对应的类称之为伴生类,伴生对象的名称应该和伴生类名一致。

(3)单例对象中的属性和方法都可以通过伴生对象名(类名)直接调用访问。

3)案例实操

//(1)伴生对象采用object关键字声明
object Person {
    var country: String = "China"
}

//(2)伴生对象对应的类称之为伴生类,伴生对象的名称应该和伴生类名一致。
class Person {
    var name: String = "bobo"
}

object Test {
    def main(args: Array[String]): Unit = {
        //(3)伴生对象中的属性和方法都可以通过伴生对象名(类名)直接调用访问。
        println(Person.country)
    }
}
package com.scala.chapter05

/**
 *    单例设计模式
 *    -懒汉式
 *    -饿汉式
 *       -单例设计模式
 * *        》构造方法私有化
 * *        》提供私有静态属性,接收单例对象
 * *        》公共的,静态的getInstance方法
 */

object Scala10_TestSingleton {
  def main(args: Array[String]): Unit = {
    val std1:Student16 =  Student16.getInstance()
    println(std1)
    val std2:Student16 =  Student16.getInstance()
    println(std2)

  }
}

//主构造方法私有化
class Student16 private(){

}
/*
//伴生对象
//懒汉式
//提供私有静态属性,接收单例对象
object Student16{
  private var std:Student16 = null
  def getInstance():Student16={
    if(std == null){
      std = new Student16
    }
    std
  }
}*/

//饿汉式

object Student16 {
  private var std:Student16 = new Student16
  def getInstance():Student16={
    std
  }
}

2.apply方法

1)说明

(1)通过伴生对象apply方法,实现不使用new方法创建对象。

(2)如果想让主构造器变成私有的,可以在()之前加上private。

(3)apply方法可以重载。

(4)Scala中obj(arg)的语句实际是在调用该对象的apply方法,即obj.apply(arg)。用以统一面向对象编程和函数式编程的风格。

(5)当使用new关键字构建对象时,调用的其实是类的构造方法,当直接使用类名构建对象时,调用的其实时伴生对象的apply方法。

2)案例实操

object Test {

    def main(args: Array[String]): Unit = {

        //(1)通过伴生对象的apply方法,实现不使用new关键字创建对象。
        val p1 = Person()
        println("p1.name=" + p1.name)

        val p2 = Person("bobo")
        println("p2.name=" + p2.name)
    }
}

//(2)如果想让主构造器变成私有的,可以在()之前加上private
class Person private(cName: String) {
    var name: String = cName
}

object Person {

    def apply(): Person = {
        println("apply空参被调用")
        new Person("xx")
    }

    def apply(name: String): Person = {
        println("apply有参被调用")
        new Person(name)
}
//注意:也可以创建其它类型对象,并不一定是伴生类对象
}

7、特质(Trait)

        Scala语言中,采用特质trait(特征)来代替接口的概念,也就是说,多个类具有相同的特质(特征)时,就可以将这个特质(特征)独立出来,采用关键字trait声明。

Scala中的trait中即可以有抽象属性和方法,也可以有具体的属性和方法一个类可以混入(mixin)多个特质。这种感觉类似于Java中的抽象类。

Scala引入trait特征,第一可以替代Java的接口,第二个也是对单继承机制的一种补充。

1.特质声明

1)基本语法

trait 特质名 {

trait主体

}

2)案例实操

package com.scala.chapter05

/**
 * 特质的声明以及混入特质
 */
object  Scala11_TestTrait {

  def main(args: Array[String]): Unit = {
    var a:PersonTrait17 = new MyClass17
    a.eat()
    println(a.name)
  }
}

//特质的声明
trait PersonTrait17{
  //抽象属性
  var name:String
  //抽象方法
  def eat():Unit
  //非抽象属性
  var age:Int = 11
  //非抽象方法
  def sleep():Unit ={
    println("Person's sleep")
  }
}
//混入特质
class MyClass17 extends PersonTrait17{
  override var name: String = "kk"

  override def eat(): Unit = {
    println("kk eat")
  }
}

2.特质基本语法

一个类具有某种特质(特征),就意味着这个类满足了这个特质(特征)的所有要素,所以在使用时,也采用了extends关键字,如果有多个特质或存在父类,那么需要采用with关键字连接。

1)基本语法:

没有父类:class  类名 extends  特质1   with    特质2   with   特质3 …

有父类:class  类名  extends  父类   with  特质1   with   特质2  with 特质3…

2)说明

(1)类和特质的关系:使用继承的关系。

(2)当一个类去继承特质时,第一个连接词是extends,后面是with。

(3)如果一个类在同时继承特质和父类时,应当把父类写在extends后。

3)案例实操

(1)特质可以同时拥有抽象方法和具体方法

(2)一个类可以混入(mixin)多个特质

(3)所有的Java接口都可以当做Scala特质使用

(4)动态混入:可灵活的扩展类的功能

(4.1)动态混入:创建对象时混入trait,而无需使类混入该trait

(4.2)如果混入的trait中有未实现的方法,则需要实现

trait PersonTrait {

    //(1)特质可以同时拥有抽象方法和具体方法
    // 声明属性
    var name: String = _

    // 抽象属性
    var age: Int

    // 声明方法
    def eat(): Unit = {
        println("eat")
    }

    // 抽象方法
    def say(): Unit
}

trait SexTrait {
    var sex: String
}

//(2)一个类可以实现/继承多个特质
//(3)所有的Java接口都可以当做Scala特质使用
class Teacher extends PersonTrait with java.io.Serializable {

    override def say(): Unit = {
        println("say")
    }

    override var age: Int = _
}


object TestTrait {

    def main(args: Array[String]): Unit = {

        val teacher = new Teacher

        teacher.say()
        teacher.eat()

        //(4)动态混入:可灵活的扩展类的功能
        val t2 = new Teacher with SexTrait {
            override var sex: String = "男"
        }

        //调用混入trait的属性
        println(t2.sex)
    }
}
package com.scala.chapter05

/**
 * 类和特质
 */
object Scala12_TestTrait {
  def main(args: Array[String]): Unit = {
    var std1:MyStudent = new MyStudent

    //特质的动态混入 ,在创建对象的时候,为该对象单独的混入某个特质
    val std2 = new MyStudent with TraitC with TraitD {
      override def buy(): Unit = {
        println("Buy")
      }

      override def rap(): Unit = {
        println("Rap")
      }
    }
    std2.buy()
    std2.rap()
  }
}

trait TraitA{
  def run():Unit
}
trait TraitB{
  def speak():Unit
}
trait TraitC{
  def buy():Unit
}
trait TraitD{
  def rap():Unit
}
class Super18{}

//如果不存在继承关系,混入多个特质
//class MyClass18 extends TraitA with TraitB{}

//如果存在继承关系,混入多个特质
class MyStudent extends Super18 with TraitB with TraitA{
  override def speak(): Unit = {
    println("Speak")
  }

  override def run(): Unit = {
    println("run")
  }
}

3.特质叠加

        由于一个类可以混入(mixin)多个trait,且trait中可以有具体的属性和方法,若混入的特质中具有相同的方法(方法名,参数列表,返回值均相同),必然会出现继承冲突问题。冲突分为以下两种:

第一种,一个类(Sub)混入的两个trait(TraitA,TraitB)中具有相同的具体方法,且两个trait之间没有任何关系,解决这类冲突问题,直接在类(Sub)中重写冲突方法。

                                                        Scala_第21张图片

        第二种,一个类(Sub)混入的两个trait(TraitA,TraitB)中具有相同的具体方法,且两个trait继承自相同的trait(TraitC),及所谓的“钻石问题”,解决这类冲突问题,Scala采用了特质叠加的策略。

                                                           Scala_第22张图片

所谓的特质叠加,就是将混入的多个trait中的冲突方法叠加起来,案例如下,

trait Ball {
   def describe(): String = {
      "ball"
   }
}

trait Color extends Ball {
   override def describe(): String = {
      "blue-" + super.describe()
   }
}

trait Category extends Ball {
   override def describe(): String = {
      "foot-" + super.describe()
   }
}

class MyBall extends Category with Color {
   override def describe(): String = {
      "my ball is a " + super.describe()
   }
}

object TestTrait {
   def main(args: Array[String]): Unit = {
      println(new MyBall().describe())
   }
}

 

4.特质叠加执行顺序

        当一个类混入多个特质的时候,scala会对所有的特质及其父特质按照一定的顺序进行排序,而此案例中的super.describe()调用的实际上是排好序后的下一个特质中的describe()方法。,排序规则如下:

Scala_第23张图片

结论:

(1)案例中的super,不是表示其父特质对象,而是表示上述叠加顺序中的下一个特质,即,MyClass中的super指代Color,Color中的super指代Category,Category中的super指代Ball。

(2)如果想要调用某个指定的混入特质中的方法,可以增加约束:super[],例如super[Category].describe()。

package com.scala.chapter05

/**
 * 特质叠加 解决特质冲突
 * -特质叠加的顺序
 * 第一步:列出第一个混入特质的继承关系,作为临时叠加的顺序
 * DBOperation-->Operation
 *
 * 第二步:列出第二个混入特质的继承关系,并且该顺序放到临时叠加顺序的前面,已经出现的特质不在出现
 * HDFSOperation-->DBOperation-->Operation
 *
 * 第三步:将子类放到临时叠加顺序的第一个
 * MyOperation-->HDFSOperation-->DBOperation-->Operation
 *
 *
 * 注意:这个时候super不是调用父类中的方法了,而是调用特质叠加顺序上下一个节点的方法
 */
object Scala_TestTrait {
  def main(args: Array[String]): Unit = {
    val mo = new MyOperation
    println(mo.describe())//向HDFS向MYSQL数据库插入数据

  }
}

trait Operation {
  def describe(): String = {
    "插入数据"
  }

}

trait DBOperation extends Operation{
  override def describe(): String = {
    "向MYSQL数据库"+ super.describe()
  }
}
trait HDFSOperation extends Operation{
  override def describe(): String = {
    "向HDFS"+ super.describe()
  }
}

class MyOperation extends  HDFSOperation with DBOperation {
  override def describe(): String = {
    "操作是" + super.describe()
  }
}

5.特质自身类型 

1)说明

自身类型可实现依赖注入的功能。

2)案例实操

class User(val name: String, val age: Int)

trait Dao {
   def insert(user: User) = {
      println("insert into database :" + user.name)
   }
}

trait APP {
   _: Dao =>
 
   def login(user: User): Unit = {
      println("login :" + user.name)
      insert(user)
   }
}

object MyApp extends APP with Dao {
   def main(args: Array[String]): Unit = {
      login(new User("bobo", 11))
   }
}
package com.scala.chapter05

/**
 * Desc: 特质自身类型
 *   1.实现了依赖注入的功能
 *   2.要求混入该特质的同时要混入特质自身类型
 */
object Scala13_TestTrait {
  def main(args: Array[String]): Unit = {
    val controller = new UserController

  }
}

class User(var name:String,var age:Int)

trait TraitA3{
  def m1():Unit={
    println("m1")
  }
}

//和数据库打交道,完成用户CRUD
trait UserDao{
  //_:TraitA3=>
  //向数据库的用户表中插入一条数据
  def insert(user:User):Unit={
    println("insert into database " + user.name)
  }
}

trait OrderDao{

  def insert(user:User):Unit={
    println("OD " + user.name)
  }
}

class UserController extends UserDao with OrderDao {
  //1.实现了依赖注入的功能
  //_:UserDao =>
  //可以注入多个 使用with连接
  _:UserDao with OrderDao=>
  def register(user:User): Unit ={
    insert(user)

  }

  override def insert(user: User): Unit = {
    println("ss")
  }
}
//所以的Java接口,都可以作为 特质 被混入
class MyClass21 extends UserDao with TraitA3 with Serializable {}

6.特质和抽象类的区别

1.优先使用特质。一个类扩展多个特质是很方便的,但却只能扩展一个抽象类。

2.如果你需要构造函数参数,使用抽象类。因为抽象类可以定义带参数的构造函数,而特质不行(有无参构造)。

package com.scala.chapter05

/**
 * 抽象类和特质关系
 * -抽象类中可以定义抽象属性、抽象方法、非抽象属性、非抽象方法
 * *   -特质中可以定义抽象属性、抽象方法、非抽象属性、非抽象方法
 * *   -抽象类和特质都不能被实例化
 * *     >抽象类有构造方法
 * *     >特质也有构造方法
 * *   -如果需要给构造方法传递参数的话,使用抽象类,因为特质不支持带参的构造
 * *   -优先选择特质,scala是单继承,如果直接继承不方便后续的扩展
 * *   -多大量对象共性进行抽象--->类--->对大量类共性进行抽象-->抽象类
 * *     一般子类和父类之间应该满足   is-a 原则
 * *   -特质一般是对行为进行抽象,定义规范
 */
object Scala14_TestTraitAndAbstract {
  def main(args: Array[String]): Unit = {
    new MyClass22
  }
}

trait TraitA22{
  println("特质的构造方法")
  def m1():Unit
}

abstract class MyAbstract22(name:String){
  println("抽象类的构造方法")
  def m1():Unit
}

class MyClass22 extends MyAbstract22 with TraitA22 {
  println("myclass 的构造方法")

  override def m1(): Unit = {
    println("myclass")
  }
}

8、类型检查和转换、枚举类和应用类、Type定义新类型

1.类型检查和转换

1)说明

(1)obj.isInstanceOf[T]:判断obj是不是T类型。

(2)obj.asInstanceOf[T]:将obj强转成T类型。

(3)classOf获取对象的类名。

2)案例实操

class Person{

}

object Person {
    def main(args: Array[String]): Unit = {

        val person = new Person

        //(1)判断对象是否为某个类型的实例
        val bool: Boolean = person.isInstanceOf[Person]

        if ( bool ) {
            //(2)将对象转换为某个类型的实例
            val p1: Person = person.asInstanceOf[Person]
            println(p1)
        }

        //(3)获取类的信息
        val pClass: Class[Person] = classOf[Person]
        println(pClass)
    }
}

2.枚举类和应用类

1)说明

枚举类:需要继承Enumeration

应用类:需要继承App

2)案例实操

object Test {
    def main(args: Array[String]): Unit = {

        println(Color.RED)
    }
}

// 枚举类
object Color extends Enumeration {
    val RED = Value(1, "red")
    val YELLOW = Value(2, "yellow")
    val BLUE = Value(3, "blue")
}

// 应用类
object Test20 extends App {
    println("xxxxxxxxxxx");
}

3.Type定义新类型

1)说明

使用type关键字可以定义新的数据数据类型名称,本质上就是类型的一个别名

2)案例实操

object Test {

    def main(args: Array[String]): Unit = {
        
        type S=String
        var v:S="abc"
        def test():S="xyz"
    }
}

 面向对象小结

1.对象和类
    -在自然界中,只要是客观存在的都是对象 (万物皆对象)
    -对大量对象共性的抽象,抽取为类
        *有什么        属性
        *能做什么    方法
    -在scala以及Java语言中,类是创建的模板
    -类是客观事物在人脑中的主观反映

2.Scala的属性
    -在Scala语言中,类、方法、属性默认修饰符是public,但是没有public关键字
    -对于Scala中的属性,底层会用private修饰,同时提供公开的设置以及获取属性的方法----面向封装
    -如果要生成满足JavaBean规范的get和set方法的话,需要在属性上加@BeanProperty注解

3.访问权限
    -Java
        *private        私有的,只能在本类中被访问
        *default        默认的,可以在本类以及同包的其它类中被访问
        *protected        受保护的,可以在本类、同包的其它类以及非同包的子类中被访问
        *public         公开的,所有类

    -Scala
        *private        私有的,只能在本类中被访问
        *public(默认)    公开的,所有类
        *protected        比Java设置的更严格,只能在本类以及子类中被访问,同包其他类访问不了
        *private[包名]     可以让指定的包进行访问

4.构造器(构造方法)
    -主构造方法
        *在声明类的同时,主构造方法也被声明
        *主构造方法只能有一个
        *如果主构造方法没有参数,那么以及调用的时候,小括号可以省略的
    -辅助构造方法
        *方法名必须叫this
        *辅助构造方法可以重载
        *辅助构造方法中的第一行必须直接或者间接调用主构造方法

5.模糊、不具体
    -使用abstract关键字定义抽象类
    -抽象类一般和抽象属性以及抽象方法配合使用
      >抽象属性
        属性只有声明,但是没有赋值
    
      >抽象方法
        方法只有声明,没有实现
    
     -在一个类中,如果存在抽象属性或者抽象方法,那么这个类一定是抽象类
     -如果一个类是抽象类,那么它不一定包含抽象属性和抽象方法
     -如果一个类中存在抽象属性或者抽象方法,那么具体的实现应该交给子类完成
     -如果子类也实现不了抽象内容,那么子类也应该声明为抽象类

     -如果重写(实现)抽象属性或者方法,那么override关键字可以省略

     -如果重写(覆盖)非抽象属性或者方法,那么override关键字不能省略,必须得加

     -如果对非抽象属性进行覆盖,要求属性必须得用val修饰

     -可以通过super关键字调用父类的方法,但是不能super调用父类的属性

     -在Scala中,属性和方法都是动态绑定
         *静态绑定(编译器绑定)
             在编译阶段,确定属性或者方法所属类型

         *动态绑定
             在运行阶段,根据实际创建的对象类型来决定属性 或者方法所属类型

6.匿名子类

7.创建对象的方式
    -new 
        底层调用的是构造方法

    -类名()
        底层调用的是伴生对象中apply方法    

    -实现单例设计模式

8.特质
    -声明特质的语法
        trait 特质名{
            抽象属性
            非抽象属性
            抽象方法
            非抽象方法
        }        
    -混入特质
        *不存在继承关系
            class 类名 extends 特质1 with 特质2

        *存在继承关系
            class 类名 extends 父类 with 特质1    with 特质2

    -特质叠加的顺序
        第一步:列出第一个混入特质的继承关系,作为临时叠加的顺序
            DBOperation-->Operation

        第二步:列出第二个混入特质的继承关系,并且该顺序放到临时叠加顺序的前面,已经出现的特质不在出现
            HDFSOperation-->DBOperation-->Operation

        第三步:将子类放到临时叠加顺序的第一个
            MyOperation-->HDFSOperation-->DBOperation-->Operation


        注意:这个时候super不是调用父类中的方法了,而是调用特质叠加顺序上下一个节点的方法
 

7章 集合

1、集合简介

1)Scala的集合有三大类:序列Seq、集Set、映射Map,所有的集合都扩展自Iterable特质。

2)对于几乎所有的集合类,Scala都同时提供了可变不可变的版本,分别位于以下两个包

不可变集合:scala.collection.immutable

可变集合:  scala.collection.mutable

3)Scala不可变集合,就是指该集合对象不可修改,每次修改就会返回一个新对象,而不会对原对象进行修改。类似于java中的String对象

4)可变集合,就是这个集合可以直接对原对象进行修改,而不会返回新的对象。类似于java中StringBuilder对象

建议:在操作集合的时候,不可变用符号,可变用方法

1.不可变集合继承图

Scala_第24张图片

1)Set、Map是Java中也有的集合

2)Seq是Java没有的,我们发现List归属到Seq了,因此这里的List就和Java不是同一个概念了

3)我们前面的for循环有一个 1 to 3,就是IndexedSeq下的Range

4)String也是属于IndexedSeq

5)我们发现经典的数据结构比如Queue和Stack被归属到LinearSeq(线性序列)

6)大家注意Scala中的Map体系有一个SortedMap,说明Scala的Map可以支持排序

7)IndexedSeq和LinearSeq的区别:

(1)IndexedSeq是通过索引来查找和定位,因此速度快,比如String就是一个索引集合,通过索引即可定位

(2)LinearSeq是线型的,即有头尾的概念,这种数据结构一般是通过遍历来查找

2.可变集合继承图

Scala_第25张图片

2数组

1.不可变数组

1)第一种方式定义数组

定义:val arr1 = new Array[Int](10)

(1)new是关键字

(2)[Int]是指定可以存放的数据类型,如果希望存放任意数据类型,则指定Any

(3)(10),表示数组的大小,确定后就不可以变化

2)案例实操

package com.scala.chapter06

/**
 * 不可变数组
 * 不可变数组,执行添加或者删除操作的时候,会创建新的数组对象
 */
object Scala01_TestArray {
  def main(args: Array[String]): Unit = {
    //创建不可变数组 方式一
    //new Array[类型](长度)
    val arr = new Array[Int](5)
    //创建不可变数组 方式二
    //val ints: Array[Int] = Array(1, 2, 3, 4, 5, 6)

    //访问数组中的元素,且修改数组中的元素
    arr(0) = 10
    arr.update(0, 20)
    //println(arr(0))

    //遍历数组中的元素
    //方式1:普通for循环
    for (i <- 0 until arr.length) {
      //println(arr(i))
    }
    //方式2:普通for循环
    //for (变量<- 数组|集合)
    for (elem <- arr) {
      //println(elem)
    }
    //方式3:迭代器
    val iterator: Iterator[Int] = arr.iterator
    //    while (iterator.hasNext){
    //      println(iterator.next())
    //    }
    for (elem2 <- iterator) {
      //println(elem2)
    }

    //方式4:增强for循环 foreach的参数是一个函数,是对当前集合中元素的具体操作
    //arr.foreach((elem3:Int)=>{println(elem3)})
    //arr.foreach(println(_))简化
    //arr.foreach(println)

    //方式5:mkString() 用指定的字符串连接数组中的元素,形成一个新的字符串
    println(arr.mkString(","))

    //向数组中添加元素 +:
    //因为Array是不可变数组,所以执行添加操作的时候,会创建新的数组对象
    //val newArr: Array[Int] = arr.+:(21)
    //val newArr: Array[Int] = arr+:21 在Scala语言中如果运算符中包含冒号,且冒号在后,运算顺序从右到左
    //简化
    val newArr: Array[Int] = 21 +: arr

    println(newArr.mkString(",")) //21,20,0,0,0,0

    //向数组中添加元素 :+
    //val newArr2: Array[Int] = arr.:+(21)
    //简化
    val newArr2: Array[Int] = arr :+ 21
    println(newArr2.mkString(",")) //20,0,0,0,0,21

  }
}

3)第二种方式定义数组

val ints: Array[Int] = Array(1, 2, 3, 4, 5, 6)

(1)在定义数组时,直接赋初始值

(2)使用apply方法创建数组对象

   //创建不可变数组 方式二
   val ints: Array[Int] = Array(1, 2, 3, 4, 5, 6)

2.可变数组

1)定义变长数组

val arr01 = ArrayBuffer[Any](3, 2, 5)

(1)[Any]存放任意数据类型

(2)(3, 2, 5)初始化好的三个元素

(3)ArrayBuffer需要引入scala.collection.mutable.ArrayBuffer

2)案例实操

(1)ArrayBuffer是有序的集合

(2)增加元素使用的是append方法(),支持可变参数

package com.scala.chapter06

import scala.collection.mutable.ArrayBuffer

/**
 * 可变数组
 * 可变数组,执行添加或删除操作的时候,不会创建新的数组对象,直接在原数组上进行操作
 */
object Scala02_TestArrayBuffer {
  def main(args: Array[String]): Unit = {
    //创建可变数组一
    //val arr = new ArrayBuffer[Int]()
    //创建可变数组二
    val arr: ArrayBuffer[Int] = ArrayBuffer(1, 2, 3, 4)

    //访问数组中的元素,对数组中的元素进行修改
    //arr(1) = 20
    //arr.update(1,22)
    //println(arr(1))

    //向数组中添加元素 建议:在操作集合的时候,不可变用符号,可变用方法
    //val newArr: arr.type = arr += 20
    //arr.append(20) 末尾追加
    //arr.insert(1,30) 下标位置插入
    //println(arr)

    //注意:并不是数组是可变的,在操作数组对象时,就不能创建新的数组对象。 也可以调用相关的方法,去创建新的对象
    val newArr: ArrayBuffer[Int] = arr.+:(30)
   // println(arr.mkString(","))
   // println(newArr.mkString(","))

    //删除数组中的元素
    // arr.remove(1) //删除指定下标元素
    arr.remove(1,3)//删除两个指定下标到区间的元素
    println(arr)


  }
}
import scala.collection.mutable.ArrayBuffer

object TestArrayBuffer {

    def main(args: Array[String]): Unit = {

        //(1)创建并初始赋值可变数组
        val arr01 = ArrayBuffer[Any](1, 2, 3)

        //(2)遍历数组
        for (i <- arr01) {
            println(i)
        }
        println(arr01.length) // 3
        println("arr01.hash=" + arr01.hashCode())

        //(3)增加元素
        //(3.1)追加数据
        arr01.+=(4)
        //(3.2)向数组最后追加数据
        arr01.append(5,6)
        //(3.3)向指定的位置插入数据
        arr01.insert(0,7,8)
        println("arr01.hash=" + arr01.hashCode())

        //(4)修改元素
        arr01(1) = 9 //修改第2个元素的值
        println("--------------------------")

        for (i <- arr01) {
            println(i)
        }
        println(arr01.length) // 5
    }
}

3.不可变数组与可变数组的转换

1)说明

arr1.toBuffer  //不可变数组转可变数组

arr2.toArray  //可变数组转不可变数组

(1)arr2.toArray返回结果才是一个不可变数组,arr2本身没有变化

(2)arr1.toBuffer返回结果才是一个可变数组,arr1本身没有变化

2)案例实操

object TestArrayBuffer {

    def main(args: Array[String]): Unit = {

        //(1)创建一个空的可变数组
        val arr2 = ArrayBuffer[Int]()

        //(2)追加值
        arr2.append(1, 2, 3)
        println(arr2) // 1,2,3

        //(3)ArrayBuffer ==> Array
        //(3.1)arr2.toArray 返回的结果是一个新的定长数组集合
        //(3.2)arr2它没有变化
        val newArr = arr2.toArray
        println(newArr)
        
        //(4)Array ===> ArrayBuffer
        //(4.1)newArr.toBuffer 返回一个变长数组 newArr2
        //(4.2)newArr 没有任何变化,依然是定长数组
        val newArr2 = newArr.toBuffer
        newArr2.append(123)

        println(newArr2)
    }
}

4.多维数组

1)多维数组定义

val arr = Array.ofDim[Double](3,4)

说明:二维数组中有三个一维数组,每个一维数组中有四个元素

2)案例实操

object DimArray {

    def main(args: Array[String]): Unit = {
        
        //(1)创建了一个二维数组, 有三个元素,每个元素是,含有4个元素一维数组()
        val arr = Array.ofDim[Int](3, 4)
        arr(1)(2) = 88

        //(2)遍历二维数组
        for (i <- arr) { //i 就是一维数组

            for (j <- i) {
                print(j + " ")
            }

            println()
        }
    }
}
package com.scala.chapter06

/**
 * 多维数组
 */
object Scala03_TestMullArray {
  def main(args: Array[String]): Unit = {
    //创建二维数组
    val arr: Array[Array[Int]] = Array.ofDim[Int](2, 3)
    arr(1)(0) = 20
    //对二维数组进行遍历
    for (i <- 0 until arr.length;j <- 0 until (arr(i).length)){
        println(arr(i)(j))
    }
    //创建五维数组
    val newArr: Array[Array[Array[Array[Array[Int]]]]] = Array.ofDim[Int](1, 2, 3, 4, 5)

  }
}

3、Seq集合(List)

Seq适合存有序重复数据,进行快速插入/删除元素等场景

1.不可变List

1)说明

(1)List默认为不可变集合

(2)创建一个List(数据有顺序,可重复)
(3)遍历List

(4)List增加数据

(5)集合间合并:将一个整体拆成一个一个的个体,称为扁平化
(6)取指定数据
(7)空集合Nil

2)案例实操

object TestList {

    def main(args: Array[String]): Unit = {

        //(1)List默认为不可变集合
        //(2)创建一个List(数据有顺序,可重复)
        val list: List[Int] = List(1,2,3,4,3)
        
        //(7)空集合Nil
        val list5 = 1::2::3::4::Nil

        //(4)List增加数据
        //(4.1)::的运算规则从右向左
        //val list1 = 5::list
        val list1 = 7::6::5::list
        //(4.2)添加到第一个元素位置
        val list2 = list.+:(5)

        //(5)集合间合并:将一个整体拆成一个一个的个体,称为扁平化
        val list3 = List(8,9)
        //val list4 = list3::list1
        val list4 = list3:::list1

        //(6)取指定数据
        println(list(0))

        //(3)遍历List
        //list.foreach(println)
        //list1.foreach(println)
        //list3.foreach(println)
        //list4.foreach(println)
        list5.foreach(println)
    }
}
package com.scala.chapter06

/**
 * 不可变集合List
 */
object Scala04_TestList {
  def main(args: Array[String]): Unit = {
    //创建List集合,因为List是抽象的,只能通过apply方式创建
    val list = List(1, 2, 3, 4)
    println(list)

    //向集合中添加元素
    val newList: List[Int] = list.+:(20)
    //println(newList) //List(20, 1, 2, 3, 4)
    val newList2: List[Int] = list.:+(20)
    //println(newList2) //List(1, 2, 3, 4, 20)


    //在Scala语言中,List() 和Nil 表示空集合
    //val newList3: List[Int] = list.::(50)
    //val newList3: List[Int] = Nil.::(40)
    //println(newList3)

    //val newList4: List[Int] = 10 :: 20 :: 30 :: 40 :: Nil
    //println(newList4)

    //扁平化
    val list2 = List(1, 2, 3, 4)
    val list3 = List(5, 6)

    //向集合中添加元素 ::
    val list4: List[Any] = list2 :: list3 //list2整个集合向list3中追加 生成新的list4
    println(list4) //List(List(1, 2, 3, 4), 5, 6)


    val list5: List[Int] = list2 ::: list3
    println(list5) //List(1, 2, 3, 4, 5, 6)

    println(list5(2))
    list5.foreach(println)
  }
}

2.可变ListBuffer

1)说明

(1)创建一个可变集合ListBuffer

(2)向集合中添加数据

(3)打印集合数据

2)案例实操

object TestList {

    def main(args: Array[String]): Unit = {

        //(1)创建一个可变集合
        val buffer = ListBuffer(1,2,3,4)

        //(2)向集合中添加数据
        buffer.+=(5)
buffer.append(6)
buffer.insert(1,2)

        //(3)打印集合数据
        buffer.foreach(println)

//(4)修改数据
buffer(1) = 6
buffer.update(1,7)

//(5)删除数据
buffer.-(5)
buffer.-=(5)
buffer.remove(5)
    }
}
package com.scala.chapter06

import scala.collection.mutable.ListBuffer

/**
 * 可变集合seq
 */
object Scala05_TestListBuffer {
  def main(args: Array[String]): Unit = {
    //创建可变集合对象
    //val list = new ListBuffer[Int]()
    val list: ListBuffer[Int] = ListBuffer(1, 2, 3, 4)

    //向集合中添加元素 建议:在操作集合的时候,不可变用符号,可变用方法
    list.append(10)  //追加
    list.insert(1,20) //指定下标插入
    println(list)

    //修改元素
    list(1) = 30
    println(list)
    list.update(1,40)
    println(list)

    //删除元素
    list.remove(1)
    println(list)
    list.remove(0,2)
    println(list)
  }
}

4、Set集合

Set是集合,适合存无序非重复数据,进行快速查找海量元素的等场景

        默认情况下,Scala使用的是不可变集合,如果你想使用可变集合,需要引用 scala.collection.mutable.Set 包

1.不可变Set

1)说明

(1)Set默认是不可变集合,数据无序

(2)数据不可重复

(3)遍历集合

2)案例实操

object TestSet {

    def main(args: Array[String]): Unit = {

        //(1)Set默认是不可变集合,数据无序
        val set = Set(1,2,3,4,5,6)

        //(2)数据不可重复
        val set1 = Set(1,2,3,4,5,6,3)

        //(3)遍历集合
        for(x<-set1){
            println(x)
        }
    }
}

2.可变mutable.Set

1)说明

(1)创建可变集合mutable.Set

(2)打印集合

(3)集合添加元素

(4)向集合中添加元素,返回一个新的Set

(5)删除数据

2)案例实操

object TestSet {

    def main(args: Array[String]): Unit = {

        //(1)创建可变集合
        val set = mutable.Set(1,2,3,4,5,6)

        //(3)集合添加元素
        set += 8

        //(4)向集合中添加元素,返回一个新的Set
        val ints = set.+(9)
        println(ints)
        println("set2=" + set)

        //(5)删除数据
        set-=(5)

        //(2)打印集合
        set.foreach(println)
        println(set.mkString(","))
    }
}
package com.scala.chapter06

import scala.collection.mutable

/**
 * set集合
 *  不可变
 * 无序,不重复
 */
object Scala06_TestSet {
  def main(args: Array[String]): Unit = {
   /*
   //不可变集合
   val set = Set(1, 2, 5, 3, 6, 8, 7, 7)

   // println(set)  Set(5, 1, 6, 2, 7, 3, 8)

    //添加元素 不可变使用 符号
    val set1: Set[Int] = set.+(10)
    val set2: Set[Int] = set.+(20)
    println(set)
    println(set1)
    println(set2)
    //删除元素
    val set4: Set[Int] = set.-(1)
    println(set4)

    */
    //可变集合
    val set: mutable.Set[Int] = mutable.Set(1, 2, 3, 4)
    //添加元素
    set.add(1)
    //删除元素
    set.remove(2)

  }
}

5、Map集合

        Scala中的Map和Java类似,也是一个散列表,它存储的内容也是键值对(key-value)映射

1 不可变Map

1)说明

(1)创建不可变集合Map

(2)循环打印

(3)访问数据

(4)如果key不存在,返回0

2)案例实操

object TestMap {

    def main(args: Array[String]): Unit = {
        // Map
        //(1)创建不可变集合Map
        val map = Map( "a"->1, "b"->2, "c"->3 )

        //(3)访问数据
        for (elem <- map.keys) {
            // 使用get访问map集合的数据,会返回特殊类型Option(选项):有值(Some),无值(None)
            println(elem + "=" + map.get(elem).get)
        }

        //(4)如果key不存在,返回0
        println(map.get("d").getOrElse(0))
        println(map.getOrElse("d", 0))

        //(2)循环打印
        map.foreach((kv)=>{println(kv)})
    }
}

2.可变Map

1)说明

(1)创建可变集合

(2)打印集合

(3)向集合增加数据

(4)删除数据

(5)修改数据

2)案例实操

object TestSet {

    def main(args: Array[String]): Unit = {

        //(1)创建可变集合
        val map = mutable.Map( "a"->1, "b"->2, "c"->3 )

        //(3)向集合增加数据
        map.+=("d"->4)

        // 将数值4添加到集合,并把集合中原值1返回
        val maybeInt: Option[Int] = map.put("a", 4)
        println(maybeInt.getOrElse(0))

        //(4)删除数据
        map.-=("b", "c")

        //(5)修改数据
        map.update("d",5)
map("d") = 5

        //(2)打印集合
        map.foreach((kv)=>{println(kv)})
    }
}
package com.scala.chapter06

import scala.collection.mutable

/**
 * Map集合
 * 无序不可重复
 */
object Scala07_TestMap {
  def main(args: Array[String]): Unit = {
    /* 不可变Map集合
    //创建Map集合 ("a",1) 底层是HashMap
     val map = Map("a" -> 1, "b" -> 2, "c" -> 3)
     //println(map.getClass)

     //遍历map集合元素
     //map.foreach((kv:(String,Int))=>{println(kv)})
     map.foreach(println)

     //key获取map集合中的所有key

     for (key <- map.keys){
       println(key + "->" + map.get(key).get)
     }

     //println(map.get("a"))//Some(1)
     println(map.get("a").get)//1

     //在Scala语言中,为了避免空指针异常,如果获取的内容,可能为空,可以用Option表示
     println(map.get("d"))//None
     println(map.get("d").getOrElse(0))//0
     println(map.getOrElse("a", 0))

     */
    //可变Map集合
    val map: mutable.Map[String, Int] = mutable.Map("a" -> 1, "b" -> 2, "c" -> 3)

    //添加元素

    map.put("d", 4)
    //删除元素
    map.remove("d")

    //修改map集合中的元素
    map.update("a", 10)
    map("a") = 30
    map.foreach(println)
  }
}

6、元组

1)说明

元组也是可以理解为一个容器,可以存放各种相同或不同类型的数据。说的简单点,就是将多个无关的数据封装为一个整体,称为元组。

注意:元组中最大只能有22个元素。

2)案例实操

(1)声明元组的方式:(元素1,元素2,元素3)

(2)访问元组

(3)Map中的键值对其实就是元组,只不过元组的元素个数为2,称之为对偶

object TestTuple {

    def main(args: Array[String]): Unit = {

        //(1)声明元组的方式:(元素1,元素2,元素3)
        val tuple: (Int, String, Boolean) = (40,"bobo",true)

        //(2)访问元组
        //(2.1)通过元素的顺序进行访问,调用方式:_顺序号
        println(tuple._1)
        println(tuple._2)
        println(tuple._3)

        //(2.2)通过索引访问数据
        println(tuple.productElement(0))

        //(2.3)通过迭代器访问数据
        for (elem <- tuple.productIterator) {
            println(elem)
        }

        //(3)Map中的键值对其实就是元组,只不过元组的元素个数为2,称之为对偶
        val map = Map("a"->1, "b"->2, "c"->3)

        val map1 = Map(("a",1), ("b",2), ("c",3))

        map.foreach(tuple=>{println(tuple._1 + "=" + tuple._2)})
    }
}
package com.scala.chapter06

/**
 * 元组
 */
object Scala08_TestTuple {
  def main(args: Array[String]): Unit = {
    //创建元组
    //数据类型 :Int\String\(参数类型)=>返回值类型\ =>(返回值类型)\(类型...)
    val tuple: (String, Int, String) = ("张三", 18, "山东")
    //访问元组中的数据
    println(tuple._1)
    println(tuple._2)
    println(tuple._3)

    val tuples = List(("张三", 18, "山东"))

    //通过索引访问数据
    println(tuple.productElement(1))

    //通过迭代器
    for (elem <- tuple.productIterator) {
      println(elem)
    }

    //Map集合总的键值对其实就是一种特殊的元素,元组中元素的个数是2个,我门称之为对偶元组
    val map1 = Map("a" -> 1, "b" -> 3)
    val map2 = Map(("a", 1), ("b", 2))
  }

}

7、集合常用函数

1 基本属性和常用操作

1)说明

(1)获取集合长度

(2)获取集合大小

(3)循环遍历

(4)迭代器

(5)生成字符串

(6)是否包含

2)案例实操

object TestList {

  def main(args: Array[String]): Unit = {

    val list: List[Int] = List(1, 2, 3, 4, 5, 6, 7)

    //(1)获取集合长度
    println(list.length)

    //(2)获取集合大小,等同于length
    println(list.size)

    //(3)循环遍历
    list.foreach(println)

    //(4)迭代器
    for (elem <- list.itera	tor) {
      println(elem)
    }

    //(5)生成字符串
    println(list.mkString(","))

    //(6)是否包含
    println(list.contains(3))
  }
}

package com.scala.chapter06

/**
 * 集合基本属性和常用的操作
 */
object Scala09_TestFun {
  def main(args: Array[String]): Unit = {
    val list = List(1, 1, 2, 2, 3, 3, 4, 4)
    //(1)获取集合长度
    println(list.length)
    //(2)获取集合大小
    println(list.size)
    //(3)循环遍历
    println(list)
    list.foreach(println)
    //(4)迭代器
    for (elem <- list.iterator) {
      println(elem)
    }
    //(5)生成字符串
    val str: String = list.mkString(",")
    println(str)
    //(6)是否包含
    println(list.contains(4))//true
  }
}

2.衍生集合

1)说明

(1)获取集合的头

(2)获取集合的尾(不是头的就是尾)

(3)集合最后一个数据

(4)集合初始数据(不包含最后一个)

(5)反转

(6)取前(后)n个元素

(7)去掉前(后)n个元素

(8)并集

(9)交集

(10)差集

(11)拉链

(12)滑窗

2)案例实操

object TestList {

  def main(args: Array[String]): Unit = {

    val list1: List[Int] = List(1, 2, 3, 4, 5, 6, 7)
    val list2: List[Int] = List(4, 5, 6, 7, 8, 9, 10)

    //(1)获取集合的头
    println(list1.head)

    //(2)获取集合的尾(不是头的就是尾)
    println(list1.tail)

    //(3)集合最后一个数据
    println(list1.last)

    //(4)集合初始数据(不包含最后一个)
    println(list1.init)

    //(5)反转
    println(list1.reverse)

    //(6)取前(后)n个元素
    println(list1.take(3))
    println(list1.takeRight(3))

    //(7)去掉前(后)n个元素
    println(list1.drop(3))
    println(list1.dropRight(3))

    //(8)并集
    println(list1.union(list2))

    //(9)交集
    println(list1.intersect(list2))

    //(10)差集
    println(list1.diff(list2))

    //(11)拉链 注:如果两个集合的元素个数不相等,那么会将同等数量的数据进行拉链,多余的数据省略不用
    println(list1.zip(list2))

    //(12)滑窗
    list1.sliding(2, 5).foreach(println)
  }
}

package com.scala.chapter06

/**
 * 衍生集合
 */
object Scala10_TestFun {
  def main(args: Array[String]): Unit = {
    val list = List(1, 1, 2, 2, 3, 3, 4, 4)
    val list2 = List(3, 3, 4, 4, 5, 5, 6, 6)
    //(1)获取集合的头
    println(list.head) //1
    //(2)获取集合的尾(不是头的就是尾)
    println(list.tail) //List(1, 2, 2, 3, 3, 4, 4)
    //(3)集合最后一个数据
    println(list.last) //4
    //(4)集合初始数据(不包含最后一个)
    println(list.init)//List(1, 1, 2, 2, 3, 3, 4)
    //(5)反转
    println(list.reverse) //List(4, 4, 3, 3, 2, 2, 1, 1)
    //(6)取前(后)n个元素
    println(list.take(3)) //List(1, 1, 2)
    println(list.takeRight(3))//List(3, 4, 4)
    //(7)去掉前(后)n个元素
    //println(list.drop(3))//List(2, 3, 3, 4, 4)
    //println(list.dropRight(3))//List(1, 1, 2, 2, 3)
    //(8)并集
    val newList: List[Int] = list.union(list2)
    println(newList) //List(1, 1, 2, 2, 3, 3, 4, 4, 3, 3, 4, 4, 5, 5, 6, 6)
    //(9)交集
    val newList2: List[Int] = list.intersect(list2)
    println(newList2)//List(3, 3, 4, 4)
    //(10)差集
    val newList3: List[Int] = list.diff(list2)
    println(newList3)//List(1, 1, 2, 2)
    val newList4: List[Int] = list2.diff(list)
    println(newList4)//List(5, 5, 6, 6)
    //(11)拉链  如果两个集合的元素个数不相等,那么会将同等数量的数据进行拉链,多余的数据省略不用
    println(list.zip(list2)) //ist((1,3), (1,3), (2,4), (2,4), (3,5), (3,5), (4,6), (4,6))
    println(list2.zip(list)) //List((3,1), (3,1), (4,2), (4,2), (5,3), (5,3), (6,4), (6,4))
    //(12)滑窗
    list.sliding(3).foreach(println) //从左向右滑动 默认每次滑动一步,窗口大小三个元素
    //List(1, 1, 2)
    //List(1, 2, 2)
    //List(2, 2, 3)
    //List(2, 3, 3)
    //List(3, 3, 4)
    //List(3, 4, 4)
    list.sliding(3,3).foreach(println)
    //List(1, 1, 2)
    //List(2, 3, 3)
    //List(4, 4)
  }
}

3.集合计算初级函数

1)说明

(1)求和

(2)求乘积

(3)最大值

(4)最小值

(5)排序

2)实操

object TestList {

  def main(args: Array[String]): Unit = {

    val list: List[Int] = List(1, 5, -3, 4, 2, -7, 6)

    //(1)求和
    println(list.sum)

    //(2)求乘积
    println(list.product)

    //(3)最大值
    println(list.max)

    //(4)最小值
    println(list.min)

    //(5)排序
    // (5.1)按照元素大小排序
    println(list.sortBy(x => x))

    // (5.2)按照元素的绝对值大小排序
    println(list.sortBy(x => x.abs))

    // (5.3)按元素大小升序排序
	println(list.sortWith((x, y) => x < y))

	// (5.4)按元素大小降序排序
    println(list.sortWith((x, y) => x > y))
  }
}

package com.scala.chapter06

/**
 * 集合计算初级函数
 */
object Scala11_TestFun {
  def main(args: Array[String]): Unit = {
    val list = List(1, 2, 3, 4, 2, 3, 8, 11)
    //(1)求和
    println(list.sum) //34
    //(2)求乘积
    println(list.product) //12672
    //(3)最大值
    println(list.max) //11
    //(4)最小值
    println(list.min) //1
    //(5)排序 sorted/sortBy/sortWith
    println(list.sorted) //List(1, 2, 2, 3, 3, 4, 8, 11)
    println(list.sorted.reverse) //List(11, 8, 4, 3, 3, 2, 2, 1)
    //自定义按照元素大小进行排序
    //如果函数参数和行函数体一致的话,不能简化
    println(list.sortBy(a => a)) //List(1, 2, 2, 3, 3, 4, 8, 11)
    val list2 = List(1, 2, 3, -4, 2, 3, -8, 11)
    //按照绝对值进行排序
    println(list2.sortBy(a => a.abs)) //List(1, 2, 2, 3, 3, -4, -8, 11)
    //简化
    println(list2.sortBy(_.abs)) //List(1, 2, 2, 3, 3, -4, -8, 11)
    //降序排序
    println(list2.sortBy(_.abs).reverse)// List(11, -8, -4, 3, 3, 2, 2, 1)
    println(list2.sortBy(-_.abs))// List(11, -8, -4, 3, 3, 2, 2, 1)

    //升序排序
    println(list2.sortWith((a: Int, b: Int) => {a < b})) //List(-8, -4, 1, 2, 2, 3, 3, 11)
    //简化
    println(list2.sortWith(_ < _))// List(-8, -4, 1, 2, 2, 3, 3, 11)
  }
}

(1)sorted

对一个集合进行自然排序,通过传递隐式的Ordering

(2)sortBy

对一个属性或多个属性进行排序,通过它的类型。

(3)sortWith

基于函数的排序,通过一个comparator函数,实现自定义排序的逻辑。

4.集合计算高级函数

1)说明

(1)过滤

遍历一个集合并从中获取满足指定条件的元素组成一个新的集合

(2)转化/映射(map)

将集合中的每一个元素映射到某一个函数

(3)扁平化

(4)扁平化+映射 注:flatMap相当于先进行map操作,在进行flatten操作

集合中的每个元素的子元素映射到某个函数并返回新集合

(5)分组(group)

按照指定的规则对集合的元素进行分组

(6)简化(归约)

(7)折叠

2)实操

object TestList {

    def main(args: Array[String]): Unit = {

        val list: List[Int] = List(1, 2, 3, 4, 5, 6, 7, 8, 9)
        val nestedList: List[List[Int]] = List(List(1, 2, 3), List(4, 5, 6), List(7, 8, 9))
        val wordList: List[String] = List("hello world", "hello atguigu", "hello scala")

        //(1)过滤
        println(list.filter(x => x % 2 == 0))

        //(2)转化/映射
        println(list.map(x => x + 1))

        //(3)扁平化
        println(nestedList.flatten)

        //(4)扁平化+映射 注:flatMap相当于先进行map操作,在进行flatten操作
        println(wordList.flatMap(x => x.split(" ")))

        //(5)分组
        println(list.groupBy(x => x % 2))
    }
}
package com.scala.chapter06

/**
 * 集合计算 高级函数
 */
object Scala12_TestFun {
  def main(args: Array[String]): Unit = {
    val list: List[Int] = List(1, 2, 3, 4, 5, 6, 7, 8, 9)
    //(1)过滤 遍历一个集合并从中获取满足指定条件的元素组成一个新的集合
    //取偶数
    println(list.filter(a => a % 2 == 0)) //List(2, 4, 6, 8)
    println(list.filter(_ % 2 == 0)) //List(2, 4, 6, 8)
    //取非偶数
    println(list.filter(a => a % 2 != 0)) //List(1, 3, 5, 7, 9)
    println(list.filter(_ % 2 != 0)) //List(1, 3, 5, 7, 9)
    //(2)转化/映射(map)将集合中的每一个元素映射到某一个函数
    println(list.map(a => a * 2))
    println(list.map(_ * 2)) //List(2, 4, 6, 8, 10, 12, 14, 16, 18)
    //(3)扁平化 :::
    val nestList: List[List[Int]] = List(List(1, 2, 3), List(4, 5, 6), List(7, 8, 9))
    //扁平处理遍历
    nestList.flatten.foreach(println)

    //(4)扁平化+映射 注:flatMap相当于先进行map操作,在进行flatten操作 集合中的每个元素的子元素映射到某个函数并返回新集合
    val strings = List("hello cha", "hello mo", "hello kk", "hello jj")
   /* //对字符串使用空格进行分割,将分割后的单词放到新的集合中
    val splitList: List[Array[String]] = strings.map(_.split(" "))
    //将集合中的元素有整体转换为个体
    val flatList: List[String] = splitList.flatten
    //输出
    println(flatList)
    */
    //使用flatMap函数完成上述功能
    val strings1: List[String] = strings.flatMap(_.split(" "))
    println(strings1)

    //(5)分组(group)
    // val list: List[Int] = List(1, 2, 3, 4, 5, 6, 7, 8, 9)
    val groupMap: Map[Int, List[Int]] = list.groupBy(_ % 2)
    println(groupMap)

    val nameList = List("zhangsan", "lisi", "wangwu", "lier", "zhangww")
    //按名字首字符分组
    println(nameList.groupBy(_.charAt(0)))     //Map(w -> List(wangwu), z -> List(zhangsan, zhangww), l -> List(lisi, lier))

  }


}

3)Reduce方法

Reduce简化(归约) :通过指定的逻辑将集合中的数据进行聚合,从而减少数据,最终获取结果。

案例实操

object TestReduce {

    def main(args: Array[String]): Unit = {

        val list = List(1,2,3,4)

        // 将数据两两结合,实现运算规则
        val i: Int = list.reduce( (x,y) => x-y )
        println("i = " + i)

        // 从源码的角度,reduce底层调用的其实就是reduceLeft
        //val i1 = list.reduceLeft((x,y) => x-y)

        // ((4-3)-2-1) = -2
        val i2 = list.reduceRight((x,y) => x-y)
        println(i2)
    }
}
package com.scala.chapter06

/**
 * 简化/规约
 * 通过指定的逻辑将集合中的数据进行聚合,从而减少数据,最终获取结果。
 * reduce
 * 底层调用 reduceLeft
 * 函数参数类型以及返回值类型必须一致
 * reduceLeft
 * 函数参数类型可以不一致
 * reduceRight
 * 函数参数类型可以不一致
 * 折叠
 *
 */
object Scala13_TestFun {
  def main(args: Array[String]): Unit = {
    val list = List(1, 2, 3, 4)
    println(list.reduce(_ - _)) //-8
    println(list.reduceLeft(_ - _)) //-8执行方向与从左向右
    println(list.reduceRight(_ - _)) //-2

    val list2 = List(3, 4, 5, 8, 10)
    println(list2.reduceRight(_ - _))//6
    //reversed.reduceLeft[B]((x, y) => op(y, x))
    //(10,8,5,4,3).reduceLeft[B]((x, y) => op(y, x))
    //3-(4-(5-(8-10)))=6
    //折叠 集合外元素和集合内元素进行聚合
    println(list.fold(10)(_ + _))

    println(list.foldLeft("s")(_ + _))//s1234
    println(list.foldLeft(10)(_ + _))//20

    println(list2.foldRight(11)(_ - _))//-5


  }
}

4)Fold方法

Fold折叠:化简的一种特殊情况。

(1)案例实操:fold基本使用

object TestFold {

    def main(args: Array[String]): Unit = {

        val list = List(1,2,3,4)

        // fold方法使用了函数柯里化,存在两个参数列表
        // 第一个参数列表为 : 零值(初始值)
        // 第二个参数列表为: 简化规则

        // fold底层其实为foldLeft
        val i = list.foldLeft(1)((x,y)=>x-y)

        val i1 = list.foldRight(10)((x,y)=>x-y)

        println(i)
        println(i1)
    }
}

(2)案例实操:两个集合合并

object TestFold {

    def main(args: Array[String]): Unit = {

        // 两个Map的数据合并
        val map1 = mutable.Map("a"->1, "b"->2, "c"->3)
        val map2 = mutable.Map("a"->4, "b"->5, "d"->6)

        val map3: mutable.Map[String, Int] = map2.foldLeft(map1) {
            (map, kv) => {
                val k = kv._1
                val v = kv._2

                map(k) = map.getOrElse(k, 0) + v

                map
            }
        }

        println(map3)
    }
}
package com.scala.chapter06

import scala.collection.mutable

/**
 * 对两个Map集合之间的数据进行合并
 */
object Scala14_TestFun {
  def main(args: Array[String]): Unit = println({
    // 两个Map的数据合并
    val map1 = mutable.Map("a" -> 1, "b" -> 2, "c" -> 3)
    val map2 = mutable.Map("a" -> 4, "b" -> 5, "d" -> 6)
    //最终合并效果 Map("a"->5,"b"->7,"c"->3,"d"->6)

    //两个集合之间合并,不属于集合内部元素聚合,所以fold折叠更合适
 /*   val res: mutable.Map[String, Int] = map1.foldLeft(map2) {
      //mm表示map2,kv表示map1中的每一个元素
      (mm, kv) => {
        //指定合并规则
        val key: String = kv._1
        val value: Int = kv._2
        //根据map1中的key,到map2中去找value
        mm(key) = mm.getOrElse(key, 0) + value
        mm
      }
    }
   println(res)
  */

    map2.foldLeft(map1)((mm, kv) => {
      val key: String = kv._1
      val value: Int = kv._2
      mm(key) = mm.getOrElse(key, 0) + value
      mm
    })
  })
}

5.普通WordCount案例

1)需求

单词计数:将集合中出现的相同的单词,进行计数,取计数排名前三的结果

2)需求分析

Scala_第26张图片

3)案例实操

package com.scala.chapter06

/**
 * WordCount
 */
object Scala_WordCount {
  def main(args: Array[String]): Unit = {
    // 单词计数:将集合中出现的相同的单词,进行计数,取计数排名前三的结果
    val stringList = List("Hello Scala Hbase kafka", "Hello Scala Hbase", "Hello Scala", "Hello")

    //第一步 将字符串切割
    val wordList: List[String] = stringList.flatMap(_.split(" "))
    //println(wordList)
    //第二步 对相同单词分组
    val groupMap: Map[String, List[String]] = wordList.groupBy(a => a)
    //第三步 将分组集合中的元素进行结构转换 map(Hello -> 4)
    //注意:map里面的函数参数是一个元组,不要误认为是两个元素
    val countMap: Map[String, Int] = groupMap.map(kv => {
      (kv._1, kv._2.size)
    })
    //println(countMap) Map(Hello -> 4, Hbase -> 2, kafka -> 1, Scala -> 3)
    //第四步 排序取前三
    //将countMap 转为 seq List((Hello,4), (Hbase,2), (kafka,1), (Scala,3))
    //val sortList: List[(String, Int)] = countMap.toList.sortBy(_._2).reverse
    val sortList: List[(String, Int)] = countMap.toList.sortWith(_._2 > _._2)
    //List((Hello,4), (Scala,3), (Hbase,2), (kafka,1))
    //取前三 take
    val finalList: List[(String, Int)] = sortList.take(3)
    println(finalList)
  }
}

object TestWordCount {

    def main(args: Array[String]): Unit = {

        // 单词计数:将集合中出现的相同的单词,进行计数,取计数排名前三的结果
        val stringList = List("Hello Scala Hbase kafka", "Hello Scala Hbase", "Hello Scala", "Hello")

        // 1) 将每一个字符串转换成一个一个单词
        val wordList: List[String] = stringList.flatMap(str=>str.split(" "))
        //println(wordList)

        // 2) 将相同的单词放置在一起
        val wordToWordsMap: Map[String, List[String]] = wordList.groupBy(word=>word)
        //println(wordToWordsMap)

        // 3) 对相同的单词进行计数
        // (word, list) => (word, count)
        val wordToCountMap: Map[String, Int] = wordToWordsMap.map(tuple=>(tuple._1, tuple._2.size))

        // 4) 对计数完成后的结果进行排序(降序)
        val sortList: List[(String, Int)] = wordToCountMap.toList.sortWith {
            (left, right) => {
                left._2 > right._2
            }
        }

        // 5) 对排序后的结果取前3名
        val resultList: List[(String, Int)] = sortList.take(3)

        println(resultList)
    }
}v

6.复杂WordCount案例

1)方式一

object TestWordCount {

    def main(args: Array[String]): Unit = {

        // 第一种方式(不通用)
        val tupleList = List(("Hello Scala Spark World ", 4), ("Hello Scala Spark", 3), ("Hello Scala", 2), ("Hello", 1))

        val stringList: List[String] = tupleList.map(t=>(t._1 + " ") * t._2)

        //val words: List[String] = stringList.flatMap(s=>s.split(" "))
        val words: List[String] = stringList.flatMap(_.split(" "))

        //在map中,如果传进来什么就返回什么,不要用_省略
        val groupMap: Map[String, List[String]] = words.groupBy(word=>word)
        //val groupMap: Map[String, List[String]] = words.groupBy(_)

        // (word, list) => (word, count)
        val wordToCount: Map[String, Int] = groupMap.map(t=>(t._1, t._2.size))

        val wordCountList: List[(String, Int)] = wordToCount.toList.sortWith {
            (left, right) => {
                left._2 > right._2
            }
        }.take(3)

        //tupleList.map(t=>(t._1 + " ") * t._2).flatMap(_.split(" ")).groupBy(word=>word).map(t=>(t._1, t._2.size))
        println(wordCountList)
    }
}

2)方式二

object TestWordCount {

    def main(args: Array[String]): Unit = {

        val tuples = List(("Hello Scala Spark World", 4), ("Hello Scala Spark", 3), ("Hello Scala", 2), ("Hello", 1))

        // (Hello,4),(Scala,4),(Spark,4),(World,4)
        // (Hello,3),(Scala,3),(Spark,3)
        // (Hello,2),(Scala,2)
        // (Hello,1)
        val wordToCountList: List[(String, Int)] = tuples.flatMap {
            t => {
                val strings: Array[String] = t._1.split(" ")
                strings.map(word => (word, t._2))
            }
        }

        // Hello, List((Hello,4), (Hello,3), (Hello,2), (Hello,1))
        // Scala, List((Scala,4), (Scala,3), (Scala,2)
        // Spark, List((Spark,4), (Spark,3)
        // Word, List((Word,4))
        val wordToTupleMap: Map[String, List[(String, Int)]] = wordToCountList.groupBy(t=>t._1)

        val stringToInts: Map[String, List[Int]] = wordToTupleMap.mapValues {
            datas => datas.map(t => t._2)
        }
        stringToInts

        /*
        val wordToCountMap: Map[String, List[Int]] = wordToTupleMap.map {
            t => {
                (t._1, t._2.map(t1 => t1._2))
            }
        }

        val wordToTotalCountMap: Map[String, Int] = wordToCountMap.map(t=>(t._1, t._2.sum))
        println(wordToTotalCountMap)
        */
    }
}

8、队列

1)说明

Scala也提供了队列(Queue)的数据结构,队列的特点就是先进先出。进队和出队的方法分别为enqueue和dequeue。

2)案例实操

object TestQueue {

    def main(args: Array[String]): Unit = {

        val que = new mutable.Queue[String]()

        que.enqueue("a", "b", "c")

        println(que.dequeue())
        println(que.dequeue())
        println(que.dequeue())
    }
}

9、并行集合

1)说明

Scala为了充分使用多核CPU,提供了并行集合(有别于前面的串行集合),用于多核环境的并行计算。

2)案例实操

object TestPar {

    def main(args: Array[String]): Unit = {

        val result1 = (0 to 100).map{case _ => Thread.currentThread.getName}
        val result2 = (0 to 100).par.map{case _ => Thread.currentThread.getName}

        println(result1)
        println(result2)
    }
}

 

集合小结


    -存放单值类型
        *Seq 
            有序,可重复
        *Set
            无序,不能重复
    -存放键值对
        *Map
            以k-v键值对的形式存放数据,其中key无序不能重复

    -对于几乎所有的集合类,Scala都同时提供了可变和不可变的版本,分别位于以下两个包
        *不可变集合:scala.collection.immutable
            对集合进行添加或者删除操作的时候,会创建新的集合对象

        *可变集合:  scala.collection.mutable
            对集合进行添加或者删除操作的时候,直接在原来的集合上操作,不会创建新的集合对象            

    -数组
        Array
        ArrayBuffer
    -Seq
        List
        ListBuffer

    -Set

    -Map
        当调用map.get方法的时候,返回的Option类型数据,Option有两个子类型,一个Some,另一个None
        可以帮我们避免对空值进行处理的情况,使用getOrElse函数,给空值赋默认值

    -元组
        将多个相同或者不相同的类型数据组合在一起 
        (数据1,数据2)
        Map集合中kv键值对就是一个特殊的元素,只有两个元素,我们称之为对偶
 

集合常用函数


    -基本属性和常用操作
        (1)获取集合长度
            length
        (2)获取集合大小
            size
        (3)循环遍历
            foreach
        (4)迭代器
            iterator
        (5)生成字符串
            mkString
        (6)是否包含
            contains
    -衍生集合
        (1)获取集合的头
            head
        (2)获取集合的尾(不是头的就是尾)
            tail
        (3)集合最后一个数据
            last
        (4)集合初始数据(不包含最后一个)
            init
        (5)反转
            reverse
        (6)取前(后)n个元素
            take|takeRight
        (7)去掉前(后)n个元素
            drop|dropRight
        (8)并集
            union
        (9)交集
            intersect
        (10)差集
            diff
        (11)拉链
            zip
        (12)滑窗
            sliding

    -集合计算初级函数
        (1)求和
            sum
        (2)求乘积
            product
        (3)最大值
            max
        (4)最小值
            min
        (5)排序
            sorded|sortBy|sortWith

    -集合计算高级函数
        (1)过滤 filter(函数:指定过滤条件) 
             遍历一个集合并从中获取满足指定条件的元素组成一个新的集合

        (2)转换/映射  map
                     
        (3)扁平化 flatten   :::
            将集合中元素由整体转换为个体的过程

        (4)扁平映射  flatMap  
            先映射再进行扁平化处理

        (5)分组    gruopBy
            按照一定的分组规则,将集合中的元素放到不同的组中

        (6)简化|规约
            >对集合内部元素之间进行聚合 
            >reduce   聚合的数据类型一致
            >reduceLeft|reduceRight     聚合的数据类型可以不一致

        (7)折叠
            >对外部元素和集合内部元素之间进行聚合
            >fold     聚合的数据类型一致
            >foldLeft|foldRight        聚合的数据类型可以不一致

    -两个map集合之间的合并

8章 模式匹配

Scala中的模式匹配类似于Java中的switch语法

int i = 10
switch (i) {
    case 10 :
System.out.println("10");
break;
    case 20 : 
System.out.println("20");
break;
    default : 
System.out.println("other number");
break;
}

但是scala从语法中补充了更多的功能,所以更加强大。

1、基本语法

模式匹配语法中,采用match关键字声明,每个分支采用case关键字进行声明,当需要匹配时,会从第一个case分支开始,如果匹配成功,那么执行对应的逻辑代码,如果匹配不成功,继续执行下一个分支进行判断。如果所有case都不匹配,那么会执行case _分支,类似于Java中default语句。

object TestMatchCase {

  def main(args: Array[String]): Unit = {

    var a: Int = 10
    var b: Int = 20
    var operator: Char = 'd'

    var result = operator match {
      case '+' => a + b
      case '-' => a - b
      case '*' => a * b
      case '/' => a / b
      case _ => "illegal"
    }

    println(result)
  }
}

1)说明

      (1)如果所有case都不匹配,那么会执行case _ 分支,类似于Java中default语句,若此时没有case _ 分支,那么会抛出MatchError。

(2)每个case中,不需要使用break语句,自动中断case。

(3)match case语句可以匹配任何类型,而不只是字面量。

      (4)=> 后面的代码块,直到下一个case语句之前的代码是作为一个整体执行,可以使用{}括起来,也可以不括。

package com.scala.chapter07

/**
 * 模式匹配 实现简单的两个整数运算
 */
object Scala01_TestMatch {
  def main(args: Array[String]): Unit = {
    var a: Int = 10
    var b = 20
    var op = '-'

    //模式匹配是有返回值的,没有break关键字,case分支执行结束后,直接跳出
    var res: Any = op match {
      case '+' => a + b
      case '-' => a - b
      case '*' => a * b
      case '/' => a / b
      case _ => "非法运算符"
    }
    println(res)

  }

}

2、模式守卫

1)说明

如果想要表达匹配某个范围的数据,就需要在模式匹配中增加条件守卫。

2)案例实操

   //通过模式守卫,求整数的绝对值
    def abs(num:Int): Int ={
      num match {
        case i:Int if i >= 0 => i
        case j:Int if j < 0 => -j
        case _ => "数据类型错误"
      }
    }

    println(abs(-5))

  }

3、模式匹配类型

1.匹配常量

1)说明

Scala中,模式匹配可以匹配所有的字面量,包括字符串,字符,数字,布尔值等等。

2)实操

object TestMatchVal {

    def main(args: Array[String]): Unit = {

        println(describe(6))

    }

    def describe(x: Any) = x match {

        case 5 => "Int five"

        case "hello" => "String hello"

        case true => "Boolean true"

        case '+' => "Char +"

    }
}

2.匹配类型

1)说明

需要进行类型判断时,可以使用前文所学的isInstanceOf[T]和asInstanceOf[T],也可使用模式匹配实现同样的功能。

2)案例实操

object TestMatchClass {

    def describe(x: Any) = x match {

        case i: Int => "Int"
        case s: String => "String hello"
        case m: List[_] => "List"
        case c: Array[Int] => "Array[Int]"
        case someThing => "something else " + someThing
    }

    def main(args: Array[String]): Unit = {

        //泛型擦除
        println(describe(List(1, 2, 3, 4, 5)))

        //数组例外,可保留泛型
        println(describe(Array(1, 2, 3, 4, 5, 6)))
        println(describe(Array("abc")))
    }
}

3.匹配数组

1)说明

scala模式匹配可以对集合进行精确的匹配,例如匹配只有两个元素的、且第一个元素为0的数组。

2)案例实操

object TestMatchArray {

    def main(args: Array[String]): Unit = {

        for (arr <- Array(Array(0), Array(1, 0), Array(0, 1, 0), Array(1, 1, 0), Array(1, 1, 0, 1), Array("hello", 90))) { // 对一个数组集合进行遍历

            val result = arr match {
                case Array(0) => "0" //匹配Array(0) 这个数组

                case Array(x, y) => x + "," + y //匹配有两个元素的数组,然后将将元素值赋给对应的x,y

                case Array(0, _*) => "以0开头的数组" //匹配以0开头和数组

                case _ => "something else"
            }

            println("result = " + result)
        }
    }
}

4.匹配列表

1)方式一

object TestMatchList {
    def main(args: Array[String]): Unit = {

        //list是一个存放List集合的数组
        //请思考,如果要匹配 List(88) 这样的只含有一个元素的列表,并原值返回.应该怎么写
        for (list <- Array(List(0), List(1, 0), List(0, 0, 0), List(1, 0, 0), List(88))) {

            val result = list match {

                case List(0) => "0" //匹配List(0)
                case List(x, y) => x + "," + y //匹配有两个元素的List
                case List(0, _*) => "0 ..."
                case _ => "something else"
            }

            println(result)
        }
    }
}

2)方式二

object TestMatchList {

    def main(args: Array[String]): Unit = {

        val list: List[Int] = List(1, 2, 5, 6, 7)

        list match {
            case first :: second :: rest => println(first + "-" + second + "-" + rest)
            case _ => println("something else")
        }
    }
}

5.匹配元组

object TestMatchTuple {

    def main(args: Array[String]): Unit = {

        //对一个元组集合进行遍历
        for (tuple <- Array((0, 1), (1, 0), (1, 1), (1, 0, 2))) {

            val result = tuple match {
                case (0, _) => "0 ..." //是第一个元素是0的元组
                case (y, 0) => "" + y + "0" // 匹配后一个元素是0的对偶元组
                case (a, b) => "" + a + " " + b
                case _ => "something else" //默认

            }
            println(result)
        }
    }
}

package com.scala.chapter07

/**
 *
 * 模式匹配
 *
 */
object Scala02_TestMatch {
  def main(args: Array[String]): Unit = {
    /*
    //匹配常量
    def describe(x: Any) = x match {
      case 5 => "Int five"
      case "hello" => "String hello"
      case true => "Boolean true"
      case '+' => "Char +"
    }

    //匹配类型
    def describe(x: Any) = x match {
      case i:Int => "Int" + i
      case s: String => "String hello"
      case m: List[String] => "List"
      case c: Array[Int] => "Array[Int]"
      // 如果以上case都没有匹配上的话,那么下面可以用下划线表示默认情况,但是通过下划线没有办法获取匹配的内容
      //  如果想要获取匹配的值,那么可以定义一个变量去接收
      case a => "something else " + a
    }

    //接收匹配的类型语法格式   定义对应类型的变量去接收
    //println(describe(20))

    //泛型擦除,在匹配的时候,和泛型无关
    //println(describe(List("a", "b", "c")))
    //println(describe(List(1,2,3)))

    //数组是没有泛型擦除,数组在匹配的时候,会保留泛型    Array[Int]==>int[]   Array[String]==>String[]
    println(describe(Array("a","b")))
    //匹配数组
    for (arr <- List(
                  Array(0),
                  Array(1, 0),
                  Array(0, 1, 0),
                  Array(1, 1, 0),
                  Array(1, 1, 0, 1),
                  Array("hello", 90))) { // 对一个数组集合进行遍历

      val result = arr match {
        case Array(0) => "0" //匹配Array(0) 这个数组

        case Array(x, y) => x + "," + y //匹配有两个元素的数组,然后将将元素值赋给对应的x,y

        case Array(0, _*) => "以0开头的数组" //匹配以0开头和数组

        case _ => "something else"
      }

      println("result = " + result)
    }

    //匹配列表 方式1
    //请思考,如果要匹配 List(88) 这样的只含有一个元素的列表,并原值返回.应该怎么写
    for (list <- List(
                    List(0),
                    List(1, 0),
                    List(0, 0, 0),
                    List(1, 0, 0),
                    List(88))) {

      val result = list match {

        case List(0) => "0" //匹配List(0)
        case List(x, y) => x + "," + y //匹配有两个元素的List
        case List(0, _*) => "0 ..."
        case List(a) => a  //返回88
        case _ => "something else"
      }

      println(result)
    }

    //列表匹配  方式2
    val list: List[Int] = List(1, 2, 5, 6, 7)

    list match {
      case first :: second :: rest => println(first + "-" + second + "-" + rest)
      case _ => println("something else")
    }
*/

    //匹配元组
    for (tuple <- List(
      (0, 1),
      (1, 0),
      (1, 1),
      (1, 0, 2))) {

      val result = tuple match {
        case (0, _) => "0 ..." //是第一个元素是0的元组
        case (y, 0) => "" + y + "0" // 匹配后一个元素是0的对偶元组
        case (a, b) => "" + a + " " + b
        case _ => "something else" //默认

      }
      println(result)
    }




  }
}

 5.5.特殊模式匹配

package com.scala.chapter07

/**
 * 模式匹配案例
 *
 */
object Scala03_TestMatch {
  def main(args: Array[String]): Unit = {
    //val list = List(("a", 1), ("b", 2), ("c", 3))

    //对list进行遍历,输出元组中的第一个元素
    //for (elem <- list) {
    //      println(elem._1)
    //    }

    //特殊的模式匹配1
    //for ((word,count) <- list) {
    //      println(word)
    //    }

    //for ((word,_) <- list) {
    //      println(word)
    //    }

    //for (("a",count) <- list) {
    //      println(count)
    //    }

    //特殊的模式匹配2 在模式匹配的时候,给元组元素命名
    //val (id,name,age): (Int, String, Int) = (1000, "zhangsan", 11)
    //println(name)

    //元组key不变,value * 2
    /*
     val list:List[(String,Int)] = List(("a", 1), ("b", 2), ("c", 3))

     val newList: List[(String, Int)] = list.map(t => {
       (t._1, t._2 * 2)
     })
     println(newList)//List((a,2), (b,4), (c,6))
       */
    //val list: List[(String, Int)] = List(("a", 1), ("b", 2), ("c", 3))

    /*val newList: List[(String, Int)] = list.map {
      t => {
        t match {
          case (word, count) => (word, count * 2)
        }
      }
    }

     */
    //简化 如果匿名函数中使用匹配模式case,要求必须使用花括号括起来
    //入过一个函数参数列表中,只有一个参数,那么参数列表中的小括号可以使用花括号代替
    /*val newList: List[(String, Int)] = list.map {
      case (word, count) => (word, count * 2)
    }
    println(newList)


     */
    //练习:使用模式匹配,  对count*2
    val list = List(("a", ("a", 5)), ("b", ("b", 10)), ("c", ("c", 20)))

    //println(list.map{
    //  //t==> ("a",("a",5))
    //  //t._2 ==>("a",5)
    //  t=>{
    //    (t._1,(t._2._1,t._2._2*2))
    //  }
    //})

    val newList: List[(String, (String, Int))] = list.map {
      case (key, (word, count)) => (key, (word, count * 2))
    }
    println(newList)


  }


}

6.匹配对象及样例类

1)基本语法

class User(val name: String, val age: Int)

object User{

    def apply(name: String, age: Int): User = new User(name, age)

    def unapply(user: User): Option[(String, Int)] = {
        if (user == null)
            None
        else
            Some(user.name, user.age)
    }
}

object TestMatchUnapply {
    def main(args: Array[String]): Unit = {
        val user: User = User("zhangsan", 11)
        val result = user match {
            case User("zhangsan", 11) => "yes"
            case _ => "no"
        }

        println(result)
    }
}
package com.scala.chapter07

/**
 * 匹配对象  
 */
object Scala04_TestMatch {

  class User(var name:String,var age:Int){}
  object User {
    //根据属性创建对象
    def apply(name:String,age:Int): User  = new User (name,age)

    //根据对象获取属性
    def unapply(user: User): Option[(String, Int)] = {
      if (user ==null){
        return None
      }else{
        return Some(user.name,user.age)
      }

    }
  }
  def main(args: Array[String]): Unit = {
    //创建用户对象
    val zs: User = User("zhangsan", 25)

    //对对象进行模式匹配
    val res = zs match {
      case User("zhangsan", 25) => "yes"
      case _ => "no"
    }
    println(res)
  }
}

小结

val user = User("zhangsan",11),该语句在执行时,实际调用的是User伴生对象中的apply方法,因此不用new关键字就能构造出相应的对象。

当将User("zhangsan", 11)写在case后时[case User("zhangsan", 11) => "yes"],会默认调用unapply方法(对象提取器),user作为unapply方法的参数,unapply方法将user对象的name和age属性提取出来,与User("zhangsan", 11)中的属性值进行匹配

case中对象的unapply方法(提取器)返回Some,且所有属性均一致,才算匹配成功,属性不一致,或返回None,则匹配失败。

若只提取对象的一个属性,则提取器为unapply(obj:Obj):Option[T]

若提取对象的多个属性,则提取器为unapply(obj:Obj):Option[(T1,T2,T3…)]

若提取对象的可变个属性,则提取器为unapplySeq(obj:Obj):Option[Seq[T]]

2)样例类

(1)语法:

case class Person (name: String, age: Int)

(2)说明

1样例类仍然是类,和普通类相比,只是其自动生成了伴生对象,并且伴生对象中自动提供了一些常用的方法,如applyunapplytoString、equals、hashCode和copy。

2样例类是为模式匹配而优化的类,因为其默认提供了unapply方法,因此,样例类可以直接使用模式匹配,而无需自己实现unapply方法。

3构造器中的每一个参数都成为val,除非它被显式地声明为var(不建议这样做)

(3)实操

上述匹配对象的案例使用样例类会节省大量代码

case class User(name: String, age: Int)

object TestMatchUnapply {
    def main(args: Array[String]): Unit = {
        val user: User = User("zhangsan", 11)
        val result = user match {
            case User("zhangsan", 11) => "yes"
            case _ => "no"
        }

        println(result)
    }
}
package com.scala.chapter07

/**
 * 样例类
 */
object Scala05_TestMatch {


  def main(args: Array[String]): Unit = {
    val std1: Student17 = Student17("zhangsan", 18)
    println(std1 match {
      case Student17("zhangsan", 18) => "yes"
      case _ => "no"
    })

  }
  case class Student17(var name:String,var age:Int){}
}

4、变量声明中的模式匹配

case class Person(name: String, age: Int)

object TestMatchVariable {
    def main(args: Array[String]): Unit = {

        val (x, y) = (1, 2)
        println(s"x=$x,y=$y")

        val Array(first, second, _*) = Array(1, 7, 2, 9)
        println(s"first=$first,second=$second")

        val Person(name, age) = Person1("zhangsan", 16)
        println(s"name=$name,age=$age")
    }
}

5、for表达式中的模式匹配

object TestMatchFor {

    def main(args: Array[String]): Unit = {

        val map = Map("A" -> 1, "B" -> 0, "C" -> 3)
        for ((k, v) <- map) { //直接将map中的k-v遍历出来
            println(k + " -> " + v) //3个
        }
        println("----------------------")

        //遍历value=0的 k-v ,如果v不是0,过滤
        for ((k, 0) <- map) {
            println(k + " --> " + 0) // B->0
        }

        println("----------------------")
        //if v == 0 是一个过滤的条件
        for ((k, v) <- map if v >= 1) {
            println(k + " ---> " + v) // A->1 和 c->33
        }
    }
}

6.偏函数中的模式匹配

偏函数也是函数的一种,通过偏函数我们可以方便的对输入参数做更精确的检查。例如该偏函数的输入类型为List[Int],而我们需要的是第一个元素是0的集合,这就是通过模式匹配实现的。

1.偏函数定义

val second: PartialFunction[List[Int], Option[Int]] = {
    case x :: y :: _ => Some(y)
}

Scala_第27张图片

:该偏函数的功能是返回输入的List集合的第二个元素

2)偏函数原理

上述代码会被scala编译器翻译成以下代码,与普通函数相比,只是多了一个用于参数检查的函数——isDefinedAt,其返回值类型为Boolean。

val second = new PartialFunction[List[Int], Option[Int]] {

    //检查输入参数是否合格
    override def isDefinedAt(list: List[Int]): Boolean = list match {
        case x :: y :: _ => true
        case _ => false
    }

    //执行函数逻辑
    override def apply(list: List[Int]): Option[Int] = list match {
        case x :: y :: _ => Some(y)
    }
}

3)偏函数使用

偏函数不能像second(List(1,2,3))这样直接使用,因为这样会直接调用apply方法,而应该调用applyOrElse方法,如下

second.applyOrElse(List(1,2,3), (_: List[Int]) => None)

applyOrElse方法的逻辑为 if (ifDefinedAt(list)) apply(list) else default。如果输入参数满足条件,即isDefinedAt返回true,则执行apply方法,否则执行defalut方法,default方法为参数不满足要求的处理逻辑。

  1. 案例实操

(1)需求

将该List(1,2,3,4,5,6,"test")中的Int类型的元素加一,并去掉字符串。

def main(args: Array[String]): Unit = {
  val list = List(1,2,3,4,5,6,"test")
  val list1 = list.map {
    a =>
      a match {
        case i: Int => i + 1
        case s: String =>s + 1
      }
  }
  println(list1.filter(a=>a.isInstanceOf[Int]))
}

 (2)实操


方法一:
List(1,2,3,4,5,6,"test").filter(_.isInstanceOf[Int]).map(_.asInstanceOf[Int] + 1).foreach(println)
方法二:
List(1, 2, 3, 4, 5, 6, "test").collect { case x: Int => x + 1 }.foreach(println)
package com.scala.chapter07

/**
 * 偏函数应用
 */
object Scala06_TestMatch {
  def main(args: Array[String]): Unit = {
    //将该集合中的Int类型的元素加一,并去掉字符串。
    val list = List(1, 2, 3, 4, 5, 6, "test")

    /*//过滤掉字符串
        val newList: List[Any] = list.filter(_.isInstanceOf[Int])

        val res: List[Int] = newList.map {
          //elem =>elem + 1
          //elem => elem.asInstanceOf[Int] + 1
          //elem =>elem.toString.toInt + 1
          case a:Int =>a+1
        }
        println(res)
    */
    //list.collect{case elem:Int=>elem + 1}.foreach(println)
    //list.map{case elem:Int=>elem + 1}.foreach(println)

    list.collect {
      case a: Int => a + 1
    }.foreach(println)
  }
}

第9章 异常

语法处理上和Java类似,但是又不尽相同。

1、Java异常处理

public class ExceptionDemo {

    public static void main(String[] args) {

        try {
            int a = 10;
            int b = 0;
            int c = a / b;
        }catch (ArithmeticException e){
// catch时,需要将范围小的写到前面
            e.printStackTrace();
        }catch (Exception e){
            e.printStackTrace();
        }finally {
            System.out.println("finally");
        }
    }
}

注意事项

(1)Java语言按照try—catch—finally的方式来处理异常

(2)不管有没有异常捕获,都会执行finally,因此通常可以在finally代码块中释放资源。

(3)可以有多个catch,分别捕获对应的异常,这时需要把范围小的异常类写在前面,把范围大的异常类写在后面,否则编译错误。

2、Scala异常处理

def main(args: Array[String]): Unit = {

    try {
        var n= 10 / 0
    }catch {
        case ex: ArithmeticException=>{
            // 发生算术异常
            println("发生算术异常")
        }
        case ex: Exception=>{
            // 对异常处理
            println("发生了异常1")
            println("发生了异常2")
        }
    }finally {
        println("finally")
    }
}

1)我们将可疑代码封装在try块中。在try块之后使用了一个catch处理程序来捕获异常。如果发生任何异常,catch处理程序将处理它,程序将不会异常终止。

2)Scala的异常的工作机制和Java一样,但是Scala没有“checked(编译期)”异常,即Scala没有编译异常这个概念,异常都是在运行的时候捕获处理。

3)异常捕捉的机制与其他语言中一样,如果有异常发生,catch子句是按次序捕捉的。因此,在catch子句中,越具体的异常越要靠前,越普遍的异常越靠后,如果把越普遍的异常写在前,把具体的异常写在后,在Scala中也不会报错,但这样是非常不好的编程风格。

4)finally子句用于执行不管是正常处理还是有异常发生时都需要执行的步骤,一般用于对象的清理工作,这点和Java一样。

5)用throw关键字,抛出一个异常对象。所有异常都是Throwable的子类型throw表达式是有类型的,就是Nothing,因为Nothing是所有类型的子类型,所以throw表达式可以用在需要类型的地方

def test():Nothing = {
    throw new Exception("不对")
}

6)java提供了throws关键字来声明异常。可以使用方法定义声明异常。它向调用者函数提供了此方法可能引发此异常的信息。它有助于调用函数处理并将该代码包含在try-catch块中,以避免程序异常终止。在Scala中,可以使用throws注解来声明异常

def main(args: Array[String]): Unit = {
  	f11()
}

@throws(classOf[NumberFormatException])
def f11()={
  	"abc".toInt
}
package com.scala.chapter08

/**
 * 异常处理
 * -Java的异常
 * -->异常的体系结构
 * ->Throwable
 * 》Error
 * 》Exception
 * #编译时异常
 * #运行时异常
 * -->异常执行原理
 * 程序运行的过程中,如果发生了异常,那么底层会创建对应的异常类型对象,通过throw关键字,将异常向上抛出
 * JVM会找能够对异常进行处理的代码(对异常进行捕获),将异常交给对应的处理代码进行处理
 *
 * -->异常处理方式
 * 》通过throw声明异常
 * 》通过try..catch进行捕获
 * try{
 * 可能发生异常的代码
 * }catch(异常类型 变量名){
 * 大范围 异常处理代码
 * }finally{
 * 不管是否发生异常,都会被执行的代码
 * 一般用于资源的释放
 * }
 * -Scala的异常
 * 》Scala中不区分编译时异常和运行时异常
 * 》Scala在进行异常捕获时,只有一个catch块,在catch块中,通过模式匹配,匹配不同类型的异常。
 * 而且在进行匹配,就算是将大类型的异常放在前,也不会报错(不建议这么操作)
 * 》通过在@Throws注解标记可能发生异常
 *
 */
object Scala01_TestException {
  def main(args: Array[String]): Unit = {
    try {
      //可能发生异常的代码
      10/0
      println("程序正常执行")
    } catch {
      //异常处理代码
      case e:ArithmeticException=>println("发生算术异常" + e.getMessage)
      case e:Exception=>println("发生异常" + e.getMessage)
    } finally {
    //不管是否发生异常,都会被执行的代码
      println("finally被执行")
    }
    println("重要代码")

  }
  def test():Nothing = {
    throw new Exception("不对")
  }
}

10章 隐式转换

        当编译器第一次编译失败的时候,会在当前的环境中查找能让代码编译通过的方法,用于将类型进行转换,实现二次编译

1、隐式函数

1)说明

隐式转换可以在不需改任何代码的情况下,扩展某个类的功能。

2)案例实操

需求:通过隐式转化为Int类型增加方法。

class MyRichInt(val self: Int) {
    def myMax(i: Int): Int = {
        if (self < i) i else self
    }

    def myMin(i: Int): Int = {
        if (self < i) self else i
    }
}

object TestImplicitFunction {
    // 使用implicit关键字声明的函数称之为隐式函数
    implicit def convert(arg: Int): MyRichInt = {
        new MyRichInt(arg)
    }

def main(args: Array[String]): Unit = {
    // 当想调用对象功能时,如果编译错误,那么编译器会尝试在当前作用域范围内查找能调用对应功能的转换规则,这个调用过程是由编译器完成的,所以称之为隐式转换。也称之为自动转换
        println(2.myMax(6))
    }
}

package com.scala.chapter09

/**
 * 隐式函数
 * -可以动态的扩展功能
 * -当编译器第一次编译失败后,会在当前环境中查找能让编译通过的非法,该方法用于类型转换
 * 转换之后,进行二次编译,实现对类功能的扩展
 */
object Scala01_TestImplicit {

  //将Int转为MyRichInt
  // 使用implicit关键字声明的函数称之为隐式函数
  /*implicit def convert(arg: Int): MyRichInt = {
    new MyRichInt(arg)
  }

   */
  implicit class MyRichInt(val self: Int) {
    def myMax(i: Int): Int = {
      if (self < i) i else self
    }

    def myMin(i: Int): Int = {
      if (self < i) self else i
    }
  }

  def main(args: Array[String]): Unit = {
    println(2.myMax(5))

  }
}

2、隐式参数

普通方法或者函数中的参数可以通过implicit关键字声明为隐式参数,调用该方法时,就可以传入该参数,编译器会在相应的作用域寻找符合条件的隐式值。

1)说明

(1)同一个作用域中,相同类型的隐式值只能有一个

(2)编译器按照隐式参数的类型去寻找对应类型的隐式值,与隐式值的名称无关。

(3)隐式参数优先于默认参数

2)案例实操

object TestImplicitParameter {s

    implicit val str: String = "hello world!"

    def hello(implicit arg: String="good bey world!"): Unit = {
        println(arg)
    }

    def main(args: Array[String]): Unit = {
        hello
    }
}
package com.scala.chapter09

/**
 * 隐式参数
 */
object Scala02_TestImplicit {
  def main(args: Array[String]): Unit = {
    //声明隐式变量
    implicit var string:String = "jax"

    def sayHi(implicit name: String): Unit = {
      println("Hello-" + name)
    }

    // sayHi("hh")
    //如果函数的参数存在默认值,调用时可以不用传参
    //sayHi() //Hello-jax

    //隐式参数在调用时,直接通过方法名调用,无需括号
    sayHi
  }
}

3、隐式类

在Scala2.10后提供了隐式类,可以使用implicit声明类,隐式类的非常强大,同样可以扩展类的功能,在集合中隐式类会发挥重要的作用。

1)隐式类说明

(1)其所带的构造参数有且只能有一个

(2)隐式类必须被定义在“类”或“伴生对象”或“包对象”里,即隐式类不能是顶级的

2)案例实操

object TestImplicitClass {

    implicit class MyRichInt(arg: Int) {

        def myMax(i: Int): Int = {
            if (arg < i) i else arg
        }

        def myMin(i: Int) = {
            if (arg < i) arg else i
        }
    }

    def main(args: Array[String]): Unit = {
        println(1.myMax(3))
    }
}

4、隐式解析机制

1)说明

(1)首先会在当前代码作用域下查找隐式实体(隐式方法、隐式类、隐式对象)。(一般是这种情况)

(2)如果第一条规则查找隐式实体失败,会继续在隐式参数的类型的作用域里查找。类型的作用域是指与该类型相关联的全部伴生对象以及该类型所在包的包对象

2)案例实操

package com.atguigu.chapter10


//(2)如果第一条规则查找隐式实体失败,会继续在隐式参数的类型的作用域里查找。类型的作用域是指与该类型相关联的全部伴生模块,
object TestTransform extends PersonTrait {

    def main(args: Array[String]): Unit = {

        //(1)首先会在当前代码作用域下查找隐式实体
        val teacher = new Teacher()
        teacher.eat()
        teacher.say()
    }

    class Teacher {
        def eat(): Unit = {
            println("eat...")
        }
    }
}

trait PersonTrait {

}

object PersonTrait {
    // 隐式类 : 类型1 => 类型2
    implicit class Person5(user:Teacher) {

        def say(): Unit = {
            println("say...")
        }
    }
}

11章 泛型

1协变和逆变

1)语法

class MyList[+T]{ //协变

}

class MyList[-T]{ //逆变

}

class MyList[T] //不变

2)说明

协变:Son是Father的子类,则MyList[Son] 也作为MyList[Father]的“子类”

逆变:Son是Father的子类,则MyList[Son]作为MyList[Father]的“父类”

不变:Son是Father的子类,则MyList[Father]与MyList[Son]“无父子关系”

3)实操

package com.scala.chapter10


/**
 * 泛型
 */
class Parent {
}

class Child extends Parent {
}

class SubChild extends Child {
}

//泛型模板
//不可变性
//class MyList[T]{}

//协变
//class MyList[+T]{} 包含子类型

//逆变
class MyList[-T] {} //包含父类型

object Scala01_TestGeneric {
  def main(args: Array[String]): Unit = {
    //val s: MyList[Child] = new MyList[Child]
    //val s1: MyList[Child] = new MyList[Parent]
    //val s2: MyList[Child] = new MyList[SubChild]

  /*test(classOf[Child])
  test(classOf[Parent])
  test2(classOf[SubChild])

   */

  }

  //泛型通配符的下界 必须是Child或它的父类
 /* def test[A >: Child](a: Class[A]): Unit = {
    println(a)
  }

  //泛型通配符的上界 必须是Child或它的子类
  def test2[A <: Child](a: Class[A]): Unit = {
    println(a)
  }

  */
  def test[A >: Child](a:A):Unit={

 }

}

2 泛型上下限

1)语法

Class PersonList[T <: Person]{ //泛型上限

}

Class PersonList[T >: Person]{ //泛型下限

}

2)说明

泛型的上下限的作用是对传入的泛型进行限定。

3)实操

class Parent{}
class Child extends Parent{}
class SubChild extends Child{}

object Scala_TestGeneric {
  def main(args: Array[String]): Unit = {

    //test(classOf[SubChild])
    //test[Child](new SubChild)
  }


  //泛型通配符之上限
  //def test[A <: Child](a:Class[A]): Unit ={
  //  println(a)
  //}

  //泛型通配符之下限
  //def test[A >: Child](a:Class[A]): Unit ={
  //  println(a)
  //}

  //泛型通配符之下限 形式扩展
  def test[A >: Child](a:A): Unit ={
    println(a.getClass.getName)
  }
}

3上下文限定

1)语法

def f[A : B](a: A) = println(a) //等同于def f[A](a:A)(implicit arg:B[A])=println(a)

2)说明

上下文限定是将泛型和隐式转换的结合产物,以下两者功能相同,使用上下文限定[A : Ordering]之后,方法内无法使用隐式参数名调用隐式参数,需要通过implicitly[Ordering[A]]获取隐式变量,如果此时无法查找到对应类型的隐式变量,会发生出错误。

implicit val x = 1
val y = implicitly[Int]
val z = implicitly[Double]

3)实操

def f[A:Ordering](a:A,b:A) =implicitly[Ordering[A]].compare(a,b)
def f[A](a: A, b: A)(implicit ord: Ordering[A]) = ord.compare(a, b)

12章 总结

Scala语言中,下划线的用法
    -标识符命名
    -导包
        *导入某一个类下的"静态成员" 
            import scala.util.control.Breaks._
        *导入某一个包下的所有类
            import java.util._
        *屏蔽类  
            import java.sql.{Date=>_,Array=>_,_}

    -匿名函数
        如果在匿名函数中,参数只出现了一次,那么参数可以省略,在函数体使用参数的时候,用下划线代替

    -在类中声明属性,如果要给属性赋默认值

    -在模式匹配中
        case _   表示上面所有case都没有匹配成功的情况,相当于default

----------------------------------------------------------------------------------------------------
1.计算机语言发展过程
    -机器语言
    -汇编语言
    -高级语言
        *解释型语言(JavaScript)
            要想运行多次,需要进行多次解释
            支持跨平台

        *编译型语言(C\C++)
            要想运行多次,不需要进行多次编译
            不支持跨平台

2.Java运行原理
    -先编译,再解释
    - .java源文件--->编译器(javac)--->.class字节码文件--->JVM(java 不同平台)--->机器指令


3.Scala运行原理
    -先编译,再解释
    - .scala源文件--->编译器(scalac)--->.class字节码文件--->JVM(scala 不同平台)--->机器指令

4.搭建Scala环境
    -解压
    -配置环境变量(SCALA_HOME,path)
    -可以通过Scala提供的交互式命令窗口(REPL)  进行测试
    -在idea上安装scala插件
        *离线方式
        *在线安装
    -在Maven的module中添加scala支持


5.第一个HelloScala案例
    object HelloScala{
        def main(args:Array[String]):Unit = {
            println("HelloScala")
        }
    }

    对第一个Scala案例进行说明
    >object
        关键字,表示声明一个伴生对象
    >Scala01_HelloWorld
        伴生对象的名字,取名的时候需要符合标识符命名规则
    >def
        关键字  标识声明一个方法
    >main
        方法的名称
    >(args: Array[String])
      &args 参数名称
      &Array[String]参数类型,在Scala语言中,[]表示泛型
      &声明参数的时候,名称在前,类型在后,名称和类型之间用冒号分隔
    >Unit
      &返回值类型为空,相当于java语言中的void关键字
      &Unit是一个类型,当前类型只有一个实例()
      &参数列表和返回值类型之间,用冒号进行分隔
      &返回值类型和函数体之间用等号进行连接
    > println("HelloScala")
      向控制台打印输出内容
      在Scala语言中,语句结束不需要加分号
6.伴生对象
    -从字面意思来讲,伴生对象就是伴随类产生的对象
    -在scala中,没有static关键字,通过伴生对象模拟static关键字的实现
    -当通过object名称定义一个伴生对象的时候,默认底层会编译生成两个字节码文件,一个是伴生类,
        另一个是伴生对象所属类。实际我们所说的伴生对象是伴生对象所属类中定义的一个单例对象,然后
        通过对象 .的方式访问属性或者方法等

    -以后在scala程序中,如果要想实现static效果,那么我们应该将属性以及方法定义在伴生对象中

-------------------------------------------------------------------------------------------------
1.注释
    -单行注释
    -多行注释
    -文档注释

2.变量和常量
    -变量
        var 变量名:数据类型  = 值

    -常量 
        val 变量名:数据类型  = 值

3.标识符命名规范
    -标识符
        在程序中,程序员可以自己命名的地方

    -和java的命名规则一样
        *由字母、数字、下划线以及货币符号组成
        *数组不能应用于首字符
        *不能含有空格
        *不能是关键字以及保留字

    -可以有操作符号组成,并且只能包含操作符
    -如果是关键字的话,需要使用``括起来

4.字符串输出
    -println("")   通过+对字符串进行拼接
    -printf(模板,参数)   %d %f %s
    -""" """ 保持字符串原样输出

     插值字符串, 通过${}获取变量内容,在字符串前,需要加s|f|raw进行标记

5.获取键盘输入
    StdIn.readXXX

6.数据类型
    -Any
        *AnyVal(值类型)
            >Byte,Short,Int,Long,Float,Double,Boolean,Char
            >Unit
                表示返回值类型为空,相当于Java中的void关键字
            >StringOps
                对字符串功能的增强

        *AnyRef(引用类型)
            >所有Java语言中的类型
            >Scala语言中的类
            >集合

            >Null
                表示变量声明后,没有指向任何对象,相当于Java中null关键字
                是所有AnyRef的子类,所以不能将null赋值给AnyValue

        *Nothing  
            是所有类的子类,一般用于方法的返回值,表示当前方法没有正常返回的情况            

7.运算符
    -Java
        *==比较的是对象的内存地址
        *equals默认比较的也是对象的内存地址,但是一般我们会对其进行重写,比较对象的内容

    -Scala
        *==和equals比较的规则一样,都是比较对象的内容是否相等
        *eq比较的是对象的内存地址
-------------------------------------------------------------------------------------------------
1.程序的流程控制
    -顺序流程
    -分支流程(选择流程)
        *单分支
            if(条件表达式){
                语句块
            }

        *双分支
            if(条件表达式){
                语句块1
            }else{
                语句块2
            }    

        *多分支
            if(条件表达式1){
                语句块1
            }else if(条件表达式2){
                语句块2
            }....else if可以有多个
            else{
                语句块3
            }
        *在scala语言中,分支是有返回值的,返回值是满足条件的分支块最后一行的执行结果;如果每一个分支块
            返回的数据类型不一致,那么就用它们共同的祖先类型作为返回值类型


    -循环流程
        *for
            >语法
                for(变量名:数据类型 <- 数组|集合){

                }
                数据类型可以省略,因为可以通过集合中的元素推断出来

            >范围数据循环    to  | until   
            
            >循环守卫
                在循环的过程中,加判断条件
                通过循环守卫或者在循环体中加if判断,可以实现类似continue效果

            >循环步长  by
                只针对于Range集合
                按照指定的步长对数据进行生成        

            >循环嵌套
                for(外层循环;内层循环){}

            >循环引入变量
                在循环的时候,定义新的变量,该变量的值受循环变量的影响

            
        *while    
            >while    
            >do...while

        *循环中断
            >在scala语言中,没有break关键字,通过抛出异常,并对异常进行处理的方式,来实现类似break的效果
            >Scala提供了Breaks类   break()  和breakable()
            >导入每一个类下的"静态"成员    

2.函数式编程
    -函数的基本语法
        def 函数名(参数列表):返回值类型={
            函数体
        }

        函数类型:    (参数形式)=>返回值

    -函数的参数
        *可变长参数
            >在一个参数列表中,至多只能有一个可变长参数
            >如果一个参数列表中,有多个参数,应该将可变长参数放到最后

        *参数默认值
            >在声明函数的时候,函数的形参可以赋默认值
            >如果参数有默认值,那么在调用函数的时候,有默认值的参数可以不用传参,使用默认值作为参数
            >如果参数有默认值,那么在调用函数的时候,也可以传递参数,会用传递的参数替换默认值作为参数

        *带名参数
            在调用函数的时候,我们可以指定给哪个参数进行赋值

    -函数至简原则(能省则省)
        *return可以省略,Scala会使用函数体的最后一行代码作为返回值
        *如果函数体只有一行代码,可以省略花括号
        *返回值类型如果能够推断出来,那么可以省略(:和返回值类型一起省略)
        *如果有return,则不能省略返回值类型,必须指定
        *如果函数明确声明unit,那么即使函数体中使用return关键字也不起作用
        *Scala如果期望是无返回值类型,可以省略等号
        *如果函数无参,但是声明了参数列表,那么调用时,小括号,可加可不加
        *如果函数没有参数列表,那么小括号可以省略,调用时小括号必须省略
        *如果不关心名称,只关心逻辑处理,那么函数名(def)可以省略

    -匿名函数
        *参数的类型可以省略,会根据形参进行自动的推导
        *类型省略之后,发现只有一个参数,则圆括号可以省略;其他情况:没有参数和参数超过1的永远不能省略圆括号。
        *匿名函数如果只有一行,则大括号也可以省略
        *如果参数只出现一次,则参数省略且后面参数可以用_代替

3.高阶函数
    -函数更高级的用法
    -在Scala语言中,函数是一等公民
        *函数可以作为值进行传递
            >语法
                var f = 函数名 _
            >如果明确了变量的数据类型,那么下划线可以省略

        *函数可以作为参数进行传递   
            >通过匿名函数
            >扩展函数的功能
            >提高函数的灵活度

        *函数可以作为返回值进行返回 
            >函数的嵌套
            
            >函数链式调用,通过参数传递数据,在执行的过程中,函数始终占据栈内存,容易导致内存溢出
            
            >闭包
                内层函数访问外层函数的局部变量,会自动延长外层函数局部变量的生命周期,与内层函数形成一个
                闭合的效果,我们称之为闭包

            >柯里化
                &将一个参数列表中的多个参数,拆分为多个参数列表
                &好处1:每一个参数列表表示函数清晰明确
                &好处2:简化闭包代码的编写    

    -控制抽象
        *值调用
            传递的是函数执行后的结果

        *名调用
            传递的是代码块  
            =>代码块返回值类型

----------------------------------------------------------------------------------------------------
1.对象和类
    -在自然界中,只要是客观存在的都是对象 (万物皆对象)
    -对大量对象共性的抽象,抽取为类
        *有什么        属性
        *能做什么    方法
    -在scala以及Java语言中,类是创建的模板
    -类是客观事物在人脑中的主观反映

2.Scala的属性
    -在Scala语言中,类、方法、属性默认修饰符是public,但是没有public关键字
    -对于Scala中的属性,底层会用private修饰,同时提供公开的设置以及获取属性的方法----面向封装
    -如果要生成满足JavaBean规范的get和set方法的话,需要在属性上加@BeanProperty注解

3.访问权限
    -Java
        *private        私有的,只能在本类中被访问
        *default        默认的,可以在本类以及同包的其它类中被访问
        *protected        受保护的,可以在本类、同包的其它类以及非同包的子类中被访问
        *public         公开的,所有类

    -Scala
        *private        私有的,只能在本类中被访问
        *public(默认)    公开的,所有类
        *protected        比Java设置的更严格,只能在本类以及子类中被访问,同包其他类访问不了
        *private[包名]     可以让指定的包进行访问

4.构造器(构造方法)
    -主构造方法
        *在声明类的同时,主构造方法也被声明
        *主构造方法只能有一个
        *如果主构造方法没有参数,那么以及调用的时候,小括号可以省略的
    -辅助构造方法
        *方法名必须叫this
        *辅助构造方法可以重载
        *辅助构造方法中的第一行必须直接或者间接调用主构造方法

5.模糊、不具体
    -使用abstract关键字定义抽象类
    -抽象类一般和抽象属性以及抽象方法配合使用
      >抽象属性
        属性只有声明,但是没有赋值
    
      >抽象方法
        方法只有声明,没有实现
    
     -在一个类中,如果存在抽象属性或者抽象方法,那么这个类一定是抽象类
     -如果一个类是抽象类,那么它不一定包含抽象属性和抽象方法
     -如果一个类中存在抽象属性或者抽象方法,那么具体的实现应该交给子类完成
     -如果子类也实现不了抽象内容,那么子类也应该声明为抽象类

     -如果重写(实现)抽象属性或者方法,那么override关键字可以省略

     -如果重写(覆盖)非抽象属性或者方法,那么override关键字不能省略,必须得加

     -如果对非抽象属性进行覆盖,要求属性必须得用val修饰

     -可以通过super关键字调用父类的方法,但是不能super调用父类的属性

     -在Scala中,属性和方法都是动态绑定
         *静态绑定(编译器绑定)
             在编译阶段,确定属性或者方法所属类型

         *动态绑定
             在运行阶段,根据实际创建的对象类型来决定属性 或者方法所属类型

6.匿名子类

7.创建对象的方式
    -new 
        底层调用的是构造方法

    -类名()
        底层调用的是伴生对象中apply方法    

    -实现单例设计模式

8.特质
    -声明特质的语法
        trait 特质名{
            抽象属性
            非抽象属性
            抽象方法
            非抽象方法
        }        
    -混入特质
        *不存在继承关系
            class 类名 extends 特质1 with 特质2

        *存在继承关系
            class 类名 extends 父类 with 特质1    with 特质2

    -特质叠加的顺序
        第一步:列出第一个混入特质的继承关系,作为临时叠加的顺序
            DBOperation-->Operation

        第二步:列出第二个混入特质的继承关系,并且该顺序放到临时叠加顺序的前面,已经出现的特质不在出现
            HDFSOperation-->DBOperation-->Operation

        第三步:将子类放到临时叠加顺序的第一个
            MyOperation-->HDFSOperation-->DBOperation-->Operation


        注意:这个时候super不是调用父类中的方法了,而是调用特质叠加顺序上下一个节点的方法

9.特质和抽象类
    -抽象类中可以定义抽象属性、抽象方法、非抽象属性、非抽象方法
    -特质中可以定义抽象属性、抽象方法、非抽象属性、非抽象方法
    -抽象类和特质都不能被实例化
      >抽象类有构造方法
      >特质也有构造方法
    -如果需要给构造方法传递参数的话,使用抽象类,因为特质不支持带参的构造
    -优先选择特质,scala是单继承,如果直接继承不方便后续的扩展
    -多大量对象共性进行抽象--->类--->对大量类共性进行抽象-->抽象类
      一般子类和父类之间应该满足   is-a 原则
    -特质一般是对行为进行抽象,定义规范  
----------------------------------------------------------------------------------------------------
1.集合
    -存放单值类型
        *Seq 
            有序,可重复
        *Set
            无序,不能重复
    -存放键值对
        *Map
            以k-v键值对的形式存放数据,其中key无序不能重复

    -对于几乎所有的集合类,Scala都同时提供了可变和不可变的版本,分别位于以下两个包
        *不可变集合:scala.collection.immutable
            对集合进行添加或者删除操作的时候,会创建新的集合对象

        *可变集合:  scala.collection.mutable
            对集合进行添加或者删除操作的时候,直接在原来的集合上操作,不会创建新的集合对象            

    -数组
        Array
        ArrayBuffer
    -Seq
        List
        ListBuffer

    -Set

    -Map
        当调用map.get方法的时候,返回的Option类型数据,Option有两个子类型,一个Some,另一个None
        可以帮我们避免对空值进行处理的情况,使用getOrElse函数,给空值赋默认值

    -元组
        将多个相同或者不相同的类型数据组合在一起 
        (数据1,数据2)
        Map集合中kv键值对就是一个特殊的元素,只有两个元素,我们称之为对偶

2.集合常用函数
    -基本属性和常用操作
        (1)获取集合长度
            length
        (2)获取集合大小
            size
        (3)循环遍历
            foreach
        (4)迭代器
            iterator
        (5)生成字符串
            mkString
        (6)是否包含
            contains
    -衍生集合
        (1)获取集合的头
            head
        (2)获取集合的尾(不是头的就是尾)
            tail
        (3)集合最后一个数据
            last
        (4)集合初始数据(不包含最后一个)
            init
        (5)反转
            reverse
        (6)取前(后)n个元素
            take|takeRight
        (7)去掉前(后)n个元素
            drop|dropRight
        (8)并集
            union
        (9)交集
            intersect
        (10)差集
            diff
        (11)拉链
            zip
        (12)滑窗
            sliding

    -集合计算初级函数
        (1)求和
            sum
        (2)求乘积
            product
        (3)最大值
            max
        (4)最小值
            min
        (5)排序
            sorded|sortBy|sortWith

    -集合计算高级函数
        (1)过滤 filter(函数:指定过滤条件) 
             遍历一个集合并从中获取满足指定条件的元素组成一个新的集合

        (2)转换/映射  map
                     
        (3)扁平化 flatten   :::
            将集合中元素由整体转换为个体的过程

        (4)扁平映射  flatMap  
            先映射再进行扁平化处理

        (5)分组    gruopBy
            按照一定的分组规则,将集合中的元素放到不同的组中

        (6)简化|规约
            >对集合内部元素之间进行聚合 
            >reduce   聚合的数据类型一致
            >reduceLeft|reduceRight     聚合的数据类型可以不一致

        (7)折叠
            >对外部元素和集合内部元素之间进行聚合
            >fold     聚合的数据类型一致
            >foldLeft|foldRight        聚合的数据类型可以不一致

    -两个map集合之间的合并

你可能感兴趣的:(Scala)