PS: 觉得写得可以,喜欢就点个赞啦亲们
今天在看别人的代码的时候,发现有
Yyy uu=new Xxx(){
public void aaa(){
//这里写代码。。。
}
}
这种形式,以前偶尔看见过,也知道是匿名内部类的情况,但一直没有仔细去研究,今天特意花点时间去写了点很简单也易懂的例子,初学时需要的技术不在于复杂程度,能让人看得懂的代码才是好代码,希望能帮助大家:
a.新建一个接口:
public interface InterfaceTest {
public String getName();
public String getAge();
}
b.新建一个测试类:
package test;
public class NewInterfaceDemo {
public static void main(String[] args) {
InterfaceTest test=new InterfaceTest() {//直接就new InterfaceTest() 敲回车后下面的方法会自动出来的
@Override
public String getName() {
return "小明";
}
@Override
public String getAge() {
return "5";
}
};
System.out.println(test.getName()+test.getAge()+"岁啦");
}
}
这是最简单的匿名内部类的例子,实际上看得懂就可以举一反三了,比如不是接口,而是抽象类的抽象方法,也是可以直接new 抽象类(){抽象方法}的。以上的例子相当于写了一个实现类,如:
public class InterfaceTestImpl implements InterfaceTest {
@Override
public String getName() {
return "小明";
}
@Override
public String getAge() {
return "5";
}
public static void main(String[] args) {
InterfaceTest test=new InterfaceTestImpl();
System.out.println(test.getName()+test.getAge()+"岁啦");
}
}
有时候我们看到的new一个Class,虽然这个class不是抽象类也不是接口,就是一个实实在在的类,然后后面也可以跟一个大括号,这又是怎么回事呢?我们可以直接拿非常常见的HashMap类来做这个例子:
public class Test {
public static void main(String[] args) {
Map<String, String> map2=new HashMap<String,String>(){
@Override
public String put(String var1, String var2) {
var1="key_value";//a行
var2="重写后的值";//b行
return super.put(var1, var2);
}
};
map2.put("start_key", "不知道start_key的值是不是这个");
System.out.println("找到start_key的值: "+map2.get("start_key"));
System.out.println("虽然没有明显把key_value当key赋值,尝试尝试key_value的值: "+map2.get("key_value"));
}
}
明明后面是写了map2.put(“start_key”, “不知道start_key的值是不是这个”);,为什么输出map2.get(“start_key”)的值是null呢?原因是代码中的a行b行重写了hashmap的put方法,把值给改过来了,实际上他们操作的是map2.put(“key_value”,“重写后的值”)! 所以从这里可以看出,在new 对象后,后面还跟着大括号,又直接写了个方法,这种情况下就是重写当前类的这个方法了,不过需要注意的是:该重写的方法只对当前对象有用!
不知道这种写法怎么说,就是new对象后,后面跟着两个大括号,比如:
List<String> list = new ArrayList<String>() {
{
add("a");
add("b");
}
};
System.out.println("list长度: "+list.size());
然后这代码输出的是list长度:2 事实上就可以知道,在new ArrayList的时候,创建构造函数时顺便给list对象添加了a b两个值了,所以list的长度为2
再弄个hashmap的:
Map<String, String> map=new HashMap<String,String>(){
{
put("haha", "heiehi");
}
};
System.out.println(map.get("haha"));
输出heihei
好了 这是怎么实现的呢?
从表面上看,其实内大括号使用的是this.add()方法,也就是说,这个方法也只是当前对象有效,其他你再new ArrayList()的时候,所得的对象长度就是0 而不是2了。
如果深入了解,我们可以把这个文件进行编译,源代码为:
public class BianyiClass {
public static void main(String[] args) {
List<String> list = new ArrayList<String>() {
{
add("a");
add("b");
}
};
}
}
然后进行编译:
得到class文件:
可以看到产生了两个class文件,我们使用java自带的javap进行反编译查看BianyiClass.class文件:
太长了复制出来看一下:
D:\>javap -verbose BianyiClass.class//这是直接在cmd窗口写的,下面是反编译出来的代码
Classfile /D:/BianyiClass.class
Last modified 2018-11-16; size 344 bytes
MD5 checksum a180fea3ce00b955e16a1fff9242e64a
Compiled from "BianyiClass.java"
public class test.BianyiClass
minor version: 0
major version: 52
flags: ACC_PUBLIC, ACC_SUPER
Constant pool:
#1 = Methodref #5.#15 // java/lang/Object."":()V
#2 = Class #16 // test/BianyiClass$1
#3 = Methodref #2.#15 // test/BianyiClass$1."":()V
#4 = Class #17 // test/BianyiClass
#5 = Class #18 // java/lang/Object
#6 = Utf8 InnerClasses
#7 = Utf8 <init>
#8 = Utf8 ()V
#9 = Utf8 Code
#10 = Utf8 LineNumberTable
#11 = Utf8 main
#12 = Utf8 ([Ljava/lang/String;)V
#13 = Utf8 SourceFile
#14 = Utf8 BianyiClass.java
#15 = NameAndType #7:#8 // "":()V
#16 = Utf8 test/BianyiClass$1
#17 = Utf8 test/BianyiClass
#18 = Utf8 java/lang/Object
{
public test.BianyiClass();
descriptor: ()V
flags: ACC_PUBLIC
Code:
stack=1, locals=1, args_size=1
0: aload_0
1: invokespecial #1 // Method java/lang/Object."":()V
4: return
LineNumberTable:
line 6: 0
public static void main(java.lang.String[]);
descriptor: ([Ljava/lang/String;)V
flags: ACC_PUBLIC, ACC_STATIC
Code:
stack=2, locals=2, args_size=1
0: new #2 // class test/BianyiClass$1
3: dup
4: invokespecial #3 // Method test/BianyiClass$1."":()V
7: astore_1
8: return
LineNumberTable:
line 9: 0
line 15: 8
}
SourceFile: "BianyiClass.java"
InnerClasses:
static #2; //class test/BianyiClass$1
有点眼花缭乱。。。。。。。。。。。。。。。。。。。缓一下神。。。。。。。。。。。
仔细看一看,发现其实查看时,自动有备注的,哈哈,继续往下,发现
class test/BianyiClass$1
这个备注,说明了这个编译文件,new了一个对象,在BianyiClass$1里面,然后我们继续javapBianyiClass$1文件吧:
把得到的信息粘贴出来:
D:\>javap -verbose BianyiClass$1.class
Classfile /D:/BianyiClass$1.class
Last modified 2018-11-16; size 468 bytes
MD5 checksum edefcd30c6aa2ed4935340e149a2c92f
Compiled from "BianyiClass.java"
final class test.BianyiClass$1 extends java.util.ArrayList<java.lang.String>
minor version: 0
major version: 52
flags: ACC_FINAL, ACC_SUPER
Constant pool:
#1 = Methodref #6.#18 // java/util/ArrayList."":()V
#2 = String #19 // a
#3 = Methodref #5.#20 // test/BianyiClass$1.add:(Ljava/lang/Object;)Z
#4 = String #21 // b
#5 = Class #22 // test/BianyiClass$1
#6 = Class #24 // java/util/ArrayList
#7 = Utf8 <init>
#8 = Utf8 ()V
#9 = Utf8 Code
#10 = Utf8 LineNumberTable
#11 = Utf8 Signature
#12 = Utf8 Ljava/util/ArrayList<Ljava/lang/String;>;
#13 = Utf8 SourceFile
#14 = Utf8 BianyiClass.java
#15 = Utf8 EnclosingMethod
#16 = Class #25 // test/BianyiClass
#17 = NameAndType #26:#27 // main:([Ljava/lang/String;)V
#18 = NameAndType #7:#8 // "":()V
#19 = Utf8 a
#20 = NameAndType #28:#29 // add:(Ljava/lang/Object;)Z
#21 = Utf8 b
#22 = Utf8 test/BianyiClass$1
#23 = Utf8 InnerClasses
#24 = Utf8 java/util/ArrayList
#25 = Utf8 test/BianyiClass
#26 = Utf8 main
#27 = Utf8 ([Ljava/lang/String;)V
#28 = Utf8 add
#29 = Utf8 (Ljava/lang/Object;)Z
{
test.BianyiClass$1();
descriptor: ()V
flags:
Code:
stack=2, locals=1, args_size=1
0: aload_0
1: invokespecial #1 // Method java/util/ArrayList."":()V
4: aload_0
5: ldc #2 // String a
7: invokevirtual #3 // Method add:(Ljava/lang/Object;)Z
10: pop
11: aload_0
12: ldc #4 // String b
14: invokevirtual #3 // Method add:(Ljava/lang/Object;)Z
17: pop
18: return
LineNumberTable:
line 9: 0
line 11: 4
line 12: 11
line 13: 18
}
Signature: #12 // Ljava/util/ArrayList;
SourceFile: "BianyiClass.java"
EnclosingMethod: #16.#17 // test.BianyiClass.main
InnerClasses:
static #5; //class test/BianyiClass$1
又是眼花缭乱。。。
注意看这一句:final class test.BianyiClass$1 extends java.util.ArrayList
这个大括号里面的内容,实际上是创建构造函数时的内容,抽取出来看一下:
{
test.BianyiClass$1();
descriptor: ()V
flags:
Code:
stack=2, locals=1, args_size=1
0: aload_0
1: invokespecial #1 // Method java/util/ArrayList."":()V
4: aload_0
5: ldc #2 // String a
7: invokevirtual #3 // Method add:(Ljava/lang/Object;)Z
10: pop
11: aload_0
12: ldc #4 // String b
14: invokevirtual #3 // Method add:(Ljava/lang/Object;)Z
17: pop
18: return
LineNumberTable:
line 9: 0
line 11: 4
line 12: 11
line 13: 18
}
然后再看这句:
11 7: invokevirtual #3 // Method add:(Ljava/lang/Object;)Z
还有这句:
15 14: invokevirtual #3 // Method add:(Ljava/lang/Object;)Z
明白了吧,实际上在初始化对象时,这个对象已经添加两次信息了,也就是那个a和b!
至此,就已经知道执行原理了!
PS: 觉得写得可以,喜欢就点个赞啦亲们