Java 类字段和最终变量

Java 类字段和最终变量,java,Java,据我所知,匿名内部类中的内部方法可以使用最终变量或类字段。 它们之间有显著差异吗? 例如: final int[] intArr = new int[1]; Button testButton1 = (Button) findViewById(R.id.btnTest1); testButton1.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { in

据我所知,匿名内部类中的内部方法可以使用最终变量或类字段。 它们之间有显著差异吗? 例如:

 final int[] intArr = new int[1];

Button testButton1 = (Button) findViewById(R.id.btnTest1);
testButton1.setOnClickListener(new View.OnClickListener() {
  @Override
  public void onClick(View v) {
    intArr[0]++;
    Log.i("test", String.valueOf(intArr[0]));
  }
});

Button testButton2 = (Button) findViewById(R.id.btnTest2);
testButton2.setOnClickListener(new View.OnClickListener() {
  @Override
  public void onClick(View v) {
    intArr[0]--;
    Log.i("test", String.valueOf(intArr[0]));
  }
});
我有两个按钮,它们都使用intArr[0]并可以获取和设置它的值。如果我将intArr[0]替换为一些类字段,如private int value,则会出现相同的行为 基于此,我得出结论,类字段和最终变量基本上是相同的(我的意思是它们在字节码中表示相同),只是在范围和赋值的可能性上有所不同。
我说的对吗?

final
指的是常量变量。不能更改常量的值。但您可以更改非最终成员变量的值

final
引用常量变量。不能更改常量的值。但是您可以更改非最终成员变量的值

最终变量仍然是一个变量。不同之处在于,只能指定一次值。否则,它们的行为相同。字段也可以是最终字段(尽管它当时被称为常量),但其作用域保持不变

final
关键字也称为“修饰符”,因为它修改成员的行为,而不是将其完全转换为其他内容


请记住,您仍然可以更改最终变量指向的对象。在您的情况下,您可以修改数组,尽管变量是final。您不能做的是为变量分配另一个数组。

最后一个变量仍然是一个变量。不同之处在于,只能指定一次值。否则,它们的行为相同。字段也可以是最终字段(尽管它当时被称为常量),但其作用域保持不变

final
关键字也称为“修饰符”,因为它修改成员的行为,而不是将其完全转换为其他内容


请记住,您仍然可以更改最终变量指向的对象。在您的情况下,您可以修改数组,尽管变量是final。您不能做的是为变量分配另一个数组。

外部类中的字段通过
outer引用。这
,实际上是一个
最终的
本地字段。所以在某种程度上,这并没有什么区别。显然,
final
只对变量有意义,而不是它所指向的对象

想象一下,如果将外部
this
复制到封闭方法中的本地
final
字段中

final Outer outer = this;
testButton1.setOnClickListener(new View.OnClickListener() {

outer
的行为与
outer相同。这
在匿名内部类中。

通过
outer引用外部类中的字段。这
实际上是一个
最终的
本地字段。所以在某种程度上,这并没有什么区别。显然,
final
只对变量有意义,而不是它所指向的对象

想象一下,如果将外部
this
复制到封闭方法中的本地
final
字段中

final Outer outer = this;
testButton1.setOnClickListener(new View.OnClickListener() {
outer
的行为与
outer相同。这在匿名内部类中

(我的意思是它们在字节码中的表示是相等的)

让我们看看。以这个密码为例

class Example {

  private static int[] outside = new int[]{1};

  public static void main(String [] args){
    final int[] inside = new int[]{2};

    Object inner = new Object(){{
      System.out.println(outside[0]);
      System.out.println(inside[0]);
    }};
   }
  }
编译它以获得两个类,然后使用
javap-c
反汇编它们以获得

class Example {
  Example();
    Code:
       0: aload_0       
       1: invokespecial #2                  // Method java/lang/Object."<init>":()V
       4: return   

  public static void main(java.lang.String[]);
    Code:
       0: iconst_1      
       1: newarray       int
       3: dup           
       4: iconst_0      
       5: iconst_2      
       6: iastore       
       7: astore_1      
       8: new           #3                  // class Example$1
      11: dup
      12: aload_1       
      13: invokespecial #4                  // Method Example$1."<init>":([I)V
      16: astore_2      
      17: return        

  static int[] access$000();
    Code:
       0: getstatic     #1                  // Field outside:[I
       3: areturn       

  static {};
    Code:
       0: iconst_1      
       1: newarray       int
       3: dup           
       4: iconst_0      
       5: iconst_1      
       6: iastore       
       7: putstatic     #1                  // Field outside:[I
      10: return        
}
类示例{
示例();
代码:
0:aload_0
1:invokespecial#2//方法java/lang/Object。“:()V
4:返回
公共静态void main(java.lang.String[]);
代码:
0:iconst_1
1:newarray int
3:dup
4:iconst_0
5:iconst_2
6:iStore
7:astore_1
8:新的#3//类示例$1
11:dup
12:aload_1
13:invokespecial#4//方法示例$1.“:([I)V
16:astore_2
17:返回
静态int[]访问$000();
代码:
0:getstatic#1//外部字段:[I]
3:areturn
静态{};
代码:
0:iconst_1
1:newarray int
3:dup
4:iconst_0
5:iconst_1
6:iStore
7:putstatic#1//外部字段:[I]
10:返回
}

final类示例$1{
内部最终整数[]val$;
示例$1(整数[]);
代码:
0:aload_0
1:aload_1
2:putfield#1//字段值$in:[I]
5:aload_0
6:invokespecial#2//方法java/lang/Object。”“:()V
9:getstatic#3//fieldjava/lang/System.out:Ljava/io/PrintStream;
12:invokestatic#4//方法示例。访问$000:()[I]
15:iconst_0
16:I负载
17:invokevirtual#5//方法java/io/PrintStream.println:(I)V
20:getstatic#3//fieldjava/lang/System.out:Ljava/io/PrintStream;
23:aload_0
24:getfield#1//Field val$内:[I
27:iconst_0
28:I负载
29:invokevirtual#5//方法java/io/PrintStream.println:(I)V
32:返回
}
我们看到变量
inside
被创建为类
Example$1
内的一个字段,而变量
external
是通过
Example
中自动生成的方法
access$000
访问的。因此,否-它们在字节码中的表示方式不同

(我的意思是它们在字节码中的表示是相等的)

让我们找出答案,用这个密码

class Example {

  private static int[] outside = new int[]{1};

  public static void main(String [] args){
    final int[] inside = new int[]{2};

    Object inner = new Object(){{
      System.out.println(outside[0]);
      System.out.println(inside[0]);
    }};
   }
  }
编译它以获得两个类,然后使用
javap-c
反汇编它们以获得

class Example {
  Example();
    Code:
       0: aload_0       
       1: invokespecial #2                  // Method java/lang/Object."<init>":()V
       4: return   

  public static void main(java.lang.String[]);
    Code:
       0: iconst_1      
       1: newarray       int
       3: dup           
       4: iconst_0      
       5: iconst_2      
       6: iastore       
       7: astore_1      
       8: new           #3                  // class Example$1
      11: dup
      12: aload_1       
      13: invokespecial #4                  // Method Example$1."<init>":([I)V
      16: astore_2      
      17: return        

  static int[] access$000();
    Code:
       0: getstatic     #1                  // Field outside:[I
       3: areturn       

  static {};
    Code:
       0: iconst_1      
       1: newarray       int
       3: dup           
       4: iconst_0      
       5: iconst_1      
       6: iastore       
       7: putstatic     #1                  // Field outside:[I
      10: return        
}