【匿名内部类】

匿名内部类(Android\Handler)

学习handler的过程中有关内存泄漏原因,让我又重新复习了匿名内部类,结合多人的经验进行了总结,如有侵权部分请联系删除!

一、匿名内部类是什么?

匿名内部类,就是没有名字的嵌套类。它是局部内部类一种特殊形式,也就是没有变量名指向这个类的实例,而且具体的类实现会写在这个类里面。
内部类:可以把一个类定义到另外一个类的内部,在类里面的这个类就叫内部类,外面的类就叫外部类。 在这种情况下,这个内部类可以看做外部类的一个成员。

二、适用场景

分析:在实际开发中,我们常常遇到这样的情况:一个接口/类的方法的某个实现方式在程序中只会执行一次,但为了使用它,我们需要创建它的实现类/子类去实现/重写。此时可以使用匿名内部类的方式,可以无需创建新的类,减少代码冗余。

总结:

  • 只用到类的一个实例;
  • 类在定义后马上用到;
  • 类非常小(SUN建议是4行代码以下);
  • 给类命名并不会导致代码更容易被理解。

三、特点

  • 匿名内部类不能定义任何静态成员、方法和类;
  • 匿名内部类中的方法不能是抽象的;
  • 匿名内部类必须实现接口或抽象类中的所有方法;
  • 匿名内部类不能定义构造器;
  • 匿名内部类访问的外部类成员变量或成员方法必须用final修饰。
  • 匿名内部类不能是public,protected,private,static。
  • 只能创建匿名内部类的一个实例。
  • 一个匿名内部类一定是在new的后面,用其隐含实现一个接口或实现一个类。
  • 因匿名内部类为局部内部类,所以局部内部类的所有限制都对其生效。

**注意:**匿名内部类必须继承或实现一个接口。

四、基本格式

new 接口/类名(参数1, 参数2...){
    实现方法1(){
        
    }
    实现方法2(){
        
    }
    ......
};

  • 发现:形式上有点像new一个接口或类的感觉,但是我们必须记住接口是不可以new出来的(实例化),我们要在内部重写需要的接口的方法,这样就new出了一个实现接口方法的对象,但是这个对象的类名不可见,所以叫做匿名类。

五、应用实例代码

1.基础应用

代码如下(示例):

package cn.tedu.innerclass;
/*本类用于测试匿名内部类
* 匿名内部类没有名字,通常与匿名对象结合在一起使用*/
public class TestInner5 {
    public static void main(String[] args) {
        //传统方式:创建接口的实现类+实现类实现接口中的抽象方法+创建实现类对象+通过对象调用方法
        //3.创建接口一对应的匿名对象与匿名内部类,并调用实现了的方法save()
        new Inter1(){
            @Override
            public void save() {
                System.out.println("save()...");
            }
            @Override
            public void get() { }
        }.save();

        //5.创建抽象类对应的匿名对象与匿名内部类
        new Inter2(){
            @Override
            public void drink() {
                System.out.println("一人饮酒醉");
            }
        }.drink();
        //7.调用普通类的功能怎么调用?创建匿名对象直接调用
        new Inter3().powerUp();
        new Inter3().powerUp();//new了2次,所以是两个匿名对象
        /*如果想要多次使用实现后的功能,还是要创建普通的对象
        * 匿名对象只能使用一次,一次只能调用一个功能
        * 匿名内部类其实就充当了实现类的角色,去实现未实现的抽象方法,只是没有名字而已*/
        Inter3 in = new Inter3();
        in.study();
        in.study();
        in.study();
        in.study();
        in.study();
        in.study();

    }
}

//1.创建接口
interface Inter1{
    //2.定义接口中的抽象方法
    void save();
    void get();
}
//4.创建抽象类
abstract class Inter2{
    public void play(){
        System.out.println("Inter2...play()");
    }
    abstract public void drink();
}
//6.创建普通类
class Inter3{
    public void study(){
        System.out.println("什么都阻挡不了我想学习赚钱的决心");
    }
    public void powerUp(){
        System.out.println("我们会越来越强的!");
    }
}


2.Thread中的应用

代码如下(示例):

        new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    Thread.sleep(1000);
                    handler.post(new Runnable() {
                        @Override
                        public void run() {
                            tvHandler3.setText("update");
                        }
                    });
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }).start();

通常我们习惯用这样的方式创建线程。

3.Handler中的应用

代码如下(示例):

    private Handler handler = new Handler(){
        @Override
        public void handleMessage(@NonNull Message msg) {
            super.handleMessage(msg);
        }
    };

此处由于内部类持有外部类的引用(Message持有Handler,handler持有activity)可能会导致内存泄漏。
解决方案:

  1. 声明handler为static
  2. 使用activity的弱引用来访问activity对象内的成员

代码如下(示例):


    /**
     * 避免handler内存泄露的写法
     * handler为何会导致内存泄露:
     * 如果handler为内部类(非静态内部类),那么会持有外部类的实例,
     * 若在handler.sendMessage的时候,activity finish掉了,那么此时activity将无法得到释放
     * <p>
     * 如果申明handler为静态内部类,则不会含有外部类的引用,
     * 但是需要在handler中更新UI(注意此时handler为static),则需要引入一个activity引用,
     * 显然必须是弱引用,否则会导致和上面一样的结果
     * <p>
     * 使用activity弱引用
     * 之所以使用弱引用,是因为handler为static,使用activity的弱引用来访问activity对象内的成员
     */

    private static class MyHandler extends Handler {
        private WeakReference<HandlerOOMActivity> weakReference;

        public MyHandler(HandlerOOMActivity activity) {
            weakReference = new WeakReference<>(activity);
        }

        @Override
        public void handleMessage(@NonNull Message msg) {
            super.handleMessage(msg);
            Log.e(TAG, "handleMessage: ");
            HandlerOOMActivity activity = weakReference.get();
            switch (msg.what) {
                case 0:
                    if (activity != null) {
                        Person person = (Person)msg.obj;
                        activity.tvText.setText("我是避免OOM更改Handler后的文字:"+person.getName());
                    }
                    break;
                case 1:
                    activity.tvText.setText("我是更改后的文字");
                    break;
            }
        }
    }

总结

匿名内部类属于局部内部类,而且是没有名字的局部内部类,通常和匿名对象一起使用

以上就是今天要讲的内容,本文仅仅简单介绍了匿名内部类的使用和在Android中一些的体现,而Android中用到的地方还有很多。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值