1 补充:IDEA调试程序

IDEA Debug 调试程序
part3面向对象 - 图1
Show Execution Point (Alt+F10) 显示断点位置
Step Over(F8) 下一步
Step Into (F7) 进入到代码,类似于eclipse中的F5
Force Step Into (Alt+Shift+F7) 强制进入代码,会走所有的代码流程(不常用)
Step out (Shift+F8) 跳出当前的方法(最后会回到原debug位置)
Drop Frame
Run to Cursor (Alt+F9) 调到下一个断点

2 面向对象

Scala的类与Java、C++的类比起来更简洁,学完之后你会更爱Scala!!!
对象: 用object关键字修饰的结构
类: 用class 关键字修饰的
类的实例(对象): new class

2.1 对象

2.1.1 单例对象

Scala中没有静态方法和静态字段,没有static,
java中,没有关键字修饰的方法,只能用new class().方法
so 对于一个class来说,所有的方法和成员变量在实例被 new 出来之前都无法访问
虽然可以在class中定义main方法,然并卵…
但是可以使用object这个语法结构来达到同样的目的
用object关键字修饰的对象是单例的

  1. //单例对象
  2. object ScalaSingleton {
  3. def saySomething(msg: String) = {
  4. println(msg)
  5. }
  6. }
  7. object test {
  8. def main(args: Array[String]): Unit = {
  9. ScalaSingleton.saySomething("singleton....")
  10. println(ScalaSingleton)
  11. println(ScalaSingleton)
  12. // 输出结果:
  13. // cn.demo.ScalaSingleton$@28f67ac7
  14. // cn.demo.ScalaSingleton$@28f67ac7
  15. }
  16. }

2.1.2 伴生对象

object单例对象,也叫静态对象,满足条件了也叫伴生对象。
条件1:在同一个源文件中,
条件2:对象名和类名相同
这样的单例对象,被称作是这个类的伴生对象。类被称为是这个单例对象的伴生类。
结论:类和伴生对象之间可以相互访问私有的方法和属性

  1. class Dog {
  2. val id = 1
  3. private var name = "xiaoqing"
  4. def printName(): Unit ={
  5. //在Dog类中可以访问伴生对象Dog的私有属性
  6. println(Dog.CONSTANT + name )
  7. }
  8. }
  9. /**
  10. * 伴生对象
  11. */
  12. object Dog {
  13. //伴生对象中的私有属性
  14. private val CONSTANT = "汪汪汪 : "
  15. def main(args: Array[String]) {
  16. val p = new Dog
  17. //访问私有的字段name
  18. p.name = "123"
  19. p.printName()
  20. }
  21. }

2.1.3 apply方法

通常我们会在类的伴生对象中定义apply方法,当遇到对象名(参数1,…参数n)时apply方法会被调用
正常情况下,对象调用时是不能带参数的,但是如果能找到对应的apply方法,就能调用成功。
part3面向对象 - 图2
当使用对象(参数列表)来调用对象时,会去对象中找对应参数的apply方法,如果找到就执行相应的逻辑,如果找不到,就报错。
注意:只能找到和参数列表对应的apply方法。
要和对象区分开来
ApplyDemo // 对象
ApplyDemo() // ApplyDemo.apply() 方法
该语法的目的:不需通过new关键字,更方便的创建类的实例对象。

  1. object ApplyDemo {
  2. def apply(msg:String) = {
  3. print(s"主食 油泼面,小菜:$msg")
  4. }
  5. def apply(i:Int):Int = {
  6. i * i
  7. }
  8. def main(args: Array[String]) {
  9. //调用了Array伴生对象的apply方法
  10. //def apply(x: Int, xs: Int*): Array[Int]
  11. //arr1中只有一个元素5
  12. val arr1 = Array(5)
  13. println(arr1.toBuffer)
  14. //new了一个长度为5的array,数组里面包含5个null
  15. var arr2 = new Array(5)
  16. println(ApplyDemo("海参炒面"))
  17. println(ApplyDemo.apply("油炸煎饼"))
  18. println(ApplyDemo(1)) }
  19. }

2.1.4 应用程序对象

Scala程序都必须从一个对象的main方法开始,可以通过扩展App特质,不写main方法。

  1. object AppObjectDemo extends App{
  2. //不用写main方法
  3. println("I love you Scala")
  4. }

2.2 类

2.2.1 类的定义

在Scala中,类并不用声明为public。
Scala源文件中可以包含多个类,所有这些类都具有公有可见性。
var 修饰的变量, 这个变量对外提供getter setter方法
val 修饰的变量,是只读属性 对外提供了getter方法,没有setter(相当于java中用final修饰的变量)

  1. class Student {
  2. val id = 666
  3. // _ 表示一个占位符, 编译器会根据变量的具体类型赋予相应初始值
  4. // 注意: 使用_ 占位符是, 变量类型必须指定 var name: String = _
  5. //用var修饰的变量既有getter又有setter
  6. var age: Int = 20
  7. }
  8. object Test{
  9. val name: String = "zhangsan"
  10. def main(args: Array[String]): Unit = {
  11. // 调用空参构造器,
  12. val student = new Student()
  13. student.name = "laowang"
  14. // 类中使用val修饰的变量不能更改
  15. // student.age = 20
  16. println(s"student.name ====== ${student.name} ${student.age}")
  17. println("Test.name ======" + Test.name)
  18. }
  19. }

2.2.2 构造器

构造方法
构造器分为两类:主构造器,辅助构造器
主构造器直接在类名后面定义。
每个类都有主构造器,主构造器的参数直接放置类名后面,与类交织在一起。
如果你没有定义构造器, 类会有一个默认的空参构造器
辅助构造器自定义,使用def this关键字,而且必须调用主构造器,或者其他的辅助构造器
注意:主构造器会执行类定义中的所有语句

  1. /**
  2. *每个类都有主构造器,主构造器的参数直接放置类名后面,与类交织在一起
  3. */class Person(val name: String, val age: Int){
  4. //主构造器会执行类定义中的所有语句
  5. println("执行主构造器")
  6. private var gender = "male"
  7. //用this关键字定义辅助构造器
  8. def this(name: String, age: Int, gender: String){
  9. //每个辅助构造器必须以主构造器或其他的辅助构造器的调用开始
  10. this(name, age)
  11. println("执行辅助构造器")
  12. this.gender = gender
  13. }
  14. var account1:String=_
  15. def this(name:String,age:Int,gender:String){
  16. this(name,age)
  17. this.gender=gender
  18. }
  19. def this(name:String,age:Int,gender:String,account:String){
  20. this(name,age,gender)
  21. this.account1=account
  22. }
  23. println("尼玛,这里还是主构造器")
  24. }
  25. object Person {
  26. def main(args: Array[String]): Unit = {
  27. val s = new Person ("laoduan", 38)
  28. println(s"${s.name} ${s.age}")
  29. val s1 = new Person (“dingding", 18, "female")
  30. println(s"${s1.gender}")
  31. val p2 =new Person("xx2",12,"female","9527")
  32. println(s"${p2.age},${p2.account1}")
  33. }
  34. }

scala的构造器
构造器的作用是用于初始化赋值
主构造器和辅助构造器:
辅助构造器的参数一般比主构造器参数多,主要目的是为了提供更多的初始化参数。
辅助构造器,的参数不能和主构造器的参数完全一致(参数个数和参数类型)
辅助构造器的第一行,必须调用主构造器,或者其他辅助构造器。因为主构造器的参数必须要赋值
如果有有参的主构造器,就不能创建无参对象的实例了,除非再创建一个无参的辅助构造器。
注意主构造器和辅助构造器的作用域:
类中,除了方法和变量的内容,以及辅助构造器内容之外,都属于主构造器。
辅助构造器的作用域只在this方法范围内。

2.2.3 访问权限

成员变量的访问权限

默认权限是public 任何地方都可以访问
private 作用域 类和其伴生对象中
private [this] ,作用域为当前类中,伴生对象中无效
private [packageName] 指定包及其子包有效

  1. /*
  2. * private var age
  3. * age 在这个类中是有getter setter方法的
  4. * 但是前面如果加上了private 修饰, 也就意味着, age只能在这个类的内部以及其伴生类对象中可以访问修改
  5. * 其他外部类不能访问
  6. * */class Student3 private (val name: String, private var age: Int) {
  7. var gender: String = _
  8. // 辅助构造器, 使用def this
  9. // 在辅助构造器中必须先调用类的主构造器
  10. def this(name: String, age:Int, gender: String){
  11. this(name, age)
  12. this.gender = gender
  13. }
  14. // private[this]关键字标识该属性只能在类的内部访问, 伴生类不能访问
  15. private[this] val province: String = "北京市"
  16. def getAge = 18}
  17. // 类的伴生对象object Student3 {
  18. def main(args: Array[String]): Unit = {
  19. // 伴生对象可以访问类的私有方法和属性
  20. val s3 = new Student3("Angelababy", 30)
  21. s3.age = 29
  22. println(s"${s3.age}")
  23. // println(s"${s3.province}") 伴生类不能访问
  24. }
  25. }

方法的访问权限

通用于主构造器,辅构造器,以及普通方法
默认权限是共有的
private 作用域为类和其伴生对象
private [this] ,作用域为当前类中,伴生对象中无效
private [packageName] 指定包及其子包有效 包名的写法,直接写报名,不需要层级路径
主构造器上一样适用于该方法的访问权限
private [cn.edu360.day03] 错误的
private [day03] 正确的

| /
private 加在主构造器前面标识这个主构造器是私有的, 外部不能访问这个构造器 /
class Student2 private (val name: String, var age: Int) {
var gender: String = _

  1. _// 辅助构造器, 使用def this<br /> // 在辅助构造器中必须先调用类的主构造器<br /> _**def this**(name: String, age:Int, gender: String){<br /> **this**(name, age)<br /> **this**._gender _= gender<br /> }<br />}**object **Student2 {<br /> **def **main(args: Array[String]): Unit = {<br /> **val **s1 = **new **Student2(**"laoYang"**, 18, **"male"**)<br /> _println_(**s"$**{s1._gender_}**"**)<br /> }<br />} |

| —- |

类包的访问权限

private 作用域为当前包及其子包 同 private [this]
private [packageName] 作用域为指定包及其子包

| /
private[包名] class 放在类最前面, 是修饰类的访问权限, 也就是说类在某些包下不可见或不能访问
private[edu360] class 代表student4在edu360包下及其子包下可以见, 同级包中不能访问
*/
private[this] class Student4(val name: String, private var age: Int) {
var xx: Int = _
}object Student4{ // 要显示声明main方法,不extends App
def main(args: Array[String]): Unit = {
val s = new Student4(“张三”, 20)

  1. _println_(**s"$**{s.name}**"**)<br /> }<br />} |

| —- |

访问权限总结:

对变量,方法,构造器(主,辅):
对类:

2.2.4 抽象类

在Scala中, 使用abstract修饰的类称为抽象类. 在抽象类中可以定义属性、未实现的方法(抽象方法)和具体实现的方法。

/
abstract修饰的类是一个抽象类
/
abstract class Animal {
println(“Animal’s constructor ….”)
// 定义一个name属性
val name: String = “animal”
// 没有任何实现的方法
def sleep()
// 带有具体的实现的方法
def eat(f: String): Unit = {
println(s”$f)
}
}

2.3 特质Trait

scala中没有interface implements
Trait(特质)相当于 java的接口。比接口功能更强大。特质中可以定义属性和方法的实现。
Scala的类只能够继承单一父类,但是可以实现(继承,混入)多个特质(Trait)使用的关键字是 with和extends
特质不能有任何的类参数,即传递给类的主构造器的参数。
trait PointTest(x: Int, y: Int) // 编译不过

| trait T1 {
// 定义普通方法,有方法实现
def youcanfly()={
println(“tai feng lai le you can fly”)
}
}

trait T2 {
// 定义一个属性
val className: String = **”NB大神班”

  1. **_// 定义一个没有实现的方法,默认就是抽象方法<br /> _**def **teacherSay(name: String)
  2. _// 定义带有具体的实现的方法<br /> _**def **doSomething() = {<br /> _println_(**"群主开始发红包了..."**)<br /> }<br />} |

| —- |

动态混入特质。

| object test{

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

  1. _// 动态混入特征,让类有了特质的方法<br /> _**val **t1 = **new **Teacher **with **T1<br /> _println_(t1.youcanfly())
  2. _// 动态混入特质不能使用extends关键字,可同时混入多个特质<br /> _**val **t = **new **Teacher() **with **T1 **with **T2{<br /> _// 如果特质中有抽象方法,则必须重写该抽象方法,可以不使用override关键字<br /> _**def **teacherSay(name:String)={<br /> _println_(**s"最高face,$**{name}**"**)<br /> }<br /> _// 重写一个有具体的实现的方法,必须使用关键字override<br /> _**override**_ _**def **doSomething() = {<br /> _println_(**"群主:抢到红包继续接龙..."**)<br /> }<br /> }<br /> _println_(t.teach(**"laozhao"**))<br /> _println_(t.doSomething)<br /> println(t.youcanfly())<br /> }<br />}**class **Teacher{<br />} |

| —- |

比较:scala的trait和java中的interface的异同?
1,java的interface只定义方法名称和参数列表,不能定义方法体。而trait则可以定义方法体。
2,在java中实现接口用implements,而在scala中,实现trait用extends和with。
3,java的interface和scala的trait的最大区别是,scala可以在一个class实例化的时候混入trait。
用特质还是用抽象类??
1,优先使用特质。一个类可以扩展多个特质,但却只能扩展一个抽象类。
2,如果需要构造方法,使用抽象类。因为抽象类可以定义带参数的构造器,而特质不行。

2.4 继承

2.4.1 扩展类

在Scala中扩展类的方式和Java一样都是使用extends关键字

2.4.2 重写方法

在Scala中重写一个非抽象的方法必须使用override修饰符

2.4.3 示例

| // 定义一个抽象类abstract class Animal(val age: Int) {
println(“Animal`s main constructor invoked” + age)
//定义一个抽象方法
def run()
def breath(): Unit = {
println(“呼吸氧气”)
}
}
// 定义一个特质,定义一个普通方法trait Fightable {
def fight(): Unit = {
println(“用嘴咬”)
}
}
// 定义一个特质,一个抽象方法trait Flyable {// 定义一个抽象方法
def fly()
}
定义一个bird类,实现多个特质
//在scala中,不论是继承还是实现特质,第一个都用extends关键字class Bird extends Flyable with Fightable {
override def fly(): Unit = {
println(“用翅膀飞”)
}
}
object Bird {
def main(args: Array[String]): Unit = {
val b = new Bird
b.fly()
}
}
定义一个类,继承类,并实现多个trait
class Monkey(age: Int) extends Animal(age) with Flyable with Fightable {
//重写抽象的方法, 可以不加override关键字
def run(): Unit = {// super()
println
(“跳着跑”)
}

//重写非抽象的方法,必须加override关键字
override def breath(): Unit = {
println(“猴子呼吸”)
}

override def fly(): Unit = {
println(“乘着筋斗云飞”)
}

override def fight(): Unit = {
println(“用棒子打”)
}
println(“monkey`s main constructor invoked” + age)
}
object Monkey {
def main(args: Array[String]): Unit = {
val a: Animal = new Monkey(100)
a.breath()
}
} | | —- |

2.4.4 总结

样例类/样例对象

样例类:使用case关键字 修饰的类,重要的特征就是支持模式匹配,多例
样例object:使用case关键字修饰的对象,支持模式匹配,单例
case class 和 class的一些区别:
case class在初始化的时候,不用new,而普通类初始化时必须要new。
默认实现了equals和hashCode
case class 支持模式匹配(最重要的特征)
有参用case class,无参用case object

/
样例类,使用case 关键字 修饰的类, 其重要的特征就是支持模式匹配
/
case class Message(msg: String)
/*
样例object, 不能封装数据, 其重要特征就是支持模式匹配
*/
case object CheckHeartBeat
object TestCaseClass extends App{
// 可以不使用new 关键字创建实例
val msg = Message(“hello”)
println(msg.msg)
}

3 模式匹配

Scala有一个十分强大的模式匹配机制,可以应用到很多场合:如switch语句、类型检查等。
并且Scala还提供了样例类,对模式匹配进行了优化,可以快速进行匹配
模式匹配就是match 和 一系列的case 语法实现
其中,每一个case 里面就是一个匿名函数
模式匹配的基本关键字 就是 match case

3.1 匹配字符串

import scala.util.Randomobject CaseDemo01 extends App{
val _arr
= Array(“YoshizawaAkiho”, “YuiHatano”, “AoiSola”)
val name = arr(Random.nextInt(arr.length))
name match {
case “YoshizawaAkiho” => println(“xx老师…”)
case “YuiHatano” => println(“oo老师…”)
case => _println(“真不知道你们在说什么…”)
}
}

3.2 匹配类型

import scala.util.Randomobject CaseDemo02 extends App{ //val v = if(x >= 5) 1 else if(x < 2) 2.0 else “hello”
val _arr = Array(“hello”, 1, 2.0, CaseDemo2)
val v = arr(Random.nextInt(arr.length))
println(v)
v match {
case x: Int => println(“Int “ + x)
case y: Double if(y >= 0) => println(“Double “+ y) // if 守卫 case z: String => println(“String “ + z)
case CaseDemo02 => {
println(“case demo 2”)
//throw new Exception(“not match exception”)
}
case _ => throw new Exception(“not match exception”)
}
}

注意:case y: Double if(y >= 0) => …
模式匹配的时候还可以添加守卫条件。如不符合守卫条件,将掉入case _中

3.3 匹配数组、元组、集合

| object CaseDemo03 extends App{

val arr = Array(1, 3, 5)
arr match {
case Array(1, x, y) => println(x + “ “ + y)
case Array(0) => println(“only 0”)
case Array(0, *) => _println(“0 …”)
case => _println(“something else”)
}

val lst = List(3, -1)
lst match {
case 0 :: Nil => println(“only 0”)
case x :: _y :: Nil => _println(s”x: $x y: $y)
case 0 :: _tail => _println(“0 …”)
case => _println(“something else”)
}

val tup = (2, 3, 5)
tup match {
case (2, x, y) => println(s”1, $x , $y)
case (, z, 5) => _println(z)
case => _println(“else”)
}
} | | —- |

3.4 样例类

在Scala中样例类是一中特殊的类,可用于模式匹配。case class是多例的,后面要跟构造参数,case object是单例的,无需参数

| import scala.util.Random
case class SubmitTask(id: String, name: String)case class HeartBeat(time: Long)case object CheckTimeOutTask
object CaseDemo04 extends App{
val _arr
= Array(CheckTimeOutTask, HeartBeat(12333), SubmitTask(“0001”, “task-0001”))

arr(Random.nextInt(arr.length)) match {
case SubmitTask(id, name) => {
println(s”$id, $name)
}
case HeartBeat(time) => {
println(time)
}
case CheckTimeOutTask => {
println(“check”)
}
}
} | | —- |

3.5 Option类型

在Scala中Option类型样例类用来表示可能存在或也可能不存在的值(Option的子类有Some和None)。Some包装了某个值,None表示没有值

object OptionDemo {
def main(args: Array[String]) {
val map = Map(“a” -> 1, “b” -> 2)
val v = map.get(“b”) match {
case Some(i) => i
case None => 0
}
println(v)
//更好的方式
val v1 = map.getOrElse(“c”, 0)
println(v1)
}
}

3.6 偏函数

被包在大括号内没有match的一组case语句是一个偏函数,它是PartialFunction[A, B]的一个实例,A代表输入参数类型,B代表返回类型,常用作输入模式匹配

object PartialFuncDemo {
def func1(num: String) : Int = num match {
case “one” => 1
case “two” => 2
case => -1
}
def func2: PartialFunction[String, Int] = {
case “one” => 1
case “two” => 2
case
=> -1
}
def main(args: Array[String]) {
println(func1(“one”))
println(func2(“one”))
}
}

偏函数本质上是由多个case语句组成的针对每一种可能的参数分别进行处理的一种“结构较为特殊”的函数,就是一个参数的函数。(Function1)
case语句声明的变量就是偏函数的参数,既然case语句只能声明一个变量,那么偏函数受限于此,也只能有一个参数

|

object PartialFunctionDemo2 {
def f:PartialFunction[Any,Int]={
case i:Int => i 10
case _ => i
10
}
def main(args: Array[String]): Unit = {

  1. **val **arr = _Array_(1,3,5,**"seven"**)<br /> _// arr.map{case t:Int =>t*10}<br /> _**val **collect: Array[Int] = arr.collect {<br /> **case **t: Int<br /> => t * 10<br /> }<br /> println(collect)
  2. arr.collect(_f_).foreach(_println_)<br /> }<br />} |

| —- |

偏函数最常用的就是方法中要求传入偏函数的类型。