zoukankan      html  css  js  c++  java
  • scala(三)

    一、面向对象编程——类

          1、定义一个简单的类

              

    class HelloWorld {
    private var name = "leo"
    def sayHello() { print("Hello, " + name) }
    def getName = name
    }
    

      创建类的对象, 并调用其方法

          

    val helloWorld = new HelloWorld
    helloWorld.sayHello()
    print(helloWorld.getName)
    

      调用类中的方法时,也可以不加括号, 如果定义方法时不带括号, 则调用方法时也 不能带括号 

         2、gettersetter

            假设一个类中有 声明一个字段  val  age=0,则在scala中getter 和setter分别叫做 age 和age_    (反编译得来)

            (1)定义不带privatevar 字段, 此时scala生成的面向JVM的类时, 会定义为private的 name字段, 并提供publicgettersetter方法 

             (2)而如果使用private修饰字段, 则生成的gettersetter也是private

              (3)如果定义val 字段, 则只会生成getter方法

              (4)如果不希望生成settergetter方法, 则将字段声明为private[this]

    class Student {
    var name = "leo"
    }
    

      调用gettersetter方法, 分别叫做namename_ =

    val leo = new Student
    print(leo.name)
    leo.name = "leo1"
    

        2.1自定义getter与setter

                 如果只是希望拥有简单的gettersetter方法, 那么就按照scala提供的语法规则, 根据需求为字段 选择合适的修饰符就好: var、 val、 private、 private[this] 

                 但是如果希望能够自己对gettersetter进行控制, 则可以自定义gettersetter方法

                 自定义setter方法的时候一定要注意scala的语法限制, 签名、 =、 参数间不能有空格

                  

    class Student {
    private var myName = "leo"
    def name = "your name is " + myName
    def name_=(newValue: String) {
    print("you cannot edit your name!!!")
    }
    } 
    val leo = new Student print(leo.name) leo.name = "leo1

       2.2仅 暴露字段的getter方法 

                如果你不希望字段有setter方法, 则可以定义为val, 但是此时就再也不能更改字段的值了    但是如果希望能够仅仅暴露出一个getter方法, 并且还能通过某些方法更改字段的值, 那么需要综合
    使用private以及自定义getter方法    此时, 由于字段是private的, 所以settergetter都是private, 对外界没有暴露; 自己可以实现修改  字段值的方法;

               

    class Student {
    private var myName = "leo"
    def updateName(newName: String) {
    if(newName == "leo1") myName = newName
    else print("not accept this new name!!!")
    }
     def name = "your name is " + myName
    }
    

        2.3private[this] 的使用

                  如果将字段使用private来修饰, 那么代表这个字段是类私有的,   在类的方法中, 可以直接访问类的其他对象的private 字段。
                  如果不希望字段被其他对象访问到, 那么可以使用private[this], 意味着是对象私有的字段,   只能被当前对象访问, 其他新创建的对象实例无法访问。 (没有访问权限)
                  

    class Student {
    private var myAge = 0
    def age_=(newValue: Int) {
    if (newValue > 0) myAge = newValue
    else print("illegal age!")
    }
    def age = myAge def older(s: Student) = { myAge > s.myAge } }

      如果myAge声明为private[this]  将报错,因为s.myAge 不能访问myAge

                

          3、(1)辅助构造器(构造函数)

                  Scala中, 可以给类定义多个辅助constructor, 类似于Java中的构造函数重载          辅助constructor之间可以互相调用, 而且必须第一行调用主constructor
                 

    class Student {
    private var name = ""
    private var age = 0
    def this(name: String) {
    this()
    this.name = name
    } 
    def this(name: String, age: Int) { this(name) this.age = age } }

      def this(name:String)   辅助构造器 ;this() 默认的主构造器;this.name 当前对象

        调用主构造器:val p =new Student                 调用第一个辅助构造器  val p1=new Student("li")      调用第二个辅助构造器  val  p2=new Student("li",22)

              (2)主构造器(构造函数)

                   Scala中, 主constructor是与类名放在一起的, 与Java不同 ,而且类中, 没有定义在任何方法或者代码块之中的代码, 就是主constructor的代码。

    class Student(val name: String, val age: Int) {
    println("your name is " + name + ", your age is " + age)
    }
    

                constructor中还可以通过使用默认参数, 来给参数默认的值 

    class Student(val name: String = "leo", val age: Int = 30) {
    println("your name is " + name + ", your age is " + age)
    }
    

    二、对象

          1、object

            object, 相当于class的单个实例, 通常在里面放一些静态的字段或者方法 

             第一次调用object的方法时, 就会执行objectconstructor, 也就是object内部不在方法中的代码; 但是object不能定义接受参数的constructor

            objectconstructor只会在其第一次被调用时执行一次, 以后再次调用就不会再次执行  constructor了 

    object Persion{
       println("zhixing")
       val age=10
       def getAge=age
    }
    

      Persion.getAge时只会打印一次 “zhixing”  ,再次调用方法时不打印

         2、伴生对象

             如果有一个class, 还有一个与class同名的object, 那么就称这个objectclass的伴生  对象, classobject的伴生类
             伴生类和伴生对象必须存放在一个.scala文件之中           伴生类和伴生对象, 最大的特点就在于, 互相可以访问private 字段
             

    package Test
    
    /**
     * 1、伴生类与伴生对象,名字相同,必须放在同一个.scala文件中
     * 2、伴生类与伴生对象之间可以访问private成员
     */
    //伴生类
    class Account{
    //  val array=Array()
      //伴生类中的私有字段
      private var num=10
      //通过Account.num2调用伴生对象中的私有字段num2
      def getNum2=println(Account.num2)
      //通过Account.getResult调用伴生对象中的私有函数
      num=Account.getResult
      
      def getInfo=println(num)
    }
    //伴生对象
    object Account {
      //伴生对象中的私有字段
      private var num2=20
      //伴生对象中的私有函数
      private def getResult={
        num2+=10
        num2
      }
      //创建apply()方法,用于创建伴生类的对象
      def apply()={
        println("这是伴生对象中的apply方法!")
        new Account
      }
      def main(args: Array[String]): Unit = {
        val account=Account()//调用的是伴生对象中的apply方法
        account.getInfo
    //    val account=new Account  调用主构造器
        //在伴生对象中,通过伴生类的对象访问类中的私有字段
    //    println(account.num)
      }
      
    }
    

      类和它的伴生对象可以相互访问私有特性,  它们必须存在于同一个源文件中。

       说明: 类的伴生对象可以被访问, 但并不在  作用域当中。 举例来说, Account类  必须通过Account.newUniqueNumber()而不  是直接用newUniqueNumber()来调用伴生对  象的方法。 

        在伴生类中访问伴生对象成员时需  伴生对象.字段;在伴生对象访问伴生类成员时,需创建类对象

           3、apply方法

              object中非常重要的一个特殊方法, 就是apply方法  

              通常在伴生对象中实现apply方法, 并在其中实现构造伴生类的对象的功能

              而创建伴生类的对象时, 通常不会使用new Class的方式, 而是使用Class()的方式, 隐式地调  用伴生对象的apply方法, 这样会让对象创建更加简洁  

              比如, Array类的伴生对象的apply方法就实现了接收可变数量的参数, 并创建一个Array对象   的功能    val a = Array(1, 2, 3, 4, 5)

            4、用object来实现枚举功能
                Scala没有直接提供类似于Java中的Enum这样的枚举特性。    如果要实现枚举, 则需要用object继承Enumeration类, 并且调用Value方法来初始化枚举值。
     

    object Season extends Enumeration {
    val SPRING, SUMMER, AUTUMN, WINTER = Value
    }
    

      还可以通过Value传入枚举值的idname, 通过idtoString可以获取; 还可以通过idname来查找枚举值

            

    object Season extends Enumeration {
    val SPRING = Value(0, "spring")
    val SUMMER = Value(1, "summer")
    val AUTUMN = Value(2, "autumn")
    val WINTER = Value(3, "winter")
    }

       调用时,可以

    Season(0)
    Season.withName("spring")
    Season.SUMMER

        使用枚举object.values可以遍历枚举值              for (ele <- Season.values) println(ele)
         5、main方法

         就如同Java中, 如果要运行一个程序, 必须编写一个包含main方法类一样;  Scala中, 如果要运行一个应用程序, 那么必须有一个main方法作为入口
         Scala中的main方法定义为def main(args: Array[String]), 而且必须定义在object

         

    object HelloWorld {
    def main(args: Array[String]) {
    println("Hello World!!!")
    }
    }
    

      除了自己实现main方法之外, 还可以继承App trait, 然后将需要在main方法中运行的代码, 直接作  objectconstructor代码; 而且用args可以接受传入的参数
          App类中的trait 底层有main方法

    object HelloWorld extends App {
    if (args.length > 0) println("hello, " + args(0))
    else println("Hello World!!!")
    }
    

      6、让object继承抽象类

             object的功能其实和class类似, 除了不能定义接受参数的constructor之外   object也可以继承抽象类, 并覆盖抽象类中的方法

    abstract class Hello(var message: String) {
    def sayHello(name: String): Unit
    }
    object HelloImpl extends Hello("hello") {
    override def sayHello(name: String) = {
    println(message + ", " + name)
    }
    def main(args: Array[String]): Unit = {
    HelloImpl.sayHello("zhangsan")
    }
    }
    

      mes:这个参数是主构造器的,它需要val或var来修饰 ;object继续Hello类时需要给参数赋值

    三、继承

           1、extends

             Scala中, 让子类继承父类, 与Java一样, 也是使用extends关键字   ; 、

             继承就代表, 子类可以从父类继承父类的字段和方法; 然后子类可以在自己内部放入父类没有, 而子类特  有的字段和方法;使用继承可以有效复用代 码

             子类可以覆盖父类的字段和方法;
             如果父类用final修饰, 字段和方法用final修饰, 则该类是无法被继承的, 字段和方法是无法被覆盖的
         
     2、override和super
              Scala中, 如果子类要覆盖一个父类中的非抽象方法, 则必须使用override关键字
              此外, 在子类覆盖父类方法之后, 如果我们在子类中就是要调用父类的被覆盖的方法,  那就可以使用super关键字, 显式地指定要调用父类的方法
              

    class Person {
    private var name = "leo"
    def getName = name
    }
    class Student extends Person { private var score = "A" def getScore = score override def getName = "Hi, I'm " + super.getName }

         override 字段  :Scala中, 子类可以覆盖父类的val 字段, 只要在子类中使用override关键字即可
              

    class Person {
    val name: String = "Person"
    def age: Int = 0
    } 
    class Student extends Person {
    override val name: String = "leo"
    override val age: Int = 30
    }
    

      例:

    class Persion{
      
      private var name="战三"
      val age=20
      def getName=name
    }
    class Persion1 extends Persion{
      override val age=30
      override def getName={
        "hi,"+super.getName
    //    println("hi,"+super.getName)
      }
    }
    object day3lianxi2 {
      def main(args: Array[String]): Unit = {
        val stu =new Persion1
        println(stu.getName)
        println(stu.age)
      //  println(stu.getName)
      }
    }
    

      (def age:Int=0      方法名age,返回类型Int;返回值类型可以写,也可以不写,递归时必须写)

         3、isInstanceOf和asInstanceOf

              如果我们创建了子类的对象, 但是又将其赋予了父类类型的变量。 则在后续的程序中, 我们又需   要将父类类型的变量转换为子类类型的变量, 应该如何做?
              首先, 需要使用isInstanceOf判断对象是否是指定类的对象, 如果是, 则可以使用asInstanceOf将  对象转换为指定类型。
             注意:如果对象是null, 则isInstanceOf一定返回falseasInstanceOf一定返回null

                     如果没有用isInstanceOf先判断对象是否为指定类的实例, 就直接用asInstanceOf 转换, 则可能会  抛出异常
               例:class Person
                    class Student extends Person
                    val p: Person = new Student
                    var s: Student = null
                    if (p.isInstanceOf[Student])                s = p.asInstanceOf[Student]

         4、getClass和classOf

              isInstanceOf只能判断出对象是否是指定类以及其子类的对象, 而不能精确判断出, 对象就是指  定类的对象 

              如果要求精确地判断对象就是指定类的对象, 那么就只能使用getClassclassOf

              对象.getClass可以精确获取对象的类, classOf []可以精确获取类, 然后使用==操作符即可  判断 

    class Person
    class Student extends Person
    val p: Person = new Student
    p.isInstanceOf[Person]
    p.isInstanceOf[Student]
    p.getClass == classOf[Person]
    p.getClass == classOf[Student]

         5、使用模式匹配进行类型判断

             在实际开发中, 比如Spark的源码中, 大量的地方都是使用了模式匹配的方式来进行类型的判断,  这种方式更加地简洁明了, 而且代码的可维护性和可扩展性也非常高。
             使用模式匹配, 功能性上来说, 与isInstanceOf一样, 主要判断是该类以及该类的子类对象即可,  不是精准判断的

    class Person
    class Student extends Person
    val p: Person = new Student
    p match {
    case per: Person => println("it's Person's object")
    case _ => println("unknown type") //注意case与下划线之间有一个空格
    }

         6、protected   (在本类或者子类中能访问)

              Java一样, Scala中同样可以使用protected关键字来修饰字段和方法,  这样在子类中就不需要super关键字, 就直接可以访问字段和方法  

              如果使用protected[this], 则只能在当前子类对象中访问父类的字段和方法, 无法通过其他子类对象访问父类的   字段和方法
            

    class Person {
    protected var name: String = "leo"
    protected[this] var hobby: String = "game"
    } 
    class Student extends Person { def sayHello = println("Hello, " + name) def makeFriends(s: Student) { println("my hobby is " + hobby + ", your hobby is " + s.hobby) //报错 s.hobby 不能够被其他子类对象访问 } }

      

         7、调  用父类的constructor 

             Scala中, 每个类可以有一个主constructor和任意多个辅助constructor。  每个辅助constructor的第一行都必须是调用其他辅助constructor或者是主constructor,   因此子类的辅助constructor
    一定不能直接调用父类的constructor。  

             只能在子类的主constructor中调用父类的constructor, 以下这种语法, 就是通过子类的主构造函数来调用父类的构  造函数  

              注意: 如果是父类中接收的参数, 比如nameage, 子类中接收时, 就不要用任何valvar来修饰了, 否则会认为   是子类要覆盖父类的字段

    class Person(val name: String, val age: Int)
    class Student(name: String, age: Int, var score: Double) extends Person(name, age) {
    def this(name: String) {        //子类辅助构造器
    this(name, 0, 0)                //子类主构造器
    } 
    def this(age: Int) {
    this("leo", age, 0)
    }
    }
    

      8、抽象类

             如果在父类中, 有某些方法无法立即实现, 而需要依赖不同的子类来覆盖, 重写实现自己不同的方法实  现。 此时可以将父类中的这些方法不给出具体的实现, 只有方法签名, 这种方法就是抽象方法。    而一个类中如果有一个抽象方法, 那么类就必须用abstract来声明为抽象类, 抽象类是不可以实例化的。
             在子类中覆盖抽象类的抽象方法时, 不需要使用override关键字

     

    abstract class Person (val name: String) {
    def sayHello: Unit
    } 
    class Student(name: String) extends Person(name) {
    def sayHello: Unit = println("Hello, " + name)
    }
    

      9、抽象字段

              如果在抽象父类中, 定义了字段, 但是没有给出初始值, 则此字段为抽象字段

              抽象字段意味着, Scala会根据自己的规则, 为varval类型的字段生成对应的gettersetter方法  

              子类必须覆盖字段, 以定义自己的具体字段, 并且覆盖抽象字段, 不需要使用override关键字

           

    abstract class Person {
    val name: String
    } 
    class Student extends Person {
    val name: String = "leo"
    }
    

    四、面向对象编程——trait 特质

         1、将trait作为接口使用

               Scala中的trait是一种特殊的概念  ,首先可以将trait作为接口来使用, 此时的trait就与Java中的接口非常类似。  triat中可以定义抽象方法, 就与抽象类中的抽象方法一样, 只要不给出方法的具体实现即可。
              类可以使用extends关键字继承trait,  注意, 这里不是implement, 而是extends, 在scala中没有implement的概念, 无论继承类还是trait统一都是extends
             类继承trait后, 必须实现其中的抽象方法, 实现时不需要使用override关键字

             scala不支持对类进行多继承, 但是支持多重继承trait使用with关键字即可

          例:

    trait Hellotrait {
    def sayHello(name: String)
    }
    trait MakeFriendstrait {
    def makeFriends(p: Person)
    }
    class Person(val name: String) extends Hellotrait with MakeFriendstrait with Serializable {
    def sayHello(name: String) = println("Hello, " + name)
    def makeFriends(p: Person) = println("Hello, my name is " + name + ", your name is " + p.name)
    }
    

      2、在 trait中定义具体方法 

              Scala中的trait不仅仅可以定义抽象方法, 还可以定义具体方法, 此时trait更像是包含了通用工具方法的东西。  比如, trait中可以包含一些很多类都通用的功能方法, 比如打印日志等等, Spark中就使用了trait来定义了通  用的日志打印方法

             

    trait Logger {
    def log(message: String) = println(message)
    } 
    class Person(val name: String) extends Logger {
    def makeFriends(p: Person) {
    println("Hi, I'm " + name + ", your name is , " + p.name)
    log("name=" + p.name)
    }
    }
    

      3、在trait中定义具体字段

             Scala中的trait可以定义具体字段, 此时继承trait的类就自动获得了trait中定义的字段  

              但是这种获取字段的方式与继承class是不同的: 如果是继承class获取的字段, 实际是定义 在父类中的; 而继承trait获取的字段, 就直接被添加到了类中 

         

    trait Person {
    val eyeNum: Int = 2
    } 
    class Student(val name: String) extends Person {
    def sayHello = println("Hi, I'm " + name + ", I have " + eyeNum + " eyes.")
    }
    

          例:

    trait Person{
    val name:String
    val age=30
    }
    
    trait Worker{
    val age=35
    }
    class Student extends Person with Worker{
    val name:String="张三"
    override val age =20
    }

      

                    特质Person和Worker中都有age字段, 当Student混入这两个特质时,需要重 写age字段,并且要用override关键字, 否则就会报错。 

            4、为实例混入trait 

                  有时我们可以在创建类的对象时, 指定该对象混入某个trait, 这样, 就只有这个对象   混入该trait的方法, 而类的其他对象则没有。
                    

    trait Logged {
    def log(msg: String) {}
    }
     trait MyLogger extends Logged {
    override def log(msg: String) { println("log: " + msg) }
    }
     class Person(val name: String) extends Logged {
    def sayHello {
    println("Hi, I'm " + name);
    log("sayHello is invoked!") }
    }
    val p1 = new Person("leo")
    p1.sayHello
    val p2 = new Person("jack") with MyLogger
    p2.sayHello
    

      5、trait调用链

            Scala中支持让类继承多个trait后, 依次调用多个trait中的同一个方法, 只要让多个trait的同一个方法中,  在最后都执行super.方法即可。
            类中调用多个trait中都有的这个方法时, 首先会从最右边的trait的方法开始执行, 然后依次往左执行,  形成一个调用链条。

            这种特性非常强大, 其实就相当于设计模式中的责任链模式的一种具体实现依赖

            补充:问题总结: 调用链必须是类继续多个trait;

                                   多个trait中必须包含同一个方法;

                                   并且多个trait有一个公共的父trait;
                                  调用的多个trait的同一个方法中,最后的语句必须是super.方法名,通过这种形式调用trait调用链中下一个trait方法。

                          满足以上条件才能构成trait调用链,在调用链中按照从右往左的顺序依次调用一个方法。

    trait Handler {
    def handle(data: String) {}
    }
    trait DataValidHandler extends Handler {
    override def handle(data: String) {
    println("check data: " + data)
    super.handle(data)
    }
    }
    trait SignatureValidHandler extends Handler {
    override def handle(data: String) {
    println("check signature: " + data)
    super.handle(data)
    }
    } 
    class Person(val name: String) extends SignatureValidHandler with DataValidHandler {
    def sayHello = {
    println("Hello, " + name);
    handle(name) }
    }
    

      

          6、混合使用trait的具体方法和抽象方法

               trait中, 可以混合使用具体方法和抽象方法    可以让抽象方法放到继承trait的类中去实现    这种trait其实就是设计模式中的模板设计模式的体现

    trait Valid {
    def getName: String //抽象方法
    def valid: Boolean = { //具体方法
    getName == "leo"
    }
    }
    class Person(val name: String) extends Valid {
    def getName = name
    println(valid)
    }
    

      7、trait的构造机制

                Scala中, trait也是有构造代码的, 也就是trait中的不包含在任何方法中的代码。

                 而继承了trait的类的构造机制如下:  

                        1、 父类的构造函数执行;

                         2trait的构造代码执行, 多个trait从左到右依次执行;

                         3、 构造trait时会先构造父trait, 如果多个trait继承同一个父trait, 则父trait只会构造一次;

                          4、 所有trait构造完毕之后, 子类的构造函数执行

    class Person { println("Person's constructor!") }
    trait Logger { println("Logger's constructor!") }
    trait MyLogger extends Logger { println("MyLogger's constructor!") }
    trait TimeLogger extends Logger { println("TimeLogger's constructor!") }
    class Student extends Person with MyLogger with TimeLogger {
    println("Student's constructor!")
    }
    

      先输出  

            Person's constructor!
            Logger's constructor!
             MyLogger's constructor!
             TimeLogger's constructor!
             Student's constructor!

            8、trait继承class

                Scala中, trait也可以继承自class, 此时这个class就会成为所有继承该trait的类的父类

     

    class MyUtil {
    def printMessage(msg: String) = println(msg)
    } 
    trait Logger extends MyUtil { def log(msg: String) = printMessage("log: " + msg) }
    class Person(val name: String) extends Logger { def sayHello { log("Hi, I'm " + name) printMessage("Hi, I'm " + name) } }

      

          

    补充:
          1、序列化相当于 保存的过程   反序列化就相当于读取的过程   (序列化原因 :
    网络传输不安全,影响传输速度

          2、

    package com.ghgj;
    
    public class A {
    
    	public static void main(String[] args) {
    		Person p =new Student();
    		Student s=new Student();
    	}
    }
    class Person{
    	public Person(){
    		System.out.println("a");
    	}
    }
    class Student extends Person{
    	public Student(){
    		System.out.println("b");
    	}
    }
    

      返回值都是 a  b   a  b

     

  • 相关阅读:
    layui
    JSON
    jQuery
    实例——模拟验证码
    实例——表格的相关操作:添加行,删除行,编辑单元格
    实例——省市区三级联动 & 还可以输入字符统计
    实例练习——轮播图 & 全选/全不选
    练习-计算器
    定时器 & 日期时间对象 & 正则
    ThinkPHP实现分页
  • 原文地址:https://www.cnblogs.com/liuwei6/p/6550609.html
Copyright © 2011-2022 走看看