Scala中的类不声明为public
语句结束的符号
可以作为语句结束的符号是:常量,标识符,保留字以及以下的分隔符:
this null true false return type <xml-start>
- ) ] }
{}和()的作用
Ø 大括号{}用于代码块,计算结果是代码最后一行;
Ø 大括号{}用于拥有代码块的函数;
Ø 大括号{}在只有一行代码时可以省略,除了case语句(Scala适用);
Ø 小括号()在函数只有一个参数时可以省略(Scala适用);
Ø 几乎没有二者都省略的情况。
$和${}拼接字符串==》var s = 1 ;print(s"$s")
tye相当于声明一个类型别名
闭包??由代码和代码用到的任何非局部变量定义构成
闭包是一个函数,返回值依赖于声明在函数外部的一个或多个变量。
闭包通常来讲可以简单的认为是可以访问一个函数里面局部变量的另外一个函数。
带名参数的参数列表可以和顺序不一致,但需要指定名称
seq _*
mkString添加分割元素
distinct 去重
包名的重命名和隐藏
伴生对象就是为了解决scala没有static的
apply 作为伴生对象的一个方法,可以直接用Object(参数*)的方式
unapply apply的反向操作
包对象 package object
只有主构造器才能调用超类的构造器
equals 方法参数类型为Any,否则不生效
foreach用于遍历集合,而map用于映射(转换)集合到另一个集合
两个方法的共同点在于(foreach和map):都是用于遍历集合对象,并对每一项执行指定的方法。
而两者的差异在于:foreach无返回值(准确说返回void),map返回集合对象。
结论就是:foreach 无法代替map. 而map方法却可以代替foreach。简单理解就是,foreach用于遍历集合,而map用于映射(转换)集合到另一个集合。
特质
多个特质从最后开始加载
特质可以作为日志的处理
特质可以继承类,对象和特质,但在继承类的时候,当其他类集成改特质的时候,特质继承的类要是其他类继承类的超类
特质的特点:
Scala中类只能继承一个超类, 可以扩展任意数量的特质
特质可以要求实现它们的类具备特定的字段, 方法和超类
与Java接口不同, Scala特质可以提供方法和字段的实现
当将多个特质叠加使用的时候, 顺序很重要
提前定义 laz
case class (样本类)是什么
答:样本类是一种不可变且可分解类的语法糖,这个语法糖的意思大概是在构建时,自动实现一些功能。「样本类」常常用于描述「不可变」的「值对象」,样本类具有以下特性:
(1)自动添加与类名一致的构造函数(这个就是前面提到的伴生对象,通过apply方法实现),即构造对象时,不需要new;
(2)样本类中的参数默认添加val关键字,即参数不能修改;
(3)默认实现了toString,equals,hashcode,copy等方法;
(4)样本类可以通过==比较两个对象,并且不在构造方法中定义的属性不会用在比较上。
自动规则
隐式地声明字段为val;
自动混入特质ProductN;
自动地生成equals, canEqual, hashCode, toString, copy等方法;
伴生对象中自动生成apply, unapply方法。
//声明一个样本类
case class MyCaseClass(number: Int, text: String, others: List[Int]){
println(number)
}
//不需要new关键字,创建一个对象
val dto = MyCaseClass(3, "text", List.empty) //打印结果3
//利用样本类默认实现的copy方法
dto.copy(number = 5) //打印结果5
val dto2 = MyCaseClass(3, "text", List.empty)
pringln(dto == dto2) // 返回true,两个不同的引用对象
class MyClass(number: Int, text: String, others: List[Int]) {}
val c1 = new MyClass(1, "txt", List.empty)
val c2 = new MyClass(1, "txt", List.empty)
println(c1 == c2 )// 返回false,两个不同的引用对象
scala建议如果函数的参数只有一个,可以考虑使用{}代替()
传值函数==先计算参数值,传名函数==不需要先计算参数值
简单来说, => Unit是 传名函数, 只传入了一个表达式, 在调用时才会去执行, 使用 code调用
() => 是传值函数, 传入的计算后的值, 使用 code() 调用
使用 @tailrec 标签可使编译器强制使用尾递归。
@tailrec //告诉编译器
def tailSum(n: Int, acc: Int = 0): Int = {
if(n == 0) {
acc
} else {
tailSum(n - 1, acc + n)
}
}
元组最多存放22个元素,元组可以存放不同类型的数据
(A,B,_)=(1,"2",3.23) ==> A = 1,B = "2"
ofDim(a,b) == 创建指定长度b的a维数组
partition(p: (A) ⇒ Boolean): (List[A], List[A]) 将列表分为两部分,第一部分为满足条件p的元素,第二部分为不满足条件p的元素
scala类实现属性原则
1、var foo:Scala自动合成一个getter和setter
2、val foo:Scala自动合成getter
3、由你来定义foo和foo_=方法
4、由你来定义foo方法
伴生类的总结:
1 伴生类中定义的字段和方法, 对应同类class类中的成员字段和成员方法;
2 伴生对象中定义的字段和方法, 对应同名类中的静态方法, 所以可以认为Scala中的object关键字是静态的另一种表示方式, 只是scala将这些静态的东西也封装成了对象;
3 伴生对象中定义的字段和方法, 对应虚构类中的成员字段和方法。
4 同名类中的静态方法, 会访问单例的虚构类对象, 将相关的逻辑调用到虚构类中的成员方法中。 由于虚构类是单例的, 所以可以保证伴生对象中的字段都是唯一的。 也就是说虚构类的单例性, 保证了伴生对象(即scala中的object修饰的单例对象)中信息的唯一性。
5 伴生对象中的逻辑, 都转移到虚构类中去处理
6 伴生类中的逻辑, 都转移到同名类中的成员方法中去处理
7 需要注意,伴生类并不是单例的,它仍然可以创建多个对象, 只要在其他地方能够访问到这个伴生类
构造器
1.辅助构造器的名称为this
2. 每个辅助构造器都必须以一个对先前已定义的其他辅助构造器或主构造器的调用开始
3. Scala中,每个类都有主构造器,但并不以this定义。
4. 只有主构造器的参数才会被编译成字段,其初始化的值为构造时传入的参数。
* 1)主构造器直接跟在类名后面。
* 2)主构造器执行的时候,会执行类中所有的语句。但不包含函数或方法
* 3)如果主构造器参数不加val或者var,为private
特质或者对象,不能含有参数
包特性
1.包的路径是相对路径
2.包可以包含类,特质和对象,但不能包含函数或变量的定义
3.包可以在文件的任何地方被引入
var d = "s";def dd = d?
class ApplyOperation {
}
class ApplyTest{
def apply() = println("I am into spark so much!!!")
def haveATry: Unit ={
println("have a try on apply")
}
}
object ApplyTest{
def apply() = {
println("I am into Scala so much")
new ApplyTest
}
}
object ApplyOperation{
def main (args: Array[String]) {
val array= Array(1,2,3,4)
val a = ApplyTest() //这里就是使用object 的使用
a.haveATry
a() // 这里就是 class 中 apply使用
}
}
Any是abstract类,它是Scala类继承结构中最底层的。所有运行环境中的scala类都是直接或间接继承自Any这个类,它就是其它语言(.Net,Java等)中的Object。
Nothing是所有类型的子类,Nothing没有对象,但是可以用来定义类型,例如一个方法抛出异常,则不论该方法应该返回哪种类型,异常的返回值可以是Nothing。
AnyRef是所有引用类型的基类。
AnyVal是所有值类的基类。
标志符:
字面量标识符
字面量标识符是用反引号`....`包括的任意字符串。如:
`x` `<clinit>` `yield`
思路是你可以把运行时环境认可的任意字符串放在反引号之间当作标识符。结果总被当作scala标识符。即使包含在反引号间的名称是scala保留字,这个规则也有效。在java的Thread类中访问静态的yield方法是它典型用例。你不能写Thread.yield(),因为yield是scala的保留字。然而可以在反引号里引用方法的名称,例如:Thread.`yield`()。
操作符标识符
操作符标识符由一个或多个操作符字符组成。操作符字符是一些如:+、:、?、-或#的可打印的ASCII字符(注:更精确地说,操作符字符属于数学符号(Sm)或其它符号(So)的Unicode集,或者不包含字母、数字、括号、方括号、花括号、单或双引号、或者下划线、句号、分号、冒号、回退字符的7位ASCII字符)。以下是一些操作符标识符的例子:
+ ++ ::: <?> : ->
Scala编译器将在内部“粉粹”操作符标识符以转换成合法的内嵌“$”的java标识符。例如,标识符:-> 将被内部表达为$colon$minus$greater。若你想从Java代码访问这个标识符,就应使用这种内部表达方式。
Scala里的操作符标识符可以变得任意长,因此在java和scala间有一些小差别。Java里,输入x<-y将会被拆分成四个词汇符号,所以写成x < - y也没什么不同。Scala里,<-将被作为一个标识符拆分,而得到x <- y。所以如果你想要以第一种方式解释,需要在“<”与“-”字符间加一个空格。
混合标识符
混合标识符由字母数字组成,后面跟着下划线和一个操作符标识符。如:unary_+被用做定义一元的“+”操作符的方法名、myvar_=被用做定义赋值操作符的方法名。
中置操作符代表一个带有两个参数的方法(一个隐式的参数和一个显式的参数)
结合性决定了当有一系列同一优先级的操作符时,它们的求值顺序。在Scala中,绝大部分的操作符都是左结合(从左到右)的,除了:1. 以冒号:结尾的操作符;2. 赋值操作符。而且,在右结合时,二元操作符的参数顺序也发生了变化:二元操作符是其第二个参数的方法。
// ::操作符用来构造列表
1 :: 2 :: Nil // Nil表示空列表
// 上面表达式表示:
1 :: (2 :: Nil)
// 如果变为完整的方法调用:
(Nil.::(2)).::(1) // 注意右结合时参数顺序的变化
柯里化是指将原来接受两个参数的函数变成新的接受一个参数的函数的过程。新的函数返回一个以原有第二个参数作为参数的函数。
集合分为三大类:Seq,Set,Map
sealed关键字可以修饰类和特质(特质)。密封类提供了一种约束:不能在类定义的文件之外定义任何新的子类,以防止继承的滥用,修饰的trait,class只能在当前文件里面被继承
进程控制
!操作符返回的结果是被执行程序返回的值:程序成功执行的话就是0,否则为错误的非0值
!!会以字符串的形式返回
偏函数:
1)是一个将类型A转为类型B的特质。
2)接受A类型的输入参数,返回值为B类型。
3)是一个一元函数,“-”符号作用于类型表示逆变,-A表明输入参数为A类型或A类型的父类,也就是说输入的参数应为A的子集,具有“部分”的含义。
4)函数有可能“偏”离了A定义域(Type)类型,而成为值域B, +表明可以是B或B的子类,具有“全部”的含义。
类似于Java,当你将Scala字段标注为 @BeanProperty时,getter和setter方法会自动生成,便可以直接调用了。
putIfAbsent()保存数据的时候,如果该链表中保存的有相同key的值,那么就不会对我们当前的value进行保存
@GuardedBy
1、@GuardedBy( "this" ) 受对象内部锁保护
2、@GuardedBy( "fieldName" ) 受 与fieldName引用相关联的锁 保护。
3、@GuardedBy( "ClassName.fieldName" ) 受 一个类的静态field的锁 保存。
4、@GuardedBy( "methodName()" ) 锁对象是 methodName() 方法的返值,受这个锁保护。
5、@GuardedBy( "ClassName.class" ) 受 ClassName类的直接锁对象保护。而不是这个类的某个实例的锁对象。
o.map(x => k) k是最终输出的结果类型
var (k,v) = (x,y) ==> k = x,v = y
scala> var (k,v) = (1,2)
k: Int = 1
v: Int = 2
lazy
被lazy修饰的变量初始化的时机是在第一次使用此变量的时候才会赋值,并且仅在第一次调用时计算值,即值只会被计算一次,赋值一次,再之后不会被更改了,所以lazy修饰的变量必须同时是val修饰的不可变变量
场景:
1.打开一个数据库连接。这对于程序来说,执行该操作,代价式昂贵的,所以我们一般希望只有在使用其的引用时才初始化。(当然实际开发中用的是连接池技术)
2.为了缩短模块启动时间,可以将当前不需要的某些工作推迟执行。
3.保证对象中其他字段的初始化能优先执行。
Option 有两个子类别,一个是 Some,一个是 None?
flatMap:最后返回的集合是以谁调用他为准的,比如Seq调用flatMap,返回的就是Seq。List就是返回List,其作用为取队列的子元素
scala> var B = List(Some(1),Some(2),List(3,4),List("10","12",Some("15")))
B: List[Equals with scala.collection.IterableOnce[Any] with java.io.Serializable
] = List(Some(1), Some(2), List(3, 4), List(10, 12, Some(15)))
scala> B.flatMap(x => x)
res22: List[Any] = List(1, 2, 3, 4, 10, 12, Some(15))
flatten:可以把嵌套的结构展开.
scala> List(List(1,2),List(3,4)).flatten
res0: List[Int] = List(1, 2, 3, 4)
::问题??
groupBy函数
groupBy[K](f: (A) ⇒ K): immutable.Map[K, Repr],参数是一个 (A) => K 的函数,即有一个输入和一个输出的函数,(A) => K 中A即Seq中的每项,而K为该项对应的key,Scala根据这个函数返回的key,判断那些项是一组的
ceateOrReplaceTempView:创建临时视图,此视图的生命周期与用于创建此数据集的[SparkSession]相关联。
createGlobalTempView:创建全局临时视图,此时图的生命周期与Spark Application绑定。
map和mapPartitions的区别
ap: 比如一个partition中有1万条数据;那么你的function要执行和计算1万次。
MapPartitions:一个task仅仅会执行一次function,function一次接收所有的partition数据。只要执行一次就可以了,性能比较高。
grouped()是将几个元素进行组合, 返回的是一个List<Iterator>的List;
groupedBy()指定分类的函数, 返回的是一个Map<K,List[Value]>的Map.
forall:对集合中的元素进行某个判断,全部为true则返回true,反之返回false。
for循环中的 yield 会把当前的元素记下来,保存在集合中,循环结束后将返回该集合。Scala中for循环是有返回值的。如果被循环的是Map,返回的就是Map,被循环的是List,返回的就是List
yield 关键字的简短总结:
针对每一次 for 循环的迭代, yield 会产生一个值,被循环记录下来 (内部实现上,像是一个缓冲区).
当循环结束后, 会返回所有 yield 的值组成的集合.
返回集合的类型与被遍历的集合类型是一致的.
@模式匹配符?
-eq(equal) : 测试两个整数是否相等;比如 $A -eq $B
-ne(inequality) : 测试两个整数是否不等;不等,为真;相等,为假;
-gt(greter than) : 测试一个数是否大于另一个数;大于,为真;否则,为假;
-lt(less than) : 测试一个数是否小于另一个数;小于,为真;否则,为假;
-ge(greter equal): 大于或等于
-le(less equal) :小于或等于
private[包名],private[this] 可以放在字段,方法和类上,用来限制访问权限;
1、private[包名]包名可以是父包名或当前包名,如果是父包名,则父包和子包都可以访问
2、private[this]修饰的方法或字段只能在本类访问,如果是字段编译成java的时候就没有get或set方法。
3、如果有两个名称相同的子包,比如 package x.y.z.x.v
有两个x,当private[x]的是否离当前包最近的一个包生效