Scala 面向对象知识归纳
1. 类和对象详解
1.1 组成结构
构造函数
成员变量
成员方法(函数)
局部变量
代码块
1.2 构造器
- 构造器的定义
- 每个类都有一个主构造器,这个构造器和类定义"交织"在一起类名后面的内容就是主构造器,
- 如果参数列表为空的话,()可以省略
- 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,作用域:辅助构造器的作用域,只在方法中,主构造器的作用域是类中除了成员属性和
成员方法之外的所有范围(可以通过反编译查看源码)
构造器的参数说明
- 主构造器的参数没有使用var/val修饰 ,那么他的参数就是局部变量
- val修饰的构造函数的参数是类的成员私有属性 ,只读
- 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方法
- 使用此方法时,可以在main函数中不通过new来创建一个对象,即可以不用专门的一次一次地进行实例化,加载创建对象的这个类的时候,会自动调用apply这个方法,类似Java中的static静态块。
- 建议将这个方法写在半生对象中 , 如果将这个方法写在一个没有半生对象的类中是无法使用的\
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);
}

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

被折叠的 条评论
为什么被折叠?



