Scala 面向对象知识归纳

本文深入解析Scala面向对象编程,涵盖类与对象、构造器、成员变量与方法、局部变量、代码块、伴生类与对象、apply方法、权限修饰符、特质与抽象类、样例类等内容,助您掌握Scala OOP核心。

Scala 面向对象知识归纳

1. 类和对象详解

1.1 组成结构

构造函数
成员变量
成员方法(函数)
局部变量
代码块

1.2 构造器

  1. 构造器的定义
  • 每个类都有一个主构造器,这个构造器和类定义"交织"在一起类名后面的内容就是主构造器,
  • 如果参数列表为空的话,()可以省略
  • scala的类有且仅有一个主构造器,要想提供更加丰富的构造器,就需要使用辅助构造器,辅助构造器是可选的,它们叫做this
  • 其实这一点和java的设计是一样的,只不过java会默认调用父类构造器,scala是所有构造方法最后都需要调用主构造器
  • 主构造器会执行类定义中的所有语句
package com.doit.oop

class User {

}
package com.doit.oop

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

    val user = new User

  }
}

  • 反编译
    在这里插入图片描述
  • 结果
    在这里插入图片描述
package com.doit.oop

// 在类名定义时跟随括号,表明就是主构造方法,没有则默认是空参构造方法
// 2个参数的主构造方法,注意没有加val var修饰构造方法参数,所有不会生成对应成员变量,创建对象后也是无法访问属性的
class User2(id:Int, name:String) {


}

object User2{
  def main(args: Array[String]): Unit = {
    val ximenchuixue = new User2(12, "ximenchuixue")

    ximenchuixue.
  }
}

在这里插入图片描述

package com.doit.oop

// 注意这里是在主构造方法中对参数进行var和val修饰
// var修饰,表明这是一个可变成员变量
// val修饰,说明这是一个不可变成员变量
class User3(var id:Int, val name:String) {

}

object User3 {
  def main(args: Array[String]): Unit = {
    val dugujian = new User3(14, "dugujian")

    println(dugujian.id)
    dugujian.id = 23
    println(dugujian.name)
  }
}
package com.doit.oop

class User4 {

  var name:String = ""
  var age: Int = 0
  
  def this(name:String, age:Int) {
    // 这是默认的主构造方法
    this()
    
    this.name = name
    this.age = age
  }

  def this(msg:String) {
    this("ww", 30)
    
    println("这是一个辅助构造函数")
  }
  
}

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

    val user = new User4()

    val user1 = new User4("haha")

    val user2 = new User4("ww", 60)
  }
}

总结:
1,有两类构造器:主构造器,辅助构造器
2,构造器的定义位置
主构造器和类交织在一起,class Student2(val name: String, var age: Int)
3, 辅助构造器是一个特殊的方法,定义在类中 def this(name:String,age:Int,gender:String)
4,辅助构造器,第一行必须调用主构造器(或者其他的辅助构造器)
5,辅助构造器的参数不能和主构造器的参数完全一致(参数个数,参数类型,参数顺序)
6,可以定义空参的辅助构造器,但是主构造器的参数必须进行初始化赋值
7,作用域:辅助构造器的作用域,只在方法中,主构造器的作用域是类中除了成员属性和
成员方法之外的所有范围(可以通过反编译查看源码)

构造器的参数说明

  1. 主构造器的参数没有使用var/val修饰 ,那么他的参数就是局部变量
  2. val修饰的构造函数的参数是类的成员私有属性 ,只读
  3. var修饰的构造函数的参数是类的私有成员属性 ,但是是可读可写的

1.3 成员变量

定义在类中的变量称为成员变量

package com.doit.oop

import scala.beans.BeanProperty

// 在类定义时,使用val修饰属性,也算是生成一个成员变量。同时也是声明主构造方法
class Person (val addr:String) {

  val name:String = "zhaoyun"
  
  var age:Int = 30
  
  // 这样注解之后,会生成get和set方法,但scala的属性如果是非privat,直接点语法进行读写即可
  @BeanProperty
  var msg:String = _
}

主构造函数中使用val 和 var修饰的变量为成员变量
val 修饰的变量默认只有getter方法 一要初始化
var 修饰的变量默认有 get和set方法 直接点属性操作 使用 _ 占位可以稍后赋值
@BeanProperty会生成getMsg setMsg方法

1.4 成员方法/函数

在类的成员位置定义的函数或者方法是类的成员的一部分

在这里插入图片描述

1.5 局部变量

定义在函数/方法的参数中 , 定义在代码块中 , 定义在流程控制的条件表达式中

package com.doit.variable

object LocalVarTest1 {

  def add(x: Int*): Int = {
    // 定义在函数和方法内部的变量,就是临时变量或者局部变量。需要初始化才能使用
    var sum: Int = 0

    for(e <- x) {
    sum += e
  }
    sum
  }

  def main(args: Array[String]): Unit = {
    println(add(2, 3, 4))


  }
}

注意 :
1 局部变量都需要设置初始值
2 局部变量的作用范围 ,定义在代码块中

1.6 代码块

在类或者对象中的代码块在实例化的时候会被调用

package com.doit.code_block

object CodeBlockTest1 {

  {
    println("xixi")

    var x:String = "haha"
    println(x)
  }

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

  }
}

1.7 伴生类和伴生对象

条件 1:在同一个源文件中, 条件 2:对象名和类名相同

package com.doit.object_class

// 这里存放非静态代码
class CompanionClassAndObject {

  var color:String = "block"
  
  def show(): Unit = {
    println("秀一下")
  }
  
}

// 这里面放静态代码
object CompanionClassAndObject {
  // 注意静态代码也是可以由方法和属性的,静态方法和静态变量
  var age:Int = 10
  val name:String = "likui"
  
  def main(args: Array[String]): Unit = {
    
  }
}

1.8 apply方法

  1. 使用此方法时,可以在main函数中不通过new来创建一个对象,即可以不用专门的一次一次地进行实例化,加载创建对象的这个类的时候,会自动调用apply这个方法,类似Java中的static静态块。
  2. 建议将这个方法写在半生对象中 , 如果将这个方法写在一个没有半生对象的类中是无法使用的\
package com.doit.apply

class ApplyTest2 {

  var age: Int = 0

  def this(age: Int) {
    this()

    this.age = age
  }
}

object ApplyTest2 {

  // 这里会默认把主构造方法进行apply处理
  def apply(): ApplyTest2 = new ApplyTest2()

  def main(args: Array[String]): Unit = {
    // 注意这里没有使用new来创建对象,因为进行了apply的处理
    val test: ApplyTest2.type = ApplyTest2

    val applyTest = new ApplyTest2(12)

  }
}

1 . apply方法的主要目的是不使用new来获取实例对象, 并且默认是单例的!!!
2.apply方法建议编写在伴生对象中
3.apply方法如果没有参数也不要省略()

1.9 classof、isInstanceOf、asInstanceof

classOf[T]看成Java里的T.class
isInstanceOf[T]判断是否是实例对象
obj.asInstanceOf[T]强制类型转换

1.10 class object的使用选择

class object 一般情况下,使用 object + 伴生的 class

1,优先使用object,object本质上拥有了的类的所有特性,object中没有构造器,也没有 参数 ,必须要有程序入口,object,main方法,必不可少
2, 如果是单例的,优先选择object,如果是多例的(封装数据,构造器),必须选择类
3, 伴生类和伴生对象都会用到。首先把object写上,如果需要有类的功能,就把伴生类 写上。

2. 权限修饰符

scala中的权限修饰符public protected private ,scala中有更加严格的访问权限的控制

1.默认情况下使用的public任何范围都可以访问
2.private 修饰的属性或者方法的访问范围是本类和伴生对象中
private [this] ,作用域为当前类中,伴生对象中无效
private [packageName] 指定包及其子包有效

2.1 属性的访问权限

注意scala中,伴生对象和伴生类是可以互相访问对方的私有属性和方法的。
大家可以理解为以往java代码中静态和非静态代码在一个文件中,scala中拆解为2个地方,所以可以互相访问是很正常的一个事情。

class User(val name: String, private var age: Int) {

  def show() = {
    // 本类中可以访问柱构造函数中定义的私有的属性age
    // 本类也可以访问伴生对象中的私有属性
    println(name + ":" + age + ":" + User.address)
  }

}

object User {
  private val address: String = "wewq"
  def main(args: Array[String]): Unit = {
    val user = new User("ZSS", 21)
    user.show()
    // 伴生对象中也可以访问伴生类中的私有属性
    println(user.name + "--" + user.age)
  }
}

外部不能访问私有属性

object Test1 {
  def main(args: Array[String]): Unit = {
    val user = new User("zss",21)
    // 外部可以访问非私有的属性
    user.name
    // 在外部不能访问私有的尚需经age
    user.age
    //外部不能访问对象中私有的属性
    User.address
  }
}

private[this] 修饰的内容只能本类访问 , 伴生对象和类不能访问

// 私有构造函数不允许外部new 对象 但是在半生对象中可以
class Person private (val name:String) {
  // 者变量只能本类访问  this
  private[this] val age = 21
  private val addres:String = "Beijing"
}

object  Person{
  def main(args: Array[String]): Unit = {
    val p = new Person("zss")
    p.age
    p.addres
  }
}

this[package] 指定包以及子包可以访问 指定属性访问的包范围
在这里插入图片描述
在这里插入图片描述
注意,实际使用时,基本就是私有、public为主,需要开放就开放,需要私有就完全私有,一般不会设置为跨包、或者限制伴生对象的访问。只是scala提供了这种能力,特殊场景确实需要这样的功能,则使用即可

2.2 方法的访问权限和类的访问权限

访问现象和属性的现象一致
通用于主构造器,辅构造器,以及普通方法
默认权限是共有的

private 作用域为类和其伴生对象
private [this] ,作用域为当前类中,伴生对象中无效
private [packageName] 指定包及其子包有效 包名的写法,直接写报名,不需要层级路径
主构造器上一样适用于该方法的访问权限
private [cn.edu360.day03] 错误的
private [day03] 正确的
protected
protected 修饰的属性 方法 在类及其伴生对象中有效,在子类及其子类的伴生对象中有效,其他地方无效。

3. 特质和抽象类

java中对方法进行抽离以及约束,使用的是接口和抽象类,但抽象类需要继承,所以接口能够很好实现多继承效果而不需要继承。
scala将这一点进一步发挥,scala的接口称之为trait,特质。抽象类还是抽象类。
关键字是extends和with

3.1 特质

3.1.1 特质使用
package com.doit.traittest

trait Trat1 {

  val id:String = ""

  def show1(): Unit = {
    println("hahah")
  }

  def show2():String
}

package com.doit.traittest

trait Trait2 {

  def show3():Int
}

实现特质

package com.doit.traittest

class Trat1Impl{

}


object Trat1Impl extends Trat1 {
  override def show2(): String = {
    "这是一个寂寞的trat特质"
  }

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

实现继承多个特质,使用with

package com.doit.traittest

class MultiTraitImpl extends Trat1 with Trait2 {
  override def show2(): String = {
    "多个trait实现"
  }

  override def show3(): Int = {
    -30
  }
}

object MultiTraitImpl {
  def main(args: Array[String]): Unit = {
    val multiTraitImpl = new MultiTraitImpl

    println(multiTraitImpl.show2())

    println(multiTraitImpl.show3())
  }
}

3.1.2 动态混入

在使用的时候再实现具体的接口重写对应的抽象方法,类比java的匿名内部类的使用

package com.doit.traittest

class TraitDynamicTest1 {

}

class B {
  
  // xixi是一个方法
  def xixi = {
    // 这里类似于创建一个java的一个匿名内部类
    val  a = new TraitDynamicTest1 with Trat1 with Trait2 {
      override def show2(): String = {
        "haha"
      }
      

      override def show3(): Int = {
        4
      } 
      
    }
    
  }
}

3.2 抽象类

在 Scala 中, 使用 abstract 修饰的类称为抽象类. 在抽象类中可以定义属性、未实现的方法
(抽象方法)和具体实现的方法。 含有抽象方法的类就是抽象类

抽象类中的属性
抽象方法
具体实现的方法
类的单继承

package com.doit.abstract_class

class AbstractClassTest1 {

}

// 和java一样,抽象类可以由属性、构造方法、非抽象方法、抽象方法
abstract class Abs {
  val name:String = "镇关西"
  
  
  def sleep()
  
  def killPig(): Unit = {
    println("杀猪,卖猪肉")
  }
}

4. 样例类

样例类对比java的java bean,但在此基础上做了很多工作,降低了编写时的冗余代码。例如idea和eclipse快捷键生成的构造方法、getter、setter方法、toString、equals、hashcode等方法,默认实现了serializable接口。

使用case修饰的类就是样例类
1 构造器中的参数默认是val修饰的
2 样例类会自动创建伴生对象, 同时在里面实现;的apply和unapply方法 ,创建对象的时候不用new
3 很好的支持匹配模式
4 默认实现了自己的toString , hashCode , copy , equals方法

package com.doit.caseclass

case class Computer(band:String, price:Double)

反编译
在这里插入图片描述

public class com.doit.caseclass.Computer implements scala.Product,scala.Serializable {
  public static scala.Option<scala.Tuple2<java.lang.String, java.lang.Object>> unapply(com.doit.caseclass.Computer);
  public static com.doit.caseclass.Computer apply(java.lang.String, double);
  public static scala.Function1<scala.Tuple2<java.lang.String, java.lang.Object>, com.doit.caseclass.Computer> tupled();
  public static scala.Function1<java.lang.String, scala.Function1<java.lang.Object, com.doit.caseclass.Computer>> curried();
  public java.lang.String band();
  public double price();
  public com.doit.caseclass.Computer copy(java.lang.String, double);
  public java.lang.String copy$default$1();
  public double copy$default$2();
  public java.lang.String productPrefix();
  public int productArity();
  public java.lang.Object productElement(int);
  public scala.collection.Iterator<java.lang.Object> productIterator();
  public boolean canEqual(java.lang.Object);
  public int hashCode();
  public java.lang.String toString();
  public boolean equals(java.lang.Object);
  public com.doit.caseclass.Computer(java.lang.String, double);
}
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值