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

来源:互联网 发布:oracle导入txt数据 编辑:程序博客网 时间:2024/06/16 10:04

场景

一、肿么理解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.scalapengyucheng@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代码的简洁性。
  • 辅助构造器必须在首行直接或者间接调用主构造器,以完成类的初始化。
0 0
原创粉丝点击