scala总结(面向对象篇)

这篇博客详细介绍了Scala的面向对象特性,包括包管理、类和对象、封装、方法、构造器、继承、抽象属性和方法、单例对象以及特质。讲解了Scala中包的命名规则、包对象、导入方式,类的构造器参数、继承机制以及抽象属性的实现。还涵盖了单例对象(伴生对象)的概念和apply方法,以及特质的声明、语法、叠加和执行顺序。此外,还讨论了类型转换和检查、枚举类和应用类以及定义新类型的Type。
摘要由CSDN通过智能技术生成

第五章 面向对象

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

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

5.1 Scala包

1)基本语法

package 包名.类名

2)Scala包的三大作用(和Java一样)

(1)区分相同名字的类

(2)当类很多时,可以很好的管理类

(3)控制访问范围

5.1.1 包的命名

1)命名规则

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

2)案例实操

demo.class.exec1  //错误,因为 class 关键字
demo.12a    //错误,数字开头

3)命名规范

一般是小写字母+小圆点

5.1.2 包说明(包语句)

1)说明

Scala有两种包的管理风格,一种方式和Java的包管理风格相同,每个源文件一个包(包名和源文件所在路径不要求必须一致),包名用“.”进行分隔以表示包的层级关系

第二种风格有以下特点:

(1)一个源文件中可以声明多个package

(2)子包中的类可以直接访问父包中的内容,而无需导包

2)案例实操

package com{

  // 定义一个外层包内的对象
  object Outer{
    var out: String = "out"

    import com.atzhao.scala.chapter06.Inner
    def main(args: Array[String]): Unit = {
      println("out main")
      println(Inner.in)
    }
  }

  package atzhao{
    package scala{
      package chapter06{

        // 定义一个内层包内的对象
        object Inner{
          val in: String = "in"
          def main(args: Array[String]): Unit = {
            Outer.main(args)
            Outer.out = "outer"
            println(Outer.out)

            import aaa.bbb.Test
            Test.main(args)

            commonMethod()
            println(commonValue)
          }
        }
      }
    }
  }
}

// 在同一个文件中定义多个包
package aaa{
  package bbb{

    import com.Outer

    object Test{
      def main(args: Array[String]): Unit = {
        Outer.main(args)

        import com.atzhao.scala.chapter06.Inner
        println(Inner.in)
      }
    }
  }

5.1.3 包对象

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

object Test02_PackageObject {
  def main(args: Array[String]): Unit = {
    println(commonValue)
    commonMethod()
  }
}

5.1.4 导包说明

1)和Java一样,可以在顶部使用import导入,在这个文件中的所有类都可以使用。

2)局部导入:什么时候使用,什么时候导入。在其作用范围内都可以使用

3)通配符导入:import java.util._

4)给类起名:import java.util.{ArrayList=>JL}

5)屏蔽类:import java.util.{ArrayList =>,}

6)导入相同包的多个类:import java.util.{HashSet, ArrayList}

7)导入包的绝对路径:new root.java.util.HashMap

package java {
    package util {
        class HashMap {

        }
    }
}

说明

import com.atguigu.Fruit引入com.atguigu包下Fruit(class和object)
import com.atguigu._引入com.atguigu下的所有成员
import com.atguigu.Fruit._引入Fruit(object)的所有成员
import com.atguigu.{Fruit,Vegetable}引入com.atguigu下的Fruit和Vegetable
import com.atguigu.{Fruit=>Shuiguo}引入com.atguigu包下的Fruit并更名为Shuiguo
import com.atguigu.{Fruit=>Shuiguo,_}引入com.atguigu包下的所有成员,并将Fruit更名为Shuiguo
import com.atguigu.{Fruit=>,}引入com.atguigu包下屏蔽Fruit类
new root.java.util.HashMap引入的Java的绝对路径

2)注意

Scala中的三个默认导入分别是

import java.lang._

import scala._

import scala.Predef._

5.2 类和对象

类:可以看成一个模板

对象:表示具体的事物

Java中的类如果是public,必须与文件名一致,一般一个.Java只有一个public类

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

1)基本语法

[修饰符] class 类名 {

	类体

} 

说明

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

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

属性是类的一个组成部分

1)基本语法

[修饰符] var|val属性名称 [:类型] = 属性值

注:Bean属性(@BeanPropetry),可以自动生成规范的setXxx/getXxx方法

2)案例实操

//    val a: String = _    // error
//    println(a)

    // 创建对象
    val student = new Student()
//    student.name     // error
    println(student.age)
    println(student.sex)
    student.sex = "男"
    println(student.sex)
  }
}

// 定义一个Student类
class Student {
  private var name: String = "Tom"
  @BeanProperty
  var age: Int = _
  var sex: String = _
}

5.3 封装

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

(1)将属性进行私有化

(2)提供一个公共的set方法,用于对属性赋值

(3)提供一个公共的get方法,用于获取属性的值

Scala中的public属性,底层实际为private,并通过get方法(obj.field())和set方法(obj.field_=(value))对其进行操作。所以Scala并不推荐将属性设为private,再为其设置public的get和set方法的做法。但由于很多Java框架都利用反射调用getXXX和setXXX方法,有时候为了和这些框架兼容,也会为Scala的属性设置getXXX和setXXX方法(通过@BeanProperty注解实现)。

5.1.5 访问权限

1)说明

在Java中,访问权限分为:public,private,protected和默认。在Scala中,你可以通过类似的修饰符达到同样的效果。但是使用上有区别。

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

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

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

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

2)案例实操

val person = new Person()
//    person.name    // error
    println(person.sex)
    println(person.age)

    person.printInfo()

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

class Worker extends Person{
  override def printInfo(): Unit = {
//    idCardNo    // error
    name = "bob"
    age = 25
    sex = "男"
    println(s"this is a worker: $name $age $sex")
  }

class Person {
  private var idCardNo: String = "123456"
  protected var name: String = "alice"
  private[chapter06] var age: Int = 18
  var sex: String = "女"

  def printInfo(): Unit ={
    println(s"this is a person: $idCardNo $name $age $sex")
  }
}

5.4 方法

1)基本语法

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

	方法体

}

2)案例实操

class Person {

    def sum(n1:Int, n2:Int) : Int = {
        n1 + n2
    }
}

object Person {

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

        val person = new Person()

        println(person.sum(10, 20))
    }
}

5.5 构造器

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

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

1)基本语法

class 类名(形参列表) {  // 主构造器

   // 类体

   def  this(形参列表) {  // 辅助构造器

   }

   def  this(形参列表) {  //辅助构造器可以有多个...

   }

} 

说明:

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

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

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

2)案例实操


    val student1 = new Student1()
    student1.Student1()

    val student2 = new Student1("alice")
    val student3 = new Student1("bob", 25)
  }
}

// 创建类,主构造器无参
class Student1() {
  println("1. 主构造方法调用")
  // 定义属性
  var name: String = _
  var age: Int = _

  println(s"default student is $name $age")

  // 声明辅助构造方法1
  def this(name: String){
    this()
    println("2. 辅助构造方法1调用")
    this.name = name
    println(s"student is $name $age")
  }

  // 声明辅助构造方法2
  def this(name: String, age: Int){
    this(name)
    println("3. 辅助构造方法2调用")
    this.age = age
    println(s"student is $name $age")
  }

  // 类名相同的方法只是一般方法
  def Student1(): Unit ={
    println("4. 一般方法调用")

5.6 构造器参数

1)说明

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

(1)未用任何修饰符修饰,这个参数就是一个局部变量

(2)var修饰参数,作为类的成员属性使用,可以修改

(3)val修饰参数,作为类只读属性使用,不能修改

2)案例实操

val student2 = new Student2
    student2.age = 18
    println(student2.age)

    val student3 = new Student3("alice", 18)
    student3.name = "Alice"
    println(student3.name + " " + student3.age)

    val student4 = new Student4("alice", 18)
//    student4.age    // error
    student4.printInfo()

    val student5 = new Student5("bob", 20)
    println(student5.age)

    val student6 = new Student6(age = 18)
    println(student6.school)
    student6.school = "Peking"
    student6.printInfo()
  }
}

// 定义类

// 1. 主构造器无参,定义可变属性,赋默认值
class Student2 {
  var name: String = _
  var age: Int = _
}

// 2. 主构造器参数中直接指定var类型,就定义了可变属性
class Student3(var name: String, var age: Int){

}

class Student4(name: String, age: Int){
  def printInfo(): Unit ={
    println(s"student4: $name $age")
  }
}

//class Student5(_name: String, _age: Int){
//  var name = _name
//  var age = _age
//}

class Student5(val name: String, val age: Int)

// 同时提供主、辅构造方法
class Student6(var name: String = "alice", var age: Int){
  var school: String = _

  def this(name: String, age: Int, school: String){
    this(name, age)
    this.school = school
  }
  def printInfo(): Unit ={
    println(s"student6: $name $age $school")

5.7 继承

Scala的继承与Java的继承一致,子类继承父类,都是单继承

5.8 抽象属性和抽样方法

5.8.1抽象属性和抽样方法

1)基本语法

​ (1)定义抽象类:abstract class Person{} //通过abstract关键字标记抽象类

​ (2)定义抽象属性:val|var name:String //一个属性没有初始化,就是抽象属性

​ (3)定义抽象方法:def hello():String //只声明而没有实现的方法,就是抽象方法

2)继承&重写

(1)如果父类为抽象类,那么子类需要将抽象的属性和方法实现,否则子类也需声明为抽象类

(2)重写非抽象方法需要用override修饰,重写抽象方法则可以不加override。

(3)子类中调用父类的方法使用super关键字

(4)子类对抽象属性进行实现,父类抽象属性可以用var修饰;

子类对非抽象属性重写,父类非抽象属性只支持val类型,而不支持var。

因为var修饰的为可变变量,子类继承之后就可以直接使用,没有必要重写

(5)Scala中属性和方法都是动态绑定,而Java中只有方法为动态绑定。

案例实操

  val student8 = new Student8()
    student8.eat()
    student8.sleep()
  }
}

// 定义抽象类
abstract class Person8{
  // 非抽象属性
  val idCardNo: String = "0003243"
  var name: String = "alice"
  // 抽象属性
  var age: Int
  // 非抽象方法
  def eat(): Unit ={
    println("person eat by mouth")
  }
  // 抽象方法
  def sleep(): Unit
}

class Student8 extends Person8 {
  // override在实现抽象属性和方法时,可以省略
  var age: Int = _

  def sleep(): Unit = println("student sleep in bed")

  override val idCardNo: String = "00003265"
  name = "bob"

  override def eat(): Unit = println("student eat by mouth")
}

5.8.2 匿名子类

1)说明

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

2)案例实操

val person: Person9 = new Person9 {
      override def eat(): Unit = println("person eat")

      override var name: String = "alice"
    }
    println(person.name)
    person.eat()
  }
}

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

5.9 单例对象(伴生对象)

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

5.9.1 单例对象语法

1)基本语法

object Person{

	val country:String="China"

}

2)说明

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

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

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

3)案例实操

   // 1. 用new的方式创建对象
//    val student1 = new Student11()
//    student1.printInfo()
//    val student2 = new Student11("alice", 18)
//    student2.printInfo()

    // 2. 用伴生对象的方法创建对象
    val student3 = Student11.newStudent()
    student3.printInfo()
    val student4 = Student11.newStudent("bob", 25)
    student4.printInfo()
  }
}

// 定义类,构造方法私有化
class Student11 private() {
  var name: String = _
  var age: Int = _

  private def this(name: String, age: Int){
    this()
    this.name = name
    this.age = age
  }

  def printInfo(): Unit ={
    println(s"student: $name $age")
  }
}

// 伴生对象
object Student11 {
  def newStudent(): Student11 = new Student11()
  def newStudent(name: String, age: Int): Student11 = new Student11(name, age)
}

5.9.2 apply方法

  // 2. 用伴生对象的方法创建对象
    val student3 = Student11.newStudent()
    student3.printInfo()
    val student4 = Student11.apply("bob", 25)
    Student11("cary", 23)
    student4.printInfo()

    println(Student11.school)
  }
}

// 定义类,构造方法私有化
class Student11 private() {
  var name: String = _
  var age: Int = _

  private def this(name: String, age: Int){
    this()
    this.name = name
    this.age = age
  }

  def printInfo(): Unit ={
    println(s"student: $name $age ${Student11.school}")
  }
}

// 伴生对象
object Student11 {
  var school: String = "atguigu"
  def newStudent(): Student11 = new Student11()
  def newStudent(name: String, age: Int): Student11 = new Student11(name, age)

  def apply(): Student11 = new Student11()

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

//  def apply(a: Int): String = a + " hello"
}

5.10 特质

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

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

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

5.10.1 特质声明

 val student = new Student13()
    student.sayHello()
    student.study()
    student.play()
    student.dating()
  }
}

// 定义一个父类
class Person13 {
  val name: String = "person"
  var age: Int = 20
  def sayHello(): Unit ={
    println("hello from person " + name)
  }
}

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

  // 声明抽象和非抽象方法
  def play(): Unit ={
    println("young people is playing")
  }

  def dating(): Unit
}

class Student13 extends Person13 with Young {
  override val name: String = "student"
  age = 18

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

  def dating(): Unit = println(s"student $name is dating")

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

5.10.2 特质基本语法

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

1)基本语法:

没有父类:class 类名 extends 特质1 with 特质2 with 特质3 …

有父类:class 类名 extends 父类 with 特质1 with 特质2 with 特质3…

2)说明

​ (1)类和特质的关系:使用继承的关系。

​ (2)当一个类去继承特质时,第一个连接词是extends,后面是with。

​ (3)如果一个类在同时继承特质和父类时,应当把父类写在extends后。

3)案例实操

(1)特质可以同时拥有抽象方法和具体方法

(2)一个类可以混入(mixin)多个特质

(3)所有的Java接口都可以当做Scala特质使用

(4)动态混入:可灵活的扩展类的功能

(4.1)动态混入:创建对象时混入trait,而无需使类混入该trait

(4.2)如果混入的trait中有未实现的方法,则需要实现

val student = new Student14
    student.study()
    student.increase()
    student.play()
    student.increase()
    student.dating()
    student.increase()

    println("===========================")

    val studentWithTalent = new Student14 with Talent {
      override def singing(): String = "good at singing"

      override def dancing(): String = "good at dancing"
    }
    studentWithTalent.sayHello()
    studentWithTalent.study()
    studentWithTalent.increase()
    println(studentWithTalent.singing())
  }
}

// 再定义一个特征
trait Knowledge {
  var amount: Int = 0
  def increase(): Unit
//  def increase(): Unit ={
//    println("knowledge increase")
//  }
}

// 天赋技能
trait Talent {
  def singing(): String
  def dancing(): String
//  def increase(): Unit ={
//    println("talent increase")
//  }
}

// 实现一个子类,混入特征
class Student14 extends Person13 with Young with Knowledge {
  override val name: String = "student"
  age = 18

  override def sayHello(): Unit = println(s"hello from student $name")

  def dating(): Unit = println(s"student $name is dating")

  def study(): Unit = println(s"student $name is studying")

  override def increase(): Unit = {
    amount += 2
    println(s"student knowledge increased: $amount")

5.10.3 特质叠加

由于一个类可以混入(mixin)多个trait,且trait中可以有具体的属性和方法,若混入的特质中具有相同的方法(方法名,参数列表,返回值均相同),必然会出现继承冲突问题。冲突分为以下两种:

第一种,一个类(Sub)混入的两个trait(TraitA,TraitB)中具有相同的具体方法,且两个trait之间没有任何关系,解决这类冲突问题,直接在类(Sub)中重写冲突方法。

第二种,一个类(Sub)混入的两个trait(TraitA,TraitB)中具有相同的具体方法,且两个trait继承自相同的trait(TraitC),及所谓的“钻石问题”,解决这类冲突问题,Scala采用了特质叠加的策略。

案例实操

 // 钻石问题特征叠加
    val myBall = new MyFootBall
    println(myBall.describe())
  }
}

// Ball特征叠加示例
trait Ball {
  def describe(): String = "ball"
}

trait ColorBall extends Ball {
  override def describe(): String = "red-" + super.describe()
}

trait CategoryBall extends Ball {
  override def describe(): String = "foot-" + super.describe()
}

5.10.4 特质叠加执行顺序

scala特质叠加顺序

(1)案例中的super,不是表示其父特质对象,而是表示上述叠加顺序中的下一个特质,即,MyClass中的super指代Color,Color中的super指代Category,Category中的super指代Ball。

(2)如果想要调用某个指定的混入特质中的方法,可以增加约束:super[],例如super[Category].describe()。

5.10.5 特质自身类型

1)说明

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

2)案例实操

class User(val name: String, val age: Int)

 

trait Dao {

   def insert(user: User) = {
 
   println("insert into database :" + user.name)

   }

}


trait APP {

   _: Dao =>

 

   def login(user: User): Unit = {

   println("login :" + user.name)

   insert(user)

   }

}

 

object MyApp extends APP with Dao {

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

   login(new User("bobo", 11))

   }

}

5.10.6特质和抽象类的区别

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

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

5.11 扩展

5.11.1 类型转换和检查

1)说明

(1)obj.isInstanceOf[T]:判断obj是不是T类型。

(2)obj.asInstanceOf[T]:将obj强转成T类型。

(3)classOf获取对象的类名。

2)案例实操

class Person{

}

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

        val person = new Person

        //(1)判断对象是否为某个类型的实例
        val bool: Boolean = person.isInstanceOf[Person]

        if ( bool ) {
            //(2)将对象转换为某个类型的实例
            val p1: Person = person.asInstanceOf[Person]
            println(p1)
        }

        //(3)获取类的信息
        val pClass: Class[Person] = classOf[Person]
        println(pClass)
    }
}

5.11.2 枚举类和应用类

1)说明

​ 枚举类:需要继承Enumeration

​ 应用类:需要继承App

2)案例实操

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

        println(Color.RED)
    }
}

// 枚举类
object Color extends Enumeration {
    val RED = Value(1, "red")
    val YELLOW = Value(2, "yellow")
    val BLUE = Value(3, "blue")
}

// 应用类
object Test20 extends App {
    println("xxxxxxxxxxx");
}

5.11.3 Type定义新类型

1)说明

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

2)案例实操

object Test {

    def main(args: Array[String]): Unit = {
        
        type S=String
        var v:S="abc"
        def test():S="xyz"
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值