Scala

Scala

  • 配置环境
  • 基础
  • 注意事项
  • 输出的三种方式
  • 变量创建
  • 数据类型
    • 整数类型Int
    • 浮点型
    • 字符类型
    • Boolean
    • Unit、Null、Nothing
    • 值类型转换
      • 强制转换
    • 标识符和命名规范
  • 核心运算符
    • 算数运算符
    • 关系运算符
    • 逻辑运算符
    • 赋值运算符
    • 位运算
    • 键盘输入
  • 程序流程控制
    • 分支流程
    • for循环
      • for循环控制
        • 循环守卫
        • 引入变量
        • for循环控制
        • for返回值
    • while循环
  • 函数
      • 困难部分
    • 递归函数
    • 惰性函数
    • 异常捕获
  • 最基本的面向对象
    • Scala包和Java包的区别
    • import
    • 访问权限
      • Java访问权限
      • Scala中访问权限
    • 伴生对象
    • 构造方法
    • 抽象类与继承
      • JVM动态访问机制
      • 重点(JVM的动态绑定)
    • 构造方法值赋给对象
    • 特质(类似于Java中接口)
  • 隐式转换
  • 集合
  • Seq
    • List和ListBuffer
    • 队列
  • Set
  • Map
  • 元组
    • 集合常用功能
      • WordCount
      • 交集并集差集、关联
        • zip关联

配置环境

配置$SCALA_HOME
在配置PATH
Scala_第1张图片
IDEA自带的下载后
Scala_第2张图片
remark一下,之后创建类即可

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

基础

可以通过编译后执行的方式,也可以通过直接执行的方式(编译的方式对用户透明的)
Scala_第3张图片

注意事项

1.源文件以.scala为扩展名
2.Scala程序的执行入口为main()函数
3.Scala语言严格区分大小写
4.Scala方法由多条语句构成,每个语句后不需要加分号(如果多条语句写在一行,除了最后一条语句,剩下的都要加)

输出的三种方式

object hello {
  def main(args: Array[String]): Unit =  {
    // 声明几个变量
    var name = "yyx"
    var age = 10
    var sex = '男'
    println("name="+name + "  age="+age +"  sex="+sex)
    printf("name = %s,age = %d ,sex = %s+\n",name,age,sex)// 数字为%d,多了个加号
    println(s"name=$name+age=$age+sex=$sex")
  }
}

Scala_第4张图片

变量创建

var|val 变量名 [:变量类型] = 变量值
  • 声明变量时,类型可以省略(类型推断
  • 类型确定后,不能修改(强数据类型语言
  • 在声明、定义一个变量时,可以使用val或者var来修饰,var修饰的变量可以改变,val修饰的变量不可变
  • val修饰的对象属性在编译后,相当于加上final
  • var修饰的对象引用可以改变,val修饰的则不可变,但是对象的状态(值)却是可以改变的。(自定义对象、数组、集合)
  • 声明变量时,必须有初始值

数据类型

  • Scala与Java有相同的数据类型,但在scala中数据类型都是对象,没有java的基本数据类型
  • Scala数据类型分为两大类 AnyVal(值类型) 和 AnyRef(引用类型), 注意:不管是AnyVal还是AnyRef 都是对象。
    Scala_第5张图片
    scala数据类型列表
    Scala_第6张图片

整数类型Int

Scala_第7张图片

var i:Byte = 10
    var d:Int = 100
    println(i+d)
    110

使用细节:
1.各整数类型有固定的表数范围和字段长度,不受OS影响,以保证可移植性。
2.Scala的整型常量\字面量默认为Int型,声明Long型常量\字面量需要加“L”或‘l’
3.Scala程序中变量常声明为Int型,除非不足以表示一个数,才使用Long

浮点型

浮点型可用来表示一个小数,如123.4f,7.8,等
浮点型分类:
在这里插入图片描述
Scala浮点型默认为Double型,声明Float型常量需加“f”或“F”
浮点型常量有两种表示方式:

十进制:5.12,512.5f
科学计数法形式:5.12e2=> 512*100

通常情况下,应该使用Double型,因为比Float更加精确


字符类型

Char,16位无符号Unicode字符(两个字节),区间为U+0000到U+FFFF
Scala_第8张图片
Scala_第9张图片
字符常量时用单引号
Scala也允许使用转义字符“\”
可以直接给Char赋值一个整数,然后输出时,会按照对应的unicode字符输出
Scala_第10张图片
Char可以进行运算,但是要用Int接
Scala_第11张图片
Scala_第12张图片


Boolean

只允许true和false
占1个字节
适用于逻辑运算(和Java相同)

Unit、Null、Nothing

Scala_第13张图片
1.NUll类只有一个实例对象,null,类似于Java中的null引用。null可以赋值给任意引用类型(AnyRef),但是不能赋值给值类型(AnyVal:如Int、Fault等)
2.Unit类用来标识过程,也就是没有明确返回值的函数,类似于Java方法中void
在这里插入图片描述
3.Nothing,可以作为没有正常返回值的方法的返回值类型,很明确的告诉你,这个方法不会正常返回
在这里插入图片描述


值类型转换

Scala_第14张图片

当Scala程序在进行赋值或者运算时,精度小的类型自动转为精度大的数据类型。自动转换数据类型(隐式转换)

有多种类型的数据混合运算时,系统会首先将所有的数据转换为容量最大的那种数据类型,然后再进行计算
当我们把精度(容量)大的数据类型赋值给精度小的数据类型时,就会报错,繁殖会进行自动转换
(byte, short) 和 char之间不会相互自动转换。byte,short,char 他们三者可以计算,在计算时首先转换为int类型。
自动提升原则: 表达式结果的类型自动提升为 操作数中最大的类型


强制转换

将容量大的数据类型转换为容量小的数据类型。使用时要加上强制转换函数,但可能造成精度降低或溢出

java  :  int num = (int)2.5
scala :  var num : Int = 2.7.toInt //灵活

强转符号只针对于最近的操作数有效,往往会使用小括号提升优先级
Scala_第15张图片
Char类型可以保存 Int的常量值,但不能保存Int的变量值,需要强转
Byte和Short类型在进行运算时,当做Int类型处理。


标识符和命名规范

Scala_第16张图片

核心运算符

算数运算符

和java一样
Scala_第17张图片
要注意的是,Scala没有++、–等操作,只能re = re +1这样赋值

关系运算符

Boolean的赋值只能是true或者false
其它的和Java一样,使用陷阱: 如果两个浮点数进行比较,应当保证数据类型一致.

逻辑运算符

Scala_第18张图片

赋值运算符

Scala_第19张图片
二进制部分
Scala_第20张图片
这部分和Java一样
小测试:有两个变量,a和b,要求将其进行交换,但是不允许使用中间变量,最终打印结果

package chapter03

object Demo01 {
  def main(args: Array[String]): Unit = {
    var a = 10
    var b = 20
    a = a + b
    b = a - b
    a = a - b
    print(a+"\t")
    print(b)

  }
}

在这里插入图片描述

位运算

Scala_第21张图片
和Java一样
且Scala不支持三元运算符
小案例:
求两个数的最大值
求三个数的最大值

object Demo01 {
  def main(args: Array[String]): Unit = {
    var a = 10
    var b = 20
    var c = 100
    println(max(a,b))
    println(max(a,b,c))
  }
  def max(b: Int,a: Int):Int={
    if(a>b){
      return a
    }else{
     return b
    }
  }
  def max(a: Int,b: Int,c: Int):Int={
    var t = max(a,b)
    if(t>c){
      return t
    }else{
      return c
    }

  }
}

键盘输入

import scala.io.StdIn

object Demo02 {
  def main(args: Array[String]): Unit = {
    // 测试键盘输入
    println("name")
    var name = StdIn.readLine();
    println(name)
  }
}

Scala_第22张图片

程序流程控制

顺序控制(正常写)
分支控制(单分支, 双分支,多分支)
循环控制(for、while)

分支流程

总体上和Java类似,不过要注意几个点

    var flag : Boolean = true
    if(flag){
      print("t")
    }else{
      print("s")
    }

在scala中,每一个语句都会有返回值,具体返回结果的值取决于满足条件的代码体的最后一行内容.

var flag : Boolean = false
    val value = if (flag) {
      "aaa"
    }else {
      "bbb"
    }
    print(value)

在scala中,没有switch语法
要通过if语法一层一层表达

for循环

package chaper04

object TestFor {
  def main(args: Array[String]): Unit = {
    for(i <- 0 to 5){
      println(i)
    }
  }
}
0
1
2
3
4
5

左包右不包

for (i <- 0 until 5){
      print(i+"\t")
    }
0	1	2	3	4	

设置步长
起始until的源数据是这样的
设置了一个Range(表示范围的类)

def until(end : scala.Int) : scala.collection.immutable.Range = { /* compiled code */ }

那我们就可以这样表示步长

for(i <- Range(0,10,2)){
      print(i+"\t")
    }
    0	2	4	6	8	

for循环控制

循环守卫

保护式为true则进入循环体内部,为false则跳过,类似于continue

for(i <- Range(0,6) if i!=4 ){
      print(i)
    }
    01235
引入变量

(可以只用一行代码for打出九层塔)

for(i <- Range(0,6) if i!=4; j=4-i){
      println(i+"iiii")
      println(j+"jjjj")
    }
    0iiii
4jjjj
1iiii
3jjjj
2iiii
2jjjj
3iiii
1jjjj
5iiii
-1jjjj

for的小括号可以变为大括号

for循环控制
 for(i<-Range(0,4) if i != 2;j <-Range(0,4) if j != 2){
      print(i+j + "\t")
    }
0	1	3	1	2	4	3	4	6	
for返回值
val unit = for (i <- Range(0, 4) if i != 2; j <- Range(0, 4) if j != 2) {
  print(i + j + "\t")
  "aaa"
}
print(unit)
0	1	3	1	2	4	3	4	6	()

我们可以改一下,用yield关键字(使结果加入一个Vector)(不能用大括号)

val unit = for (i <- Range(0, 4) if i != 2; j <- Range(0, 4) if j != 2)yield i + j
    print(unit)
    Vector(0, 1, 3, 1, 2, 4, 3, 4, 6)

while循环

scala中break实际上是抛出了一个异常,来终止循环

var flag:Boolean = true;
    var i = 0
    while(flag){
      print(i)
      i+=1
      if(i==9) Breaks.break()
    }
    print("循环结束")
    012345678Exception in thread "main" scala.util.control.BreakControl

注意导入类

package chaper04
// 用来导入
import scala.util.control.Breaks._

object TestWhile {
  def main(args: Array[String]): Unit = {
    var flag:Boolean = true;
    var i = 0
    breakable {//breakable要将整个while套在里面
      while(flag){
        print(i)
        i += 1
        if (i == 9){break()}
      }
    }
    print("循环结束")
  }
}

函数

package chapter05

object TestFunction {
  def main(args: Array[String]): Unit = {
    // 在方法中创建scala方法,不能用相同的类名,在方法外可以
    def test():Unit={
      print("sssss")
    }
    test()
  }

  // 创建scala方法
  def max(b: Int, a: Int): Int = {
    if (a > b) {
      return a
    } else {
      return b
    }
  }

  def max(a: Int, b: Int, c: Int): Int = {
    var t = max(a, b)
    if (t > c) {
      return t
    } else {
      return c
    }
  }
}

scala的function和java的method最大的区别是method依靠类,而function不需要
scala中function可以省略很多东西

    // 有很多可省
//    def sum(b: Int,a: Int) :Int = {
//      return a+b
//    }
    // 可以省略return和返回值类型()必须将return的卸载最后一行
//    def sum(b:Int,a:Int) ={
//      a+b
//    }
    // 省略大括号
    //def sum(b:Int,a:Int) =a+b

省略=

def test(){
      print("a")
    }
    test()

省略()
此时{}不能省略

// 在函数无参时,可以省略小括号
  def test {print("a")}
  test// 调用时,没有参数,()可加可不加

匿名函数
直接运行即可,不需要调用

//匿名函数
    ()->print("xxxxxxxxx")

设置可变参数

// 设置可变参数
    def test(name:String*): Unit ={
      println(name)
    }
    test("aaa","bbb")
    test()
    
    WrappedArray(aaa, bbb)
	List()

设置默认值

def test(name : String = "zhang"): Unit ={
      print(name)
    }
    test()
    zhang

带名参数(防止给错)

    // 设置参数默认值
    def test(name2:String,name : String = "zhang"): Unit ={
      print(s"${name},${name2}")
    }
//    test("zhang","sss")//此时容易给错
    test(name2="sss")//必须要设置了默认值的

困难部分

Scala是完全面向对象的、同时也是完全面向函数式编程的
函数作为另一个函数的参数

    def test(string: String):Unit={
    print(string)
  }
    def p(): Unit ={
      print("p运行了")
      test _
    }
    p()
    p运行了

同时注意,如果调用时没有加括号

 def a = 100
 def b = a _
 print(b())//100
 print(b)//
    //将函数作为另一个函数的参数,需要_
    def test(string: String):Unit={
    print(string)
  }
    def p() ={// def p():String=>Unit ={}可省
      print("p运行了")
      test _
    }
    p()("sss")

以上代码存在闭包:第一个函数的参数的生命周期被改变,即其参数可以被第二个函数使用
结果相同

  def p():String=>Unit ={
    print("p运行了")
    def test(string: String):Unit={
      print(string)
    }
    test _
  }

函数柯里化

    // 函数柯里化
    def te(b : Int)(a: Int): Int ={
      a*b
    }

    println(te(6)(5))

使用匿名函数改善

    //使用匿名函数改善代码
    def test(f:()=>Unit): Unit ={//表示参数列表为空,返回值为空的函数
      f()//f函数
    }
    test(()=>{print("xx")})
  }

递归函数

斐波那契数列(递归不能省返回值类型)

object FunctionRe {
  def main(args: Array[String]): Unit = {
    println(fib(20))
  }
  // 来实现一个递归
  def fib(i:Int):Int={
    if(i<2)  i;
    else {
      fib(i-1)+fib(i-2)
    }
  }
}

惰性函数

lazy关键字(只能对val的变量使用)

object FunctionRe {
  def main(args: Array[String]): Unit = {
    lazy val a:Int = test(5)
    println("++++++++++++++++")
    println("++++++++++++++++")
    println("++++++++++++++++")
    println("++++++++++++++++")
    println("++++++++++++++++")
    println(a)
  }
  def test(int: Int): Int ={
    println("test在运行")
    int
  }
}
++++++++++++++++
++++++++++++++++
++++++++++++++++
++++++++++++++++
test在运行
5

如果去掉关键字

test在运行
++++++++++++++++
++++++++++++++++
++++++++++++++++
++++++++++++++++
++++++++++++++++
5

异常捕获

java的异常捕获

public class TestException {
    public static void main(String[] args) {
        try {
            System.out.println(1/0);
        }catch (ArithmeticException e){
            System.out.println("ArithmeticException");
            e.printStackTrace();
        }catch (Exception e){
            System.out.println("Exception");
            e.printStackTrace();
        }finally {
            System.out.println("finally");
        }
    }
}
ArithmeticException
finally
java.lang.ArithmeticException: / by zero
	at TestException.main(TestException.java:4)

当然,和顺序有关
这样在Java中会报错

public class TestException {
    public static void main(String[] args) {
        try {
            System.out.println(1/0);
        }catch (Exception e){
            System.out.println("Exception");
            e.printStackTrace();
        }catch (ArithmeticException e){
            System.out.println("ArithmeticException");
            e.printStackTrace();
        }finally {
            System.out.println("finally");
        }
    }
}

Scala中

object ExceptionTest {
  def main(args: Array[String]): Unit = {
    try {
      println(1/0)
    }catch {
      case ce:ArithmeticException=>println("ArithmeticException")
      case ce:Exception=>println("Exception")
    }finally {
      println("finally")
    }
  }
}
ArithmeticException
finally

但是如果把顺序换一下也不会报错,并且结果:

object ExceptionTest {
  def main(args: Array[String]): Unit = {
    try {
      println(1/0)
    }catch {
      case ce:Exception=>println("Exception")
      case ce:ArithmeticException=>println("ArithmeticException")
    }finally {
      println("finally")
    }
  }
}
Exception
finally

最基本的面向对象

package chapter060708

object TheFirstDemo {
  def main(args: Array[String]): Unit = {
    var user:User = new User
    user.name = "yyx"
    user.age = 18
    println(user.getName())
  }
}
class User{
  var name : String = _
  var age : Int = _
  def getName():String={
    name
  }
}

Scala包和Java包的区别

1.Scala中包可以多次声明,声明后,下面的对象在最后的那个包中,即:

package chapter060708

package test1

class PackageTest {

}
那么这个对象就在chapter060708.test1中

2.Scala中所有语法都可以进行嵌套

package chapter060708

package test1{
  package test2{
    class T{
      
    }
  }
}

class PackageTest {

}

2.package可以进行多次嵌套
此时,PacketageTest不在test1中,而T在test2中,且包嵌套中方法和变量只能在类中声明

package test1{
  package test2{
    class T{
      def test(i:Int):Unit ={}
    }
    
  }
}
class PackageTest 
}

3.Scala中,父包中的类子包也可以调用
4.引用了包对象的概念

package object test{
      val i:Int = 1
    }

import

class TestImport {
 // scala中import用来导入类、包,且可以在任意地方,但是只能用在下一行
  import java.util.Date
  var D:Date = new Date();
  
}

import java.util._


class TestImport {
  // scala中import想导入包全部类,不用*而是_
  var D:Date = new Date();
  var M:HashMap[Nothing, Nothing] = new HashMap();
}

import java.util.{Date,HashMap}
class TestImport {
  var D:Date = new Date();
  // scala中import某个包中大部分类,可以用{}
  var M:HashMap[Nothing, Nothing] = new HashMap();
}

import java.util
class TestImport {
  var D:util.Date = new util.Date();
  // scala中import某个包,调用类时要特别点
  var M:util.HashMap[Nothing, Nothing] = new util.HashMap();
}

package chapter060708

// 用特殊的方式来隐藏指定的类{类名=>_}
import java.util.{Date=>_}
import java.sql.Date
class TestImport {
  var D:Date = new Date(0);
}

// 当包名和java相同时
package java{
  package util{
    class Map{
    }
  }
}
import _root_.java.util._
object TestImport {
  def main(args: Array[String]): Unit = {
    // 想要调用正常的java.util.map
    var v:HashMap[Nothing,Nothing] = new HashMap()
    println(v)
  }
}
//{}
// 修改导入类名
import _root_.java.util.{HashMap=>JavaHashMap}
object TestImport {
  def main(args: Array[String]): Unit = {
    // 想要调用正常的java.util.map
    var v:JavaHashMap[Nothing,Nothing] = new JavaHashMap()
    println(v)
  }
}

访问权限

先不说scala,Java中,Object的clone()方法时project的,也就是同包、同类、其子类可以调用,那么如下例子:

Java访问权限

public class TestPro {
    public static void main(String[] args) {
        A a = new A();
        a.clone();
    }
}
class A{

}

此时调用a.clone()会报错,因为:
Scala_第23张图片
我们此时调用的时A中Object中的clone,可是这个Object不是我们TestPro的父类,所以,我们要在A中重写方法,才能调用

public class TestPro {
    public static void main(String[] args) throws CloneNotSupportedException {
        A a = new A();
        a.clone();
    }
}
class A{
    @Override
    protected Object clone() throws CloneNotSupportedException {
        return super.clone();
    }
}

Scala中访问权限

package chapter060708
package P1{
  package P2{
    class Us{
      var usname:String=_
      private var password:String="123456"
      private[P3] var address:String = "xxx"
      protected var age:Int = 18;
    }
  }
  package P3{

    import chapter060708.P1.P2.Us

    class test{
      def get():Unit={
        var us:Us = new Us;//需要倒类
        us.usname="x"
      }
    }
  }
}

此时能访问的有address和username
Scala_第24张图片
但是将包名修改一下,address就不能访问了

private[P2] var address:String = "xxx"

password属于私有属性,无法访问
age只有Us子类才能访问
Scala_第25张图片
总结:
public 是默认权限,没有关键字
protected是只有子类才能访问,同包不能访问
private只有本类能调用,是私有的
private[包名] 指定包可以调用

伴生对象

class Student {
  var sname:String=_
  private var age:Int=_
}
// student的伴生对象
object Student{
  // 伴生对象中可以调用伴生类的私有属性
  new Student().age=1
  
}

并且可以通过伴生类(apply方式)

class Student {
  var sname:String=_
  private var age:Int=_
}
// student的伴生对象
object Student{
  // 伴生对象中可以调用伴生类的私有属性
  //new Student().age=1
  def apply(): Student = new Student()
}
object TestStudent {
  def main(args: Array[String]): Unit = {
    //通过伴生对象来创建伴生类对象
    var student=Student()
    println(student)
  }
}
//chapter060708.Student@1888ff2c

为了符合JavaBean的标准(迎合多种框架),Bean类可以使用 @BeanProperty注解
是对象生成公共的set、get方法

object ScalaField {
  def main(args: Array[String]): Unit = {
    val user:User = new User
    println(user.name)
    user.setAddress("sss")
    println(user.getAddress)
  }
}
class User{
  // 相当于定义了一个private String name,并且定义了public的getter和setter
  var name:String = _
  // 相当于用final定义的变量,并且,只有一个get方法
  val sex:String = "男"
  // 该属性在外部无法访问,因为该属性是一个私有的,只在内部private了get和set方法
  private var age:Int = 18
  // 为了符合java的规范,我们可以调用一个注解来实现get和set方法
  @BeanProperty var address:String = "xxx"
}

构造方法

Scala中,构造方法分为两类:主构造方法、辅助构造方法
辅助构造方法可以用来创建对象,但是必须要调用主构造方法
Scala是面向函数编程语言,所以类也是一个函数,并且,类所代表的函数起始就是这个类的构造方法,默认情况下,Scala提供无参构造,所以类后的()可以省略
在类后声明的构造方法为主构造方法,在类中声明的构造方法为辅助构造方法
1.空构造方法(默认构造方法)

object TestPu {
  def main(args: Array[String]): Unit = {
    var teacher:Teacher = new Teacher()
    teacher.name = "LL"
    println(teacher.name)
  }
}
class Teacher(){// ()可省
  var name:String=_
}

2.带参数的构造方法

object TestPu {
  def main(args: Array[String]): Unit = {
    var teacher:Teacher = new Teacher("调用主构造方法")
    teacher.name = "LL"
    println(teacher.name)
  }
}
class Teacher(s:String){
  var name:String=_
}
// LL

3.使用辅助构造方法(this)

package chapter060708

object TestPu {
  def main(args: Array[String]): Unit = {
    var teacher:Teacher = new Teacher(" ")// 调用主构造方法
    var teacher1:Teacher = new Teacher()// 调用辅助构造方法
    println(teacher)
    println(teacher1)
  }
}
class Teacher(s:String){// 类体、构造方法体
  println("调用主构造方法")
  var name:String=_
  def this(){
    this("ssss")//在辅助构造方法中调用主构造方法
    println(s)
  }
}
/*
调用主构造方法
调用主构造方法
ssss
chapter060708.Teacher@1888ff2c
chapter060708.Teacher@35851384
*/

辅助构造方法调用其它构造方法

package chapter060708

object TestPu {
  def main(args: Array[String]): Unit = {
    var teacher1:Teacher = new Teacher("ss",5)// 调用辅助构造方法
    println(teacher1)
  }
}
class Teacher(s:String){// 类体、构造方法体
  println("调用主构造方法")
  var name:String=_

  def this(){
    this("在辅助构造方法1中调用主构造方法")//在辅助构造方法中调用主构造方法
    println(s)
  }

  def this(string: String,int: Int){
    this()//调用辅助构造方法
    println("在辅助构造方法2中调用辅助构造方法1")
  }
}
/*
调用主构造方法
在辅助构造方法1中调用主构造方法
在辅助构造方法2中调用辅助构造方法1
chapter060708.Teacher@1888ff2c
*/

抽象类与继承

只继承时

object TestAbstract {
  def main(args: Array[String]): Unit = {
    var user:Ub = new Ub
    //user.name = "sss" 不能修改属性
    println(user.name)
  }
}
class Person{
  val name:String = "yyy" // 属性无法改变
  def test():Unit={// 声明方法
    println("父类方法")
  }

}
class Ub extends Person{
  // 声明的name属性也必须是val
  var age:Int = _
  //override def test(): Unit = {println("继承父类方法")}
}
// yyy

重写父类name时

object TestAbstract {
  def main(args: Array[String]): Unit = {
    var user:Ub = new Ub
    //user.name = "sss" 不能修改属性
    println(user.name)
  }
}
class Person{
  val name:String = "yyy" // 属性无法改变
  def test():Unit={// 声明方法
    println("父类方法")
  }

}
class Ub extends Person{
  // 声明的name属性也必须是val,并且要重写
  override val name:String = "sss"
  var age:Int = _
  //override def test(): Unit = {println("继承父类方法")}
}
/*
sss
*/

当不重写父类方法时

object TestAbstract {
  def main(args: Array[String]): Unit = {
    var user:Ub = new Ub
    user.test()
    //user.name = "sss" 不能修改属性
    println(user.name)
  }
}
class Person{
  val name:String = "yyy" // 属性无法改变
  def test():Unit={// 声明方法
    println("父类方法")
  }

}
class Ub extends Person{
  // 声明的name属性也必须是val
  var age:Int = _
  //override def test(): Unit = {println("继承父类方法")}
}
/*
父类方法
yyy

*/

重写父类方法:

object TestAbstract {
  def main(args: Array[String]): Unit = {
    var user:Ub = new Ub
    user.test()
    //user.name = "sss" 不能修改属性
    println(user.name)
  }
}
class Person{
  val name:String = "yyy" // 属性无法改变
  def test():Unit={// 声明方法
    println("父类方法")
  }

}
class Ub extends Person{
  // 声明的name属性也必须是val,并且要重写
  override val name:String = "sss"
  var age:Int = _
  override def test(): Unit = {println("继承父类方法")}
}
/*
继承父类方法
sss
*/

当父类改为抽象类时
abstract实现抽象类
有抽象方法的类一定是抽象类,抽象类不一定都是抽象方法
子类必须实现父类的抽象方法
实现子类实现父类抽象方法override可省,其他情况都不可以

object TestAbstract {
  def main(args: Array[String]): Unit = {
    var user:Ub = new Ub
    user.test()
    user.test1()
    //user.name = "sss" 不能修改属性
    println(user.name)
  }
}
abstract class Person{
  val name:String = "yyy" // 属性无法改变
  def test():Unit={// 声明方法
    println("父类方法")
  }
  // 声明抽象方法,不需要关键字,没有方法体即可
  def test1()

}
class Ub extends Person{
  // 声明的name属性也必须是val,并且要重写
  override val name:String = "sss"
  var age:Int = _
  override def test(): Unit = {println("继承父类方法")}
  // 子类必须实现抽象方法
  override def test1(): Unit = {println("父类抽象方法")}
}
/*
继承父类方法
父类抽象方法
sss
*/

JVM动态访问机制

父类属性要子类重写时,一定要使用val声明,否则运行时Error

object TestAbstract {
  def main(args: Array[String]): Unit = {
    // 多态
    var person:Person = new Ub()
    println(person.name)
  }
}
abstract class Person{
  // 子类要重写的属性,一定是val的,否则运行时报错
  var name:String="aaa"
}
class Ub extends Person{
  override var name:String="bbb"
}

在这里插入图片描述
改为val后

object TestAbstract {
  def main(args: Array[String]): Unit = {
    // 多态
    var person:Person = new Ub()
    println(person.name)
  }
}
abstract class Person{
  // 子类要重写的属性,一定是val的,否则运行时报错
  val name:String="aaa"
}
class Ub extends Person{
  override val name:String="bbb"
}
// bbb

声明抽象属性

abstract class Person{
  // 子类要重写的属性,一定是val的,否则运行时报错
  val name:String="aaa"
  // 声明抽象属性,不初始化即可
  var age:Int
}
class Ub extends Person{
  override val name:String="bbb"
  var age: Int = _ // override 可省
}

重点(JVM的动态绑定)

先通过Java来看

public class TestDynamic {
    public static void main(String[] args) {
        AAA a = new BBB();
        System.out.println(a.getI());// 输出结果为22,但是如果将BBB类中方法注释掉,输出11
    }
}
class AAA{
    public int i = 1;

    public int getI() {
        return i + 10;
    }
}
class BBB extends AAA{
    public int i = 2;

//    public int getI() {
//        return i + 20;
//    }
}

Scala_第26张图片

构造方法值赋给对象

package chapter060708
// Scala 中声明构造方法
object TestPu2 {
  def main(args: Array[String]): Unit = {
    var s:Stu = new Stu("SSS");
    println(s.name)
  }
}
class Stu(){
  var name : String =_
  def this(string: String){
    this()
    name = string
  }
}
// SSS

这样写太麻烦了,我们改一下
结果相同

package chapter060708
// Scala 中声明构造方法
object TestPu2 {
  def main(args: Array[String]): Unit = {
    var s:Stu = new Stu("SSS");
    println(s.name)
  }
}
class Stu(var name:String){
  
}

特质(类似于Java中接口)

在java中,接口声明sout会执行错误,但是scala中,trait中声明print方法不会报错,可以把它看作一个特殊的类,不过不能创建对象
trait的执行顺序:

object TestTrait1 {
  def main(args: Array[String]): Unit = {
    new Teachersss()
  }
}
trait People{
  println("trait....")
}
class Headmaster{
  println("person class....")
}
class Teachersss extends Headmaster with People{
  println("class....")
}
/*
person class....
trait....
class....
*/

当父类子类都实现trait时

object TestTrait1 {
  def main(args: Array[String]): Unit = {
    new Teachersss()
  }
}
trait People{
  println("trait....")
}
class Headmaster extends People {
  println("person class....")
}
class Teachersss extends Headmaster with People{
  println("class....")
}
/*
trait....
person class....
class....
*/

trait只会实现一次,并且在实现类之前就会先输出trait的结果
特值可以继承其它特值
特质也可使设置属性

object TestTrait1 {
  def main(args: Array[String]): Unit = {
    var t = new Teachersss()
    println(t.name)
  }
}
trait People{
  val name:String = "xxx"
  println("trait....")
}
class Headmaster extends People {
  println("person class....")
}
class Teachersss extends Headmaster with People{
  println("class....")
}
/*
trait....
person class....
class....
xxx
*/

动态混入

object TestTrait1 {
  def main(args: Array[String]): Unit = {
    // 特质动态混入
    var t = new Te with People
    
  }
}
trait People{
  println("success")
}
class Te{
  // 假设Te要实现people的功能,但是还不能继承
}
/*
success
*/

特值可以继承独立的类、Java接口

object TestTrait1 {
  def main(args: Array[String]): Unit = {
    // 特质动态混入
    var t = new Te
    val test = t.test
    println(test)
  }
}
trait People {
  this:Exception =>
  def test: Unit ={
    println("success")
    // 想要获取异常的方法,继承可以实现
    this.getMessage
  }
}
class Te extends Exception with People {
  // 假设Te要实现people的功能,但是还不能继承
}

隐式转换

一下这种情况会报错

object TestImplicit {
  def main(args: Array[String]): Unit = {
    // 隐式转换
    var int:Int = 5.0
  }
}

隐式转换后
关键字:implicit

object TestImplicit {
  def main(args: Array[String]): Unit = {
    // 隐式转换
    implicit def test(double: Double): Int = {
      double.toInt
    }
    var int:Int = 5.0
    println(int)
  }
}
/*
5
*/

注意事项:
隐式转换方法自动调用,与方法名无关,但是与签名(参数类型、返回值类型)有关,同一个签名的隐式转换方法只能有一个
还可以用来增加方法:
在如下mysql类中添加delete方法:

object TestImplicit {
  def main(args: Array[String]): Unit = {
    
  }
}
class MySql{
  def insert(): Unit ={
    println("insert")
  }
}
class DB{
  def delete(): Unit ={
    println("delete")
  }
}

除了继承的方式,还可以使用隐式转换的方法:

object TestImplicit {
  def main(args: Array[String]): Unit = {
    implicit def addDelete(mySql: MySql): DB = {
      new DB
    }
    var s:MySql = new MySql
    s.delete()
  }

  
}
class MySql {
  def insert(): Unit ={
    println("insert")
  }
}
class DB{
  def delete(): Unit ={
    println("delete")
  }
}

隐式值
如下代码

object TestImplicit {
  def main(args: Array[String]): Unit = {
    
    def test(name:String = "xxx"):Unit={// 默认值
      println(name)
    }
    test()
  }
}

如果某天我们想换一个默认值:
调用函数时不能加括号

object TestImplicit {
  def main(args: Array[String]): Unit = {
    implicit var name:String = "yyx"
    def test(implicit name:String = "xxx"):Unit={// 默认值
      println(name)
    }
    test
  }
}

集合

Scala中集合分为可变集合和不可变集合
可变集合在包scala.collection.mutable
不可变集合在包scala.collection.immutable
Scala对几乎所有集合类都有可变和不可变版本(默认用不可变版本)

1.不可变集合:scala不可变集合,就是这个集合本身不能变内存地址不能变(类似java的数值,是不可以动态增长的)
2.可变集合:可变集合(ArrayList , 是可以动态增长的) 就是这个集合的本身是可以变的内存地址可变,因为成为一个新的集合

不可变集合
Scala_第27张图片
可变集合
Scala_第28张图片

不可变数组
所谓不可变,不是数值不能变,而是地址(类似String)

object TestArray {
  def main(args: Array[String]): Unit = {
    // 不可边数组
    val ints: Array[Int] = Array(1, 2, 3)
    println("不可变数组长度"+ints.length)
    // 输出对应索引位值的值
    val i = ints.apply(2)
    println(i)
    // 添加元素,已经变为一个新数组ints1
    val ints1 = ints :+ 5
    val ints2 = 5+:ints
    // 更新元素
    ints.update(0,55)
    // 遍历元素
    println(ints.mkString(" "))
    println(ints1.mkString("|"))
    println(ints2.mkString("|"))
    for (i<-ints1){
      println(i)
    }

    def testxxxx(int: Int)= {
      print("foreach遍历" + int + "  ")
    }
    /*
    ints1.foreach(testxxxx)
    ints2.foreach((i:Int)=>{println(i)})//简单点
    ints2.foreach((i)=>{println(i)})//再简单点
    ints2.foreach({println(_)})//再再简单点
    ints2.foreach({println})//再再再简单点
     */
  }
}

不可变数组:

import scala.collection.mutable.ArrayBuffer

object TestArray {
  def main(args: Array[String]): Unit = {
    // 可变数组
    val arrayBuffer = ArrayBuffer(1, 2, 3, 4)
    // 访问
    println(arrayBuffer(2))//3
    // 修改
    arrayBuffer(0) = 9
    println(arrayBuffer(0))//9
    // 增
    //val ints = arrayBuffer :+ 5//:+会返回一个新的arrayBuffer
    //arrayBuffer.insert(4,6)// 按索引添加,如果长度不够会扩容,返回值为空(在当前数组上修改)
    //arrayBuffer+=(5,6)//添加多个元素
    // 删除元素
    //arrayBuffer.remove(0) 按照索引删除
    arrayBuffer.remove(0,2) // 从当前索引开始删除多少位
    // 遍历
    println(arrayBuffer.mkString("|"))
    arrayBuffer.foreach(println) // foreach遍历和不可变数组一样
  }
}

可变数组与不可变数组转换:

val buffer = arrayBuffer.toArray.toBuffer

Seq

List和ListBuffer

import scala.collection.mutable.ListBuffer

object TestList {
  def main(args: Array[String]): Unit = {
    // 创建一个List

    val ints = List(1, 2, 3)
    // List也有空集合

    //println(Nil)
    //println(List())
    // 访问list中元素

    //println(ints(0))
    // 添加元素并返回新List

    //val ints1: List[Int] = ints :+ 4
    //println(ints1)
    //List追加
    val ints1 = 6 :: ints
    val ints2 = List(4,5,6)
    val ints3 = ints2:::ints// 如果不使用三个:会添加一整个List(返回的List存储类型也会变为object)
    println(ints3.mkString("|"))
    // 可变的List
    val ints4 = ListBuffer(1, 2, 3, 4)
    val ints5 = ListBuffer(5,6,7,8)
    ints4.insertAll(1, ints5)// 第一个变量为插入位置
    println(ints4.mkString("|"))
  }
}

队列

import scala.collection.mutable.Queue

object TestQueue {
  def main(args: Array[String]): Unit = {
    // 创建一个队列,队列要可变
    val ints = Queue(1, 2, 3)
    // 添加数据
    ints.enqueue(5,6)
    // 遍历
    println(ints.mkString("|"))
    // 出队
    //ints.dequeue()队首
    //ints.dequeueAll((i:Int)=>{false})// 是否全部出队
    ints.dequeueFirst((i:Int)=>{true})// 是否队首出队
    println(ints.mkString("|"))
    // 其它
    println(ints.head)// 返回队首
    println(ints.tail)// 返回除了队首以外的元素形成的队列
    println(ints.last)// 返回队列最后的元素
  }
}

Set

无序、不可重复

import scala.collection.mutable

object TestSet {
  def main(args: Array[String]): Unit = {
    // 无序、不可重复
    val set: Set[Int] = Set(1, 2, 3, 4 )
//    for (elem <- set) {
//      print(elem)不可重复
//    }
    // 通过+添加元素都会生成新的Set
    // 但是,可变set有+=方法,不可变set没有此方法
    //val ints = set +(5, 6, 8, 9, 8)
    //println(set.mkString("|"))
    val set1: mutable.Set[Int] = mutable.Set(1, 5, 3, 5, 4)
    set1.+=(5)
    // 删除元素
    val ints2: Set[Int] = set - (1)
    val value: set1.type = set1 -= (5)
    println(set.mkString("|"))
    println(set1.mkString("|"))
    val ints = set +(5, 6, 8, 9, 8)
    val value1: set1.type = set1 ++= ints
    println(set1)// Set(9, 1, 5, 2, 6, 3, 4, 8)
  }
}

set其他操作
Scala_第29张图片

Map

不可变的Map是有序的、可变的Map是无序的

object TestMap {
  def main(args: Array[String]): Unit = {
    // 默认不可变
    val map = Map("a" -> 1, "b" -> 2, "c" -> 3)
    println(map.mkString("|"))
    val map1 = map.+("ss" -> 5)
    println(map.mkString("|"))
    println(map1.mkString("|"))
    // 创建可变map
    val map2 = mutable.Map("a" -> 1, "b" -> 2, "c" -> 3)
    map2.+=("c" -> 4, "d" -> 5, "e" -> 6)
    println(map2.mkString("|"))
    // 获取数据
    println(map2.get("s"))// None
    println(map2.getOrElse("s", "没得"))// 没得
    println(map2.get("a"))// Some(1)
    println(map2.get("a").get)// 1
  }
}

元组

// 类似于一个容器,将不相关的属性连接到一起
    // 创建一个元组,元组中最大只能有22个元素
    val tuple: (String, Int, Double) = ("sss", 1, 5.0)
    // 访问元素
    println(tuple._1)
    println(tuple._2)
    println(tuple._3)
    /*
    sss
    1
    5.0
    */
    //整体遍历
    for (elem <- tuple.productIterator){
      println(elem)// 结果相同
    }

集合常用功能

import scala.collection.immutable

object TestMethodUsU {
  // 常用的List方法
  def main(args: Array[String]): Unit = {
    // 创建List
    val ints: List[Int] = List(6, 7, 5, 6, 78, 5, 1, 5, 4, 3, 5)
    // 最值
    println(s"ints.max=${ints.max}")
    println(s"ints.min=${ints.min}")
    // 所有元素相乘
    println(s"product=${ints.product}")
    // 倒叙集合
    println(s"reserve=${ints.reverse}") // 按照初始顺序倒叙
    // 分组
    println(ints.groupBy(x => x)) // 直接按照值分组
    /*
    Map(5 -> List(5, 5, 5, 5), 78 -> List(78), 1 -> List(1), 6 -> List(6, 6), 7 -> List(7), 3 -> List(3), 4 -> List(4))
     */
    // 排序
    println(ints.sortBy(x => x > 5)) // 按照是否大于5排
    /*
    List(5, 5, 1, 5, 4, 3, 5, 6, 7, 6, 78)
     */
    // 按顺序排
    println(ints.sortWith((x, y) => x > y)) // 左边大于右边=降序
    /*
    List(78, 7, 6, 6, 5, 5, 5, 5, 4, 3, 1)
     */
    // 只输出固定数量元素
    println(ints.take(4)) // List(6, 7, 5, 6)
    // 转换类型:map,想做到输出数字以及数字出现次数
    val tuples: List[(Int, Int)] = ints.map(x => (x, 1))//输出的类型为tuple
    println(tuples) // List((6,1), (7,1), (5,1), (6,1), (78,1), (5,1), (1,1), (5,1), (4,1), (3,1), (5,1))
    // 想做到次数
    val intToTuples: Map[Int, List[(Int, Int)]] = tuples.groupBy(t => t._1) // 分组
    println(intToTuples)
    /*
    Map(5 -> List((5,1), (5,1), (5,1), (5,1)), 78 -> List((78,1)), 1 -> List((1,1)), 6 -> List((6,1), (6,1)), 7 -> List((7,1)), 3 -> List((3,1)), 4 -> List((4,1)))
     */
    // 再次map
    val strings: immutable.Iterable[String] = intToTuples.map(t => t._1 + "--" + t._2.size)
    println(strings)// List(5--4, 78--1, 1--1, 6--2, 7--1, 3--1, 4--1)
  	// reduce
  	val ints3 = List(5, 6, 7, 6, 78,66,55,44,889,564)
    //ints3.reduce((l,r)=>{l+r})// 用于减少结果(对数据每两个进行操作)
    val i = ints3.reduce(_ + _)
    println(i)// 1720
    // 类似的,有一个折叠
    //val i1 = ints3.fold(1000)(_ - _)
    val ints4 = List(1, 2, 3, 4)
    val i = ints4.foldRight(10)(_ - _)
    // reserve => 4,3,2,1
    // foldLeft => (((10-4)-3)-2)-1
    // foldRight => 1-(2-(3-(4-10)))
    println(i)// 8
  }
}

WordCount

object WordCount {
  def main(args: Array[String]): Unit = {
    val list = List("String Hello", "Hadoop Scala", "Hello Int", "MapReduce Hive HBase")
    // wordCount,可是每个单词中间有空格
    val strings = list.flatMap(s => s.split(" "))//扁平化转
    val stringToStrings = strings.groupBy(x => x) // 分组
    val stringToInt: Map[String, Int] = stringToStrings.map(s => (s._1, s._2.size))// 转为map
    // 按降序拍个序,转为List后为List((K,V),(K,V)...)
    val tuples = stringToInt.toList.sortWith((l, r) => {
      l._2 > r._2
    })
    // 遍历
    println(tuples.mkString("|"))
  }
}
/*
(Hello,2)|(Hive,1)|(MapReduce,1)|(Scala,1)|(HBase,1)|(Int,1)|(Hadoop,1)|(String,1)
*/

```java
object WordCount {
  def main(args: Array[String]): Unit = {
    // 新的WordCount测试
    val list = List(("hello world", 7), ("hello hadoop", 5), ("hello MR", 5))
    val tuples = list.flatMap(l => {
      // 首先,找到单词
      val word = l._1.split(" ")
      val count = l._2
      word.map(w => (w, count))
    }) // List((hello,7), (world,7), (hello,5), (hadoop,5), (hello,5), (MR,5))
    // 分组
    val stringToTuples = tuples.groupBy(s => s._1) //按照第一位分组
    // Map(hadoop -> List((hadoop,5)), MR -> List((MR,5))
    val stringToInt = stringToTuples.mapValues(data => {
      data.map(tt => tt._2).sum
    })
    println(stringToInt)
  }
}
/*
Map(hadoop -> 5, MR -> 5, world -> 7, hello -> 17)
*/

fold的应用:

import scala.collection.mutable

object TestFold {
  def main(args: Array[String]): Unit = {
    // 将两个map合并,相同K的V进行累加
    val map1 = mutable.Map("a" -> 1,"b" -> 2,"c" -> 4)//一定要可变的
    val map2 = mutable.Map("d" -> 5,"b" -> 7,"c" -> 5)
    val stringToInt = map1.foldLeft(map2)((map, t) => {
      map(t._1) = map.getOrElse(t._1, 0) + t._2 // map的赋值方式map(k) = v
      map
    })
    println(stringToInt)// Map(b -> 9, d -> 5, a -> 1, c -> 9)
  }
}

交集并集差集、关联

zip关联
val ints1 = List(5, 5, 1, 5, 4, 3, 5, 6, 7, 6, 78)
    val ints2 = List(5, 6, 7, 6, 78,66,55,44,889,564)
    // 关联
    val tuples1 = ints1.zip(ints2)
    println(tuples1)//List((5,5), (5,6), (1,7), (5,6), (4,78), (3,66), (5,55), (6,44), (7,889), (6,564))

联合(并集)

// 联合
    println(ints1.union(ints2))// List(5, 5, 1, 5, 4, 3, 5, 6, 7, 6, 78, 5, 6, 7, 6, 78, 66, 55, 44, 889, 564)

交集:

// 求交集
    println(ints1.intersect(ints2))//List(5, 6, 7, 6, 78)

差集:

// 差集
    println(ints1.diff(ints2))// List(5, 1, 5, 4, 3, 5)

你可能感兴趣的:(笔记)