Scala深入浅出——从Java到Scala

本文适合有一定Java基础的,并想系统学习Scala的小伙伴借鉴学习。文章有大量实例,建议自己跑一遍。

Scala深入浅出——从Java到Scala

Scala

一、介绍

1、什么是Scala?

  1. Spark基于内存的大数据计算框架(MR是基于磁盘计算框架)
  2. 能用什么开发呢(Java【必须】、Scala【必须】、Python【加分项】、R)
  3. Spark/Kafka 流行度很高,推动了Scala发展
  4. Scala是很容易的语言。它跑在标准的Java平台上,可以与所有的Java库实现无缝交互

Scala:隐式转换、延迟加载、函数式编程、流式操作、完全面向对象(对象.XXX)

Java1.8:函数式编程、Stream流、延迟加载、Fork/join,Java1.8编程风格和Scala函数式编程风格一模一样。

2、特点

  1. Scala运行在JVM虚拟机里面(为什么要编译成.class——跨平台)

  2. java代码(.java —> .class)

    scala代码(.scala —> .class)

    scala 代码编译成.class 反编译成 java 代码,也就是说 scala 可以无条件的用 java 里面的类。

3、安装

  • java安装:解压、配置环境变量、cmd java -version

  • scala安装:解压、配置环境变量(SCALA_HOME=‘C:\KaiFa\scala’ 、Path = ‘C:\KaiFa\scala\bin’)


二、Scala特点

  • 1、scala主函数写在object里,它是JVM的入口(区别Java),对于java 来说 main 是JVM的入口,且入口必须是静态的。

  • 2、scala没有static修饰词(scala没有这个关键字)

    scala : 完全面向对象语言、面向函数编程语言(java8也实现了面向函数编程【复习】)

    scala 和 python 一样:万物皆对象、能省则省(语法很灵活)

  • 3、object语法:

    编译之后会产生2个class文件

    有一个带有$类文件产生对象:伴生对象

  • 4、scala完全面向对象(JVM需要静态、内部模拟静态形式(伴生对象模拟的))

  • 5、scala里面可以用java的类、scala里面还有独有的类

object Scala01_HelloWorld {
  /**
    * 能装静态属性和静态方法
    */
  def main(args: Array[String]): Unit =  {
    println(" Hello World ")
    Scala01_HelloWorld.test()
  }
  def test(): Unit ={
    println(" hello zhangyishan ")
  }
  
}
    
class Scala01_HelloWorld{
  /**
    * 只装成员的属性和方法
    */
  def test3(): Unit ={
    println(" hello yangzi ")
  }
  def test4(): Unit ={
    println(" hello yangzi ")
  }
}

总结:

  1. java类里面可以放成员方法、成员属性、静态方法、静态属性;

  2. scala里面拆开 (成员方法、成员属性:class), (静态方法、静态属性 : object)


三、scala的基本语法

1、声明变量

在scala中,可以使用val或者var来定义变量,语法格式如下:

val/var 变量标识:变量类型 = 初始值
val xingzuo = "天蝎座"

其中

  • val定义的是不可重新赋值的变量【常量】
  • var定义的是可重新赋值的变量
  • scala中定义变量类型写在变量名后面
  • scala的语句最后可以不添加分号
  • scala变量的类型会自动推断
  • scala局部变量必须显示声明

问题:val 和 var修饰的变量有什么区别?

var name2 : String  = ""
var name3 = ""
val name4 = ""
name4 = "haha" //报错

2、输出

println("name " + name + " age " + age + " xingzuo " + xingzuo)
printf("name=%s ,age=%d , xingzuo=%s \n ", name, age, xingzuo)

字符串插值

自2.10.0版本开始,Scala提供了一种新的机制来根据数据生成字符串:字符串插值。字符串插值允许使用者将变量引用直接插入处理过的字面字符中。

s字符串插值器:

val name = "赵文卓"
val age = 40
val xingzuo = "天蝎座"
println(s"name=$name,age=$age%.2f,xingzuo=$xingzuo")//name=赵文卓,age=40%.2f,xingzuo=天蝎座
println(s"name=${name},age=${age}%.2f,xingzuo=${xingzuo}")//name=赵文卓,age=40%.2f,xingzuo=天蝎座
println(s"1+1=${1+1}") //1+1=2

f 插值器:在任何字符串字面前加上 f,就可以生成简单的格式化串,功能相似于其他语言中的 printf 函数。

println(f"name=${name},age=${age}%.2f,xingzuo=${xingzuo}")//name=赵文卓 , age=40.00 , xingzuo=天蝎座

3、字符串

Java中的字符串是不能修改的(不能改变堆的栈中的引用,但是可以改变堆中的值,不能让引用重新指向新对象,包括null),原因:private final char value[](其实反射可以修改)

scala提供多种定义字符串的方式,将来我们可以根据需要来选择最方便的定义方式。

  • 使用双引号

    val/var 变量名 = “字符串”
    
  • 使用插值表达式

    val/var 变量名 = s"${变量/表达式}字符串"
    
  • 使用三引号

    val/var 变量名 = """字符串1
    字符串2"""    
    

4、数据类型

基础类型类型说明
Byte8位带符号整数
Short16位带符号整数
Char16位无符号Unicode字符
Int32位带符号整数
Long64位带符号整数
StringChar类型的序列(字符串)
Float32位单精度浮点数
Double64位双精度浮点数
Booleantrue或false

注意下 scala类型与Java的区别

  1. scala中所有的类型都使用大写字母开头
  2. 整形使用Int而不是Integer
  3. scala中定义变量可以不写类型,让scala编译器自动推断
  4. scala的类型不是Java的包装类
//补充Java中的字符类型:
public static void main(String[] args) {
	byte b = 97 ;
	t1(b);
	/**
	 *有t1 (byte b1)的话输出:一号选手....
	 *t1 (byte b1)被注释掉的话输出:二号选手....
	 *t1 (short b1)也被注释会输出:四号选手....    注意没有三号选手
	 *t1 (int b1)被注释了会输出:五号选手....
	 */
}

public static void t1 (byte b1){
	System.out.println("一号选手....");
}

public static void t1 (short b1){
	System.out.println("二号选手....");
}

public static void t1 (char b1){
	System.out.println("三号选手....");
}

public static void t1 (int  b1){
	System.out.println("四号选手....");
}

public static void t1 (long  b1){
	System.out.println("五号选手....");
}
//下面这段代码会怎么样?
short s = 1 ;
byte b = 10
s =  s + b ;	//直接编译报错,等号右边会自动类型转换为int,  short = int 需要强转->short = (short)int
System.out.println(s);

关于Java的类型转换问题可以参考我之前的博客

4.1、scala类型层次结构

  • Unit 就是 void
  • Null 能够为所有引用对象类型的变量赋值
  • Nothing 所有类型子类、也是Null子类
def  haha(): Unit ={}
val s : Double = null		//会报错
println(s)         
val s1 :String =null		//不会报错
println(s1)

在这里插入图片描述

在这里插入图片描述

5、标识符

  • java:字母、数字、下划线、$,其中首字母不能是数字

  • scala:支持特殊符号(+ - * / @ `),还需要跟上至少一个特殊符号(必须两个或两个以上特殊符号连用)

    其实jvm是不支持特殊符号的

    var ++ = 123 其实是 var $plus$plus = 123

补充:

  • java 的默认访问权限是包访问权限,(private、default、protect、public)
  • scala 的默认访问权限是public (但是并没有这个关键字【不能写public,但是默认就是public】)

6、运算符

  • Java中的i++与++i

    int i =  0 ;
    i = i++ ;
    System.out.println(i) ;		//输出是什么?——0
    
    byte c = 0 ;
    c += 1;			//√
    c  = c + 1 ;	//会报错吗?为什么。编译时报错,类型转换
    System.out.println(c);
    
  • Scala没有++运算符

    var c :Byte = 0
    c += 1		//运行时报错
    c  = c + 1	//编译时报错
    println(c)
    
  • Scala 与 Java 实现两个变量不用中间变量进行交换操作:

    var a = 10
    var b = 20
    a  = a ^ b	//按位异或
    b  = a ^ b
    a  = a ^ b
    //变量的/2和*2操作也可以用>>1和<<1实现
    

7、表达式

7.1、条件表达式

条件表达式就是if表达式,if表达式可以根据给定的条件是否满足,根据条件的结果(真或假)决定执行对应的操作。

scala条件表达式的语法和Java一样(单分支、双分支、多分支)。与Java不一样的是,

  • 在scala中,条件表达式也是有返回值的
  • 在scala中,没有三元表达式,可以使用if表达式替代三元表达式

Scala中没有三目运算符,是通过if else实现的

var flag = false
var value = if(flag){1}else{"2"}	//自动类型判断,有返回值【区别Java】
println(value)						//2
print(value.getClass)				//class java.lang.String
7.2、块表达式
  • scala中,使用{}表示一个块表达式
  • 和if表达式一样,块表达式也是有返回值的
  • 返回值就是最后一个表达式的值
val a = {
  println("1 + 1")
  1 + 1
}
println(a)	//2
println(a.getClass)		//int
7.3、循环

在scala中,可以使用for和while,但一般推荐使用for表达式,因为for表达式语法更简洁

7.3.1、for循环

语法

for(i <- 表达式/数组/集合) {
    // 表达式
}

tountil

//闭区间
for(i <- 1 to 5){
  println(s"${i}")
}

// 开区间
for(i <- 1 until  5){
  println(s"${i}")
}

增强for循环

for(i <- new Range(1, 5, 1)){
  println(s"${i}")
}

补充:

class User{
  def printUserInfo(num : Int): Unit ={
    println("我的写法终究是一个谜......")
  }
}
var user = new User()
user.printUserInfo(5)
user printUserInfo 5
var sum = 1.+(1)		//√,+是一个function,按住ctrl有提示【区别Java】
println(sum)

嵌套for循环

练习:

小练习,打印:
     *
    * *
   * * *  
要求一层循环,一条语句
for(i <- new Range(1, 18, 2); j=(18-i)/2){
	println(" " * j + "*"*i + " " * j )
}
//如果for循环需要换行的话,需要小括号换成大括号
for{i <- new Range(1, 18, 2);
    j=(18-i)/2}{
      println(" " * j + "*"*i + " " * j )
}

**for + if **:

for(i <- 1 to 5 if i % 2 == 0){
   println(i)
}

for 添加变量

for(i <- 1 to 5 ; k = 1){
  println(k)
}

yield:把每次for循环的结果放到一个集合、之后进行返回

var res: Unit = for(i <- 1 to 4) yield i * 2
var res1: Seq[Int] = for(i <- 1 to 4) yield i * 2
println(res)		//()
println(res1)		//Vector(2, 4, 6, 8)
7.3.2、Breaks类

Scala没有continue,只能通过程序模拟。也没有break关键字,但是有Breaks类

Scala的import是导类,Java是导包

import scala.util.control.Breaks._   //_等同于Java的*
Breaks breakable {		//try-catch捕获异常
  for (i <- 1 to 10) {

    if (i == 5) {
        Breaks break	//实际是抛出异常
    }
    print(i+"\t")
  }
}
println("hello pkq")		//1	2	3	4	hello pkq
7.3.3、while循环

scala中while循环和Java中是一致的

示例

打印1-10的数字

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

8、方法

方法与函数的区别:

方法可以作为一个表达式的一部分出现(调用函数并传参),但带参方法不能作为最终的表达式出现,无参方法可以作为最终表达式出现;但是函数可以作为最终的表达式出现。方法从属与类,函数不属于任何类。(不必特意去区分)

语法

def methodName (参数名:参数类型, 参数名:参数类型) : [return type] = {
    // 方法体:一系列的代码
}
  • 参数列表的参数类型不能省略
  • 返回值类型可以省略,由scala编译器自动推断
  • 如果不写 = 号 ,代表一律没有返回值,返回类型Unit
  • 返回值可以不写 return ,方法体最后一行表达式会被认为是返回结果
  • 方法体若只有一行,可以省略大括号
  • 如果没有参数列表 ,参数列表括号也可以省略掉
//返回值类型可以省略,由scala编译器自动推断
def t3() = {
     "haha"		//这里不能加return
}
println(t3())	//haha
//如果不写 = 号 ,代表一律没有返回值,返回类型Unit
def t4(){
  return "haha"
}
println(t4())	//()
def t6  = "hahammd"	//方法体若只有一行,可以省略大括号。如果没有参数列表 ,参数列表也可以省略掉
println( t6 )		//调用函数的时候,如果参数列表为空,可以省略括号
println( t6() )		//× 如果声明函数的时候,不存在括号,调用的时候,必须没有括号
//无参可以省括号
def methodName:Int= {
    1+1
}
println(methodName)

示例

  1. 定义一个方法,实现两个整形数值相加,返回相加后的结果
  2. 调用该方法

参考代码

//省略返回值,编译器自动推断
def add(a:Int, b:Int) = a + b
add(1,2)

8.1、方法参数

scala中的方法参数,使用比较灵活。它支持以下几种类型的参数:

  • 默认参数
  • 带名参数
  • 变长参数

默认参数

在定义方法时可以给参数定义一个默认值。

// x,y带有默认值为0 
def add(x:Int = 0, y:Int = 0) = x + y
add()
def f2(name : String , age :Int = 20): Unit ={
  println(s"name=${name}   age=${age}")
}
f2("luhan")	//name=luhan   age=20

带名参数

在调用方法时,可以指定参数的名称来进行调用。

def add(x:Int = 0, y:Int = 0) = x + y
add(x=1)

变长参数

如果方法的参数是不固定的,可以定义一个方法的参数是变长参数。

语法格式:

def 方法名(参数名:参数类型*):返回值类型 = {
    方法体
}

在参数类型后面加一个*号,表示参数可以是0个或者多个

def add(num:Int*) = num.sum
add(1,2,3,4,5)

9、函数

方法与函数的区别:

方法可以作为一个表达式的一部分出现(调用函数并传参),但带参方法不能作为最终的表达式出现,无参方法可以作为最终表达式出现;但是函数可以作为最终的表达式出现。方法从属与类,函数不属于任何类。(不必特意去区分)

语法

val 函数变量名 = (参数名:参数类型, 参数名:参数类型....) => 函数体
  • 函数是一个对象(变量)
  • 类似于方法,函数也有输入参数和返回值
  • 函数定义不需要使用def定义
  • 无需指定返回值类型

定义一个两个数值相加的函数:

val add = (x:Int, y:Int) => x + y
add(1,2)

9.1、函数当成参数返回

//方法嵌套方法
def f1(): Unit ={
println("hello world")
}
def f2() ={
	f1
}
f2			//hello world
//函数当成参数返回,函数如果想当成参数返回的话,需要添加 _
def f2() ={
    f1 _
}
val f3 = f2();
val f3: () => Unit = f2()
() => Unit 函数描述符(当前函数无参无返回值) --->针对函数式接口来说的

在这里插入图片描述

在这里插入图片描述

def f1(s : String): Unit ={
	println("hello world  "+ s)
}
def f2() ={
	f1 _
}
val function: String => Unit = f2()
f2()("pikaqiu")			//执行f2返回f1函数,然后传参
function("leiqiu")		//直接通过函数变量名执行

9.2、闭包与柯里化

闭包:函数实现逻辑:将外部局部变量引入当前函数内部,f1的i这个局部变量生命周期转化到f2的生命周期

def f1(i:Int) ={
    def f2(j : Int): Int ={
        i * j
    }
    f2 _
}
//f1中的i这个局部变量会自动转换到f2生命周期里
println(f1(2)(3))		//6
//f1(2)会将f2这个函数返回出来,f2(3) 返回Int类型

柯里化:柯里化是对上面的函数使用的优化,嵌套函数会很简单

def f1(i:Int)(j:Int)={
    i * j
}
println(f1(2)(3))		//6
//与方法传2个参数不同,柯里化执行 f1(2)会返回一个函数
val intToInt: Int => Int = f1(2)		
val i = intToInt(3)		//相当于f1(2)(3)
print(i)				//6

9.3、函数作为函数的参数

def f1(f:(Int)=>Int): Int ={
    f(10) + 10
}

def f2(i:Int): Int ={
    i * 3
}

f1(f2(6))  		//报错,因为f1执行调用, f2(6)代表的是执行了f2,返回Int类型
f1(f2)  		//√	   传递引用
println(f1(f2))	//40	10*3+10

四、Scala面向对象

Scala是完全面向对象,Java存在非面向对象的东西(基本数据类型、静态方法)。Scala没有static,对应的是伴生对象。

1、定义一个类

[访问修饰符] class 类名()

说明:

  • Java的默认访问权限是default;
  • scala里面修饰一个类,不能写访问修饰符,默认是public,但是不能写,scala没有public;
  • 一个scala源文件可以包含很多类,Java也可以包含很多类,但只能有一个public修饰的类;

伴生类与伴生对象

//伴生对象
object User{}
//伴生类
class User{}
/**
  * 伴生类
  */
class User{

  var array = Array //成员属性
  def printHA(): Unit ={ //成员方法
	var array1 =  array
  }

}

/**
  * 伴生对象【反编译之后产生了一个真正能够调用方法的类】
  */
object User{
  var array1 = Array //静态属性
  def printHA1(): Unit ={ //静态方法
	var array1 =  array
  }
}	

1)声明属性

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

var name = “hello”	//如果类型不写的话,编译器根据具体的值进行类型推断
private var age : Int = _

如果类型不写的话,会类型推断

Scala数据类型分为两类:

  • 值类型【AnyVal】

  • 引用类型【AnyRef】

Java里面声明一个属性,不需要显式初始化

Scala里面声明一个属性,需要显式初识化(抽象属性不用初始化,下面再聊)

  • var 声明的属性,可以用 _ 赋初始(默认)值

    var a : int = _

    默认值和Java一样

  • val 声明的属性,不能用 _ 赋初始值

    var b : int = 2

    底层只提供了getter,没有提供setter

2)声明方法

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

方法和函数的区别:

方法从属与类,函数不属于任何类

Scala方法执行流程

  1. Scala程序执行的时候,在栈区开辟一个栈帧(局部变量)
  2. scala执行的时候每遇到一个新的方法的时候,开辟新的栈帧进行压栈
  3. 每个栈帧是独立的(变量:基本数据类型)不影响的、如果要是引用外部的成员属性的话,可能会有影响
  4. 每个方法执行结束之后,栈帧会被弹出

2、包

  • 包是对类的精细化管理
  • 命名规范:数字、字母、下划线、小圆点 、不能数字开头、不要使用关键字,全部小写字母,遵循一般命名规范。

包的使用:

  1. 普通方式导入:

    import java.util.ArrayList //导入一个类
    import java.util._    //java * | scala _
    
  2. 类中改变类的路径(改变包的路径)

    package kylin3
    package haha //相当于在kylin3里面声明一个子包
    
    object Scala_List {
      def main(args: Array[String]): Unit = {
      }
    }
    
    class UserHaha{} //kylin3.haha.UserHaha
    
  3. scala中父包和子包的关系

  • scala中可以声明父包和子包【包里面嵌套包】,子包中可以使用父包的类,但是父包不可以使用子包的类

  • package包里面可以声明 :伴生类、伴生对象

  • 包里面不可以声明属性和方法

  • 包里面不可以直接声明可执行的代码

  1. 包对象

    package object kylin3 {
      println() // 可以执行的代码
      class User{}
      object User{}
      var name : String = _  //
      def printHaha(): Unit ={ //
      }
    }
    

3、import

Scala中的import是导类

//import java.util.Date
//import java.sql.{Date=>_} //屏蔽这个类

import java.util.{Date=>_}//屏蔽这个类
import java.sql.Date

object Test {
  def main(args: Array[String]): Unit = {
	println(new Date(1,1,1))
  }
}
//导入多个类简写
import java.util.{ArrayList,HashMap,List}
//类重名的话,如何导入系统类(_root_)

package kylin3
package java{
  package util{
	class HashMap{
	  
	}
  }
}
import _root_.java.util

object Test {
  def main(args: Array[String]): Unit = {
	 var  hashMap = new _root_.java.util.HashMap[String,Int]()
	hashMap.put("",1)
  }
}
}

4、字段(属性)

val修饰的属性,只能访问,不能修改。原因是底层只对外提供了一个public权限的getter方法,没有setter方法

object Test {
  def main(args: Array[String]): Unit = {
	var user = new User()
	user.username = ""
	println(user.username)
  }
}

/**
  * public | private
  */
class User{
  /**
	* 在类的外部可以进行属性的读写,底层原因是提供了
	* public权限的getter和setter方法
	*/
  var username : String =  _   //可以修改值
  /**
	* 如果给属性添加了private修饰符、属性无法在其它类的外部进行访问(伴生对象除外)
	* 底层原因是,getter and setter都进行了私有化
	*/
  private var age : Int = _
  /**
	* 如果val修饰的属性,那么是能访问不能修改,原因底层只对外提供了一个public权限的getter方法,没有setter方法
	*/
  val email : String = ""  // 不能修改值
}
//反编译后的结果:
public class User
{
  private String username;
  private int age;
  private final String email = "";

  public String username()
    {return this.username; } 
  public void username_$eq(String x$1) { this.username = x$1; } 
  private int age() { return this.age; } 
  private void age_$eq(int x$1) { this.age = x$1; } 
  public String email() { return this.email; }//没有提供setter方法
}

4、方法

1)伴生对象访问伴生类私有属性

伴生对象可以访问伴生类的私有属性,可以把伴生类和伴生对象看成一个类

// 伴生类
class User{
  var username : String =  _
  private var age : Int = _
  val email : String = ""  // 不能修改值
}

//伴生对象
object User{
  def printInfo(): Unit ={
	 println(new User().age)//伴生对象可以访问伴生类的私有属性
  }
}

2)Scala通过 apply 实现单例

class MUser {
   var name : String = _
}

object MUser{
  def apply: MUser = new MUser()
}

//实验方法:
/**
  * apply 了解
  */
object Scala_List {

  def main(args: Array[String]): Unit = {
	var muSer1 = new MUser()
	var muSer2 = new MUser()
	//如果通过这种方式创建的对象是单例对象
	var muSer3 = MUser
	var muSer4 = MUser
    println(muSer1==muSer2)//false
    println(muSer3==muSer4)//true
    println(muSer1==muSer3)//false

  }

}

5、访问修饰符

1)Java访问修饰符

  • public :公有权限【任何包任何类可以访问】
  • protected :子类 、同包
  • default[package] : 同包
  • private :当前类

2)Scala访问修饰符

4种访问权限、3种访问修饰符

  • public:默认就是权限,但是scala没有这个关键词
  • protected:访问权限只能子类访问、同包都不行(区别Java)
  • private:只能当前类访问 (伴生对象中也能访问)
  • private[包修饰]:给包提供权限

Scala没有default

package p1{
  package p2{
	class UserTestProtected{ //p1.p2  private类本身、protected 同包访问不了
	  def printInfo(): Unit ={
		var user =  new UserP2()
	  }
	}

	class UserP2{ // p1.p2
	  var username = "jiajingwen" // 1
	  private var password = "123456"  // 2
	  protected var email = "haha@qq.com" // 3
	  var address = "厦门" // 4
	  private[p3] var sex = "" // 5 了解

	  def printInfo(): Unit ={
		var user =  new UserP2()
	  }
	}
  }

  package p3{

	import kylin3.p1.p2.UserP2

	class EmpP3{ //p1.p3
	  def printInfo(): Unit ={
	    var user =  new UserP2()
        var user =  new UserP2()
        user.username
        user.password	//报错:private
        user.email		//报错:protect
        user.address
        user.sex		//√:private[p3]
	  }
	}
  }
}

6、构造方法

作用:创建对象的时候,给属性赋初始值

1)Java构造器

[访问修饰符] 类名 (参数列表){构造方法体 }

特点:

  • 方法重载(构造器重载)
  • 默认提供了一个无参数的构造方法
  • 新定义一个构造方法后,无参的会被覆盖,需要自己手动补全一个无参构造器

2)Scala构造器

scala和java一样的,scala class可以拥有多个构造方法,构造方法也有重载的说法

scala构造器有两种:主构造器、辅助构造器

基本语法:

class 类名(形参列表){ //主构造器,名字是类名
	def this(形参列表) {
	  //辅助构造器 
	}
	def this(形参列表...) {//名字就是this
	  //辅助构造器 
	}
}

辅助构造器函数名称都是this ,可以存在多个,构建对象的时候进行区分

object Scala_List {
  def main(args: Array[String]): Unit = {
	new Student()
  }
}

class Student(){//主构造器是无参的
  println("hello")
  var name =  "瓜子皮"
  var age = 22
  println(name + age + "岁了")
}
//输出
hello
瓜子皮22岁了
  • Scala创建对象的时候如果用的辅助构造方法,必须先调用主构造方法

  • Scala主构造方法是无参的,小括号可以省略不写,从这个角度看,类也是一个函数

  • Scala的辅助构造方法可以用private修饰、当前辅助构造方法就被私有化了

  • Scala不能存在和主构造方法同名且同参数的辅助构造方法

object Scala_List {
  def main(args: Array[String]): Unit = {
	new Student()
  }
}

class Student(s:String){
	println("我是一个主构造方法")

  /**
	* 如果想声明辅助构造方法,必须显示的调用主构造方法
	*/
  def this(s:String ,ss:String){
	this(s)
	println("我是一个两个参数的构造方法.....")
  }

  //private  可以修饰辅助构造方法,这样就被私有化了(单例)
   def this(){
	this("1","2")
	println("我是一个无参的构造方法呀......")
  }
  
}
//输出:
"我是一个主构造方法"
"我是一个两个参数的构造方法....."
"我是一个无参的构造方法呀......"

7、抽象类和继承

jvm加载类的信息(字节码文件:属性、方法),会先看当前类是否存在父类,如果存在父类要从继承体系结构,先加载父类,再加载子类,一直到我们想创建的子类对象为止。

创建对象过程:

  1. 在内存(堆)中开辟空间
  2. 执行父类的构造器(主、辅)
  3. 执行本类构造器
  4. 将堆空间的内存地址的引用给栈帧
object Scala_List {
  def main(args: Array[String]): Unit = {
	var person = new Mperson()
	person.test()
  }
}

class Mperson extends Person  {

  /**
	* 如果当前方法,在父类是抽象的,子类可以选择性的写override
	* ???是让我们思考方法具体实现应该怎么写、不是默认值
	*/
//   def test(): Unit = ???
  override def test(): Unit = {
	println("hello world ......")
  }

}


abstract  class Person{
  var name : String =  _	//抽象属性,隐式abstract
  def test()  //抽象方法,区别Java,Java需要显式写abstract
}

class Muser extends User{
  //override关键词依然是可选的
   var name: String = _

  /**
	* 抽象方法和抽象属性 override可选
	* 具体的方法和具体的属性如果被重写,必须要添加override
	*/
  override def printInfo(): Unit ={
	println("hello world ......")
  }

  override var sex : String = ""	//必须加override

}

abstract  class User{
  var name : String  //抽象属性,只有抽象类中的抽象属性可以不用显式初始化
  var sex : String = ""
  def printInfo(): Unit ={	//抽象方法
	println("hello world ......")
  }
}

/**
  * 伴生对象里面没有 : 抽象属性和抽象方法说法
  */
object User{
  var name1 : String = _
}

Scala里面声明一个属性,需要显式初识化,除了抽象类中的抽象属性可以不用显式初始化


五、动态绑定(多态)

动态绑定即继承体系的多态概念。

动态绑定机制 (多态):

  • 成员方法在执行的时候,jvm将方法和当前调用方法的对象的实际内存进行绑定
  • 属性是没有动态绑定机制的,属性在哪里声明就用那个属性

Java代码理解动态绑定:

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

		BBB b = new BBB();
		System.out.println(b.getResult()); // 30

		AAA b1 = new BBB();
		System.out.println(b1.getResult()); // 30

	}
}

class AAA{
	
	public int i = 10 ;
	
	public int getResult(){
		return i + 10 ;
	}
	
}

class BBB extends  AAA{
	public int i = 20 ;
	//方法的重写
	public int getResult(){
		return i + 10 ;
	}
} 
//如果BBB改为,则输出为 20  20(成员方法动态绑定),执行AAA中10+10
class BBB extends  AAA{
	public int i = 20 ;
	//方法的重写
//	public int getResult(){
//		return i + 10 ;
//	}
} 

Scala中:

  • 抽象方法重写的时候,override 是可选的
  • 抽象类中有方法的具体实现的话,那么重写方法需要显式添加 override
  • 父类中抽象的属性如果子类重写的话,var声明的属性被重写后,可以使用 _ 做属性值的自动推断,val声明的属性被重写后,不能使用 _ 做属性值的自动推断 ,需要显示的初始化

这几点前面之前提到过:7、抽象类和继承

继承体系中构造方法的使用:

object Scala_List {
  def main(args: Array[String]): Unit = {
		new User("")
  }         
}       
    
class Person(s:String){//无参的主构造方法
  println("Person 主构造方法 ...... " + s)
}

class User extends  Person("hello"){
  println("User 主构造方法 ......")
  def this(s:String) {
	this() // 辅助构造方法需要显示的调用主构造方法
	println("User 辅助构造方法......")
  }
}

输出:
Person 主构造方法 ...... hello
User 主构造方法 ......
User 辅助构造方法......

六、特质

Java的接口《——》Scala的特质

Java接口的实现《——》 Scala特质的混入

1)特质初识

  • scala中没有接口(interface)的概念,取而代之的是特质(trait)的概念
  • 声明特质:trait TestTrait{}
  • 特质可以多混入采用with进行连接(Java也可以多实现接口)
class TestClass{}  //声明类
trait TestTrait1{}  //声明特质

class User01 extends TestClass with TestTrait1{}//继承类混入特质
trait TestTrait{}  //声明特质
trait TestTrait1{}  //声明特质

class User01 extends TestTrait with TestTrait1{}//多混入
  • 类的继承体系中存在类和特质的话,类要写在前 ,所有的特质都写在后

2)Java 和 Scala 对比

Java——接口Scala——特质
java 中的接口不可以直接构建对象scala的特质也不可以直接创建对象
java 中可以声明常量、抽象方法(Java8 新增 静态方法、默认方法)scala的特质可以声明属性和方法,抽象具体都可以(属性的话 var 和 val都可以)
java 中不可以存在直接可执行的代码scala的特质中可以存在直接可执行的代码
trait T1{
  println("hello world")  //可执行的代码
  var name : String       //var修饰的属性
  val name1 :String       //val修饰的属性
  def test()              //抽象方法
  def test1(){}           //具体方法
}

class User extends T1{
  override var name: String = _
  override val name1: String = ""   //val修饰的属性必须显式赋值,不能使用 _
  override def test(): Unit = {
  }
}

3)Scala继承与特质混入执行顺序

object Scala_List {
  def main(args: Array[String]): Unit = {
	new User()
  }
}

trait  T1{
  println("T1 code ......")
}
trait T2{
  println("T2 code ......")
}
class Person{
  println("Person code .....")
}

/**
  * 从左到右顺序执行
  */
class User extends Person with T2 with T1{
  println("child user code ......")
}
输出:
Person code .....
T2 code ......
T1 code ......
child user code ......

特质多混入执行流程:

  • 特质如果被多次混入的话,那么只会被执行一次
object Scala_List {
  def main(args: Array[String]): Unit = {
    new User()
  }
}

trait  T1{
  println("T1 code ......")
}
trait T2{
  println("T2 code ......")
}
trait T3{
  println("t3 code ....")
}
class Person extends T1 with T2{
  println("Person code .....")
}
class User extends Person with T2 with T1 with T3{
  println("child user code ......")
}

输出:
T1 code ......
T2 code ......
Person code .....
t3 code ....
child user code ......

4)特质方法调用的传递性

  • 可执行代码的执行顺序是从左向右、方法调用从右向左

  • super指向的是上一级、但是如果想中途改变顺序super[Operate]。注意区别于构造方法中的super()

object Scala_List {
  def main(args: Array[String]): Unit = {
    new T().insert()
  }
}

trait Operate{
  println("Operate code.......")    //特质被多次混入只会执行一次
  def insert(): Unit ={println("插入数据.......")}
}
//上一级的概念
trait MySql extends Operate {
  println("MySql code.......")
  override def insert(): Unit = {
    println("向MySql数据库......")
//    super[Operate].insert()		//如果这个释放
    super.insert()					//这个注释结果会变成什么?
  }
}
trait File extends Operate {
  println("File code.......")
  override def insert(): Unit = {
    println("向文件......")
    super.insert()
  }
}

class T extends File with MySql {
  println("T code  ......")
}
输出:
Operate code.......
File code.......
MySql code.......
T code  ......
向MySql数据库......
向文件......
插入数据.......

如果更改注释:

输出:
Operate code.......
File code.......
MySql code.......
T code  ......
向MySql数据库......
插入数据.......

5)动态混入

为了满足OCP设计原则,代码设计要对功能开放,对修改关闭

object Scala_List {

  def main(args: Array[String]): Unit = {
	//用户层
	var mysql = new MySql() with Operate //使用with进行动态混入
	mysql.insert()
	mysql.delete()
	//增加删除方法
  }

}

//底层代码添加 , 没有改变原来mysql没有
trait Operate{
  def delete(): Unit ={
	println("删除数据")
  }
}

//底层研发
class MySql{
  def insert(): Unit ={
	println("插入数据")
  }
}

七、集合

1)Scala 集合基本介绍

集合整体进行了解,Tuple(☆☆☆☆☆)、List(☆☆☆☆)、Map(☆☆☆)需要掌握,其他不常用。

  1. 集合分类 : 不可变集合 和 可变的集合

  2. Scala默认的话都是不可变集合

  3. scala集合的类型分为三大类:序列(Seq)、Set、映射(Map)

    这些都【混入】(实现)一个Iterable 、可变(mutable)、不可变(immutable)

下面的图表显示 scala.collection.immutable 中的所有集合类。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Nii4KXTk-1599312600566)(Scala/20180823103129183)]

下面的图表显示 scala.collection.mutable 中的所有集合类。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-NuoQm7zK-1599312600569)(Scala/20180823103202819)]

2)集合初识

  • Scala里面很多集合类都有 apply() 相应方法,可以直接 List() ,而不需要 new
object Scala_List {
  def main(args: Array[String]): Unit = {
    var list: List[Int] = List(1,2,3,4)
    var array:Array[Int] = Array(1,2,3,4)
    println(array.mkString("|"))	//1|2|3|4
    var array1: Array[Int] = apply(1,2,3,4,5,6)
    println(array1.mkString("|"))	//1|2|3|4|5|6
  }

  //可变长参数要写在参数的末尾
  //x = 1  ,  xs = 2,3,4,5,6  
  def apply(x: Int, xs: Int*): Array[Int] = {
    val array = new Array[Int](xs.length + 1) // 6个长度
    array(0) = x // 索引0 --》x
    var i = 1
    //索引1-5赋予2-6值
    for (x <- xs.iterator) { array(i) = x; i += 1 }
    array //返回了一个数组
  }
}

3)数组

不可变数组 VS 可变数组

  • 不可变数组:
object Scala_List {
  def main(args: Array[String]): Unit = {
    f1()
  }
  /**
    * 不可变数组 : 增加、删除、修改、查询、特殊方法
    */
  def f1(): Unit ={

    /**
      * 1.创建一个数组
      */
    var ints: Array[Int] = Array(1,2,3,4)
    // scala 小圆括号进行访问元素(单个元素访问),传递索引,区别Java
    println(ints(0))                //1

    /**
      * 2. 简单方法
      */
    //数组的长度:
    println(ints.length)            //4
    println(ints.size)              //4
    //把集合数据打印的方法:
    println(ints.mkString("-"))     //1-2-3-4

    /**
      * 3.遍历数组
      */
    //for
    for(elem <- ints){
      print(s"${elem}  ")
    }
    println("")
    //foreach
    def printNum(i:Int): Unit ={
            print(s"${i} - ")
    }
    ints.foreach(printNum)
    println("")

    //可以如此简化
    ints.foreach((i:Int)=>{print(s"${i} + ")})
    println("")
    //还可以简化,会自动类型推断
    ints.foreach((i)=>{print(s"${i} = ")})
    println("")
    //还可以简化
    ints.foreach(print(_))
    println("")
    //最简版,但是不可以这样:print()    ×
    ints.foreach(print)
    println("")

    /**
      * 修改 |  不可变引用|内存数据可变的
      */
    ints(0) = 2
    println(ints.mkString(","))       //2,2,3,4

    ints.update(0,1)
    println(ints.mkString(","))      //1,2,3,4

    /**
      * 添加数据方式一
      */
    val resultInts: Array[Int] = ints :+ 5
    println(resultInts.mkString("|"))   //1|2|3|4|5

    /**
      * 添加数据方式二
      */
    var ints1  = ints :+ 6//  后加
    var ints2  = 8 +: ints // 前加
    println(ints1.mkString("|"))  //1|2|3|4|6
    println(ints2.mkString("|"))  //8|1|2|3|4

  }
}
  • 可变数组
import scala.collection.mutable.ArrayBuffer

object Scala_List {
  def main(args: Array[String]): Unit = {
    f1()
  }
  def f1(): Unit ={
    /**
      * 1.创建可变数组
      */
    var ints :ArrayBuffer[Int]  = ArrayBuffer(1,2,3,4)

    /**
      * 2.访问元素
      */
    println(ints(0))
    ints.foreach(print)
    println("")

    /**
      * 3.修改元素
      */
     ints(0) = 5
     println(ints.mkString("|"))

    /**
      * 4.插入数据
      */
     ints = ints += 5   //scala中 += 也是个def
     println(ints.mkString("|"))

    //可变的集合 (insert)
     println(ints.mkString("|"))    //5|2|3|4|5
     ints.insert(2,1,2,3) //是从索引的位置2开始,插入数据123
     println(ints.mkString("|"))    //5|2|1|2|3|3|4|5

    /**
      * 5.删除元素
      */
    println(ints.mkString("|"))
    ints.remove(1) //索引为1的位置数据进行移除
    println(ints.mkString("|"))

    /**
      * 6. 可变数据和不可变数组的转换[了解]
      */
    var array1 : ArrayBuffer[Int] = ArrayBuffer(1,2,3,4,5)
    println(array1)     //ArrayBuffer(1, 2, 3, 4, 5)
    var array : Array[Int] = array1.toArray
    print(array)        //[I@1554909b
  }
}

4)seq相关的集合

ListBuffer

import scala.collection.mutable.ListBuffer

object Scala_List {

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

  /**
    * 可变 List
    */
  def f1(): Unit ={

    var  list: ListBuffer[Int] = ListBuffer(1,2,3,4)

    /**
      * 增删改查
      */
       list.insert(2,2) //第2个位置插入2
       list.update(0,0) //第0个位置改为0
       list.remove(2)   //删除第二个位置元素


       println(list.head) //头元素
       println(list.last) //尾元素
       println(list.tail) //除了头之外的元素
       println(list.init) // 除了尾之外的元素

    /**
      * 1.访问元素
      */
       println(list(1))
       for(item <- list){
         println(item)
       }
      list.foreach(print)

    /**
      * 2.添加元素
      */
     list += 5
     println(list.mkString("|"))  //ListBuffer(0, 2, 2, 3)

    /**
      * 3.集合添加集合
      */
    var list1: ListBuffer[Int] = ListBuffer(5,6,7,8)
    var nList  = list ++ list1
    println(nList.mkString("|"))  //0|2|2|3|4|5|6|7|8

    /**
      * 可以用
      */
    var temp1 =  list1 :+ 9  // 后加
    //   println(temp1.mkString("|"))
    var temp2 = 9 +: list1  // 前加
    //   println(temp2.mkString("|"))

    /**
      * 删除
      */
     var temp3 = list1.remove(1)  //返回值,是删除的数据
     println(temp3)
    println(list1.mkString("|"))  //5|6|7|8

    list1.-=(6) //按照值进行删除
    println(list1.mkString("|"))  //5|7|8
  }
}

Queue

import scala.collection.mutable

object Scala_List {
  def main(args: Array[String]): Unit = {
    f1()
  }
  /**
    * Queue : 先入先出
    */
  def f1(): Unit ={
    var q1: mutable.Queue[Int] =  new mutable.Queue[Int]()
    println(q1)
    /**
      * 队列追加数据
      */
    q1 += 1
    q1 += 2
    q1 += 3

    q1.++=(List(2,3,4)) // ++= 追加List
    println(q1)
    q1 ++= (List(2,3,4))

    println(q1)
    println(q1.mkString("|"))

    q1.dequeue() //出队  先进先出
    println(q1.mkString("|"))

    q1.enqueue(20,30) // 可以一个变长的数据追加到队列
    println(q1.mkString("|"))

    println(q1.head) //查看头元素
    println(q1.last) //查看尾部元素
    println(q1.tail) //除了头之外的所有元素
    println(q1.tail.tail)
  }
}

Stack

import scala.collection.mutable

object Scala_List {
  def main(args: Array[String]): Unit = {
    f1()
  }
  /**
    * Queue : 先入先出
    */
  def f1(): Unit ={
    var q1: mutable.Queue[Int] =  new mutable.Queue[Int]()
    println(q1)
    /**
      * 队列追加数据
      */
    q1 += 1
    q1 += 2
    q1 += 3

    q1.++=(List(2,3,4)) // ++= 追加List
    println(q1)
    q1 ++= (List(2,3,4))

    println(q1)
    println(q1.mkString("|"))

    q1.dequeue() //出队  先进先出
    println(q1.mkString("|"))

    q1.enqueue(20,30) // 可以一个变长的数据追加到队列
    println(q1.mkString("|"))

    println(q1.head) //查看头元素
    println(q1.last) //查看尾部元素
    println(q1.tail) //除了头之外的所有元素
    println(q1.tail.tail)
  }
}

List

object Scala_List {

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

  def f3(): Unit ={

    /**
      * 因为6不是一个列表
      */
    var list1 = List(1,2,3)  //创建数据结构 、 遍历
//    var list2 = 4 :: 5 :: 6 ::: list1 ::: Nil  //报错,因为6不是一个列表
//    var list2 =  4::5::6::list1::9 // 最后一定是列表(有值的,空列表)
    var list3 = 4 :: 5 :: 6 :: list1 ::: Nil    //List(4, 5, 6, 1, 2, 3)
    println(list3)

  }

  def f2(): Unit ={
    var list1 = List(1,2,3)   //创建
    var list2 = List(4,5,6)   //创建
    var temp1 = list1 ++ list2
    println(temp1)

    var list3 = 7 :: 8 :: Nil
    var list4 = 7 :: (8 :: list1)
    println(list3)
    println(list4)

    /**
      * var list1 = List(1,2,3)   //创建
      * var list2 = List(4,5,6)   //创建
      */
    var temp4 =  9 :: list1 :: list2
    println(temp4)

    /**
      * ::: 是把一个列表里面的没有元素追加到后面的列表 (从右向左)
      */
    var temp5 = 9::list1:::list2
    println(temp5)

    /**
      * 修改 | 删除   List测试构建数据(统计)| 可变包
      */
    //  list1.updated(1,4)
    //  list1.drop(3)

  }


  def f1(): Unit ={
    var list = List(1,2,3,4)   //创建
    println(list)
    var list1 = Nil  //空列表
    println(list1)
    //List里面可以存放不同类型的元素吗??? 可以的
    //   var temp: List[Any] = List(1,"")

    /**
      * 访问数据
      */
    println(list(0))
    list.foreach(print) //内部迭代 、for 外部迭代(显示迭代)

    /**
      * 追加元素
      *   不可变体系:一般没有insert、remove方法 、 :+  ,::
      */
    println("")
    var temp1 = list :+ 4
    println(temp1)
    
    var temp2 = 4 +: list
    println(temp2)
    
    println(list)
  }
}

5)Set

  • 默认情况下,Scala使用的是不可变集合,在Scala里面有可变的Set也有不可变的Set

  • set不能存储重复元素

/**
  * 默认情况下,scala使用是不可变集合
  *    在scala里面有可变的Set也有不可变的Set
  */
//创建不可变set集合 【List】
var set1 =  Set(1,2,3)
println(set1)

//创建可变的set集合
var set2 = mutable.Set(1,2,3)
println(set2)

添加数据:

var set01 = Set(1,2,4,"abc") //不可变
//        set01.add  不可变集合一般没有这样的方法
//        set01.insert    也没有这个方法
val set = set01.+= (3)
set01 += (3)
set01 += 3
println(set01)
println(set==set01)   //false

var set02 = mutable.Set(1,2,4,"abc") //可变
set02.add(3)
var set021 = set02.+=(3)
set02 += 3
println(set02)
println(set021==set02)    //true

删除数据:

var set01 = Set(1,2,4,"abc") //不可变
val set: Set[Any] = set01.drop(3) // 删除前几个元素
println(set)    //Set(abc)
println(set01)    //Set(1, 2, 4, abc)

var set02 = mutable.Set(1,2,4,"abc") //可变

set02.remove("abc")
set02 -= 1
println(set02)    //Set(2, 4)

6)Tuple(重点)

元组可以理解为一个容器,这个容器可以存放不同的数据类型,可以承载很多元素数据

  • 构建元组的时候,最多只能有22个值,如果想要存储多个值,可以放 List ;
  • 元组没有foreach,因为 Tuple 里面可以存不同类型的数据;
val tuple1: Tuple4[String, Int, String, Boolean] =  ("liming",30,"xianggang",true)
val tuple2: (String, Int, String, Boolean) =  ("liming",30,"xianggang",true)
val tuple3: Tuple3[String, Int, String] =  ("liming",30,"xianggang")
/**
  * 访问元素
  */
println(tuple3._1)
println(tuple3._2)
println(tuple3._3)
//    tuple3.foreach(f U=》println(_))   tuple没有foreach方法
for(elem <- tuple3.productIterator){
  println(elem)
}

7)Map

Scala中:

  • 可变 : 无序

  • 不可变 : 有序

/**
  * 有序  key -> value 
  */
var map1 =Map("jiajingwen"->40 , "zhangting"->20 , "linruiyang"->30)
println(map1)

/**
  * 无序
  */
var map2 =mutable.Map("jiajingwen"->40 , "zhangting"->20 , "linruiyang"->30)
println(map2)

Map 增删改查:

import scala.collection.mutable

object Scala_List {
  def main(args: Array[String]): Unit = {
    f8()
  }
  /**
    * 遍历
    */
  def f8(): Unit ={
    var map3: mutable.Map[String, Int] = mutable.Map(("A",1),("B",2),("C",3))
    map3.foreach(print)
    println()
    for(v <- map3){ // java编程套路
      print(v+"  ")
    }
    println()

    for((k,v)<-map3){
      print(k+":"+v+"\t")
    }
    println("")

    for(v<-map3.keys){
      println(map3.values)
    }
  }

  /**
    * 删除
    */
  def f7(): Unit ={
    var map3: mutable.Map[String, Int] = mutable.Map(("A",1),("B",2),("C",3))
    map3.remove("C")
    map3 -= ("A","B")
    println(map3)   //Map()
  }

  def f6(): Unit ={
    var map3: mutable.Map[String, Int] = mutable.Map(("A",1),("B",2),("C",3))
    println(map3)
    var map4= map3 + ("E"->1 , "F"->2) // 这个方法 返回一个新的map
    println(map4)
    map3 += ("EE"->1 , "FF"->2) //追加数据没有改变原有的内存地址
    println(map3)
  }

  /**
    * 增加元素
    */
  def f5(): Unit ={
    var map3: mutable.Map[String, Int] = mutable.Map(("A",1),("B",2),("C",3),("D",4))
    map3 += ("D"->3) //如果key存在的话、值会覆盖
    println(map3)
    map3 += ("E"->3) //如果key不存在的话,那么相当于增加了一个键值对
    println(map3)
  }

  def f4(): Unit ={
    var map3: mutable.Map[String, Int] = mutable.Map(("A",1),("B",2),("C",3),("D",4))
    map3.getOrElse("A","默认值") // 如果key存在那么会返回value,如果不存在返回默认值
    println(map3.getOrElse("A","默认值"))    //1
    println(map3.getOrElse("X","默认值"))    //默认值
  }

  def f3(): Unit ={
    var map3: mutable.Map[String, Int] = mutable.Map(("A",1),("B",2),("C",3),("D",4))
    println(map3.get("X")) //不存在返回 :  None
    println(map3.get("A")) //存在返回 : Some

    //    println(map3.get("X").get)
    println(map3.get("A").get)      //   1

  }


  def f2(): Unit ={
    var map3: mutable.Map[String, Int] = mutable.Map(("A",1),("B",2),("C",3),("D",4))
    if(map3.contains("E")){
      println("执行存在逻辑")
    }else{
      println("执行不存在逻辑")
    }
  }

  def f1(): Unit ={

    // 注意事项
    //      Map[Tuple[String,Int]]
    var map3: mutable.Map[String, Int] = mutable.Map(("A",1),("B",2),("C",3),("D",4))
    //访问数据
    println(map3("A"))

    /**
      * java里面访问不存在的key ---> null
      * java.util.NoSuchElementException: key not found: E
      */
    //    println(map3("E"))
    //    mutable.Map[String,Int]  构建一个空Map

  }

}

八、隐式转换

1)Java 与 Scala

Java:

  • Java 有强制类型转换

  • int i = 3.5  //×
    

Scala类型不匹配问题:

  • 使用对象的方法进行数据类型转换,隐式转换。

  • 隐式转换函数的名字是任意的,但是不能重名隐式转换和函数名无关,和函数签名有关(参数列表、返回值)

  • 一个作用域内,可以存在多个隐式函数,但需要保证能被识别的隐式函数只能有一个,不能有二义性

  • object Scala_List {
    
    def main(args: Array[String]): Unit = {
    	/**
    	  * 隐式转换函数 :
    	  */
    	implicit def f1(d:Double): Int ={
    		println("double ---> int")
    		d.toInt
    	}
    	//不能有二义性,释放即报错
    //    implicit def f2(d:Double): Int ={
    //      d.toInt
    //    }
    
    	implicit def f3(d:Long): Int ={
    	  println("long ---> int")
    	  d.toInt
    	}
    
        var num : Double = 1
        var num1 : Int = 3.5  // Double => Int  (函数签名:参数列表->返回值)
        var num2 : Int = 200L // Long => Int
    	
      }
    }
    //输出:
    double ---> int
    long ---> int
    

2)隐式转换函数高级使用

需求:在类 MySQL 中 加入 delete 方法

解决方法:

  1. 修改源码(重新修改MySql)
  2. 动态混入 (OCP)参考之前:5)动态混入
  3. 隐式转换 (OCP)

在任何一个程序里,类可能存在继承体系,如果直接修改源码的话,可能会给其它的类带来副作用。

隐式转换可以帮助我们进行类型转换(隐式:无法直观看到数据类型)

  • 好处:编码简洁(spark)

  • 缺点:源码看不懂(存在隐式转换)

object Scala_List {

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

	implicit def addDeleteMethod(mysql:MySql): DBUtils ={
	  new DBUtils
	}

	/**
	  * 如果当前对象发现调用的方法不存在、那么会在作用域(-)内进行搜索(隐式转换函数)
	  * 查询,看看是否有一个转换后的数据类型存在这个方法
	  */
	var mysql: MySql = new MySql()
	mysql.insert()
	mysql.delete()

  }

}

class DBUtils{
  def delete(): Unit ={
	println("从mysql中删除一条数据.....")
  }
}

class MySql{
  def insert(): Unit ={
	println("向mysql数据库中插入一条数据......")
  }
}

3)隐式值

object Scala_List {
  def main(args: Array[String]): Unit = {
    test1("lizi")
    test1()
    test1     //如果不用implicit修饰就会报错
    /**
      * 函数形参如果用implicit修饰了,那么调用函数的时候,可以不写小括号,默认用的就是隐式参数(形参)
      */
    def test1(implicit name:String = "huge"): Unit ={
      println(name+"   ......   ")
    }
  }
}
object Scala_List {
  def main(args: Array[String]): Unit = {
    implicit var username1 : String = "zhourunfa"
    //implicit var username2 : String = "huawuque"  //只能有一个相同类型的隐式值,释放注释运行报错
    implicit var username3 : Int = 1
    def test1(implicit name:String = "huge"): Unit ={
      println(name+"   ......   ")
    }
    def test(name:String = "guofucheng"): Unit ={
      println(name+"   ......   ")
    }
    /**
      * 调用函数的时候,使用了implicit隐式转换关键词修饰了形参,调用函数的时候使用了小括号,使用的是自己的形参
      */
    test1()   //huge   ......
    /**
      * 调用函数的时候,使用了implicit隐式转换关键词修饰了形参,调用函数没有使用小括号
      * 优先所有隐式参数(隐式参数【成员】要和形参类型一样)、如果没有隐式参数,那么就使用自己的形参
      */
    test1     //zhourunfa   ......
    /**
      * 如果调用的函数,没有用implicit隐式转换关键词(关键字),就是调用方法、使用的是形参
      */
    test()    //guofucheng   ......
    //test   //没有用implicit修饰  释放注释即报错
  }
}
object Scala_List {
  def main(args: Array[String]): Unit = {
    implicit var username1 : String = "zhourunfa"
//    implicit var username2 : String = "zhourunfa"
    implicit var username3 : Int = 1
    def test1(implicit name:String ): Unit ={
      println(name+"   ......   ")
    }
    test1("")
    test1
  }
}

4)隐式类

2.10.x 版本开始才有隐式类

作用:扩展类的功能(之前使用:特质、类|混入、继承 来扩展类功能)

object Scala_List {

  def main(args: Array[String]): Unit = {
    /**
      * var user: User01 = new User01()
      * user.sayHello()
      * user.printInfo()
      *
      * 详细分析:
      *   User01类里面之有sayHello方法、没有printInfo方法、开发时候不可能一次性想的很全面
      *   一定会进行程序的迭代(类:属性、方法修改、增加其它类和特质)
      *   面对我们这个场景,我们有多少种解决方案 :
      *       1.修改源码
      *       2.动态混入 [2]其次考虑
      *       3.底层(研发人员角度)增加一个类、调用层(main用户角度)
      *          、添加隐式函数  [2]其次考虑
      *       4、用户层添加隐式类(*****) [1]首先考虑
      */
    var user: User01 = new User01()
    user.sayHello()   //user sayhello ......
    user.printInfo()    //我在打印信息......
    /**
      * 隐式类补充说明:
      *   1.构造函数参数只能有一个
      *   2.隐式类必须在 类、伴生对象、包对象中, 不能顶级类
      */
    implicit class NUser(u:User01){
      def printInfo(): Unit ={
        println("我在打印信息......")
      }
    }//隐式类
  }
}
class User01{
  def sayHello(): Unit ={
    println("user sayhello ......")
  }
}
object Scala_List {
  def main(args: Array[String]): Unit = {
    var user: User01 = new User01()
    user.sayHello()     //user sayhello ......
    user.printInfo()    //我在打印信息......
    print(user.name)    //hello world
    /**
      * 隐式类中可以有属性
      */
    implicit class NUser(u:User01){
      var name : String = "hello world"
      def printInfo(): Unit ={
        println("我在打印信息......")
      }
    }//隐式类
  }
}
class User01{
  def sayHello(): Unit ={
    println("user sayhello ......")
  }
}

5)隐式转换总结

前提:

  1. 不能存在二义性
  2. 不能嵌套使用

隐式转换机制:

  1. 首先会在代码的作用域内查找隐式实体(隐式方法、隐式类、隐式对象(值))

  2. 如果第一条检索失败了,会继续查找从隐式参数类型的作用域开始查找
    class A extends B with C (从左向右)

    常用从伴生对象里面去找,再去找继承和混入

  3. 特质也有伴生对象

object Scala_List {
  /**
    * main是当前代码作用域
    */
  def main(args: Array[String]): Unit = {
    /**
      * 开闭原则(需求变更,不要修改源码【可以扩展】)
      */
    var user: User01 = new User01()
    user.sayHello()
    user.printInfo()
    user.name
    //      implicit class Nuser(u:User01){
    //        var name : String = "hello world"
    //        def printInfo(): Unit ={
    //          println("我在打印信息......")
    //        }
    //      }//隐式类
  }
}
class User01 extends T1{
  def sayHello(): Unit ={
    println("user sayhello ......")
  }
  //  implicit class Nuser(u:User01){
  //    var name : String = "hello world"
  //          def printInfo(): Unit ={
  //            println("我在打印信息......")
  //          }
  //        }//隐式类
}
object User01{
  implicit class Nuser(u:User01){
    var name : String = "hello world"
    def printInfo(): Unit ={
      println("1......")
      println("我在打印信息......")
    }
  }//隐式类
}
trait T1{
      implicit class Nuser(u:User01){
        var name : String = "hello world"
          def printInfo(): Unit ={
            println("我是T1,我在打印信息......")
          }
      }//隐式类
}
object T1{
//  implicit class Nuser(u:User01){
//    var name : String = "hello world"
//    def printInfo(): Unit ={
//      println("0......")
//      println("我在打印信息......")
//    }
//  }//隐式类
}
输出
user sayhello ......
1......
我在打印信息......

九、集合常用函数

1)常用方法

var list = List(1,2,3,4)
//求和
println("sum = "+list.sum)
//求最大值
println("max = "+list.max)
//求最小值
println("min = "+list.min)
//求乘积
println("product = "+list.product)
//反转
println("reverse = "+list.reverse)

2)高级函数初识

group by

var list = List(1,2,3,4,4,4)
//Tuple2[Int, List[Int]] | (Int,List[Int])
val intToInts: Map[Int, List[Int]] = list.groupBy(t=>t)
intToInts.foreach(t=>print(t._1 +"   "+t._2 + "\n"))
val stringToInts: Map[String, List[Int]] = list.groupBy(t=>t+"_")
输出:
2   List(2)
4   List(4, 4, 4)
1   List(1)
3   List(3)

按首字母分组:

var list: List[String] = List("11","12","33","34")
val charToStrings: Map[Char, List[String]] = list.groupBy(e=>e.charAt(0))//1:List(11, 12)  3:List(33, 34) 
val stringToStrings: Map[String, List[String]] = list.groupBy(e=>e.substring(0,1))//1:List(11, 12)  3:List(33, 34) 
charToStrings.foreach(t=>print(t._1+":"+t._2+ "  "))
println()
stringToStrings.foreach(t=>print(t._1+":"+t._2+ "  "))

按基偶数进行分组:

var list: List[Int] = List(1,2,3,4)
val booleanToInts: Map[Boolean, List[Int]] = list.groupBy(i=>i%2==0)
booleanToInts.foreach(t=>print(t._1 +":"+t._2 +"  "))

sortWith

sortWith:指定规则进行排序

输入的形参的两个值,比较规则:

  • 左边小于右面 ——>升序
  • 左边大于右边——>降序
object TestScala{
  def main(args: Array[String]): Unit = {
    f2()
  }

  def f2(): Unit ={
    var list: List[Int] = List(5,6,1,2,3,4)
    //排序规则:输入什么就按照什么进行排序就好了
    val ints: List[Int] = list.sortBy(x=>x)   //升序
    println(ints.mkString("|"))

    val reverseIntList: List[Int] = list.sortBy(x=>x).reverse   //升序反转——降序
    println(reverseIntList.mkString("|"))

    //sortWith(_.compareTo(_) < 0)
    //  def sortWith(lt: (A, A) => Boolean): Repr = sorted(Ordering fromLessThan lt)
    /**
      * 列表排序的 :
      *    需要传递函数:
      *       输入列表中两个数值 ==》 比较规则
      *       输入的形参的两个值,左边小于右面 升序
      *                           左边大于右边 降序
      */
    println(list.sortWith( (x,y)=> x<y ) .mkString("|"))
    println(list.sortWith( (x,y)=> x>y ) .mkString("|"))

    var list1: List[String] = List("11","32","23","42")
    //看源码
    //1|2|3|4|5|6
    println(list1.sortWith((x,y)=>x.charAt(0) < y.charAt(0)).mkString("|"))

    /**
      * 中间输出
      * 便于调试
      */
    list1.sortWith((x,y)=>{
      //       print(x+"   "+ y +"  ")
      x.charAt(0) < y.charAt(0)
    })

    /**
      * list迭代
      */
    for(elem <- list.iterator){
      print(elem)
    }
  }
}

简易 wordcount

object Scala_List {
  def main(args: Array[String]): Unit = {
    f2
  }
  def f2(): Unit ={
    var list: List[Int] = List(1,2,3,4,4)
    /**
      * 映射 :转换
      *   输入一个 | 返回一个
      *   礼尚往来
      *  i
      *  jump
      *  you
      *  jump
      *  (i,1) , (jump,1), (you,1) ,(jump,1)
      */
    val tuples: List[(Int, Int)] = list.map(x=>(x,1))
    println(tuples)//List((1,1), (2,1), (3,1), (4,1), (4,1))
    val intToTuples: Map[Int, List[(Int, Int)]] = tuples.groupBy(x=>x._1)
    println(intToTuples)//Map(2 -> List((2,1)), 4 -> List((4,1), (4,1)), 1 -> List((1,1)), 3 -> List((3,1)))
    /**
      * wordcount : 统计和需求
      * 日志文件(hdfs)
      * spark-->读取hdfs文件--->List[Log]--->filter过滤2020-09-02访问农心记录
      *     --->groupby--->map size ---> sortwith ---> Top30访问量最高的
      */
    val intToInt: Map[Int, Int] = intToTuples.map(t=>(t._1,t._2.size))
    println(intToInt.mkString("|"))//2 -> 1|4 -> 2|1 -> 1|3 -> 1
  }
}

take / flatmap / fliter / zip

wordcount 分组计数排序截取
def f2(): Unit ={
//map + groupby
// 单词出现次数最多的前Top3
var wordList: List[String] = List("hello","xuchu","hello"
,"zhoucang","hello","sunshangxiang"
,"memghuo" , "zhoucang","menghuo"
,"huangwudie","hello")

//  按照单词进行分组
val groupByRDD: Map[String, List[String]] = wordList.groupBy(word=>word)
println(groupByRDD.mkString("|"))//huangwudie -> List(huangwudie)|sunshangxiang -> List(sunshangxiang)...
// 单词进行计数
val mapRDD: Map[String, Int] = groupByRDD.map(t=>(t._1,t._2.size))
println(mapRDD.mkString("|"))//huangwudie -> 1|sunshangxiang -> 1...

//先转成List,再通过sortWith排序(降序)
val sortWithRDD: List[(String, Int)] = mapRDD.toList.sortWith((left, right)=>left._2 > right._2)
println(sortWithRDD.mkString("|"))//(hello,4)|(zhoucang,2)...

//截取前三个
val result: List[(String, Int)] = sortWithRDD.take(3)
println(result.mkString("|"))//(hello,4)|(zhoucang,2)|(huangwudie,1)
}

flatmap
  • flatMap 将输出的泛型作为结果的泛型
  • map 输出作为结果的泛型
def f1(): Unit ={
    //flatmap  map 关系
    val list: List[String] = List("hello guanfeng","hello guanping","hello guanyu")
    //扁平化【数组里面的值的类型作为泛型】【扁平后数据】
    val flatMapRDD: List[String] = list.flatMap(x=>x.split(" "))
    val groupByRDD: Map[String, List[String]] = flatMapRDD.groupBy(word=>word)
    groupByRDD.map(t=>(t._1,t._2.size))

    /**
      * tolist --> sortWith  | foreach遍历
      */

    //映射 【数组作为泛型】
    val mapRDD: List[Array[String]] = list.map(x=>x.split(" "))
    println(flatMapRDD.mkString("|"))//hello|guanfeng|hello|guanping|hello|guanyu
    println(mapRDD.mkString("|"))//[Ljava.lang.String;@13c78c0b|[Ljava.lang.String;@12843fce|[Ljava.lang.String;@3dd3bc
filter
def f3(): Unit ={
  val list = List(1,2,3,4)
  val filterRDD: List[Int] = list.filter(x=>false)
  filterRDD.foreach(t=>print(t+" "))  //输出:空
  println()
  val filterRDD1: List[Int] = list.filter(x=>{true})
  filterRDD1.foreach(t=>print(t+" "))   //输出:1 2 3 4
}

zip

def f4(): Unit ={
    //木桶原则
    var list1= List(1,2,3)
    var list2 = List(4,5,6,7)
    val tuples: List[(Int, Int)] = list1.zip(list2)
    println(tuples.mkString("|"))//(1,4)|(2,5)|(3,6)

    var list3= List(1,2,3)
    var list4 = List(1,2,4)
    val ints: List[Int] = list3.union(list4) // 列表合并
    println(ints.mkString("|"))//1|2|3|1|2|4

    val ints1 = list3.intersect(list4)  //交集
    println(ints1.mkString("|"))  //1|2

    val ints2 = list3.diff(list4) // 差集
    println(ints2.mkString("|"))  //3
}

加强版wordcount

object Scala_List {
  def main(args: Array[String]): Unit = {
    f4
  }
  def f4(): Unit ={
    //  需求: hello 出现 14 次  |  word  出现 7 次
    var lineList: List[(String, Int)] =  List(("hello scala hello world",4)
      ,("hello world",3)
      , ("hello hadoop",2)
      ,("hello hbase",1))
    val flatMapRDD: List[(String, Int)] = lineList.flatMap(t => {
      val line: String = t._1
      //"hello scala hello world"
      val words: Array[String] = line.split(" ")
      // "hello" "scala" "hello" "world"
      val mapRDD: Array[(String, Int)] = words.map(w => (w, t._2))
      mapRDD.foreach(x=>print(x+" "))
      //(hello,4) (scala,4) (hello,4) (world,4) (hello,3) (world,3) (hello,2) (hadoop,2) (hello,1) (hbase,1) (world,7)
      mapRDD
    })
    println()
    val groupByRDD: Map[String, List[(String, Int)]] = flatMapRDD.groupBy(t=>t._1)
    println(groupByRDD.mkString(","))
  //scala -> List((scala,4)),hello -> List((hello,4), (hello,4), (hello,3), (hello,2), (hello,1))......

    val finalMapRDD: Map[String, Int] = groupByRDD.map(t => {
      // t==> world -> List((world,4), (world,3))
      // word , 7
      //      List((world,4), (world,3)) ==> List(4,3)
      val countList: List[Int] = t._2.map(tt => tt._2) // List(4,3)
      (t._1, countList.sum)
    })
    finalMapRDD.foreach(println)
  }
}

reduce

规约、聚合

def f1(): Unit ={

    val list = List(1,2,3,4)  // 10

    //def reduce[A1 >: A](op: (A1, A1) => A1): A1 = reduceLeft(op)

    val result: Int = list.reduce((left, right)=>left+right)
    //如果形参在代码体中只被使用过一次、就可以使用下滑线进行代替
    val result1: Int = list.reduce(_ + _)
    println(result1)

}

十、模式匹配

Scala 对比 Java :

  • Java:switch case
  • Scala:模式匹配(功能很强大)

1)基本使用

  • 如果能够匹配上,会自动执行case后面的语句。Scala 默认会有 break
  • 如果都匹配不上,会自动执行 case _
  • => 类似java的 :
  • 一个case里面可以写代码体 ,需要添加{}
def f1(): Unit ={

    var oper = "s"
    var res = 0
    var num1 = 10
    var num2 = 2

    oper match {
        case "+" => res = num1 + num2
        case "-" => res = num1 - num2
        case "*" => res = num1 * num2
        case "/" => res = num1 / num2
        case _ => println(" 啥也不是 ...... ")
    }
    println(res)

}

2)模式匹配(守卫)

  • scala的模式匹配也是顺序执行的
  • Scala 的 case _ 与 Java 的 default 还是有区别的。
def f1(): Unit ={

    for(ch <- "+-3!"){
      var temp1 =  0
      ch match {
        case _ => temp1 = 4		//永远都会只执行第一个
        case '+' => temp1 = 1
        case '-' => temp1 = -1
        case _ if ch.toString.equals("3") => temp1 = 3
    //        case _ => temp1 = 4
      }
      println(temp1)	// 4  4  4  4
    }

}

3)模式匹配中的变量

  • case 关键字后面可以写变量名,match表达式的值会赋值给这个变量
  • 模式匹配中如果 没有任何匹配 会报错 : MatchError
def f1(): Unit ={

    var ch  = 'v'
    ch match{
      case '+' => println("haha ~ ")
      case mChar =>println("haha ~ "+mChar)
    }

}
输出:
haha ~ v

4)类型匹配

def f1(): Unit ={

  var  obj =  "guapi"
  val result = obj match {
    case a : String => println("对象是String类型 : "+ a)
    case _ => println("其他")
  }

}

5)数组匹配

def f1(): Unit ={

    for(arr <- Array(Array(0),Array(1,0),Array(0,1,0))){
        var result = arr match {
            case Array(0) => "0"  //精确匹配
            case Array(x,y)=>x + "   " + y  //带有变量匹配方式
            case Array(0,_*) => "以0开头的数组" //模糊匹配
            case _ =>"啥也不是......"
        }
        println(result)
    }

}
输出:
0
1   0
以0开头的数组

6)列表匹配

def f1(): Unit ={

  for(arr <- Array(List(0),List(1,0),List(0,1,0))){
    var result = arr match {
      case 0::Nil => "0"  //精确匹配
      case x::y::Nil=>x + "   " + y  //带有变量匹配方式
      case 0::tail => "以0开头的数组" //模糊匹配
      case _ =>"啥也不是......"
    }
    println(result)
  }

}
输出:
0
1   00开头的数组

7)元组匹配

def f1(): Unit ={

  for(arr <- Array((0,1),(1,0),(2,1),(1,0,2) )){
    var result = arr match {
      case (0,_) => "0"  //精确匹配
      case (y,0)=>  y  //带有变量匹配方式
      case (a,b) => a+" "+b //模糊匹配
      case _ =>"啥也不是......"
    }
    println(result)
  }
}
输出:
0
1
2 1
啥也不是......

十一、泛型

泛型是对类型的约束防止错误

Java:

 ArrayList<String> list = new ArrayList<String>();

Scala:

object Scala_List {
  /**
    * java没什么区别
    * @param args
    */
  def main(args: Array[String]): Unit = {

    /**Java中的泛型:
      * 
      * 上限 (当前类和子类)
      * public void test1(Class<? extends Person> c)
      *
      * 下限 (当前类和父类)
      * public void test2(Class<? super Person> c)
      *
      */
    //     def reduceLeft[B <: A](f: (B, A) => B)   上限(当前类和子类)
    //     def reduceLeft[B >: A](f: (B, A) => B)  下限(当前类和父类)
    //类 、方法

    //    test[User](new Person)
    //    test[User](new User)
    //    test[User](new Child)
    /**
      * class Person{}
      * class User extends  Person{}
      * class Child extends  User{}
      * 下限是没用的.....
      */
    test1[Person](new Person)
    test1[User](new User)
    test1[User](new Child)

  }

  def test2[T <: User](t:T): Unit ={} //底层框架
  def test1[T >: User](t:T): Unit ={} //底层框架
  def test[T](t : T): Unit ={} // 开发中


}

/**
  *       协变和逆变【使用】
  * public static <T> void test(T paramT)
  * {
  * Scala_List..MODULE$.test(paramT);
  * }
  *
  * public static <T> void test1(T paramT)
  * {
  * Scala_List..MODULE$.test1(paramT);
  * }
  *
  * public static <T extends User> void test2(T paramT)
  * {
  * Scala_List..MODULE$.test2(paramT);
  * }
  */
class Person{}
class User extends  Person{}
class Child extends  User{}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值