您的位置:首页 > 编程语言

[1.1] 面向对象编程之get-set方法与构造器详解

2016-08-01 19:20 483 查看

场景

一、肿么理解scala中的val与var关键字?有人说:val标识常量,且有公有get方法、私有set方法 - 有私有set方法吗?

二、代码:

class Person5(name:String){
}
class Person6(val name:String){
}


中字段name加 val前缀与不加,有什么区别?

由于scala代码被scala编译器编译成字节码字,最终在JVM上运行.因此,以上两个问题,可以通过反编译工具将字节码字反编译成我们熟悉的java代码,类比理解scala中的val与var 进行求证。

知识点

一. get与set方法与private关键字

scala字段默认由private关键字修饰。其中, val 标识常量-无set方法,有公有get方法、var标识变量-具有公有 set、get方法。

set方法的命名规则:属性名称_=(字段类型) 或 属性名称_$eq(字段类型)

;get方法命名规则:属性名称()。

由显式private修饰的字段只有私有方法。

二、构造器

主构造器中的参数可以有 val 与 var 等修饰,也可以没有 - 而辅助构造器中的参数不能 有val与var修饰 。类中没有定义在任何方法或者是代码块之中的代码,就是主constructor的代码-类初始化时会被执行。

辅助构造器中必须直接或者间接调用主构造器,以完成类的初始化

实验

先介绍一个JDK自带的反编译工具 : javap.

$ javap -c -p className 表示 : 对字节码className进行反编译,且显示所有的类与成员.

好,来看一个案例吧!

一、val标识的字段无私有set方法的证明

package cool.pengych.objectoriented

/**
* function: fun面向对象 之 案例详解 val,var,private关键字 与 类的主构造器,辅助构造器
*/
class Person(val father:String) {

//公有 set, get方法
var name:String = _ //字段需要显式赋值(这里值为 _,相当于赋null),而不像java可以不赋值

//公有get,无set方法
val friend = "ivy"

//私有set,get方法
private var age = 18

//私有get方法,无set方法
private val interests = "basketball"

/*
* 1. 虽然是 age 是private 的,但是在类的内部,可以通过自定义方法,改变 age 的值
* 2. 由于由于interests是 val 的,因此在类的内部也不能改变其值
*/
def setAge(age:Int){
this.age = age
}
def setInterests (interests:String){
//  this.interests = interests;
}

/*
* 1. 辅助构造器中第一行必须要直接或者间接调用主构造器
* 2. 为了保证类结构的一致性,辅助构造器中,不能传递 val 与 var等修饰的常量与变量
*  eg. this方法如果传 var age : Int这种变量就不行
*/
def this(father:String = "spark",age:Int = 18){
this(father) // 直接调用主构造器
this.age = age
}

def this(father:String,son:String){
this // 间接调用主构造器-通过调用辅助构造器间接调用主构造器.
// 这里其实调用的是 辅助构造器 this(father:String = "spark",age:Int = 18) - 参数默认都有值,所以可以不传
}
}

object HelloOOP{
def main(args: Array[String]): Unit = {
val person = new Person("spark")
person.name = "ben"
person.setAge(27)
println(person.friend)

// println(person.interests)
// println(person.age) private 修饰的常量与变量 ,其set与get方法都是私有的,因此通过get方法获取其值
}
}


执行以下命令,进行反编译:

pengyucheng@sparker:~$ scalac Person.scala
pengyucheng@sparker:~$ javap -c -p cool/pengych/objectoriented/Person.class


反编译后的部分结果如下(简单起见,删除了一些冗长的代码):

Compiled from "Person.scala"
public class cool.pengych.objectoriented.Person {
private final java.lang.String father;
// [snail注]证明了:字段默认就是由private关键字修饰的?
// 因为scala代码的主构造器中的father没有用private修饰,而这里出现了private.

private java.lang.String name;

private final java.lang.String friend;

private int age;

private final java.lang.String interests;

public java.lang.String father();
1: getfield      #31                 // Field

public java.lang.String name();
1: getfield      #35                 // Field

public void name_$eq(java.lang.String);
2: putfield      #35                 // Field

public java.lang.String friend(); //[snail注]val修饰的字段只有公有get方法,没有set方法
Code:
0: aload_0
1: getfield      #40                 // Field friend:Ljava/lang/String;
4: areturn

private int age();//get方法的名称:属性名称()
Code:
0: aload_0
1: getfield      #42                 // Field age:I
4: ireturn

private void age_$eq(int);
//[注]set方法的名称:1、属性名称_=(字段类型) 2、属性名称_$eq(字段类型)
Code:
0: aload_0
1: iload_1
2: putfield      #42                 // Field age:I
5: return

private java.lang.String interests();
Code:
0: aload_0
1: getfield      #46                 // Field interests:Ljava/lang/String;
4: areturn

public void setAge(int);
Code:
0: aload_0
1: iload_1
2: invokespecial #49                 // Method age_$eq:(I)V
5: return

public void setInterests(java.lang.String);
Code:
0: return

public cool.pengych.objectoriented.Person(java.lang.String);// [snail注]类的主构造器

public cool.pengych.objectoriented.Person(java.lang.String, int);

public cool.pengych.objectoriented.Person(java.lang.String, java.lang.String);

}


看到了吗?只有var修饰的字段,比如说age才有set方法:age_$eq(int)-注意方法内部调用的是下面的putfield方法,而get方法调用的是底层JVM的getfield方法。

private void age_$eq(int);
Code:
0: aload_0
1: iload_1
2: putfield      #42                 // Field age:I
5: return


二、主构造器中参数有无 val或者var修饰的区别

反编译

package cool.pengych.scala.objectoriented
/**
* Created by 彭宇成 on 2016/8/6.
*/
class Person5(name:String){ } class Person6(val name:String){ }


后的结果如下:

Compiled from "Person5.scala"
public class cool.pengych.scala.objectoriented.Person5 {
public cool.pengych.scala.objectoriented.Person5(java.lang.String);
//[注]类中没有 name属性
Code:
0: aload_0
1: invokespecial #13                 // Method java/lang/Object."<init>":()V
4: return
}

Compiled from "Person5.scala"
public class cool.pengych.scala.objectoriented.Person6 {
private final java.lang.String name;

public java.lang.String name();[注]val修饰的构造器参数name,反编译后类中有 name属性
Code:
0: aload_0
1: getfield      #13                 // Field name:Ljava/lang/String;
4: areturn

public cool.pengych.scala.objectoriented.Person6(java.lang.String);
Code:
0: aload_0
1: aload_1
2: putfield      #13                 // Field name:Ljava/lang/String;
5: aload_0
6: invokespecial #20                 // Method java/lang/Object."<init>":()V
9: return
}


总结

scala中字段默认由 private 关键字修饰的;var标识的字段有公有set与get方法,而val标识的字段只有公有get方法。

无val与var修饰的类主构造器参数,不会映射成类的同名字段。有val或者var修饰的主构造器参数,会映射成类的同名字段 - 编译器会帮我们自动生成相关的get与set方法,以及类的含参构造器 - 这在很大程度上增强了scala代码的简洁性。

辅助构造器必须在首行直接或者间接调用主构造器,以完成类的初始化。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: