scala复习(3)

1.面向对象
类: class 关键字修饰的
对象: object 关键字修饰的
类的实例:new 类()  类的实例对象

Scala的类与Java、C++的类比起来更简洁,学完之后你会更爱Scala!!!
对象: 用object关键字修饰的结构
类: 用class 关键字修饰的
类的实例(对象): new class
1.1.对象
1.1.1.单例对象
Scala中没有静态方法和静态字段,没有static,
java中,没有关键字修饰的方法,只能用new class().方法
so 对于一个class来说,所有的方法和成员变量在实例被 new 出来之前都无法访问
虽然可以在class中定义main方法,然并卵…
但是可以使用object这个语法结构来达到同样的目的
用object关键字修饰的对象是单例的,称为单例对象,静态对象。
 //单例对象
object ScalaSingleton {
    def saySomething(msg: String) = {
        println(msg)
    }
}
object test {
    def main(args: Array[String]): Unit = {
        ScalaSingleton.saySomething("singleton....")
        println(ScalaSingleton)
        println(ScalaSingleton)
        // 输出结果:
        // cn.demo.ScalaSingleton$@28f67ac7
        // cn.demo.ScalaSingleton$@28f67ac7
    }
}
1.1.2.伴生对象
伴生对象是一种特殊的单例对象。是一种相对概念,需要满足两个条件:
条件1:在同一个源文件中,
条件2:对象名和类名相同
这样的单例对象,被称作是这个类的伴生对象。类被称为是这个单例对象的伴生类。
结论:类和伴生对象之间可以相互访问私有的方法和属性
class Dog {
  val id = 1
  private var name = "xiaoqing"

  def printName(): Unit ={
    //在Dog类中可以访问伴生对象Dog的私有属性
    println(Dog.CONSTANT + name )
  }
}

/**
  * 伴生对象
  */
object Dog {

  //伴生对象中的私有属性
  private val CONSTANT = "汪汪汪 : "

  def main(args: Array[String]) {
    val p = new Dog
    //访问私有的字段name
    p.name = "123"
    p.printName()
  }
}
1.1.3.apply方法
通常我们会在类的伴生对象中定义apply方法,当遇到对象名(参数1,...参数n)时apply方法会被调用
正常情况下,对象调用时是不能带参数的,但是如果能找到对应的apply方法,就能调用成功。

当使用对象(参数列表)来调用对象时,会去对象中找对应参数的apply方法,如果找到就执行相应的逻辑,如果找不到,就报错。
注意:只能找到和参数列表对应的apply方法。
要和对象区分开来
ApplyDemo   // 对象
ApplyDemo() // ApplyDemo.apply()   方法
该语法的目的:不需通过new关键字,更方便的完成类和实例对象的初始化。
object ApplyDemo {
def apply(msg:String) = {
print(s"主食 油泼面,小菜:$msg")
}
def apply(i:Int):Int = {
i * i
}


  def main(args: Array[String]) {
    //调用了Array伴生对象的apply方法
    //def apply(x: Int, xs: Int*): Array[Int]
    //arr1中只有一个元素5
    val arr1 = Array(5)
    println(arr1.toBuffer)

    //new了一个长度为5的array,数组里面包含5个null
    var arr2 = new Array(5)

  println(ApplyDemo("海参炒面"))
    println(ApplyDemo.apply("油炸煎饼"))
println(ApplyDemo(1))
  }
}

1.1.4.IDEA调试程序
IDEA Debug  调试程序


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)  调到下一个断点

1.1.5.应用程序对象
Scala程序都必须从一个对象的main方法开始,可以通过扩展App特质,不写main方法。
object AppObjectDemo extends App{
  //不用写main方法
  println("I love you Scala")
}
1.2.类
类和对象,应该怎么使用???
优先使用对象,如果需要构造器,那么只能使用类。
如果需要封装数据的时候,Person,需要使用到类。
object,class   伴生类

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

  val id = 666

    // _ 表示一个占位符, 编译器会根据变量的具体类型赋予相应初始值
    // 注意: 使用_ 占位符是, 变量类型必须指定
    var name: String = _

  //用var修饰的变量既有getter又有setter
  var age: Int = 20
}

object Test{
    val name: String = "zhangsan"
    def main(args: Array[String]): Unit = {

        // 调用空参构造器, 
        val student = new Student()
        student.name = "laowang"

        // 类中使用val修饰的变量不能更改
        // student.age = 20

        println(s"student.name ====== ${student.name} ${student.age}")
        println("Test.name ======" + Test.name)
    }
}

1.2.2.构造器
1,主构造器
2,辅助构造器
同java中的构造方法
构造器分为两类:主构造器,辅助构造器
主构造器直接在类名后面定义。
每个类都有主构造器,主构造器的参数直接放置类名后面,与类交织在一起。
如果没有定义构造器, 类会有一个默认的空参构造器
辅助构造器自定义,使用def this关键字,而且必须调用主构造器,或者其他的辅助构造器
注意:主构造器会执行类定义中的所有语句
/**
  *每个类都有主构造器,主构造器的参数直接放置类名后面,与类交织在一起
  */
class Person(val name: String, val age: Int){
  //主构造器会执行类定义中的所有语句
  println("执行主构造器")

  private var gender = "male"

  //用this关键字定义辅助构造器
  def this(name: String, age: Int, gender: String){
    //每个辅助构造器必须以主构造器或其他的辅助构造器的调用开始
this(name, age)
    println("执行辅助构造器")
    this.gender = gender
  }
  var account1:String=_

  def this(name:String,age:Int,gender:String){
    this(name,age)
    this.gender=gender
  }
  def this(name:String,age:Int,gender:String,account:String){
    this(name,age,gender)
    this.account1=account
  }

    println("尼玛,这里还是主构造器")

}

object Person {
    def main(args: Array[String]): Unit = {
        val s = new Person ("laoduan", 38)
        println(s"${s.name} ${s.age}")

        val s1 = new Person (“dingding", 18, "female")
        println(s"${s1.gender}")
val p2 =new Person("xx2",12,"female","9527")
println(s"${p2.age},${p2.account1}")
    }
}

scala的构造器
主构造器和辅助构造器
创建对象的同时对属性赋值,初始化赋值
主构造器,和类的定义交织在一起,如果主构造器的变量使用了val或var修饰,变成了类的一个成员属性。
辅助构造器,def this(参数列表),
辅助构造器的第一行,必须调用其他的构造器(主,辅助。。。)
辅助构造器和主构造器的参数列表,不能完全一致,参数类型和个数一致。
如果想定义空参的辅助构造器,调用的主构造器的参数必须赋值(初始化)
作用域范围:
主: 类中除了属性和方法之外,都是构造器的作用域
辅助: 只在自己的方法范围之内

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

/*
*   private var age
*   age 在这个类中是有getter setter方法的
*   但是前面如果加上了private 修饰, 也就意味着, age只能在这个类的内部以及其伴生类对象中可以访问修改
*   其他外部类不能访问
* */
class Student3 private (val name: String, private var age: Int) {

    var gender: String = _

    // 辅助构造器, 使用def this
    // 在辅助构造器中必须先调用类的主构造器
    def this(name: String, age:Int, gender: String){
        this(name, age)
        this.gender = gender
    }
    // private[this]关键字标识该属性只能在类的内部访问, 伴生类不能访问
    private[this] val province: String = "北京市"

    def getAge = 18
}

// 类的伴生对象
object Student3 {
    def main(args: Array[String]): Unit = {
        // 伴生对象可以访问类的私有方法和属性
        val s3 = new Student3("Angelababy", 30)
        s3.age = 29
        println(s"${s3.age}")
        // println(s"${s3.province}") 伴生类不能访问
    }
}
方法的访问权限
通用于主构造器,辅构造器,以及普通方法
默认权限是共有的
private 作用域为类和其伴生对象
private [this] ,作用域为当前类中,伴生对象中无效  
private [packageName]  指定包及其子包有效   包名的写法,直接写报名,不需要层级路径
主构造器上一样适用于该方法的访问权限
private [cn.edu360.day03]    错误的   
private [day03]				正确的

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

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

类包的访问权限
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{ 
    def main(args: Array[String]): Unit = {
        val s = new Student4("张三", 20)

        println(s"${s.name}")
    }
}

访问权限总结:
对于成员属性,成员方法,构造器(主构造器,赋值xxx):
默认的: public
private: 类和其伴生对象中有效,其他地方无效
private [this] : 只在类中有效
private [package] : 指定包及其子包内有效
对于主构造器,private写在哪里?构造器的前面 

对于类:
默认的:public
private private[this]:在当前包及其子包范围内有效
private [package]: 在指定包及其子包范围内有效
[edu360.scala.day02]  : package写法是错误的
[day02]:正确的,只需要一个包名就ok了。
面向对象的特性:封装 继承 多态
1.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")
    }}

类和对象,该怎么选择?
可以认为,对象本质上拥有类的所有特性,但是object没有构造器,必须是无参的。
如果不需要构造器封装数据,都优先使用object。

1.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大神班"

    // 定义一个没有实现的方法,默认就是抽象方法
    def teacherSay(name: String)

    // 定义带有具体的实现的方法
    def doSomething() = {
        println("群主开始发红包了...")
    }
}
动态混入特质。
object test{

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

    // 动态混入特征,让类有了特质的方法
    val t1 = new Teacher with T1
    println(t1.youcanfly())

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

比较: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,如果需要构造方法,使用抽象类。因为抽象类可以定义带参数的构造器,而特质不行。



1.4.继承
1.4.1.扩展类
在Scala中扩展类的方式和Java一样都是使用extends关键字
1.4.2.重写方法
在Scala中重写一个非抽象的方法必须使用override修饰符
1.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()
  }
}


1.4.4.总结
在继承抽象类或者特质的时候,继承抽象类使用extends,实现特质用with和extends,当类只实现了特质的时候,第一个关键字必须是extends
实现类还是特质,抽象方法都必须被实现,而且可以不使用override关键字
重写非抽象方法,必须使用override关键字
特质支持动态混入,在类实例化的时候,可以通过with关键字混入特质。
特质不能有构造器,抽象类可以有。

样例类/样例对象
样例类:使用case关键字 修饰的类,重要的特征就是支持模式匹配,多例
样例object:使用case关键字修饰的对象,支持模式匹配,单例
case class 和 class的一些区别:
case class在初始化的时候,不用new,而普通类初始化时必须要new。
case class 重写了toString方法。
默认实现了equals和hashCode
case class 实现了序列化接口
case class 支持模式匹配(最重要的特征),所有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)
}


2.模式匹配

相亲
候选人: 帅哥美女

择偶标准: 只要有一个条件满足了,就可以进行下一步,然后其他的条件就不管
温柔体贴
美丽大方
智商高
距离
xxx

一直找到一个能满足的条件,say bye bye



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

object 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("真不知道你们在说什么...")
  }
}

2.2.匹配类型
import scala.util.Random

object 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 _中
2.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")
  }
}

2.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")
    }
  }
}

2.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)
  }
}

2.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)


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

    val arr = Array(1,3,5,"seven")
    //    arr.map{case t:Int =>t*10}
    val collect: Array[Int] = arr.collect {
      case t: Int
      => t * 10
    }
    println(collect)

    arr.collect(f).foreach(println)
  }
}

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


猜你喜欢

转载自blog.csdn.net/a331685690/article/details/80618202