Scala对象

面向对象

Scala的面向对象思想和Java的面向思想和概念是一致的。

Scala中语法和Java不同,补充了更多的功能。

Scala包

package 包名

三大作用:

  • 区分相同名字的类
  • 当类很多的时候,可以很好的管理类
  • 控制访问范围

包的命名

只能包含数字、字母、下划线、小圆点,但不能用数字开头,也不要使用关键字。

一般是小写字母 + 小圆点

com.公司名.项目名.业务模块名

包说明

包对象

在Scala中可以为每个包定义一个同名的包对象,定义在包对象的中的成员,作为其对应包下所有class 和 object 的共享变量,可以被直接访问。

package object com {
    val shareValue= "share"
    def shareMethod() = {}
}

导包说明

  • 和Java一样,可以在顶部使用import导入,在这个文件中的所有类都可以使用
  • 局部导入,什么时候使用,什么时候导入。在其作用范围内都可以使用
  • 通配符导入 import java.util._
  • 给类起名 import java.util.{ArrayList => JL}
  • 导入相同包的多个类 import java.util.{HashSet, ArrayList}
  • 屏蔽类 import java.util.{ArrayList => _,_}
  • 导入包的绝对路径 import new_root_.java.util.ArrayListi

类和对象

类:可以看成一个模板

对象:表示具体的事物

定义类

Scala中没有public,一个Scala中可以写多个类

Scala语法中,类并不声明为public,所有这些类都具有公共可见性,即默认就是public

一个Scala源文件可以包含多个类

package chapter06

import scala.beans.BeanProperty

object Test03_Class {
  def main(args: Array[String]): Unit = {
    // 创建一个对象
    val student = new Student()

    // 不能通过以下的方式访问name属性,因为它是private
    println(student.age)
    println(student.sex)

    student.setSex("male")
    println(student.sex)
    
    student.age = 20
    println(student.getAge)
  }
}

// 定义一个类
class Student{
  // 定义属性
  // 属性默认为public
  // 增加注解添加getter和setter

  private var name: String = "zihoo"
//  @BeanProperty
//  val age: Int = 18
//
//  val sex: String = "male"
//  var nullValue: String = _

  @BeanProperty
  var age: Int = _
  @BeanProperty
  var sex: String = _
}

封装

封装就是把抽象出的数据和数据的操作封装在一起,数据被保护在内部,程序的其它部分只有通过被授权的操作(成员方法),才能对数据进行操作。

Scala中的public属性,底层实际为private,并通过get方法和set方法对其进行操作。所以Scala并不推荐将其属性设为private,再为其设置public的get和set方法的做法。

但由于很多Java框架都利用反射调用get和set的方法,有时候为了和这些框架兼容,也会通过@BeanProperty注解为类实现get和set的方法。

访问权限

Scala中属性和方法的默认访问权限为public,但是Scala中无public关键字。

private为私有权限,只有类的内部和伴生对象中可用。

protected为受保护权限,Scala中受保护权限比Java中更严格,同类、子类可以访问,同包无法访问。

private[包名] 增加包的访问权限,包名下的其它类也可以使用

package chapter06

object Test04_Access {
  def main(args: Array[String]): Unit = {
    // 创建对象
    val person: Person = new Person()
    // protected只能在子类或本类中可以访问
    // person.name
    println(person.age)
    println(person.sex)
    person.printInfo()

    val worker: Worker = new Worker()
    worker.printInfo()
  }
}

class Worker extends Person {
  override def printInfo(): Unit = {
    name = "Worker"
    age = 25
    sex = "female"
    println(s"Worker => {姓名:${name}, 性别:${sex},年龄:${age}}")
  }
}

方法

就是定义在类中的函数

def 方法名(参数列表) [: 返回值类型] = {
    	方法体
}

创建对象

和Java类似

val person: Person = new Person()

当使用修饰符 val 后,表示常量,也就是不可更改的。所有当使用person = new Person() 是不被允许的。但是可以修改person的属性值 person.age = 12 ,这样是被允许的。

构造器

和 Java一样,Scala构造对象也需要调用构造方法,并且可以有任意多个构造方法。

Scala的构造器包括:主构造器和辅助构造器

class 类名(形参列表) {	// 主构造器
    // 类体
    def this(形参列表) {	// 辅助构造器
        
    }
    def this(形参列表) {	// 辅助构造器
        
    }
}

辅助构造器,函数的名称this,可以有多个,编译器通过参数的个数及类型来区分

辅助构造方法不能直接构造对象,必须直接或者间接调用主构造方法

构造器调用其他另外的构造器,要求被调用的构造器必须提前声明

package chapter06

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

  }
}

// 定义一个类
class Animal() {
  // 定义属性
  var name: String = _
  var age: Int = _

  println("1. 主构造方法调用")
  def this(name: String, age: Int){
    // 直接调用著构造器
    this()
    println("2. 辅助构造方法1被调用")
    this.name = name
    this.age = age
    println(s"name: ${name}, age:${age}")

  }

  def this(name: String) {

    this(name, 20)
    println("3. 辅助构造方法2被调用")

    
  }
}

构造器参数

Scala类的主构造器函数的形参包括三种类型:未用任何修饰符、var修饰符、val修饰符

  • 未用任何修饰符,这个参数就是一个局部变量
  • var修饰符,作为类的成员属性使用,可修改
  • val修饰符,作为类的只读属性使用,不可修改
package chapter06

object Test06_ConstructorParams {
  def main(args: Array[String]): Unit = {
    val model = new ConstructorModel("zihoo", 21, 'M')

    model.age = 20
    // model.sex = 'W' 常量不可以修改
    println(s"model => {age: ${model.age}, sex: ${model.sex}")
  }
}

// 定义一个类
class ConstructorModel(name: String, var age: Int, val sex:Char)

继承和多态

class 子类名 extends 父类名 {
    类体
}

子类继承父类的属性和方法

scala是单继承

package chapter06

object Test07_Inherit {
  def main(args: Array[String]): Unit = {
    val student = new Test07Student("zihoo", 21)
    val student2 = new Test07Student("gazikel", 21, "20194077")
  }
}

// 定义一个父类
class Test07Person {
  var name: String = _
  var age: Int = _

  println("1. 父类的主构造器调用")

  def this(name: String, age:Int) {
    this()
    println("2. 父类的辅助构造器调用")
    this.name = name
    this.age = age
  }

  def printInfo( ): Unit = {
    println(s"Person => {${name}, ${age}")
  }
}

// 定义子类
class Test07Student(name: String, age: Int) extends Test07Person {

  var stuNo: String = _


  println("3. 子类的主构造器调用")

  def this(name: String, age:Int, stuNo:String) {
    this(name, age)
    println("4. 子类的辅助构造器调用")
    this.stuNo = stuNo
  }

  override def printInfo(): Unit = {
    println(s"Student => {${name}, ${age}, ${stuNo}")
  }


}

在Java中,属性是静态绑定的。

而在Scala中,属性和方法都是动态绑定的。

package chapter06

object Test08_DynamicBind {
  def main(args: Array[String]): Unit = {
    val student: Test08Person = new Test08Student()

    println(student.name)
    student.hello()
  }
}

class Test08Person() {
  val name:String = "Person"
  def hello(): Unit = {
    println("Hello Person")
  }
}

class Test08Student extends Test08Person {
  override val name:String = "Student"

  override def hello(): Unit = {
    print("Hello Student")
  }
}

抽象类

抽象属性和抽象方法

  • 定义抽象类 abstract class Person {} ,通过abstract关键字标记抽象类
  • 定义抽象属性 val | var name: String ,一个属性没有初始化,就是抽象属性
  • 定义抽象方法 def hello(): String ,只声明而没有实现的方法,就是抽象方法

只要存在了抽象属性或抽象方法,那么这个类一定是抽象类。

package chapter06

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

// 定义一个抽象类
abstract class Test09Person{
  // 非抽象
  val name: String = "Person"
  
  def eat(): Unit = {
    println("eat")
  }
  
  // 抽象
  var age: Int
  
  def sleep(): Unit
}

class Test09Student extends Test09Person {
  // 实现抽象类和方法
  var age: Int = 21

  def sleep(): Unit = {
    println("Student Sleep")
  }
}

匿名子类

和Java一样,可以通过包含带有定义或重写代码块的方式创建一个匿名的子类

package chapter06
// 匿名子类
object Test10_AnnoymousClass {
  def main(args: Array[String]): Unit = {
    val person: Test10Person = new Test10Person {
      override var name: String = "zihoo"

      override def eat(): Unit = println("person eat")
    }

    println(person.name)
    person.eat()
  }
}

// 定义一个抽象类
abstract class Test10Person{
  var name: String
  def eat(): Unit
}

单例对象

Scala语言是完全面向对象的语言,所以并没有静态的操作(即在Scala中没有静态的概念)。但是为了能够和Java语言交互(因为Java中有静态概念),就产生了一种特殊的对象来模拟类对象,该对象为单例对象。若单例对象名与类名一致,则称该单例对象这个类的伴生对象,这个类的所有“静态”内容都可以放置在它的伴生对象中声明

单例对象语法

object Person {
    val country: String = "China"
}

单例对象采用object关键字声明

单例对象对应的类称之为伴生类,伴生对象的名称应该和伴生类名一致。

单例对象中的属性和方法都可以通过伴生对象名(类名)直接调用访问。

package chapter06

object Test11_Object {
  def main(args: Array[String]): Unit = {
    val person: Test11Person = Test11Person.newPerson("zihoo", 21)

    // 使用apply
    val person2: Test11Person = Test11Person.apply("gazikel", 22)

    // 在Scala的底层,如果使用apply方法创建对象的实例,那么我们可以省区aplly调用方法名,如下。
    val person3: Test11Person = Test11Person("gazikel_apply", 23)
    person3.printInfo()
  }
}

// 构造方法私有化
class Test11Person private(var name: String, var age: Int) {
  def printInfo(): Unit = {
    println(s"Person => {${name}, ${age}, ${Test11Person.school}}")
  }
}

object Test11Person {
  val school: String = "atguigu"

  // 定义一个创建类对象实例的方法
  def newPerson(name: String, age: Int): Test11Person = new Test11Person(name, age)

  def apply(name: String, age: Int): Test11Person = new Test11Person(name, age)
}

特质

​ Scala语言中,采用特质trait来代替接口的概念,也就是说,多个类具有相同的特质时,就可以将这个特质独立出来,采用关键字trait声明。

​ Scala中的trait中 既可以有抽象属性和方法,也可以有具体的属性和方法,一个类可以混入多个特质。这种感觉类似于Java中的抽象类。

​ Scala引入trait特质,第一可以替代Java的接口,第二个也是对单继承机制的一种补充。

特质声明

trait 特质名 {
    trait主体
}

特质基本语法

一个类具有某种性质,就意味着这个类满足了这个特质的所有要素,所以在使用时,也采用了extends关键字,如果有多个特质或存在父类,那么采用with关键字进行连接。

# 没有父类
class 类名 extends 特质1 with 特质2 with 特质3
# 有父类
class 类名 extends 父类 with 特质1 with 特质2 with 特质3
package chapter06

object Test13_Trait {
  def main(args: Array[String]): Unit = {
    val student: Test13Student = new Test13Student()
    student.study()
    student.dating()
  }
}

class Test13Person{
  val name: String = "person"
  var age: Int = 21

  def sayHello(): Unit = {
    println("Hello" + name)
  }
}

// 定义一个特质
trait Young {
  // 可以声明抽象或非抽象属性
  var age: Int
  val name: String = "young"

  def play(): Unit = {
    println("Young people is playing.")
  }

  def dating(): Unit
}

class Test13Student extends Test13Person with Young {

  // 重写冲突的属性
  override val name: String = "Student"

  override def dating(): Unit = {
    println(s"Student ${name} is dating.")
  }

  def study(): Unit = println(s"Student ${name} is studying")

  override def sayHello(): Unit = {
    super.sayHello()
    println(s"Hello from Student ${name}")
  }
}

特质的覆盖

父类和叠加的特征是平等的。当有相同的方法或属性时,后面的特征会覆盖前面的特征。

叠加顺序是从右到左。

如果遇到两个特征继承自同一个特征,则父特征在最右边。

super指的是此特征的叠加顺序的上一级。

如果想让super为父类,则需要写为 super[类名]

特征和抽象类的区别

1.优先使用特质。一个类扩展多个特质是很方便的,但却只能扩展一个抽象类。

2.如果你需要构造函数参数,使用抽象类。因为抽象类可以定义带参数的构造函数,而特质不行。

自身类型

自身类型可实现依赖注入功能

package chapter06

object Test16_TraitSelfType {
  def main(args: Array[String]): Unit = {
    val register: RegisterUesr = new RegisterUesr("zihoo", "123456")
    register.insert()
  }
}

// 定义一个用户类
class User(val name: String, val password: String)

trait UserDao {

  // 自身属性
  _: User =>

  def insert(): Unit = {
    println(s"insert into db values(${name}, ${password})")
  }
}

class RegisterUesr(name: String, password: String) extends User(name, password) with UserDao {

}

扩展

类型检查和转换

obj.isInstanceOf[T]: 判断obj是不是T类型
obj.asInstanceOf[T]: 判断obj强转成T类型
classOf: 获取对象的类名

枚举类和应用类

枚举类:需要继承Enumeration

应用类:需要继承App

Type定义新类型

使用type关键字可以定义新的数据类型名称,本质上就是类型的一个别名。

object Test{
    def main(args: Array[String]): Unit = {
        type S = String
        var v: S = "abc"
        def test(): S = "xyz"
    }
}
posted @ 2022-01-06 19:55  Gazikel  阅读(137)  评论(0编辑  收藏  举报