本文适合有一定Java基础的,并想系统学习Scala的小伙伴借鉴学习。文章有大量实例,建议自己跑一遍。
Scala深入浅出——从Java到Scala
Scala
一、介绍
1、什么是Scala?
- Spark基于内存的大数据计算框架(MR是基于磁盘计算框架)
- 能用什么开发呢(Java【必须】、Scala【必须】、Python【加分项】、R)
- Spark/Kafka 流行度很高,推动了Scala发展
- Scala是很容易的语言。它跑在标准的Java平台上,可以与所有的Java库实现无缝交互
Scala:隐式转换、延迟加载、函数式编程、流式操作、完全面向对象(对象.XXX)
Java1.8:函数式编程、Stream流、延迟加载、Fork/join,Java1.8编程风格和Scala函数式编程风格一模一样。
2、特点
-
Scala运行在JVM虚拟机里面(为什么要编译成.class——跨平台)
-
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 ")
}
}
总结:
-
java类里面可以放成员方法、成员属性、静态方法、静态属性;
-
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、数据类型
基础类型 | 类型说明 |
---|---|
Byte | 8位带符号整数 |
Short | 16位带符号整数 |
Char | 16位无符号Unicode字符 |
Int | 32位带符号整数 |
Long | 64位带符号整数 |
String | Char类型的序列(字符串) |
Float | 32位单精度浮点数 |
Double | 64位双精度浮点数 |
Boolean | true或false |
注意下 scala类型与Java的区别
- scala中所有的类型都使用大写字母开头
- 整形使用
Int
而不是Integer- scala中定义变量可以不写类型,让scala编译器自动推断
- 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 <- 表达式/数组/集合) {
// 表达式
}
to
与until
//闭区间
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)
示例
- 定义一个方法,实现两个整形数值相加,返回相加后的结果
- 调用该方法
参考代码
//省略返回值,编译器自动推断
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方法执行流程
- Scala程序执行的时候,在栈区开辟一个栈帧(局部变量)
- scala执行的时候每遇到一个新的方法的时候,开辟新的栈帧进行压栈
- 每个栈帧是独立的(变量:基本数据类型)不影响的、如果要是引用外部的成员属性的话,可能会有影响
- 每个方法执行结束之后,栈帧会被弹出
2、包
- 包是对类的精细化管理
- 命名规范:数字、字母、下划线、小圆点 、不能数字开头、不要使用关键字,全部小写字母,遵循一般命名规范。
包的使用:
-
普通方式导入:
import java.util.ArrayList //导入一个类 import java.util._ //java * | scala _
-
类中改变类的路径(改变包的路径)
package kylin3 package haha //相当于在kylin3里面声明一个子包 object Scala_List { def main(args: Array[String]): Unit = { } } class UserHaha{} //kylin3.haha.UserHaha
-
scala中父包和子包的关系
-
scala中可以声明父包和子包【包里面嵌套包】,子包中可以使用父包的类,但是父包不可以使用子包的类
-
package包里面可以声明 :伴生类、伴生对象
-
包里面不可以声明属性和方法
-
包里面不可以直接声明可执行的代码
-
包对象
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加载类的信息(字节码文件:属性、方法),会先看当前类是否存在父类,如果存在父类要从继承体系结构,先加载父类,再加载子类,一直到我们想创建的子类对象为止。
创建对象过程:
- 在内存(堆)中开辟空间
- 执行父类的构造器(主、辅)
- 执行本类构造器
- 将堆空间的内存地址的引用给栈帧
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(☆☆☆)需要掌握,其他不常用。
-
集合分类 : 不可变集合 和 可变的集合
-
Scala默认的话都是不可变集合
-
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 方法
解决方法:
- 修改源码(重新修改MySql)
- 动态混入 (OCP)参考之前:5)动态混入
- 隐式转换 (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)隐式转换总结
前提:
- 不能存在二义性
- 不能嵌套使用
隐式转换机制:
-
首先会在代码的作用域内查找隐式实体(隐式方法、隐式类、隐式对象(值))
-
如果第一条检索失败了,会继续查找从隐式参数类型的作用域开始查找
class A extends B with C (从左向右)常用从伴生对象里面去找,再去找继承和混入
-
特质也有伴生对象
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 0
以0开头的数组
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{}