scala开发快速入门 | 第六篇 面向对象编程(上)

类的定义、变量的声明初始化、private修饰变量、伴生类伴生对象

1)Class 关键字声明一个类Person   

2)类成员变量的声明的时候必须初始化   

3)编译完后 通过字节码文件发现 定义的变量都是private类型。   

*    val 声明的成员变量 只有get方法 方法名为 变量名()   

*    var 声明的成员变量 既有set方法也有get方法 set方法的名称为 变量名_$eq(int x$1) get方法的名称 变量名()   

4)private 修饰的变量编译后 其set get方法 都是private类型的。   

*   被private修饰的字段只能在本类以及伴生对象中。   

*  伴生对象和伴生类   

*  在同一个scala文件中,如果有一个class 和object 的名字一样,那么称class是object的伴生类,object是class的伴生对象 

/**
  * @author Andy
  *
  * 1)Class 关键字声明一个类Person
  * 2)类成员变量的声明的时候必须初始化
  * 3)编译完后 通过字节码文件发现 定义的变量都是private类型。
  * val 声明的成员变量 只有get方法 方法名为 变量名()
  * var 声明的成员变量 既有set方法也有get方法 set方法的名称为 变量名_$eq(int x$1) get方法的名称 变量名()
  * 4)private 修饰的变量编译后 其set get方法 都是private类型的。
  * 被private修饰的字段只能在本类以及伴生对象中。
  * 伴生对象和伴生类
  * 在同一个scala文件中,如果有一个class 和object 的名字一样,那么称class是object的伴生类,object是class的伴生对象
  *
  */
class Person {
  val name: String = "ysj"
  var age: Int = 23
  private var sex: String = "Man"

  //  为了在其他类中能够访问这个显示指明的私有变量 给他定义set、get方法
  def setSex(sex: String): Unit = {
    this.sex = sex
  }
  def getSex(): String = {
    this.sex
  }
}
class Person2 {
  val person2 = new Person
  //  person2.sex Person类显示声明的私有变量无法访问 但是可以访问其他的非显示私有变量
  println(person2.age) //Person类实例调用get方法访问变量
  //  为了能在其他类中访问某个类(Person)显示声明的私有变量
  //  可以在声明这个变量是显示私有变量的类中定义set、get方法
  println(person2.getSex())
}
object Person {
  def main(args: Array[String]): Unit = {
    val person = new Person
    println(person.sex) //显示声明的私有变量在伴生对象中使用
    val Person2=new Person2//实例化Person2 类默认调用无参数构造方法并执行构造器方法中的代码
  }
}

运行结果:

Man
23
Man

5)对象的实例化 

val person =new Person()

Person类的构造方法没有参数,类的实例化可以定义如下

val person=new Person

6)类中的成员变量的访问以及赋值

由于类的成员变量都是私有的,所以访问的时候只能通过set、get方法进行访问

person.name 其实是调用的get方法进行属性访问的。

person.name_=("yangshj") 通过set方法将变量赋值

person.name="yangshj" 直接给变量进行赋值,实际上底层还是调用的set方法    

类的构造器

Scala类的构造器包含1个主构造器和若干个(0个或多个)辅助构造器。

主构造器:

1)Scala的每个类都有主构造器。但是,Scala的主构造器和Java有着明显的不同,Scala的主构造器是整个类体,需要在类名称后面罗列出构造器所需的所有参数,这些参数被编译成类的字段属性,字段的值就是创建对象时传入的参数的值。

主构造器的参数中,没有被任何关键字(val 或者val)修饰的参数,会被标注为private[this] 这样的参数只能在本类中访问,其他地方无法访问。

Note:不包含在任何方法中的代码,就是主构造器的代码。

辅助构造器:

2)辅助构造器的名称为this,每个辅助构造器都第一行代码必须调用一个此前已经定义的辅助构造器或主构造器。

Note:创建对象时代码的执行顺序一定是主构造器代码先执行。

package com.netcloud.scalalearning.day03.classconstruter

/*在主构造器参数值,没有被任何关键字(var、val)修饰的参数,会被标注为private[this]
* class Person(var name: String, var age: Int,hobby:String) hobby这个字段就会被标注为 private[this]
* 只能在本类访问,其他的任何地方都不能访问。
*
* */
class Person(var name: String, var age: Int) {

  println("这是主构造器的代码!")
  var sex: String = null
  var address: String = null

  /*定义第一个辅助构造器*/
  def this(name: String, age: Int, sex: String) {
    this(name, age) //直接调用主构造器
    this.sex = sex
    println(s"sex=$sex")

  }

  /*定义第二个辅助构造器*/
  def this(name: String, age: Int, sex: String, address: String) {
    this(name: String, age: Int, sex: String) //直接调用此前的辅助构造器
    this.address = address
    println(s"address=$address")

  }

  println(s"Person($name,$age,$sex,$address)")
}

object Person {
  def main(args: Array[String]): Unit = {
    //创建对象
    val person01 = new Person("yangshj", 25, "man", "北京")
  }

}

运行结果:

这是主构造器的代码!
Person(yangshj,25,null,null)
sex=man
address=北京

Object的介绍

在scala中被object关键字修饰的类具有如下特征:

1)它是单例的
2)不需要通过关键字new创建对象 直接通过类名创建对象
3)没有 有参数的构造器 但是有主构造代码块(不包含任何方法中的代码 )
4)通常用来封装一些常量、工具类、枚举、和隐式转换函数
5)主构造器代码块只执行一次,因为他是单例的。  

/*
* 1)它是单例的
* 2)不需要通过关键字new创建对象 直接通过类名创建对象
* 3)没有 有参数的构造器 但是有主构造代码块(不包含任何方法中的代码 )
* 4)通常用来封装一些常量、工具类、枚举、和隐式转换函数
* 5)主构造器代码块只执行一次,因为他是单例的。
* */
object Person {
  var name: String = "ysj"
  println("Person 的主构造代码块01")
  def sayHello() = {
    println("Hello World !")
  }
  println("Person 的主构造代码块02")
}
object Person01 {
  def main(args: Array[String]): Unit = {
    val person = Person //object的实例不需要new  直接通过类名创建
    person.sayHello()
    println(person.name) //实际上调用的name的get方法 即 person.name()
  }
}

运行结果:

Person 的主构造代码块01
Person 的主构造代码块02
Hello World !
ysj

Object的apply方法

object Object_apply {
  def apply() = {
    println("hello")
  }
  def apply(nums: Int*): Array[Int] = {
    val arr = new Array[Int](nums.length)
    for (i <- 0 to nums.length - 1) {
      arr(i) = nums(i)
    }
    arr
  }
}
object Object_apply01 {
  def main(args: Array[String]): Unit = {
    // 创建 Object_apply对象 并且调用这个对象的无参数的apply方法
    val obj1 = Object_apply()
    // 不会再去创建对象了,因为上面一句代码已经创建了对象,这里只是调用他的带参数的apply方法
    val obj2 = Object_apply(1, 2, 3, 4, 5) //返回数组对象
    obj2.foreach(println(_))
    // 显示调用 apply方法
    Object_apply.apply(3, 4, 5, 6, 7)
    val tt=Array(1,2,3)// 这样的方式创建的数组 实际是调用的Array的apply方法 返回一个数组
  }
}

伴生类和伴生对象

伴生类和伴生对象一定是在同一个scala类文件中,并且类名相同
伴生类和伴生对象的特点就是可以相互访问被private修饰的关键字段

/*伴生类*/
class MyArry {
  private var name: String = "ysj"
  //  伴生类中调用伴生对象的私有方法
  MyArry.name
}

/*伴生对象*/
object MyArry {
  private var name: String = "qin"
  //  伴生对象中调用伴生类的私有属性
  new MyArry().name
  //  定义apply方法
  def apply(nums: Int*): Array[Int] = {
    var arr = new Array[Int](nums.length)
    for (i <- 0 to nums.length - 1) {
      arr(i) = nums(i)
    }
    arr
  }
}
object ObjectDemo {
  MyArry(5) //调用伴生对象的apply方法 返回一个数组
  //MyArry.name 无法访问伴生对象的私有方法
}

Object 继承App类

 object 类继承App 类后 可以直接执行类中的打印语句以及调用其中的方法, 不需要再去写main方法 App父类中就有main方法。

object ExtendsApp extends App{
  println("object 类继承App 类后 可以直接执行类中的打印语句 不需要再去写main方法")
}

Object 实现枚举功能

1)定义一个object继承Enumeration

2)声明枚举的方式

3)获取枚举方式

/*object实现枚举功能
* 1)定义一个object继承Enumeration
* 2)声明枚举*/
object MyEnum extends Enumeration {
  /*声明枚举*/
  //  方式一:声明枚举的时候不需要指定下标值.
  //  val WOLF, DOG, CAT = Value
  //  方式二:声明枚举的时候指定下标,同时指定值。
  val WOLF = Value(0, "wolf")
  val DOG = Value(1, "dog")
  val Cat = Value(2, "Cat")
}

object Main {
  def main(args: Array[String]): Unit = {
    println(MyEnum.WOLF)//wolf
   //通过枚举下标获取枚举
    println(MyEnum(0))//wolf
    //通过枚举名称获取枚举
    println(MyEnum.withName("wolf"))//wolf
    //获取所有的枚举值
    MyEnum.values.foreach(println(_))
  }

}

外部类、内部类、内部对象

内部类就是定义在对象或者类内部的类;内部对象就是定义在对象或者类内部的对象。

代码示例:

下面的代码中定义了一个伴生类(Student)和伴生对象(Student),在伴生类中定义了一个内部类Grade和一个内部对象Utils01,在伴生对象中则定义了一个内部类Printer 和内部对象Utils02.

1)创建内部类Grade的时候使用 外部类对象.内部类名称(内部类构造方法参数)的方式创建内部类对象。可以看到访问内部类就像是内部类是其成员变量一样。

    外部类对象.内部对象的方式访问内部对象。

2)伴生对象.内部类名称(内部类构造参数)的方式创建伴生对象中的内部类对象。

     伴生对象.内部对象的方式访问伴生对象中的内部对象。    

/*伴生类Student*/
class Student(var name: String, var age: Int) {
  //  内部类
  class Grade(var name: String) {
  }
  //  内部对象
  object Utils01 {
    def prinnt(name: String) = {
      println(name)
    }
  }
}
/*伴生对象Student*/
object Student {

  //  伴生对象的内部类printer
  class printer {
    def print(name: String): Unit = {
    }
  }
  // 伴生对象的单例对象
  object Untils2 {
    def print(name: String) = {
      println(name)
    }
  }
}
object InnerClassAndInnerObject {
  def main(args: Array[String]): Unit = {
    //    创建伴生类对象
    val student = new Student("ysj", 25)
    //    创建伴生类中的内部类对象
    val grade = new student.Grade("qff")
    println("访问伴生类对象中内部类中的属性" + grade.name)
    //    调用伴生类内部对象的方法
    student.Utils01.prinnt("ysjloveqff")

    /*创建伴生对象的内部类对象*/
    val printer = new Student.printer
    printer.print("调用伴生对象的内部类方法!")
    Student.Untils2.print("调用伴生对象的内部对象方法!")
  }
}
class Person01(val name: String) {
  /*第一种方式
   * 在内部类通过【外部类.this.成员名称】 访问外部类成员*/
  class Student01(val name: String) {
    def getInfo(): Unit = {
      println("Outer Class name:" + Person01.this.name + " Inner Class name:" + name)
    }
  }

}
/*
  * 第二种方式
  * 在内部类通过【外部类别名】 访问外部类成员*/
class Person02(val name: String) {
  outer =>
  class Student02(val name: String) {
    def getInfo(): Unit = {
      println("Outer Class name:" + outer.name + " Inner Class name:" + name)
    }

  }

}
object Main {
  def main(args: Array[String]): Unit = {
    println("第一种方式访问")
    val outer01 = new Person01("ysj")
    val inner01 = new outer01.Student01("qff")
    inner01.getInfo()

    println("第二种方式访问")
    val outer02 = new Person02("ysj")
    val inner02 = new outer02.Student02("qff")
    inner02.getInfo()
  }
}

样例类(case class)

被关键字 case 修饰的类就是样例类;

特点:其实样例类和普通的类没有什么区别,唯一的区别就是样例类的实例化不需要通过关键字new的方式。当然也可以通过new的方式实例化

定义一个样例类 构造器的参数名可以不需要var 和val修饰  如果样例类中没有其他的方法可以省略 {}
case class CaseClass(var name:String){

}
object Main2{
  def main(args: Array[String]): Unit = {
    val caseclass=CaseClass("ysj")
    println(caseclass.name)

  }
}

抽象类和继承

抽象类特点

1)与java相同都是被关键字abstract修饰

2)抽象类中可以定义抽象字段,抽象方法,非抽象字段,非抽象方法。

3)抽象字段的声明 其实就是普通字段声明的未初始化,抽象方法的声明没有方法体

4)抽象字段不能被private修饰,这样子类继承父类后就无法访问父类的这个字段,也就无法实现这个抽象字段。

子类继承父类

1)子类继承抽象父类,必须实现父类中的抽象字段和抽象方法,子类中的override关键字可以省略

2)子类继承抽象父类,对于非抽象字段和方法,可以进行重写,但是这个字段不能被var声明,否则异常。

      同时override关键字不能省略。

abstract class FatherClass {
  //  定义一个抽象字段
  var name: String
  //  定义一个具体的字段
  val age: Int = 23

  //  定义一个抽象方法
  def sayHello()

  //定义具体的方法
  def say(): Unit = {
    println("hello Father !")
  }

}

class SonClass extends FatherClass {
  //  子类继承抽象父类,必须实现父类的抽象字段和抽象方法 override 可以省略
  override var name: String = "son"

  override def sayHello(): Unit = {
    println("子类实现父类的抽象方法!")
  }

  //  对于父类中的非抽象字段和方法 子类可以进行重写
  /*Note:
     1)字段不能被var声明否则运行报错
     2)override不能省略,否则编译报错*/
  override val age = 25

  override def say(): Unit = {
    super.say() //调用父类的say方法
    println("hello Son !")

  }

}

object Application {
  def main(args: Array[String]): Unit = {
    val son = new SonClass
    println(son.age)
    son.say()

  }
}

isInstanceOf 和asInstanceOf方法的使用

class Animal {

  def say(): Unit = {
    println("say multi way")
  }
}
class Dog extends Animal {
  override def say(): Unit = {
    println("say wang wang wang!")
  }
}
class Cat extends Animal {
  override def say(): Unit = {
    println("say miao miao miao!")
  }
}
object Application {
  def main(args: Array[String]): Unit = {
    val dog: Any = new Dog
    //用来判断实例对象是否是指定的类类型
    if (dog.isInstanceOf[Animal]) {
      //将对象转换成指定的类型 在用asInstanceOf之前,应该使用isInstanceOf进行判断
      val d = dog.asInstanceOf[Animal]
      d.say()
    }
  }
}

getClass和classOf方法的使用

用来判断实例对象到底是哪个类创建出来的

class Person

class Student03 extends Person
class Teacher03 extends Person

object GetClassAndclassOf {
  def main(args: Array[String]): Unit = {
    //用isInstanceOf 不能做精确判断 如果做精确判断使用getClass和classOf
    val student=new Student03
    println(student.getClass==classOf[Student03])//true
    println(student.getClass==classOf[Person])//false

  }

子类调用父类的构造方法

子类继承父类后,当实例化子类的实例的时候,一定是先执行父类的构造函数,再去执行自己的构造函数。

Note:如果子类的构造函数和父类的构造函数接受的参数名称一样,那么子类相同的参数名字就不能添加任何关键字就行修饰,否则scala会认为子类要重写父类的字段。

class FatherConstrutor(var name: String, var age: Int) {
  println("这是父类的主构造代码块!")
  println("name=" + name + " " + "age=" + age)
}
/*如果子类的构造函数和父类的构造函数接受的参数名称一样,那么子类相同的参数名字就不能添加任何关键字就行修饰,
否则scala会认为子类要重写父类的字段*/
class SonConstrutor(name: String, age: Int, var sex: String) extends FatherConstrutor("ysj", 25) {
  println("这是子类的构造代码块!")
  println("name=" + name + " " + "age=" + age + " " + "sex=" + sex)
}
object ApplicationMain {
  def main(args: Array[String]): Unit = {
    new SonConstrutor("xiaoming", 24, "man")
  }
}

运行结果:

这是父类的主构造代码块!
name=ysj age=25
这是子类的构造代码块!
name=xiaoming age=24 sex=man

匿名类

匿名类顾名思义就是没有名字的类,当某个类在代码中仅使用一次的时候,可以考虑使用匿名类。

/*匿名类*/

abstract  class NiMingClass {
  def say()
}
class Animal04(var name:String){
  def say(): Unit ={
    println("大家好 我是"+name)
  }
}
object Application05 {
  def main(args: Array[String]): Unit = {
    /*这里的new NiMingClass 并不是实例化抽象类而是实例化匿名类*/
    val s1 = new NiMingClass() {
      override def say(): Unit = {
        println("我说抽象类的匿名类!")

      }
    }
    s1.say()
  }
  val cat=new Animal04("Cat"){

    override def say(): Unit = {
      super.say()
      println("我是Animal的匿名类")
    }

  }
  cat.say()
}

猜你喜欢

转载自blog.csdn.net/yangshaojun1992/article/details/80891177