个人的Scala学习笔记

记录一些新学到的scala知识,偶尔更新。


1、构造方法的问题

class SparkSession private(
    @transient val sparkContext: SparkContext,
    @transient private val existingSharedState: Option[SharedState],
    @transient private val parentSessionState: Option[SessionState],
    @transient private[sql] val extensions: SparkSessionExtensions)
  extends Serializable with Closeable with Logging { self =>

private() 表示构造方法私有化

Scala比Java更面向对象的一个方面是Scala没有静态成员。替代品是,Scala有单例对象:singleton object。 ​ 当单例对象与某个类共享同一个名称时,他被称作是这个类的伴生对象:companion object。你必须在同一个源文件里定义类和它的伴生对象。类被称为是这个单例对象的伴生类:companion class。类和它的伴生对象可以互相访问其私有成员。

private[sql] def this(sc: SparkContext) {
    this(sc, None, None,
      SparkSession.applyExtensions(
        sc.getConf.get(StaticSQLConf.SPARK_SESSION_EXTENSIONS).getOrElse(Seq.empty),
        new SparkSessionExtensions))
  }

这个是辅助构造方法,private[sql]表示包私有,需要在一个包内才能使用

1.1、表示有伴生类和伴生对象

 

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

单例设计模式

伴生类

可以实现全局静态属性或方法

单例对象的属性和方法都可以由伴生类直接调用

Scala比Java更面向对象的一个方面是Scala没有静态成员(无Static)。替代品是,Scala有单例对象:singleton object。

当单例对象与某个类共享同一个名称时,他被称作是这个类的伴生对象:companion object。你必须在同一个源文件里定义类和它的伴生对象。类被称为是这个单例对象的伴生类:companion class。类和它的伴生对象可以互相访问其私有成员。

package com.test.scala.scalaTest
​
object TestObject {
  def main(args: Array[String]): Unit = {
    //val student=new Student("zhangsan",13)
    //student.printInfo()
    Student.newStudent("zhangasn",13).printInfo()
​
    //apply调用
    val student1=Student("adwa",11)
    student1.printInfo()
  }
}
​
//val  既是主构造器的参数,也是属性
//private 构造方法私有化
class Student private(val name:String,val age:Int){
  def printInfo(): Unit ={
    println(s"name=${name},age=${age},School=${Student.School}")//这个Student是伴生对象
  }
}
​
//伴生对象
object Student{
  val School:String="nanhu"
​
  //定义一个类的对象实例创建方法,无需new
  def newStudent(name:String,age:Int): Student=new Student(name,age)
​
  //调用apply的时候,有优惠政策,可以直接省略掉
  def apply(name:String,age:Int): Student=new Student(name,age)
}

3、require 测试,参数检查

@inline final def require(requirement: Boolean, message: => Any) {
    if (!requirement)
      throw new IllegalArgumentException("requirement failed: "+ message)
  }

4、try isSuccess

调用isSuccess来检查一个try是否成功

5、集合、数组就有可能出现 空指针异常

6、运算符的本质

和java本质上的不同

运算符当作对象的方法调用

如:

val n1: Int = 12

n1看作对象

n1+n2  
//等同于
n1.+(n2)
//调同了n1的+方法

真正面向对象的思路!

属于合成方法,可以绕过JVM,sdk中找不到实现

7、if

if有返回值,值是if中的最后一条语句的值

不同分支,返回值类型不同,用公共父类类型的值来接收。如Any

val x:Any=if(x>0){
    "小王"
}else(x<0){
    11
}

//java中的三元运算符: a?b:c

val res:String=if(x>11){
    "1111"
}else{
    "2222"
}
//缩写成
val res=if(x>11) "1111" else  "2222"

8、for 非常适合集合的转换

//不包含10,to的底层是实现
for(i <- Range(1,10)){}
//更好的方法,until的底层就是Range
for(i <- 1 until 10){}

//遍历集合,数组
for(i <- Set(12,23,44)){}

for(i <- Array(1,2,3,4))

循环守卫

作用相当于java中的 continue

for(i <- 1 to 10 if i != 5){}

循环引入变量

for{
    i <- 1 to 10
    j=10 -i
}{
    循环体
}

生成集合类型

yield关键字:保存当前元素,生成集合

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

实现类似java中的break操作

try{
    for(i <- 1 to 10){
        if(i == 5)
        	throw new RuntimeException
        println(i)
    }
}catch{
    case e: Exception => //do nothing
}

//更好的方法 使用Scala中的Breaks类的break方法,实现异常的抛出和捕捉
Breaks.breakable(
	for(i <- 0 util 5){
        Breaks.break()
        println(i)
    }
)

9、While

变量必须在外部定义,不适合处理集合

10、函数式编程 & 面向对象、面向过程

都是scala的特点

面向过程:效率高

面向对象:可维护性强,更重要

都是命令式编程,服务于硬件,每一行代码都能翻译成机器指令


函数式编程:函数指的是数学方面的函数,对人更好理解

都使用val

都有返回值

不停 y=f(x) 来转换

得到的结果是确定的,适合分布式,提高并行处理能力,如RDD

11、函数 & 方法

java一般都说方法,因为方法是指通过对象才能调用的,不能独立调用

scala讲函数,可以不依赖于对象

函数概念:

思想来源:y=f(x)

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

  2. 类中的函数称为方法。

scala中的函数可以随处定义,可以在函数中定义函数

函数参数

  • 可变参数,多个参数时可变参数放最后

//可以接收数组
def f(str: String*): Unit={}
  • 带默认值

def f(str: String = "awdaw"): Unit={}
  • 带名参数 (有默认值的时候适合使用)

f(age=21)

12、Lamda表达式

即 匿名函数,(x:Int)=> {函数体}

可以以函数作为参数

val fun: String => Unit = (name: String) => println(name)
     fun("dad")

def f(func:String=>Unit): Unit ={
    func("wad")
}

f(fun)
//更好的做法
f((name: String) => println(name))

简化:

(1)参数类型可以省略

(2)只有一个参数,圆括号可以省略

(3)如函数体只有一行,大括号{}可以省略

(4)如果每个参数只出现一次,参数省略,=>省略,且后面参数可以用 _表示

(5)如果可以推断出,当前传入的println是函数体,而非调用语句,可以省略下划线

例:

arr.foreach(println)

函数类型 如 String => Unit

一般的函数传入的都是数,这个可以实现把操作传入函数

def f(fun:(Int,Int)=>Int):Int={
      fun(1,2)
    }

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

println(f(add))

//简化
f((x:Int,y:Int) => x+y
f((x,y) => x+y)
f(_+_)  

13、高阶函数

  • 函数可以作为值传递 =

  • 函数可以作为参数传递

  • 函数可以作为返回值传递

//表示函数整体
val f1=f(_)
//或者
val f1=f _

14、mkString

用于把集合/数组里的元素转化为字段串

可以在前后以及间隔添加任意字符

val a=Array("apple","banana","orange")
val b=Seq("dawd","dad")
println(a.mkString("[",",","]"))
println(b.mkString(","))

15、type关键字,<:

type相当于声明一个类型别名:

//String类型用S代替
type S = String

泛型:

上边界:<: 前者必须是后者的子类

下边界:>: 后者是前者的子类

16、惰性赋值:lazy

用lazy定义的变量只有在使用时才会执行初始化。

好处:

  • 打开一个数据库连接。这对于程序来说,执行该操作,代价式昂贵的,所以我们一般希望只有在使用其的引用时才初始化。(当然实际开发中用的是连接池技术)

  • 为了缩短模块启动时间,可以将当前不需要的某些工作推迟执行。

  • 保证对象中其他字段的初始化能优先执行

17、Java序列化 /@transient

@transient是类型修饰符,只能用来修饰字段。在对象序列化的过程中,@transient标记的变量不会被序列化。

Scala序列化继承自Java

序列化的前提:

  1. 类要继承Serializable

  2. 类中所有字段都必须是可序列化的

18、map映射操作

函数式编程的特点,map是一个高阶函数,可以接收函数作为参数

def add(n:Int):Int = {
	n++
}
val list2 = list1.map(add)

说明:map

  1. 将集合遍历

  2. 将各个元素值传给函数

  3. 将得到新值放入到一个新的集合并返回

19、:+ / +:

:+方法用于在集合或数组尾部添加元素

+:方法用于在头部添加元素

20、ClassTag

使用范型时,只要方法体或函数体内包含了T,K,V此类泛型标识,必然会有类型擦除

  • 使用[T:ClassTage]即可,import

def buildArray[T:ClassTag](a1:T, a2:T): Array[T] ={
    val array:Array[T]=Array(a1,a2)
    array
  }
  • Manifest也可,无需import

21、隐式转换 implicit

  • 隐式转换的核心是from类型和to类型

  • 隐式转换只关心类型

使用方法:

  1. 隐式值:用于给方法提供参数

implicit val p = "mobin"  
def person(implicit name : String) = name
  1. 隐式视图:用于类型间转换,或调用类中本不存在的方法(通过import)

def foo(msg : String) = println(msg)
implicit def intToString(x : Int) = x.toString
 foo(10)

class SwingType{
  def  wantLearned(sw : String) = println("兔子已经学会了"+sw)
}
object swimming{
  implicit def learningType(s : AminalType) = new SwingType
}
class AminalType
object AminalType extends  App{
  import com.mobin.scala.Scalaimplicit.swimming._
  val rabbit = new AminalType
    rabbit.wantLearned("breaststroke")         //蛙泳
}

转换时机:

  1. 当方法中的参数的类型与目标类型不一致时

  2. 当对象调用类中不存在的方法或成员时,编译器会自动将对象进行隐式转换

解析机制:

即编译器是如何查找到缺失信息的,解析具有以下两种规则:

1.首先会在当前代码作用域下查找隐式实体(隐式方法 隐式类 隐式对象)

2.如果第一条规则查找隐式实体失败,会继续在隐式参数的类型的作用域里查找

类型的作用域是指与该类型相关联的全部伴生模块,一个隐式实体的类型T它的查找范围如下:

(1)如果T被定义为T with A with B with C,那么A,B,C都是T的部分,在T的隐式解析过程中,它们的伴生对象都会被搜索

(2)如果T是参数化类型,那么类型参数和与类型参数相关联的部分都算作T的部分,比如List[String]的隐式搜索会搜索List的

伴生对象和String的伴生对象

(3) 如果T是一个单例类型p.T,即T是属于某个p对象内,那么这个p对象也会被搜索

(4) 如果T是个类型注入S#T,那么S和T都会被搜索

转换前提:

1.不存在二义性

2.隐式操作不能嵌套使用,即一次编译只隐式转换一次(One-at-a-time Rule)

3.代码能够在不使用隐式转换的前提下能编译通过,就不会进行隐式转换。

22、函数柯里化(currying)

柯里化(Currying)是把接受多个参数的函数变换成接受一个单一参数(最初函数的第一个参数)的函数,并且返回接受余下的参数且返回结果的新函数的技术。

隐式转换中经常用到

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

21、获取类型

getClass/classof[]

p.getClass==classof[Person]

22、Option

Option存在的意义, 就是为了在代码中注明, 让大家一看就知道: "这个东西可能是空的! 用的时候给我小心点

val a:Option[A]=x
val b:A=if(a.isEmpty) 0 else Option[A]

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值