java匿名内部类详解

匿名内部类,顾名思义,就是没有名字的类,匿名内部类格式如下:

new name(parameter)
{
   ......
}

name:父类或是接口的名字。

parameter:若name为父类,则parameter为父类构造函数的参数。


匿名内部类具有一系列的限制,原因待会解释:

1、不能具有static成员域和成员函数和类。

2、不能具有static final 修饰的引用类型。

3、不能有自定义的构造函数。

4、不能具有静态代码块。

5、匿名内部类不能有类修饰符


使用匿名内部类的方式:

class A
{   
    int clsvalue;
    A(int v)
    {
        clsvalue=v;
    }
}
interface B
{
    int invalue=0;
}
public class Try {
    public static void main(String[] args) {
        /*方法一:利用父类或接口的引用指向匿名内部类,此时引用只能访问父类或是接口中的成员域和成员函数,
          例如下面的a和b只能访问clsvalue和invalue,无法访问x和y。但可以使用被覆盖的方法。
        */
        A a= new A(1)
        {
           static final int x=0;  
        };
        B b=new B()
        {
           static final int y=0;
        };
        
        
        /*方法二:直接通过下列代码访问,此时可以访问父类或接口、匿名内部类中定义的成员域或是成员方法。但只能使用一次(由于没有变量名)*/
        new A(2){static final int y=0;};
    }
}


匿名内部类仅仅是局部有效的,方法一仅在作用域内有效,方法二仅在当行代码中有效。


接下来我们编译如下代码:

class A
{   
    int clsvalue;
    A(int v)
    {
        clsvalue=v;
    }
}
public class Try {
    public static void main(String[] args) {
        A a= new A(1){ };
    }
}

首先,匿名内部类会有属于自己的class文件。反编译匿名内部类的class文件得:

  Last modified 2018-2-6; size 274 bytes
  MD5 checksum e1349e62178fb6ad8402c5ec6ffe0bbf
  Compiled from "Try.java"
final class Try$1 extends A
  minor version: 0
  major version: 52
  flags: ACC_FINAL, ACC_SUPER
Constant pool:
   #1 = Methodref          #3.#13         // A."<init>":(I)V
   #2 = Class              #14            // Try$1
   #3 = Class              #16            // A
   #4 = Utf8               <init>
   #5 = Utf8               (I)V
   #6 = Utf8               Code
   #7 = Utf8               LineNumberTable
   #8 = Utf8               SourceFile
   #9 = Utf8               Try.java
  #10 = Utf8               EnclosingMethod
  #11 = Class              #17            // Try
  #12 = NameAndType        #18:#19        // main:([Ljava/lang/String;)V
  #13 = NameAndType        #4:#5          // "<init>":(I)V
  #14 = Utf8               Try$1
  #15 = Utf8               InnerClasses
  #16 = Utf8               A
  #17 = Utf8               Try
  #18 = Utf8               main
  #19 = Utf8               ([Ljava/lang/String;)V
{
  Try$1(int);
    descriptor: (I)V
    flags:
    Code:
      stack=2, locals=2, args_size=2
         0: aload_0                           //将this指针从局部变量表压入操作数栈。
         1: iload_1                           //将局部变量表中的int型变量的值压入操作数栈
         2: invokespecial #1                  // Method A."<init>":(I)V,调用父类构造函数,首先传入栈顶的int型变量,接着将值写入this指针所指内存的对应位置。
         5: return
      LineNumberTable:
        line 11: 0
}
SourceFile: "Try.java"
EnclosingMethod: #11.#12                // Try.main
InnerClasses:
     static #2; //class Try$1
首先注意到匿名内部类的类修饰符为final,且编译器自动加上了extends A,若 name为接口,则为implements+接口名。

编译器会根据parameter创建一个匿名内部类的构造函数。

问题出现了,从类文件来看,匿名内部类和普通的类没什么不同,我们知道可以用父类引用指向子类对象,并且可以通过父类引用调用子类方法和成员域,但匿名内部类的父类引用不行(即用方法一使用匿名内部类),为什么要这么设计?下面谈谈我自己的理解:


从使用角度来看:由匿名内部类的生命周期,我们可以知道,使用匿名内部类是为了使用一种'短期"效果,若父类是抽象类(接口),我们可以随时定义一个抽象方法使用,若类不是抽象类,我们可以使用在子类中被覆盖的方法,该功能的方法我们只想使用一次(或在当前作用域使用),此时我们便不用再去另外定义一个类,代码也更加简洁,可读性也更强(不然可能还需要拼命滑鼠标去找类,一串代码找的很痛苦的!)。当然,若父类是抽象类或接口,我们要定义所有的抽象方法。

以上,匿名内部类的父类引用只能访问父类的(被覆盖的)成员方法和成员域,设计者这么做应该是让匿名内部类的使用目的更加明确。

接下来解释各种限制的原因,以下均为个人理解:


1、为什么不能具有静态成员域和成员函数、静态代码块、static final修饰的引用类型、静态内部类?


答:和实名内部类类似,“静态”意味着与实例对象无关就可以访问,从匿名内部类的使用代码(方法一、方法二)我们可以看到,其一定会实例化一个对象,此时使用“静态”便毫无意义。static final修饰引用类型的目的:1、不需要实例对象就可访问,2、不希望指针指向改变,匿名内部类的使用方式使1变得毫无意义,所以规定不能用static final修饰引用类型,由于匿名内部类没有名字,想要使用静态内部类必须通过一个匿名内部类的实例对象,与“静态”定义矛盾。


2、为什么不能有自定义构造函数?


答:从class文件中,我们可以看到匿名内部类其实含有编译器自动加上的构造函数,且JVM只调用该构造函数,自定义构造函数无效,另外,匿名内部类名字都没有怎么定义构造函数(滑稽.jpg)?


3、为什么匿名内部类不能有类修饰符

emmmm,加类修饰符可能与有效域冲突,例如加public,但匿名内部类只在当前作用域或是当前行有效,且这些类修饰符本来就是规定访问范围的,但匿名内部类访问范围已经规定。


4、为什么可以有static final修饰的具有字面量的变量?

其实对于匿名内部类来说,static final与final修饰的具有字面量的变量效果一样,只是初始化时机不一样,保存位置不一样,对于性能没有太多影响,所以保留。


如有错误,欢迎各位指出,另附实名内部类详解:点击打开链接

  • 14
    点赞
  • 55
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
要给JPanel添加背景图片,可以通过以下步骤实现: 1. 创建一个继承自JPanel的类,命名为BackgroundPanel。 2. 在BackgroundPanel中添加一个BufferedImage类型的属性,用于保存背景图片。 3. 在BackgroundPanel的构造方法中,将背景图片加载到BufferedImage中。 4. 重写BackgroundPanel的paintComponent方法,在该方法中将背景图片绘制在JPanel上。 下面是具体的代码实现: ``` import java.awt.Dimension; import java.awt.Graphics; import java.awt.image.BufferedImage; import java.io.File; import java.io.IOException; import javax.imageio.ImageIO; import javax.swing.JPanel; public class BackgroundPanel extends JPanel { private BufferedImage backgroundImage; public BackgroundPanel(String imagePath) { try { backgroundImage = ImageIO.read(new File(imagePath)); } catch (IOException e) { e.printStackTrace(); } } @Override public Dimension getPreferredSize() { return new Dimension(backgroundImage.getWidth(), backgroundImage.getHeight()); } @Override protected void paintComponent(Graphics g) { super.paintComponent(g); g.drawImage(backgroundImage, 0, 0, null); } } ``` 使用匿名内部类的方式创建JPanel并添加背景图片的示例如下: ``` import javax.swing.JFrame; public class Main { public static void main(String[] args) { JFrame frame = new JFrame(); // 使用匿名内部类创建BackgroundPanel并设置背景图片 JPanel panel = new BackgroundPanel("background.jpg") { @Override public Dimension getPreferredSize() { return new Dimension(800, 600); } }; frame.add(panel); frame.pack(); frame.setVisible(true); } } ``` 在上面的示例中,我们使用了匿名内部类的方式来创建BackgroundPanel,并且重写了getPreferredSize方法,以指定JPanel的大小。同时,我们也在构造方法中加载了背景图片,并在paintComponent方法中将背景图片绘制在JPanel上。最后,我们将创建好的BackgroundPanel添加到JFrame中。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值