有 Java 编程相关的问题?

你可以在下面搜索框中键入要查询的问题!

c#在访问实例变量时使用this关键字是否更有效?

在访问C#和Java等语言中的实例字段、属性和方法时,this关键字是可选的

我最近一直在对各种语言进行一些最佳实践研究,并注意到许多地方建议在方法中创建实例字段的本地引用,因为这样更有效。最近一次提到的是Android教程

在我看来,如果指定this._obj,它应该和局部变量一样有效。这是正确的还是与不使用this一样“昂贵”

答案是否从Android Dalvik虚拟机变为标准Java,变为C#

public class test {
    private Object[] _obj;

    protected void myMethod() {
        Object[] obj = _obj;

        // Is there an appreciable differnce between
        for(int i = 0; i < obj.length; i++) { 
            // do stuff
        }

        // and this?
        for(int i = 0; i < this._obj.length; i++) { 
            // do stuff
        }
    }
}

共 (6) 个答案

  1. # 1 楼答案

    this关键字用于可读性,最重要的是使变量名明确无误。它对性能没有任何影响

  2. # 2 楼答案

    至少对于标准Java来说,有一个非常小的区别

    我对您的示例进行了一些修改:

    public class test {
        private Object[] _obj;
    
        protected void myMethodLocal() {
            Object[] obj = _obj;
    
            // Is there an appreciable differnce between
            for(int i = 0; i < obj.length; i++) { 
                // do stuff
            }
        }
    
       protected void myMethodMember() {
            // and this?
            for(int i = 0; i < this._obj.length; i++) { 
                // do stuff
            }
       }
    }
    

    因此myMethodLocal()_obj缓存到局部变量中,而myMethodMember()使用类成员_obj

    现在,让我们对其进行反编译(使用javap):

    protected void myMethodLocal();
      Code:
       0:   aload_0
       1:   getfield    #2; //Field _obj:[Ljava/lang/Object;
       4:   astore_1
       5:   iconst_0
       6:   istore_2
       7:   iload_2
       8:   aload_1
       9:   arraylength
       10:  if_icmpge   19
       13:  iinc    2, 1
       16:  goto    7
       19:  return
    
    protected void myMethodMember();
      Code:
       0:   iconst_0
       1:   istore_1
       2:   iload_1
       3:   aload_0
       4:   getfield    #2; //Field _obj:[Ljava/lang/Object;
       7:   arraylength
       8:   if_icmpge   17
       11:  iinc    1, 1
       14:  goto    2
       17:  return
    

    在不深入讨论细节的情况下,后一个示例必须在每次循环迭代中访问_obj字段,而第一个示例已经将其缓存在本地引用中,只需要访问本地引用

    这在速度差上等于什么

    不多

    虽然在Python这样的语言中,访问本地引用和类引用之间的差异意味着更多,但对于Java,您真的不必担心。保持代码的可读性和可维护性比为这样的细节而烦恼要重要得多

    (另外,上面的字节码没有考虑JIT编译器可能会做什么)

    如果您通过函数获得实例字段,比如getObj(),我会将其插入一个变量中,这样您就不必在每次使用同一字段时都继续调用getObj()


    另外,作为一个次要的注意事项,您可能应该调用类Test,而不是test.。Java倾向于使用大写骆驼字母作为类名

  3. # 3 楼答案

    理论上说,没有。访问“this._obj.Length”最终生成如下代码:

    mov eax, [ecx + offset_of_obj]
    mov eax, [eax + offset_of_length]
    

    其中as“obj.length”最终生成如下代码:

    mov eax, [esp + offset_of_obj]
    mov eax, [eax + offset_of_length]
    

    在实践中,也许是,但可能不是。几乎每个x86调用约定只有3个暂存寄存器“eax”、“ecx”和“edx”。所有其他寄存器必须保存在堆栈上才能更新。如果您有一个长函数,并且不需要访问“this”,那么ecx寄存器可以重新调整用途以保存临时变量,从而减少需要发生的堆栈溢出量。但是,为了创建局部变量,您必须在堆栈上推送新值,因此改进的场景是有限的。我不会理会谁告诉过你的

  4. # 4 楼答案

    在现代PC上,由于缓存的原因,这可能不会对任何语言产生任何影响——如果内存位置缓存在芯片上,则不会产生任何影响

  5. # 5 楼答案

    不,效率绝对没有变化。请记住,在许多语言中,在底层字节码或汇编或更高级语言翻译成的任何语言中,几个等价的表达式都会简化为相同的语句

    答案在您提到的语言和虚拟机之间是一致的

    必要时使用它,例如当方法参数与实例变量同名时

    除非CPU周期(或内存等)是最高优先级,否则应将清晰度置于表达能力较差但效率较高的语言语法之上

  6. # 6 楼答案

    我怀疑使用局部变量只是一个引用(值在堆栈上),而使用成员变量是2引用(引用堆栈上的this,然后引用堆上的变量本身)

    根据系统的不同,堆或堆栈访问可能更快

    但是就像Jonathon所说的,除非速度非常重要,否则不要为此烦恼。它只会降低可读性,性能可以忽略不计