0

我刚开始使用 Java,我想确保我的格式和约定从一开始就一切正常。

基本上,我的老师会这样做(这让我很恼火,因为它不可读)

public class MyClass
{   
    String name;
    int age;

    public MyClass (String n, int a)
    {
        name = n;
        age = a;
    }
}

而我更喜欢做这样的事情:

public class MyClass
{   
    private String name;
    private int age;

    public MyClass (String name, int age)
    {
        this.name = name;
        this.age = age;
    }
}

我只是想知道两者之间是否存在性能差异,哪个更被接受。

4

5 回答 5

4

两个片段之间绝对没有性能差异。它归结为可读性

通常首选第二种变体,因为您为构造函数参数使用有意义的名称,而不是n真正a没有意义的名称。看起来您的老师使用不同的名称只是为了避免使用this.,我不建议这样做,因为明确this.强调您正在分配班级的字段,而不仅仅是任意变量,这可能是一个重要的区别一个构造函数。

于 2013-08-14T17:31:12.510 回答
3

在这两种情况下,用于访问字段的字节码完全相同。对于第一个,javap -c返回:

public MyClass(java.lang.String, int);
  Code:
   0:   aload_0
   1:   invokespecial   #1; //Method java/lang/Object."<init>":()V
   4:   aload_0
   5:   aload_1
   6:   putfield        #2; //Field name:Ljava/lang/String;
   9:   aload_0
   10:  iload_2
   11:  putfield        #3; //Field age:I
   14:  return

}

第二个:

public MyClass(java.lang.String, int);
  Code:
   0:   aload_0
   1:   invokespecial   #1; //Method java/lang/Object."<init>":()V
   4:   aload_0
   5:   aload_1
   6:   putfield        #2; //Field name:Ljava/lang/String;
   9:   aload_0
   10:  iload_2
   11:  putfield        #3; //Field age:I
   14:  return

}

如您所见,它们在功能上是等效的。重要的是能够理解代码。对于构造器和设置器,Java 社区的标准是使用名称阴影(参数与字段共享名称),然后使用this.限定符访问字段。但是,在逻辑方法中,强烈建议您不要隐藏字段。例如,我会避免这样的事情:

public class MyClass {

    // ...

    public int calculateAgeDifference(MyClass other) {
        int age = other.age; // This hides this.age, don't do this
        return this.age - age;
    }
}

相反,请执行以下操作:

int otherAge = other.age;

这使您的代码更具可读性。因此,如果您使用它,请仅在设置器和构造器中使用阴影,我强烈建议在其他任何地方避免使用它。

于 2013-08-14T17:31:55.317 回答
0

我使用反编译器来反编译这个源:

public class Test {

    String s;

    public void a(String p){
        s=p;
    }

    public void b(String p){
        this.s=p;
    }

}

反编译器从字节码创建了这个源:

public class Test {

    String s;

    public void a(String p){
        this.s=p;
    }

    public void b(String p){
        this.s=p;
    }

}

正如@Brian 在上面的评论中所说。两种方法完全相同。

于 2013-08-14T17:29:19.957 回答
0

两种形式生成的字节码是相同的,因此性能是相同的。

如果您javap -vMyClass对象在编译后,这两种情况下的标头都将具有相同的 md5 校验和值。

表单this.name和非限定表单都name生成相同的 putfield 操作码,如下面的输出所示6: putfield #2

的完整输出javap -v MyClass

Classfile /tmp/java/MyClass.class
  Last modified Aug 14, 2013; size 309 bytes
  MD5 checksum a6981396520454dd2aeb9a474dd92c90
  Compiled from "MyClass.java"
public class MyClass
  SourceFile: "MyClass.java"
  minor version: 0
  major version: 51
  flags: ACC_PUBLIC, ACC_SUPER
Constant pool:
   #1 = Methodref          #5.#16         //  java/lang/Object."<init>":()V
   #2 = Fieldref           #4.#17         //  MyClass.name:Ljava/lang/String;
   #3 = Fieldref           #4.#18         //  MyClass.age:I
   #4 = Class              #19            //  MyClass
   #5 = Class              #20            //  java/lang/Object
   #6 = Utf8               name
   #7 = Utf8               Ljava/lang/String;
   #8 = Utf8               age
   #9 = Utf8               I
  #10 = Utf8               <init>
  #11 = Utf8               (Ljava/lang/String;I)V
  #12 = Utf8               Code
  #13 = Utf8               LineNumberTable
  #14 = Utf8               SourceFile
  #15 = Utf8               MyClass.java
  #16 = NameAndType        #10:#21        //  "<init>":()V
  #17 = NameAndType        #6:#7          //  name:Ljava/lang/String;
  #18 = NameAndType        #8:#9          //  age:I
  #19 = Utf8               MyClass
  #20 = Utf8               java/lang/Object
  #21 = Utf8               ()V
{
  java.lang.String name;
    flags: 

  int age;
    flags: 

  public MyClass(java.lang.String, int);
    flags: ACC_PUBLIC
    Code:
      stack=2, locals=3, args_size=3
         0: aload_0       
         1: invokespecial #1                  // Method java/lang/Object."<init>":()V
         4: aload_0       
         5: aload_1       
         6: putfield      #2                  // Field name:Ljava/lang/String;
         9: aload_0       
        10: iload_2       
        11: putfield      #3                  // Field age:I
        14: return        
      LineNumberTable:
        line 7: 0
        line 8: 4
        line 9: 9
        line 10: 14
}
于 2013-08-14T17:29:54.857 回答
0

没有性能差异。实际上,编译器本质上是在this.每次引用每个成员变量时预先挂起它。这是一个偏好问题,你喜欢用哪种方式来做

于 2013-08-14T20:07:16.383 回答