java匿名内部类的使用 (比如new对象后的大括号, List list = new ArrayList() { { }}这用用法等)

PS: 觉得写得可以,喜欢就点个赞啦亲们

今天在看别人的代码的时候,发现有

Yyy uu=new Xxx(){
public void aaa(){
//这里写代码。。。
}
}

这种形式,以前偶尔看见过,也知道是匿名内部类的情况,但一直没有仔细去研究,今天特意花点时间去写了点很简单也易懂的例子,初学时需要的技术不在于复杂程度,能让人看得懂的代码才是好代码,希望能帮助大家:

一、例子1::匿名写一个接口实现类。看下我这个例子(直接copy后在自己的IDE练习即可):

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()+"岁啦");
	}
}

运行后打印出:
java匿名内部类的使用 (比如new对象后的大括号, List list = new ArrayList() { { }}这用用法等)_第1张图片

这是最简单的匿名内部类的例子,实际上看得懂就可以举一反三了,比如不是接口,而是抽象类的抽象方法,也是可以直接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()+"岁啦");
	}
}

二、例子2:new对象后的大括号。

有时候我们看到的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"));
	}
}

这例子够简单吧,我们这里看下输出:
java匿名内部类的使用 (比如new对象后的大括号, List list = new ArrayList() { { }}这用用法等)_第2张图片

明明后面是写了map2.put(“start_key”, “不知道start_key的值是不是这个”);,为什么输出map2.get(“start_key”)的值是null呢?原因是代码中的a行b行重写了hashmap的put方法,把值给改过来了,实际上他们操作的是map2.put(“key_value”,“重写后的值”)! 所以从这里可以看出,在new 对象后,后面还跟着大括号,又直接写了个方法,这种情况下就是重写当前类的这个方法了,不过需要注意的是:该重写的方法只对当前对象有用!

三、List list = new ArrayList() { { }}这种写法:

不知道这种写法怎么说,就是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");
	            }
	        };
	}
}

提取出来放入D盘:
java匿名内部类的使用 (比如new对象后的大括号, List list = new ArrayList() { { }}这用用法等)_第3张图片

然后进行编译:

java匿名内部类的使用 (比如new对象后的大括号, List list = new ArrayList() { { }}这用用法等)_第4张图片

得到class文件:

在这里插入图片描述

可以看到产生了两个class文件,我们使用java自带的javap进行反编译查看BianyiClass.class文件:
java匿名内部类的使用 (比如new对象后的大括号, List list = new ArrayList() { { }}这用用法等)_第5张图片
太长了复制出来看一下:

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文件吧:
java匿名内部类的使用 (比如new对象后的大括号, List list = new ArrayList() { { }}这用用法等)_第6张图片

把得到的信息粘贴出来:


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 原来是new了一个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: 觉得写得可以,喜欢就点个赞啦亲们

你可能感兴趣的:(【Java基础】)