Scala

Scala

// 重点讲述的是与java不同的地方。

一.基本介绍

1.第一个Scala程序

Scala_第1张图片

1:object:关键字,声明一个单利对象(伴生对象,跟自己的同名类相伴相生),解决静态功能。 

2.变量和数据类型

2.1 常量和变量

        // 类型可以推断的时候可以省略,声明的时候就要有初始值。
var 变量名[:变量类型] = 初始值 //可变
val 常量名[:常量类型] = 初始值 //不可变              

2.2 标识符命名规范

// Scala中标识符分为两部分
// 1.普通起名和java一样。
// 2.scala中的运算符都可以自己起名
        ----总结,报错就改。尽量别用$开头
// 3.如何在java中访问scala对象?
    ---直接对象.方法调用即可。
// 4.非要使用关键字命名的时候,可以加飘号。

2.3 字符串

// scala中没有字符串,使用的就是java中的字符串,所以可以直接使用java中字符串的方法。
​
// 补充:什么叫Json?
    --javaScript object notition。对象字符串。
    --一个{}就代表一个对象,里面就是属性和属性值,如果有多个对象的话,可以用[]括起来所有的{}。
    --在网络中传输对象需要序列化,太麻烦了,所以可以把对象包装为Json格式,就可以直接传输了。
    ---所以把数据转化为Json字符串是一个重要操作。
​
//字符串输出
    
    ---------------用这个--------------
    (1)插值字符串:通过$获取变量值 
            // 前面要加s
        println(s"${age}岁的${name}在学校复习")
        
    (2)多行字符串
            ---主要用于封装Json或者SQL命令
        S"""
            |select *
            |from
            |   student
            |where
            |   name=${name}
            |and
            |   age > ${age}
            |""".stripMargin            

2.5 IO

// 标准化屏幕输入
val age:Int = scala.io.StdIn.readInt()//阻塞,直到控制台有输入、回车。读一个整数
println(age)
    
StdIn.readline()//读一行。
​
// 从文件中获取输入
​
    1.从文件中读取数据,并按原格式打印出来。
        //IDea中的取的是相对路径,相对的是当前的project而言。
            // 绝对路径:不可改变的路径
                    --协议://地址(路径),有的没写会默认补全。
                    //网络:http://www.baidu.com:8/test/test.html
                    //本地:file://c:/test/test.html
            // 相对路径:可以改变的路径,应该存在一个基准路径
                    --idea中默认的基准路径就是项目的根路径。
                
    Source.fromFile("路径").foreach(print)
    
    2.将数据写入文件
     val writer = new PrintWriter(new File("output/test.txt" ))
      writer.write("Hello Scala")
      writer.close()
​
// 网络中的IO
    //先定义server
object Scala_IO_Server{
    def main(args:Array[String]):Unit = {
        val server = new ServerSocket(9999) 
        println("服务器启动成功....等待客户端连接")   
        val client:Socket = server.accept() // 阻塞       
        println("服务器已经连接,等待客户端传送的数据")
        val in:InputStream = client.getInputStream
        val data:Int = in.read()
        println("获取客户端发送的数据" + data)
        in.close()
        client.close()
        server.close()      
    }    
}
    //再定义client
object Scala_IO_Server{
    def main(args:Array[String]):Unit = {
        val client = new Socket("localhsot",9999)
        println("服务器连接成功")
        val out:OutputStream = client.getOutputStream
        out.write(1)// 300
        out.close()
        println("向服务器发送数据成功")
        client.close()
    }    
}
// 测试时,先启动服务器,然后启动客户端。
// 这里传输的是ASC码,不能传输数字。ASC码的范围
​
​
// 网络IO-对象输出流
    //先定义server
object Scala_IO_Server_Obj{
    def main(args:Array[String]):Unit = {
        val server = new ServerSocket(9999) 
        println("服务器启动成功....等待客户端连接")   
        val client:Socket = server.accept() // 阻塞       
        println("服务器已经连接,等待接收客户端传送的数据")
        val objIn = new ObjectInputStream(client.getInputStream)
        val obj = objIn.readObject()
        println("获取客户端发送的数据" + data)
        in.close()
        client.close()
        server.close()      
    }    
}
    //再定义client
object Scala_IO_Server_Obj{
    def main(args:Array[String]):Unit = {
        val client = new Socket("localhsot",9999)
        println("服务器连接成功")
        val user = new User();
        user.age = 20
        val objOut = new ObjectOutputStream(client.getOutputStream)
        objOut.writeObject(user)
        objOut.close()
        println("向服务器发送数据成功")
        client.close()
    }    
}
    //定义一个对象,并且序列化
public class User implements Serializable{
    
    public static int age;
    @override
    public String toString(){
        return "User["+age+"]";
    }
    
}
// 测试时,先启动服务器,然后启动客户端。

2.6 数据类型概述

1. Scala中一切的数据都是对象,都是Any的子类。
2. Scala中数据类型分为两大类:数值类型 AnyVal,引用类型AnyRef,不管是值类型还是引用类型都是对象。
3. Scala数据类型仍然遵守,低精度的值类型向高精度值类型自动转换(隐式转换)。
4. Scala中的StringOps是对java中的string的增强。
5. Unit 对应java在的void,只有一个对象,就是()
6. Null是一个类,只有一个对象就是null,是所有引用类型的子类。
7. Nothing,是所有数据类型的子类,主要用在一个函数没有明确返回值的时候使用,因为这样我们可以把抛出的返回值,返回给任何的变量或者函数。(比如一个函数在运行的时候,抛出了异常,根本就不考虑他的返回值。) 

2.7 强制类型转换

val n2:Int = (2.9 + 3.7).toInt
  --强转会造成精度损失,因为是截断。
  
    程序中输入的数字到底层的时候会以补码的形式存储,正数的补码就是自己,负数的补码就是自己全部取反再+1。在补码中,整数的符号位是0,负数的符号位是1,强转就是在补码上直接截断。
  
  
数值类型和String类型的转换。
    //(1)数值转换String
    val n:Int = 27
    val s:String = n +""
    //(2)String转数值
    val m:Int = "12".toInt
    val f2:Int = "12.3".toDouble.toInt运算符

3.运算符

逻辑运算符
    --在java中==判断地址,object.equals()判断值。
    --在scala中,==和.equals()被统一为判断值。用.eq()判断引用地址。
        在scala中,==底层调用了.equals()。
        
赋值运算符
    在scala中是没有 ++ --的,避免产生歧义。
    --在java中
        x=23
        x=x++会被分解为 tmp = x++ ,x = tmp
                    tmp=23,x++后x变为24,但是x=24后又被tmp=23覆盖为23   
                    
                    
// Scala运算符是一种方法,只是省略成为了计算形式。
// 字符串也可以有乘法。
 // n + m 等价 n.+(m)
    -----这也解释了为什么 +等可以作为标识符。
    在scala中调用方法可以用空格代替.
        n +(m)
        进一步的,()里面只有一个m,可以把()去掉变为n + m
    
    java:7.5.toString()
    scala:7.5 toString
    连续调用的时候更明显
    scala:7.5 toInt toString

4.流程控制

// Scala中的表达式都是有返回值的,每一个分支都有返回值。
	-----就是该分支最后一行表达式的值。

for循环

// 1.增强for循环,常使用,先准备一个集合、
	//for循环里面的数据集可以是任意类型的数据集合,比如字符串(视为字符数组)、集合、数组等。
val list = 1 to 5 // 1.to(5)
val list = 1 until 5 // 1,2,3,4
val list = range(1,5) // 1,2,3,4,默认步长为1,第三个参数可以设置步长。
val list = range(1,5,2)
    for(num <- list){
        println(num)
    }

val s = "zhangsan"
    for(c <- s){
        println(c)
    }
// 循环守卫(增加的判断条件)
for(i <- Range(1,5) if i != 3){
    println(i)
}
// 循环步长
for(i <- 1 to 5 by 2){
    println(i)
}
// 嵌套循环
	//每次i和j同时进入一次。
for(i <- Range(1,5);j <- Range(1,4)){
    println("i = " + i + ",j = " + j)
}
	-------------这里的代码块会在两个循环中都使用到

for(){
	// 如果在这个位置写代码,就要用这种循环。
	
    for(){
    
    }
}

// 循环的返回值
//循环的默认返回值为Unit,但可以利用yield关键字可以返回每一步的执行结果。
val result = for(i <- 1 to 4)yield{
	i*2
}
println(result) // vector(2,4,6,8)

// 循环跳出
breakable{
	for(i <- 1 to 5){
    	if(i == 3){
    		break
    	}
    	println(i)
	}
}

二、函数式编程

2.1 函数和方法?

如何区分函数和方法?

1.简单理解:方法也是函数。函数声明在类中称方法,其他场合是函数。方法有重写、重载,函数没有这些。但函数可嵌套声明使用,方法不行。

2.scala 方法是类的一部分,而函数是一个对象,可以赋值给一个变量。

3.Scala中函数会编译为方法,且改了方法名字,方法名字后增加$1,嵌套的同名方法会变加上$2……,而且增加了private final static。调用时会自动更改为变了名字和修饰符的方法。

4.Scala语言不关心函数的实现方式,只关心如何调用。

2.2 函数基本声明和调用

对一个Scala函数来讲,最重要的是IO,即需要传入什么,需要返回什么。


1.无参无返回值
  // 声明,右侧是一个代码块,代码块是有返回值的,相当于赋值操作。
	def fun1():Unit = {
        
	}
 // 调用
 	fun1()
        --无参的时候可以省略(),变为fun1,所以单独出现一个函数名fun1的时候,就是认为在调用和执行该函数。如果fun1_则考虑为一个静态对象,如果是fun1()则考虑为调用或者静态对象。
        	
2.无参有返回值

	// 声明
    def fun2():String = {
 		return "zhangsan"
    }
   // 调用
   val str:String = fun2()
       --无参的时候可以省略(),变为val str:String = fun2
       println(str)
       
3.有参无返回值
	// 声明
    def fun3(s:String):Unit = {
    	println(s)
    }
	// 调用,必须传参。
	fun3("zhangsan")
        --可以传递子类对象类型。

4.有参有返回值
	// 声明
    def fun4(name:String):Unit = {
    	return "Name:" + name	
    }
    //调用
    val name:String = fun4("zhangsan")
        println(name)
5.多参无返回值
	// 声明  
    def fun5(name:String,age:Int):Unit = {
    	println(s"Name:${name},Age:${Age}")
    }
    // 调用
    fun5("zhangsan",10)
        	--传参必须按顺序按类型。    
6.多参有返回值
	// 声明
    def fun6(name:String,age:Int):String = {
    	// 这里将属性封装到json中。
    	val json = 
    		s"""
    			|{"name":"${name}","age":${age}}// json里面的属性名和属性值都需要用双引号包起来,但是如果属性名不是字符串类型时,可以省略双引号。
    			|""".stripMargin
    			return json
    }
    
	// 调用
	val json = fun6("zhangsan",10)
        println(json)
        
 
        
        
 7.可变参数
 	// 声明
 def fun7(age:Int,name:String*):Unit={
 	println(name)
 }
 		--如果参数不确定,且参数之间含义相同,可以使用可变参数。
 		--参数类型后面使用*,表示可变参数,调用时可以传递参数,也可以不传。
 		--可变参数要放到参数列表的最后面。(所以不可能有两个可变参数。)
 	
 	// 调用
 	fun7()
        --不传参数的时候,不可以省略小括号,此时打印获取的结果是一个空集合List()。
    fun7("zhangsan")
        --传值后,打印输出的结果会被包装为一个集合WrapperArray(zhangsan)

8.带默认值的参数
	// 声明
def fun8(password:String = "000000"):Unit = {
	println(password)
}
		--默认值放在类型的后面。
		--带默认值的参数不允许和可变参数一起使用。
	// 调用
	fun8()
        --不传参的时候,会使用默认值
	fun8("123123")

9.带名传参
   // 声明
 def fun9(password:String = "000000",name:String) = {}
   // 调用
   fun9(name="zhangsan")
       	---遇到这种比较棘手的情况的时候,可以使用 形参名=值的方式传参调用。

2.3 函数声明时的至简原则

// 能省则省
1. return 可以省略
def test():String = {
    return "zhangsan"
}

2.函数体只有一行,可以省略{}
def test():String = "zhangsan"

3.如果根据返回结果可以推断返回值类型,那么返回值类型可以省略。
	---如果出现选择结构,可能有多个分支返回不同的类型,那么返回值类型会向高级自动推断。
def test()="zhangsan"

4.如果该函数没有参数,参数列表小()可以省略。
def test = "zhangsan"
    ----如果声明的时候没加小括号,那么使用的时候也不能加小括号。

5.如果函数明确声明返回值类型为Unit,那么函数体中仍然可以声明return,但是无法起作用,主要目的是为了改变分支。
  
  	如果函数体中使用return返回结果(虽然不起作用),那么返回值类型必须声明。  
def test():Unit = {
	return "zhangsan"
}
 	如果我们希望省略Unit,也要让return失效,那么可以省略Unit和=
def test(){
    return "zhangsan"
}

6.def和函数名也可以省:匿名函数,但需要转换格式。
(s:String) => {
	println("zhangsan")
}

2.4 高阶函数编程

1.将函数对象赋值给一个变量,那么这个变量的类型就是函数类型in => out, x∈【0,22】
	// 这里声明一个函数,等于声明一个函数对象。即对象。
def fun():Unit = {return "zhangsan"}
	// 将函数对象赋值给一个变量。
val ff:    () => Unit    = fun         // 也可以 val ff = fun_
	// 怎么使用这个变量呢?一旦加上(),就表示执行了该函数,即加括号表示调用该函数,不加括号表示函数对象。
		ff()---调用函数
		ff ----函数对象
	
    ---引入匿名函数:
    	// 直接在等号右侧给一个匿名参数,避免先声明再赋值。
    val ff:() => Unit =       ()=>{return "zhangsan"}

1.1 将函数返回值赋给一个变量。
 def fun():Unit = {}
	而调用fun()时,因为没有参数,可以直接使用fun
	// 这里判定为将函数返回值赋给一个变量
  		val f = fun()
   // 如果仍然只是想用函数对象,而不是返回值,则在函数名后加上下划线。
   		val f = fun_
   		// 或者
   		val f:()=>Unit = fun
1.2 函数类型的in => out中,如果in的参数只有一个,小括号可以省略。

2.将函数对象作为参数传递给另一个函数。
	def fun1():Unit = {}
	def fun2(fun1:() => Unit):Unit = {f()}
	
	fun2(fun1)
        
    def sum(a:Int,b:Int):Int = {a + b}
	def fun(f:(Int,Int) => Int):Int = {
    	val r = f(10,20)
        println(r)
	}
	fun(sum)
        ---将传入参数转变为匿名参数,以匿名参数作为参数传入。
     	fun(
        (a:Int,b:Int) => {
            a - b
        }
    )  
        	---引入匿名参数的至简原则,把匿名函数也能省则省。
        	fun((a:Int,b:Int) =>{a - b}) 
        	fun(a:Int,b:Int) =>a - b)
        			---只有一行,可以省略{}
			fun(a,b) =>a - b)
                	---参数类型在传参后可以推断出来,可以省略参数类型
            fun(_-_)
                	---参数按顺序只访问了一次,那么参数列表可以省略,表达式中的参数可以用下划线代替。
  
  
  例:
  def fun(s:String):Unit = {
  	println(s)
  }
  def test(f:String => Unit):Unit ={
  	f()
  }
  test(fun)
      
      test(
      (s:String)=> {println(s)}  
  )
      ---test((s:String)=>println(s))
      ---test((s)=>println(s))
      ---test(println(_))
      
      还可以继续,但这里已经不是函数至简原则了,相当于回到了最开始,在一个函数中传入另一个函数对象。
      ---test(println)

3.将函数作为返回值使用
def test():Unit = {}
def fun():Unit = {
	test //这里是执行了test,所以返回Unit。
}
def fun1() = {
    test_ //这里没有执行test,而是将test作为一个函数对象返回。返回值类型就是test函数的类型。
}
	val f = fun()

        
        
       // 在实际使用中:
        def outer():Unit={
        	def inner():Unit={
        		println("inner....")
        	}
        	inner_
        }
        
		// 调用
		outer(外层参数)(内层参数)
4.闭包
	// 在上面,内层函数是没有使用到外层函数的参数的,那如果内层函数运用到外层函数的参数呢,函数就是方法,外层方法结束后会弹栈,也会带走自己这一层的参数。所以需要闭包。
	// 闭包的意思就是将外层的参数打包保存到heap中下来,防止外层函数执行结束后弹栈,带走了参数。相当于延长了外层参数的生命周期。
	// 闭包的应用场景:大数据场景下,固定相同的数,然后改变其他加数。
  // 将固定的加数作为另一个参数传入,但是是作为”第一层参数传入“
  
5.控制抽象
	// 如果函数声明时,有一个参数是函数对象,且这个函数对象的参数列表没有写,就是抽象。
	// 调用的时候不可以增加参数列表的小括号。只需要传递逻辑代码,传递多行逻辑代码的时候()要变为{}
	// 应用场景:模板设计模式
    def test(f: =>Unit):Unit={
    	f //使用该不完整参数时,也要保持没有参数列表。
    }
	
	// 调用test时,只需要传递逻辑代码。
	test(println("zzzzz"))
        
     object Function_Desigh{
        def main(args:Array[String]):Unit={
            // 声明规范
        	def operationDB(op:=>Unit):Unit={
            	println("init connection...")
                op
                println("close connection....")
        	}
        	// 使用时传入逻辑。
        	operationDB{
            	println("operation db......")
        	}
    	}
	}
6.函数科里化
	// 将无关的参数给分离开,形成多个参数列表
    def test(i:Int)(j:Int):Unit={
    	for(a <- 1 to i){
    		println(a)
    	}
        for(a <- 1 to j){
        	println(a)
        }
    }
    // 使用,有的参数列表可以根据场景省略。
    test(i)(j)
7.递归
// 下面是正常的递归。
	// 方法自己调用自己,需要有跳出逻辑(但仍可能栈溢出,递归此处太多。)
			--递归在IDEA中有一个回环图标就表示递归。
	// 递归函数中不可以省略返回值类型
	
    def test(num:Int):Int={
    	if(num <= 1){
			1    
        }else{
        	num * test(num - 1)    
        }
    }
    
	----引入尾递归,尾递归在scala中执行完方法体后,会立即弹栈,然后去递归执行下一个函数,因为一直弹栈,所以不会栈溢出。
    def test():Unit={
    	println("abc")
    	test()
    }

8.惰性函数
	// lazy修饰,可以使函数的执行会被推迟,知道我们首次用上这个函数的返回值的时候,该函数才会执行。
	def fun():String={}
	lazy val f = fun() // 暂时不执行,等到打印f时才执行。
        println("zhangsan")
        println(f)

2.5 函数体现分布式计算

// 定义Master对象,所有的slaver节点都需要向master注册,让master知道集群里有多少个节点。另外Master可以把自己的元数据交给Zookeeper,可以做成高可用。
object Scala_Function_Master{
    def main(args:Array[String]):Unit = {
        // 准备数据
        val num = 4
        // 准备逻辑
        val logic = (num:Int) => {println(num)}
        // 调度(按特定方式,切分任务)
        val task1 = new Task()
        task1.start = 1
        task1.end = num/2
        task1.logic = logic
            
        val task2 = new Task()
        task2.start = num/2 + 1
        task2.end = num + 1
        task2.logic = logic
       
        // 给节点发送切分完成的任务
        val slaver1 = new Socket("localhost",9999)
        val objOut1 = new ObjectOutputStream(slaver1.getOutputStream) 
        objOut1.writeObject(task1)
        objOut1.flush()// flush的作用是清空缓存区的数据,使缓存区的数据写出去,避免close时有数据丢失
        objOut1.close() 
           
            
        
        val slaver2 = new Socket("localhost",8888)
        val objOut2 = new ObjectOutputStream(slaver2.getOutputStream) 
        objOut2.writeObject(task2)
        objOut2.flush()// flush的作用是清空缓存区的数据,使缓存区的数据写出去,避免close时有数据丢失
        objOut2.close()
            
        //  
        slaver1.close()
        slaver2.close() 
    }
}

// 定义一个类封装计算任务,任务对象要从master发往slaver,涉及到传输对象,所以需要序列化。
class Task extends Serializable{
    val start:Int = 0
    val end:Int = 0
    val logic :(Int) => Unit = null
    
    def run():Unit = {
        for(i <- start to end){
            logic(i)
        }    
    }
        
}

// 定义slaver1,slaver可以动态扩容,每个节点的逻辑都是一样的,所以只需要外面动态的传递端口号就行了。
object Scala_Function_Slaver1{
    def main(args:Array[String]):Unit = {
        val server = new ServerSocket(9999)
        println("slaver[9999]服务启动,等待master连接")
        val master:Socket = server.accept()
        println("slaver[9999]等待master传输数据")
            
        val objIn = new ObjectInputStream(master.getInputStream)
        val obj = objIn.readObject()
        val task:Task = obj.asInstanceOf[Task]
        
        // 执行任务
        task.run()
        objIn.close()
        
        // 一般是不需要停止的,这里是演示而已。
        master.close()
        server.close() 
    }
}

// 定义slaver2
object Scala_Function_Slaver1{
    def main(args:Array[String]):Unit = {
        val server = new ServerSocket(8888)
        println("slaver[8888]服务启动,等待master连接")            
        val master:Socket = server.accept()
        println("slaver[8888]等待master传输数据")
            
        val objIn = new ObjectInputStream(master.getInputStream)
        val obj = objIn.readObject()
        val task:Task = obj.asInstanceOf[Task]
        
        // 执行任务
        task.run()
        objIn.close()
        
        // 一般是不需要停止的,这里是演示而已。
        master.close()
        server.close() 
    }
}

测试的时候,需要先启动slaver1和slaver2,然后启动master。

2.6 下划线的作用

//1.可以声明变量,但是不能使用
val _ = "zhangsan"
//2.将函数作为对象来使用。
val f = test_
//3.在匿名函数中可以代替参数,但是不可以嵌套使用。另外如果参数给定什么,就返回什么的场合,是不可以使用下划线代替的,如word => word是不可以的。
test(_+_)
//4.导入类时,可以代替*
import java.util._
//5.属性的默认初始化
var name:String = _
//6.模式匹配中,作为任意值进行匹配,类似于java中国switch中的default操作。
case_ => xxxxx
//7.泛型中,下划线表示任意类型。

三、面向对象编程

3.1 package

// 1.同一个源码文件中子包(内包)可以直接访问父包(外包),而无需import
// 2.外包访问内包,需要导包。
// 3.package也可以看作对象,并声明属性和函数,作为其对应包下的所有class和object的共享变量,可以直接被访问。

3.2 import

// 1.import关键字可以声明在任何要用的地方
// 2.同一个包中的类可以简化为一行,用一个花括号括起来,用逗号隔开。
	import java.util.{ArrayList,HashMap}
// 3.导包。包是个对象,那么包里面的东西都可以使用了。
	import java.util
    	new util.ArrayList()
// 4.屏蔽类
    import java.util._
    import java.sql.{Data =>_,_}
// 5.改类名
	import java.util.{HashMap=>JavaHashMap}

3.3 类的声明和使用

// class声明类, new构建对象。

3.4 类的属性

// scala中所谓类的属性其实就是类中声明的变量
	//但是作用域不一样
class User{
    val name = "zhangsan"
    var age = 30
        // var email : String是错的,因为scala中变量必须使用显示初始化。可以用下划线代替,但是val不可以这么使用,因为val后续不许更改,占位没有意义。
    var email : String = _    
}
// scala给类声明属性,在编译后,这个属性是私有的,但是同时编译出了两个公共方法(不是java中的那种setXXX和getXXX方法),用于set/get属性。

// java中很多的框架要求bean对象必须符合bean规范(属性私有化,并提供标准get/set方法)。scala做出的处理是在var属性前面加上一个注解:@BeanProperty,这样属性就具有了set/get方法。

3.5 访问权限

// scala权限
1.private    同类
2.private[包名] 同类、同包
3.protected 同包 同类 子类
4.(public,默认)公共

3.6 方法

// 方法就是函数,只不过是声明在类中。
	//对象.eq()方法可以直接使用,比较内存地址
	//对象.isInstanceOf()方法可以直接使用,表示是否为每个类型的实例。
	//对象.asInstanceOf()可以直接使用,表示将对象作为某个类型的实例来使用。

3.7 构造方法

// 万物皆函数,类其实就是函数。scala中的构造方法分为两大类
	//1.完成类的初始化:主构造方法
	//2.辅助操作:辅助构造方法,用this关键字声明的构造方法
		---辅助构造方法必须要首先完成类的初始化操作,然后再进行辅助操作,所以必须在代码中首先调用主构造方法。
		---辅助构造方法之间可以相互调用,调用前必须声明过。

class User{ // 主构造函数
    var username : String = _ 
    def this( name:String ) { // 辅助构造函数,使用this关键字声明
        this() // 辅助构造函数应该直接或间接调用主构造函数
        username = name
	}
	def this( name:String, password:String ) {
    	this(name) // 构造器调用其他另外的构造器,要求被调用构造器必须提前声明
	}
}

3.8 继承/封装

// 与java一致

3.9 抽象

// 如果一个类中有抽象方法,那么这个类需要用abstract修饰为抽象类。
// 抽象方法直接写出声明,不用方法体。
// 抽象类中完整的方法也可以被实现类重写,但是要在方法前加上关键字override,跟java的@Override注解不一样。
// 声明属性而没有初始化,称之为抽象属性。重写抽象属性补充完整即可。重写完整val属性,需要在属性前面增加override关键字。
abstract class User{
    def test():Unit
}

3.10 单例对象/伴生对象

// Scala如果想使用到单例模式,直接在类的前面使用object关键字就行。
object User{
}

//若单例对象名与某个类的类名相同,则称该单例对象这个类的伴生对象,这个类的所有“静态”内容都可以放置在它的伴生对象中声明,然后通过伴生对象名称直接调用

// 一般会将成员方法和属性声明在伴生类中。
// 一般会将模拟静态方法和静态属性声明在伴生对象中。伴生对象可以访问半生类中的所有内容。

3.11 Apply

// scala中为了方便构建对象,如果构建对象的方法名是一个特殊的名称的话,那么这个方法名可以不用写。

// 伴生类 
class User{
    
}
// 伴生对象
object User{
    // apply可以给不同参数,重载。
    def apply():User = {
        new User()
    }
}

  // 构建对象
 val user2 : User = User.apply()
     // apply 可以省略,集合中常用 
 val user1 : User = User()
     // 通过调用伴生类的构造方法来new。如果伴生类声明的时候加上了private,这种方法就没法使用了。
 val user3 : User = new User()
     // 单例对象,即本省就是一个对象(User$)
 val user4 : User = User

3.12 特质

// 将多个对象中相同的特征剥离出来,形成一个独立的语法结构,称之为特质(特征)
// 如果有一个类符合这个特征,那么可以将这个特征混入到这个类中。
// 如果一个类可以混入多个特征,第一个特征使用extends,后续使用with连接。
// 如果一个类既有父类,同时还混入了特征,那么父类使用extends关键字,所有特征都使用with连接。
		---scala中用trait统一了java抽象类和接口。java中的所有接口在scala中都是特质。
trait Runable{
    def run()
}

class Person extends Runable{
    override def run():Unit = {
        println("person runnning..........")
    }
}


// 特质的动态混入:在new对象的时候混入新功能,可以避免修改源码。
val user = new User() with Runable


// 特质初始化顺序
	1.如果类和特质同一个级别,那么特质先初始化
	2.如果多个特质混入,那么初始化顺序是从左到右
	3.初始化只会执行一次。

// 特质功能叠加
	从右到左。super【父类名】可以指定跳跃到某个父类特质。

四、集合

不可变与可变

// 不可变 scala.collection.immutable 方法尽量使用运算符,默认情况下,scala中的集合都是不可变的。
// 可变 scala.collection.immutable 方法使用英文单词

//分可变集合和不可变集合的意义

// 在数仓中,我们会将原始数据保留住,对原始数据进行一些数据清洗的操作,然后把清洗的结果发送的另一个地方去。如果下次我们仍然想使用原始数据,就应该将原始数据保存在一个不可变集合中。(重点是强调不可变,因为java中是可变的。)

4.1 Array

4.1.1 不可变数组Array

// 数组的声明方式2,声明的同时赋值。----这是最常见的方式。   
    val arr:Array[Int] = Array(1,2,3)
1. 访问、赋值、修改元素(通过索引)
	arr(0)=3	
2. 遍历
// 遍历方式1
arr.mkString(",")
// 遍历方式2
arr.foreach(println)
        
 // Scala中的集合默认是不可变的。如果运算符使用冒号结尾,那么运算规则从右向左。
         // 增加元素+。(冒号的位置就是原集合的位置)
       val ints:Array[Int] = arr :+ 4  //(1,2,3,4)
       val ints1:Array[Int] = 5 +: arr //(5,1,2,3)
         // 把另一个集合的元素链接过来++,记一个就行,自己更改朝向。
           // array={4,5,6},array1={1,2,3}
         val ints2:Array[Int] = arr ++: arr1,//{4,5,6,1,2,3}
         val ints3:Array[Int] = arr1 :++ arr //{1,2,3,4,5,6}

    
    // 多维数组(数组里面有数组) 3*3二维数组。
    var myMatrix = Array.ofDim[Int](3,3)
    // 二维数组遍历,这里的list只是一个变量名,完全可以使用arr。
    myMatrix.foreach(List=>println(List.mkString(",")))

    // 合并数组
        val arr1 = Array(1,2,3)
        val arr2 = Array(4,5,6)
        val arr3:Array[Int] = Array.concat(arr1,arr2)
            
    // 创建指定范围的数组
        val arr7:Array[Int] = Array.range(0,2) // [0.2)
            
    // 创建并以指定元素填充数组,下面是以-1填充长度为5的数组。常用于状态监测。
        val arr8:Array[Int] = Array.fill[Int](5)(-1)
    

4.1.2 可变数组ArrayBuffer

// new 一个空的
val buff = new ArrayBuffer[String]()
// 赋值构建
val buff1 = ArrayBuffer("1","2","3")   
    // 追加元素,后面是可变参数
    buff.append("a","b","c")
    // 追加集合
    buff.appendAll(buff1) 
     
     // 遍历方式1,可变的数组直接打印。
        println(buff)        
     
     // 更改元素,更改自身
        buff.update(index,element)<=> buff(index) = element        
     // 更改元素生成新的集合,不改变原来的集合
        val ints:ArrayBuffer[Int] = buff.updated(index,element)
    
     // 插入元素,在index处,插入指定数量的元素(可变参数)
        buff.insert(index,ele1,ele2,........)
     
     // 删除元素
        buff.remove(index)
     // 从index处,删除count个元素,注意不要越界。
        buff.remove(index,count)           

4.1.3 Array <=> ArrayBuffer

val array = Array(1,2,3,4)
val buffer = ArrayBuffer(1,2,3,4)
// 不可变转换 => 可变
array.toBuffer
// 可变 => 不可变
buffer.toArray

4.2 Seq-List(有序可重)

4.2.1 不可变序列List

// 不可变的Seq特质,使用List最多。
val list = List(1,2,3,4,1,4)
val list1 = List(1,3,2,4)        
    // 思考,为什么Java中要这么写?
    	List list = new ArrayList()
     // 而不是这么写
    	ArrayList list = new ArrayList()
    // 当考虑拓展性后,后面无论是哪个类实现了List接口,只要左边是List接口,list.add()都是可以使用的。
    // 当使用ArrayList特有的方法,用Arraylist类。        
    
    // 增加数据::
    val 1ist2 = 1::2::3::list::List(1) //把1,2,3和list集合都加入到List中。
   	val list3 = 1::2::3::Nil // 增加数据到空集合
    val list4 = 1::2::3::list:::Nil // 将整体的list拆分为个体,放入到新集合中(叫做扁平化)

4.2.2 可变序列ListBuffer

val buffer = ListBuffer(1,2,3,4)
buffer.append()
buffer.appendAll()
buffer.insert()
buffer.update()
buffer.updated()
buffer.remove()

4.2.3 List <=> ListBuffer

val list = List(1,2,3,4)
val buffer = ListBuffer(1,2,3,4)
// 不可变转换 => 可变
list.toBuffer
// 可变 => 不可变
buffer.toList

4.3 Set(无序不重)

// 因为set是无序的,所以就没有了索引操作。

4.3.1 不可变Set

val Set = Set(1,2,3,4)

4.3.2 可变mutable.Set

	// 创建
	val set = mutable.Set(1,2,3,4)
   //增加数据,没有索引、顺序,所以是单纯的增加。
    set.add(5)
    //修改集合,true就是使集合包含该元素,fasle就是使集合不包含该元素。 
    set.update(element,false)
    // 删除元素
    set.remove(2)
    // 遍历
    println(set.mkString(","))
    set.foreach(println)

4.4 Map

4.4.1 不可变的Map

// 键值对
val kv = "a" -> 1
    
// 无序,kv键值对,k不可重复,v可以重复。a指向3,后者将前者覆盖了。
val map = Map("a"->1,"b"->2,"c"->3,"d"->3,"a"->3)  

4.4.2 可变mutable.Map

// 创建
val map = mutable.Map("a"->1,"b"->2,"c"->3,"d"->3,"a"->3)
val map = mutable.Map(("a",1),("b",2),("c",3))
// 删除
	map.remove("c")
// 遍历
    map.mkString(",")
    map.foreach(println)

    // 如果元组中的元素只有两个,称之为对偶元组,也称之为键值对对象
    "a"->1,等价于 ("a",1) 
    // map遍历
    map.foreach(
    kv =>{
        println(kv._1 + "=" + kv._2)
    }

// 增加/修改
    map.update("a",5) 
    map("a") = 5    
    map.put("a",4)//返回值类型为Option

// 如果有该key,就返回value,如果没有改key,就返回默认值。
    map.getOrElse("a",0)
// 如果有该key,就返回value,否则将该key和value增加进map。
    map.getOrElseUpdate("a",1)

            // 单独取出所有的key
            val keys:Iterable[String] = map.keys
            // hashmap是可以放空key,空null的
            map.put(null,null)
              //hashtable是不可以放空key,空null的。  

// 排序,因为map没有索引的概念,所以没有sort方法,要先转化为List
  val list1 = map.toList
  list1.sort()
    ----当List中有kv时,也可以转换为Map。

4.5 Tuple

// scala将无关的数据作为整体使用的时候,形成一个元素的组合,简称为元素,称之为元组Tuple
 
//声明
val tuple = (1,"zhangsan",30)
    //因为数据无关,所以一般通过位置编号进行访问。
    println(tuple._1)
    
    // 遍历
    tuple.productElement(index)
    tuple.Iterator
    
    // 迭代(套娃)
    t->2->3->4->5

4.6 队列

//Scala也提供了队列(Queue)的数据结构,队列的特点就是先进先出。进队和出队的方法分别为enqueue和dequeue。
object ScalaCollection{
    def main(args: Array[String]): Unit = {
        val que = new mutable.Queue[String]()
        // 添加元素
        que.enqueue("a", "b", "c")
        val que1: mutable.Queue[String] = que += "d"
        println(que eq que1)
        // 获取元素
        println(que.dequeue())
        println(que.dequeue())
        println(que.dequeue())
    }
}

4.7 并行集合

// Scala为了充分使用多核CPU,提供了并行集合(有别于前面的串行集合),用于多核环境的并行计算。
object ScalaCollection{
    def main(args: Array[String]): Unit = {
        val result1 = (0 to 100).map{x => Thread.currentThread.getName}
        	//par就是并行的意思。
        val result2 = (0 to 100).par.map{x => Thread.currentThread.getName}

        println(result1)
        println(result2)
    }
}

4.8 常用方法

object ScalaCollection{
    def main(args: Array[String]): Unit = {
        val list = List(1,2,3,4)

        // 集合长度
        println("size =>" + list.size)
        println("length =>" + list.length)
        // 判断集合是否为空
        println("isEmpty =>" + list.isEmpty)
        // 集合迭代器
        println("iterator =>" + list.iterator)
        // 循环遍历集合
        list.foreach(println)
        // 将集合转换为字符串
        println("mkString =>" + list.mkString(","))
        // 判断集合中是否包含某个元素
        println("contains =>" + list.contains(2))
        // 取集合的前几个元素
        println("take =>" + list.take(2))
        // 顺序取集合的后几个元素
        println("takeRight =>" + list.takeRight(2))
        // 查找元素
        println("find =>" + list.find(x => x % 2== 0))
        // 丢弃前几个元素
        println("drop =>" + list.drop(2))
        // 丢弃后几个元素
        println("dropRight =>" + list.dropRight(2))
        // 反转集合
        println("reverse =>" + list.reverse)
        // 去重
        println("distinct =>" + list.distinct)
    }
}
2)	衍生集合
object ScalaCollection{
def main(args: Array[String]): Unit = {
    	val list = List(1,2,3,4)
        val list1 = List(1,2,3,4)
        val list2 = List(3,4,5,6)

        // 集合头
        println("head => " + list.head)
        // 集合尾
        println("tail => " + list.tail)
        // 集合尾迭代
        println("tails => " + list.tails)
        // 集合初始值
        println("init => " + list.init)
        // 集合初始值迭代
        println("inits => " + list.inits)
        // 集合最后元素
        println("last => " + list.last)

// 双集合操作
        // 集合并集
        println("union => " + list.union(list1))
        // 集合交集(如果是set,会去重)
        println("intersect => " + list.intersect(list1))
        // 集合差集,哪个集合调用方法,就以哪个集合为中心。
        println("diff => " + list.diff(list1))
        // 切分集合
        println("splitAt => " + list.splitAt(2))
        	// 按条件将集合切分为两个符合条件的集合。
        list.partition(_>3)
        // 滑动窗口,每次滑动一步(窗口大小)
        println("sliding => " + list.sliding(2))
        // 多步长滑动,(窗口大小,滑动步长)
        println("sliding => " + list.sliding(2,2))
        
        // 拉链,将相同位置上的元素连接再一次,谁调用谁在左边,拉不上的就舍弃。
        println("zip => " + list.zip(list1))
        // 数据索引拉链,将数据和索引构建在一起。
        println("zipWithIndex => " + list.zipWithIndex)
    }
}

3)	计算函数
object ScalaCollection{
    def main(args: Array[String]): Unit = {
        val list = List(1,2,3,4)
        val list1 = List(3,4,5,6)

        // 集合最小值
        println("min => " + list.min)
        // 集合最大值
        println("max => " + list.max)
        // 集合求和
        println("sum => " + list.sum)
        // 集合乘积
        println("product => " + list.product)
        // 聚合(两两计算)
        println("reduce => " + list.reduce(_+_))
        // 聚合(左),从左边开始迭代加括号
        println("reduceLeft => " + list.reduceLeft(_+_))
        // 聚合(右),从右边开始迭代加括号
        println("reduceRight => " + list.reduceRight(_+_))
        
        // 折叠,fold方法存在函数柯里化,第一个参数列表表示集合之外的计算初始值,第二个参数列表表示计算规则:两两计算。要求计算初始值和集合原始值类型相同
        println("fold => " + list.fold(0)(_+_))
        // 折叠(左),内外类型可以不一样,以外为主。从左开始,迭代加括号。
        println("foldLeft => " + list.foldLeft(0)(_+_))
        // 折叠(右),先reverse.foldleft,然后把外面元素放在前面,然后左右交换位置上括号。
        println("foldRight => " + list.foldRight(0)(_+_))
        
        
        // 扫描,将每一步的计算结果保留下来。
        println("scan => " + list.scan(0)(_+_))
        // 扫描(左)
        println("scanLeft => " + list.scanLeft(0)(_+_))
        // 扫描(右),结果从右向左保存为一个List
        println("scanRight => " + list.scanRight(0)(_+_))
    }
}

4)	功能函数
object ScalaCollection{
    def main(args: Array[String]): Unit = {
        val list = List(1,2,3,4)

        // 映射map,将每一条数据转换映射,产生新的集合,能改变结构
        println("map => " + list.map(_*2))
        // 扁平化,整体拆成个体
        val list1 = List(
            List(1,2),
            List(3,4)
        )
        println("flatten =>" + list1.flatten)
        // 自定义扁平化
        println("flatMap =>" + list1.flatMap(list=>list))
        
        val list = List(
        	"hello Scala","hello Spark"
        )
        val list2 = list.flatMap(
        	s => s.split(" ") // 这里的s指的是,list中的每一个值。
        )
        
        // 过滤,根据指定条件对集合每一个元素进行筛选
        println("filter =>" + list.filter(_%2 == 0))
        val list1 = List("Hadoop","Hive","Scala","Spark")
        list1.filter(
        	s => { 
        	s.starsWith("S")//每一个值都判断一下是否以S开头。	
        	}
        )
        
        // 分组,根据指定规则进行分组,,返回结果为Map类型,其中的key就是分组的表示,value就是相同标识的数据集合。
        println("groupBy =>" + list.groupBy(_%2))
        
        // 排序,默认为升序(如果是字符串排序的话,按字典序。)
        	//单关键字排序
        println("sortBy =>" + list.sortBy(num=>num)(Ordering.Int.reverse)
        val list1 = List("1","2","3","11","22")
    	val sortList1 = list1.sortBy(s => s)// 按字典序
    	val sortList2 = list1.sortBy(s => s.toInt)// 按数值大小	
        	// 多关键字默认升序
        	//使用元组排序,元组在排序时,会先按照第一个元素排,然后按第二个元素排....()
        println("sortWith =>" + list.sortWith((left, right) => {left < right}))
    		// 自定义多关键字排序,将自己真实想要的排序结果返回为true
    	val sortList1 = list.sortWith(
        	(a,b) => {
        		//将预期结果返回为true
                if(a.age < b.age){
                	true
                    }else if(a.age == b.age){
                    	a.salary > b.salary
                    }else{
                   	false
                }
        	}
    	)

    	
    	// 改变value
    	val list = List(
    		("hello",2),("hello",4),("hello",6)
    	)
    	val mapList = list.toMap.mapValues(_*2)
    	println(mapList)
    }
}

4.9 wordcount topN

4.9.1 粗糙版

object SelfScalaTest {

    def main(args: Array[String]): Unit = {
      //1.获取文件原始数据
      val source: BufferedSource = Source.fromFile("data/word")
        //返回每行的迭代器
      val strings: Iterator[String] = source.getLines()
        //可以将全部迭代结果(每一行的所有元素),返回到一个list中
      val list: List[String] = strings.toList
      source.close()
      //2.将原始数据分解为一个一个的单词
      val strings1: List[String] = list.flatMap(_.split(" "))
      //3.将单词分组
      val stringToStrings: Map[String, List[String]] = strings1.groupBy(s => s)

      //4.对分组后的单词进行统计
      val stringToInt: Map[String, Int] = stringToStrings.map(
        kv => (kv._1, kv._2.size)
      )
      //5.对分组统计完成之后的touple排序降序
      val tuples: List[(String, Int)] = stringToInt.toList.sortBy(
        t => t._2
      )(Ordering.Int.reverse)
      //6.将排序结果输出到打印台
      println(tuples)
}
}

4.9.2 精简版

// 初学的时候最好把类型带出来.
object ScalaWordCount{
    def main(args: Array[String]): Unit = {
		// 将文件中的数据读取出来,放到list中
        val lineList: List[String] = Source.fromFile("input/word.txt").getLines().toList
        
        source.close()

		val top3 = 
			LineList
			.flatMap(_.split(" "))
			.groupBy(word=>word)//不可以简化,简化会报错。
			.mapValues(_.size)//mapValues返回的是一个map
			.toList
			.sortBy(t => t._2)(Ordering.Int.reverse)
			.take(3)
		
		println(top3)
    }
} 

4.10 集合的小练习(2种思路)

// 工作中要先写出来。
object Scala_Collection_Test{
    def main(args:Array[String]):Unit={
        val list = List(
            ("hello",4),
            ("hello spark",3),
            ("hello spark scala",2),
            ("hello spark scala hive",3)
        )
        // 将上面的数据进行WordCount后排序取前三名
        	//方法1
        val mapList = list.map(
        	t => (t._1 + " ") * t._2
        )
               
       val top3 = 
			LineList
			.flatMap(_.split(" "))
			.groupBy(word=>word)
			.mapValues(_.size)
			.toList
			.sortBy(t => t._2)(Ordering.Int.reverse)
			.take(3)
        	
        // 方法2
        	//拆分为局部WordCount
       val flatList = list.flatMap{
            t => {
                val str = t._1
                val cnt = t._2
                val words = str.split(" ")
                word.map((_,cnt))
            }
        }
        	//分组
       val groupMap:Map[String,List[(String,Int)] = flatList.groupBy(_._1) 
            //局部统计
      val sumMap = groupMap.mapValues(list.list.map(_._2).sum)
        	//排序
       val top = sumMap.toList.sortBy(t => t._2)(Ordering.Int.reverse)                  
                        
    }
}

五、模式匹配

// Scala中的模式匹配类似于Java中的switch语法 
// 顺序匹配分支,成功则执行分支代码,不成功,继续下一个分支判断。case _分支兜底(一般放到最后,在前面就顺序执行)。无法匹配成功会报错

		var a: Int = 10
        var b: Int = 20
        var operator: Char = 'd'
        var result = operator match {
            case '+' => a + b
            case '-' => a - b
            case '*' => a * b
            case '/' => a / b
            case _ => "illegal"
        }
        println(result)

// 1.匹配常量
// 2.匹配类型
	---匹配类型时,类型前要加临时变量名,后续的计算要使用该变量名参与计算。
	---忽略泛型(你写不写都没用)。
	---数组需要考虑泛型(转化为字节码的时候不是泛型,泛型就是数组的元素类型)。
// 3.匹配数组
for (arr <- Array(Array(0), Array(1, 0), Array(0, 1, 0), Array(1, 1, 0), Array(1, 1, 0, 1), Array("hello", 90))) { // 对一个数组集合进行遍历
    val result = arr match {
        case Array(0) => "0" //匹配Array(0) 这个数组
        case Array(x, y) => x + "," + y //匹配有两个元素的数组,然后将将元素值赋给对应的x,y
        case Array(0, _*) => "以0开头的数组" //匹配以0开头和数组
        case _ => "something else"
    }
    println("result = " + result)
}

// 4.匹配列表
for (list <- Array(List(0), List(1, 0), List(0, 0, 0), List(1, 0, 0), List(88))) {
    val result = list match {
        case List(0) => "0" //匹配List(0)
        case List(x, y) => x + "," + y //匹配有两个元素的List
        case List(0, _*) => "0 ..."
        case _ => "something else"
    }
    println(result)
}

val list: List[Int] = List(1, 2, 5, 6, 7)
list match {
    	// 看集合能否分为三个部分。 ::是连接,空集合也是一个部分。
    case first :: second :: rest => println(first + "-" + second + "-" + rest)
    case _ => println("something else")
}
// 5.匹配元组
for (tuple <- Array((0, 1), (1, 0), (1, 1), (1, 0, 2))) {
    val result = tuple match {
        case (0, _) => "0 ..." //是第一个元素是0的元组
        case (y, 0) => "" + y + "0" // 匹配后一个元素是0的对偶元组
        case (a, b) => "" + a + " " + b
        case _ => "something else" //默认
    }
    println(result)
}
---------------------------使用场景---------------------
///过滤
val (id,name,age,_) = (1,"zhangsan",30,100)

// 6.样例类(比较类的内容)
case class User(name: String, var age: Int)
object ScalaCaseClass {
    def main(args: Array[String]): Unit = {
        val user: User = User("zhangsan", 11)
        val result = user match {
            case User("zhangsan", 11) => "yes"
            case _ => "no"
        }
        println(result)
    }
}
// 7.匹配偏函数,只对集合中的部分数据进行处理。
	//将该List(1,2,3,4,5,6,"test")中的Int类型的元素加一,并去掉字符串。
List(1, 2, 3, 4, 5, 6, "test").collect { case x: Int => x + 1 }.foreach(println)

你可能感兴趣的:(scala,scala)