匿名内部类可以访问的变量---静态成员变量和final修饰的局
部变量
  在学习多线程的时候⽤到了匿名内部类,匿名内部类可以访问static静态成员变量或者final修饰的局部变量。
  匿名内部类在编译之后会⽣成class⽂件,⽐如Test内的第⼀个匿名内部类编译之后就是Test$1.class;
  匿名内部类中访问的final修饰的局部变量在⽣成Test$1.class之后会作为构造⽅法的参数传⼊class中;如果匿名内部类访问的是另⼀个类的静态成员变量则直接访问,不会作为构造⽅法的参数。
1.访问final修饰的局部变量
  局部变量需要是final修饰,如果访问⽅法参数,⽅法的参数也需要是final修饰的
am.test;
import java.util.ArrayList;
import java.util.List;
public class Test1 {
public static void main(String[] args) {
final List list = new ArrayList<>();
list.add("111");
new Thread(new Runnable() {
@Override
public void run() {
System.out.println(list + ",threadName->" + Thread.currentThread().getName());
}
}).start();
test1("xxx");
}
public static void test1(final Object object) {
new Thread(new Runnable() {
@Override
public void run() {
System.out.println(object + ",threadName->" + Thread.currentThread().getName());
}
}).start();
}
}
结果:
[111],threadName->Thread-0
xxx,threadName->Thread-1
需要⽤final修饰的原因:
  内部类⾥⾯使⽤外部类的局部变量时,其实就是内部类的对象在使⽤它,内部类对象⽣命周期中都可能调⽤它,⽽内部类试图访问外部⽅法中的局部变量时,外部⽅法的局部变量很可能已经不存在了,那么就得延续其⽣命,拷贝到内部类中,⽽拷贝会带来不⼀致性,从⽽需要使⽤final声明保证⼀致性。说⽩了,内部类会⾃动拷贝外部变量的引⽤,为了避免:1. 外部⽅法修改引⽤,⽽导致内部类得到的引⽤值不⼀致 2.内部类修改引⽤,⽽导致外部⽅法的参数值在修改前和修改后不⼀致。于是就⽤ final 来让该引⽤不可改变
 Java为了避免数据不同步的问题,做出了匿名内部类只可以访问final的局部变量的限制。
反编译查看源码:(⼀个Java⽂件反编译出来三个class⽂件,也就是匿名内部类也被编译为class)
C:\Users\liqiang\Desktop\新建⽂件夹>ls
'Test1$1.class'  'Test1$2.class'  Test1.class  Test1.java
(1)查看Test1$1.class(可以理解为Test1的第⼀个内部类,实际是将内部访问的final修饰的变量作为参数传⼊此类的构造⽅法):
javap反汇编查看:
C:\Users\liqiang\Desktop\新建⽂件夹>javap -c Test1$1.class
Compiled from "Test1.java"
final am.test.Test1$1 implements java.lang.Runnable {
final java.util.List val$list;
Code:
0: aload_0
1: aload_1
2: putfield      #1                  // Field val$list:Ljava/util/List;
5: aload_0
6: invokespecial #2                  // Method java/lang/Object."<init>":()V
9: return
public void run();
Code:
0: getstatic    #3                  // Field java/lang/System.out:Ljava/io/PrintStream;
3: new          #4                  // class java/lang/StringBuilder
6: dup
7: invokespecial #5                  // Method java/lang/StringBuilder."<init>":()V
10: aload_0
11: getfield      #1                  // Field val$list:Ljava/util/List;
14: invokevirtual #6                  // Method java/lang/StringBuilder.append:(Ljava/lang/Object;)Ljava/lang/Stri
ngBuilder;
17: ldc          #7                  // String ,threadName->
19: invokevirtual #8                  // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/Stri
ngBuilder;
22: invokestatic  #9                  // Method java/lang/Thread.currentThread:()Ljava/lang/Thread;
25: invokevirtual #10                // Method java/Name:()Ljava/lang/String;
28: invokevirtual #8                  // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/Stri
ngBuilder;
31: invokevirtual #11                // Method java/String:()Ljava/lang/String;
34: invokevirtual #12                // Method java/io/PrintStream.println:(Ljava/lang/String;)V
37: return
}
(2)查看Test1$2.class(可以理解为Test1的第⼆个内部类,实际是将内部访问的final修饰的变量作为参数传⼊此类的构造⽅法):
(3)查看Test1.class
反编译看不出来,直接反汇编查看:可以看出是创建了对应的匿名内部类,并且将参数掺⼊构造⽅法中(main⽅法创建Test1$1类实例,test1⽅法创建Test2$2类实例)
Compiled from "Test1.java"
public am.test.Test1 {
am.test.Test1();
Code:
0: aload_0
1: invokespecial #1                  // Method java/lang/Object."<init>":()V
4: return
public static void main(java.lang.String[]);
Code:
0: new          #2                  // class java/util/ArrayList
3: dup
4: invokespecial #3                  // Method java/util/ArrayList."<init>":()V
7: astore_1
8: aload_1
9: ldc          #4                  // String 111
11: invokeinterface #5,  2            // InterfaceMethod java/util/List.add:(Ljava/lang/Object;)Z
16: pop
17: new          #6                  // class java/lang/Thread
20: dup
21: new          #7                  // class cn/xm/exam/test/Test1$1
24: dup
25: aload_1
26: invokespecial #8                  // Method cn/xm/exam/test/Test1$1."<init>":(Ljava/util/List;)V
29: invokespecial #9                  // Method java/lang/Thread."<init>":(Ljava/lang/Runnable;)V
32: invokevirtual #10                // Method java/lang/Thread.start:()V
35: ldc          #11                // String xxx
37: invokestatic  #12                // Method test1:(Ljava/lang/Object;)V
40: return
public static void test1(java.lang.Object);
Code:
0: new          #6                  // class java/lang/Thread
3: dup
4: new          #13                // class cn/xm/exam/test/Test1$2
7: dup
8: aload_0
9: invokespecial #14                // Method cn/xm/exam/test/Test1$2."<init>":(Ljava/lang/Object;)V      12: invokespecial #9                  // Method java/lang/Thread."<init>":(Ljava/lang/Runnable;)V
15: invokevirtual #10                // Method java/lang/Thread.start:()V
18: return
}
2.访问静态成员变量
  静态变量⾮常容易理解,直接通过类名.属性在任何地⽅都可以访问到,所以不⽤final修饰也可以。am.test;
import java.util.ArrayList;
import java.util.List;
public class Test3 {
private static List list = new ArrayList<>();
static {
list.add("111");
}
public static void main(String[] args) {
new Thread(new Runnable() {
@Override
public void run() {
System.out.println(list);
}
}).start();
}
}
结果:
[111]
反编译与反汇编分别查看源码:编译之后也是两个class⽂件:
C:\Users\liqiang\Desktop\新建⽂件夹>javac Test3.java
注: Test3.java使⽤了未经检查或不安全的操作。
注: 有关详细信息, 请使⽤ -Xlint:unchecked 重新编译。
C:\Users\liqiang\Desktop\新建⽂件夹>ls
'Test3$1.class'  Test3.class  Test3.java
反编译与反汇编查看Test3$1:(直接在run⽅法中访问静态成员变量)
C:\Users\liqiang\Desktop\新建⽂件夹>javap -c Test3$1.class
Compiled from "Test3.java"
final am.test.Test3$1 implements java.lang.Runnable {
Code:
0: aload_0
1: invokespecial #1                  // Method java/lang/Object."<init>":()V
4: return
public void run();
Code:
0: getstatic    #2                  // Field java/lang/System.out:Ljava/io/PrintStream;
3: invokestatic  #3                  // Method cn/xm/exam/test/Test3.access$000:()Ljava/util/List;
6: invokevirtual #4                  // Method java/io/PrintStream.println:(Ljava/lang/Object;)V
9: return
}
反汇编查看Test3.class:(可以看出静态代码块初始化了 list,并且在main函数创建了Test3$1实例,调⽤start⽅法启动线程) C:\Users\liqiang\Desktop\新建⽂件夹>javap -c Test3.class
Compiled from "Test3.java"
public am.test.Test3 {
am.test.Test3();
Code:
0: aload_0
1: invokespecial #2                  // Method java/lang/Object."<init>":()V
4: return
public static void main(java.lang.String[]);
Code:
0: new          #3                  // class java/lang/Thread
3: dup
4: new          #4                  // class cn/xm/exam/test/Test3$1
7: dup
8: invokespecial #5                  // Method cn/xm/exam/test/Test3$1."<init>":()V
11: invokespecial #6                  // Method java/lang/Thread."<init>":(Ljava/lang/Runnable;)V
14: invokevirtual #7                  // Method java/lang/Thread.start:()V
17: return
static java.util.List access$000();
Code:
0: getstatic    #1                  // Field list:Ljava/util/List;
3: areturn
static {};
Code:
0: new          #8                  // class java/util/ArrayList
3: dup
4: invokespecial #9                  // Method java/util/ArrayList."<init>":()V
7: putstatic    #1                  // Field list:Ljava/util/List;
10: getstatic    #1                  // Field list:Ljava/util/List;
13: ldc          #10                // String 111
15: invokeinterface #11,  2          // InterfaceMethod java/util/List.add:(Ljava/lang/Object;)Z      20: pop
21: return
}
总结:关于javap命令的详细⽤法:
C:\Users\liqiang\Desktop\新建⽂件夹>javap
⽤法: javap <options> <classes>
其中, 可能的选项包括:
-help  --help  -?        输出此⽤法消息
-version                版本信息
-v  -verbose            输出附加信息
-l                      输出⾏号和本地变量表
-public仅显⽰公共类和成员
-protected显⽰受保护的/公共类和成员
-
java arraylist用法
package显⽰程序包/受保护的/公共类
和成员 (默认)
-p  -private显⽰所有类和成员
-c                      对代码进⾏反汇编
-s                      输出内部类型签名
-sysinfo                显⽰正在处理的类的
系统信息 (路径, ⼤⼩, ⽇期, MD5 散列)
-constants              显⽰静态最终常量
-classpath <path>        指定查⽤户类⽂件的位置
-bootclasspath <path>    覆盖引导类⽂件的位置

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。