# 面向对象

Scala语言是面向对象的

  • Java是面向对象的编程语言,由于历史原因,Java中还存在着非面向对象的内容:基本类型 ,null,静态方法等。
  • Scala语言来自于Java,所以天生就是面向对象的语言,而且Scala是纯粹的面向对象的语言,即在Scala中,一切皆为对象。

# 1. 类与对象

  • 类是抽象的,概念的,代表一类事物,比如人类,猫类..
  • 对象是具体的,实际的,代表一个具体事物
  • 类是对象的模板,对象是类的一个个体,对应一个实例
  • Scala中类和对象的区别和联系 和 Java是一样的。

# 1.1 定义

[修饰符] class 类名 {
   类体
} 
1
2
3

# 定义类的注意事项

  • Scala语法中,类并不声明为public,所有这些类都具有公有可见性(即默认就是public)
  • 一个Scala源文件可以包含多个类.

# 1.2 属性/成员变量

属性是类的一个组成部分,一般是值数据类型,也可是引用类型。

# 注意事项和细节说明

  • 属性的定义语法同变量,示例:[访问修饰符] var 属性名称 [:类型] = 属性值
  • 属性的定义类型可以为任意类型,包含值类型或引用类型
  • Scala中声明一个属性,必须显示的初始化,然后根据初始化数据的类型自动推断,属性类型可以省略(这点和Java不同)。
  • 如果赋值为null,则一定要加类型,因为不加类型, 那么该属性的类型就是Null类型.
  • 如果在定义属性时,暂时不赋值,也可以使用符号_(下划线),让系统分配默认值.
类型 _ 对应的值
Byte Short Int Long 0
Float Double 0.0
String 和 引用类型 null
Boolean false
  • 不同对象的属性是独立,互不影响,一个对象对属性的更改,不影响另外一个。这点和java完全一样

# 1.3 方法

Scala中的方法其实就是函数。

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

# 方法的调用机制原理

  1. 当我们scala开始执行时,先在栈区开辟一个main栈。main栈是最后被销毁
  2. 当scala程序在执行到一个方法时,总会开一个新的栈。
  3. 每个栈是独立的空间,变量(基本数据类型)是独立的,相互不影响(引用类型除外)
  4. 当方法执行完毕后,该方法开辟的栈就会被jvm机回收。

# 1.4 创建对象

val | var 对象名 [:类型]  = new 类型()
1
  • 如果我们不希望改变对象的引用(即:内存地址), 应该声明为val 性质的,否则声明为var, scala设计者推荐使用val ,因为一般来说,在程序中,我们只是改变对象属性的值,而不是改变对象的引用。
  • scala在声明对象变量时,可以根据创建对象的类型自动推断,所以类型声明可以省略,但当类型和后面new 对象类型有继承关系即多态时,就必须写了
# 实例
object OopTest {

	def main(args: Array[String]): Unit = {
		// 创建对象
		// 如果我们不希望改变对象的引用(即:内存地址), 应该声明为val 性质的,否则声明为var
		val person: Person = new Person
		// scala在声明对象变量时,可以根据创建对象的类型自动推断。所以 :Person可以省略
		// 但 当类型和后面 new 对象类型有继承关系即多态时,就必须写了
		// val person = new Person

		// 属性赋值
		person.id = 1
		person.name = "leichu"
		person.age = 30
		person.address = "合肥"
		person.del = false
		// 调用 say 方法
		println(person.say(person.name))
		// 调用 toString 方法
		println(person)
	}

}

class Person {

	var id: Long = _ // 暂不赋值,让系统自动赋默认值。使用 _ 时,必须定义出类型。
	var name: String = "" // 类型 + 默认值
	var age: Int = 0
	var address: String = null
	var del: Boolean = false

	// 定义方法
	def say(word: String): String = {
		s"hello $word"
	}

	// 重写父类 toString 方法
	override def toString: String = {
		s"id: $id \t name: $name \t age: $age \t address: $address \t del: $del"
	}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42

# 2. 构造器

和Java一样,Scala构造对象也需要调用构造方法,并且可以有任意多个构造方法(即scala中构造器也支持重载)。Scala类的构造器包括: 主构造器辅助构造器

class 类名(形参列表) {  // 主构造器
   // 类体
   def  this(形参列表) {  // 辅助构造器
   }
   def  this(形参列表) {  //辅助构造器可以有多个...
   }
} 
1
2
3
4
5
6
7
# Scala构造器注意事项和细节
  • Scala构造器作用是完成对新对象的初始化,构造器没有返回值。
  • 主构造器的声明直接放置于类名之后
  • 主构造器会执行类定义中的所有语句,这里可以体会到Scala的函数式编程和面向对象编程融合在一起,即:构造器也是方法(函数),传递参数和使用方法和前面的函数部分内容没有区别
  • 如果主构造器无参数,小括号可省略,构建对象时调用的构造方法的小括号也可以省略
  • 辅助构造器名称为this(这个和Java是不一样的),多个辅助构造器通过不同参数列表进行区分, 在底层就是f构造器重载。
# 实例
object OopTest4Constructor {
	def main(args: Array[String]): Unit = {
		val user1 = new User(1, "leichu")
		val id1 = user1.id
		val name1 = user1.name
		println(s"id:$id1 name:$name1")

		val user2 = new User("leichu")
		val id2 = user2.id
		val name2 = user2.name
		println(s"id:$id2 name:$name2")
	}
}

class User(_id: Int) { // 主构造器

	var id: Int = _id
	var name: String = _

	def this(_id: Int, _name: String) { // 辅助构造器
		this(_id)
		name = _name
	}

	def this(_name: String) { // 辅助构造器重载
		this(0)
		name = _name
	}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29

# 2.1 构造器参数

  • Scala类的主构造器的形参未用任何修饰符修饰,那么这个参数是局部变量。
  • 如果参数使用val关键字声明,那么Scala会将参数作为类的私有的只读属性使用
  • 如果参数使用var关键字声明,那么那么Scala会将参数作为类的成员属性使用,并会提供属性对应的xxx()[类似getter]/xxx_$eq()[类似setter]方法,即这时的成员属性是私有的,但是可读写
# 反编译前
class User(_id: Int) {

	var id: Int = _id
	var name: String = _

	def this(_id: Int, _name: String) {
		this(_id)
		name = _name
	}

	def this(_name: String) {
		this(0)
		name = _name
	}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# 反编译后
public class User
{
  private int id;
  private String name;
  
  public void id_$eq(int x$1)
  {
    this.id = x$1;
  }
  
  public User(int _id)
  {
    this.id = _id;
  }
  
  public int id()
  {
    return this.id;
  }
  
  public void name_$eq(String x$1)
  {
    this.name = x$1;
  }
  
  public String name()
  {
    return this.name;
  }
  
  public User(int _id, String _name)
  {
    this(_id);
    name_$eq(_name);
  }
  
  public User(String _name)
  {
    this(0);
    name_$eq(_name);
  }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42

# 3. Bean属性

JavaBeans规范定义了Java的属性是像getXxx()和setXxx()的方法。许多Java工具(框架)都依赖这个命名习惯。为了Java的互操作性。将Scala字段加@BeanProperty时,这样会自动生成规范的 setXxx/getXxx 方法。这时可以使用 对象.setXxx() 和 对象.getXxx() 来调用属性。

注意: 给某个属性加入@BeanPropetry注解后,会生成getXXX和setXXX的方法,并且对原来底层自动生成类似xxx(),xxx_$eq()方法,没有冲突,二者可以共存。

# 反编译前
import scala.beans.BeanProperty

object BeanPropertyTest {
	def main(args: Array[String]): Unit = {
		val student = new Student
		student.setName("leichu")
		println(student)
	}
}

class Student {
	var id: Int = _
	@BeanProperty var name: String = _
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
# 反编译后
public class Student
{
  private int id;
  private String name;
  
  public void id_$eq(int x$1)
  {
    this.id = x$1;
  }
  
  public int id()
  {
    return this.id;
  }
  
  public void name_$eq(String x$1)
  {
    this.name = x$1;
  }
  
  public String getName()
  {
    return name();
  }
  
  public void setName(String x$1)
  {
    name_$eq(x$1);
  }
  
  public String name()
  {
    return this.name;
  }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
更新时间: 2021-06-03 21:16:13