内部类

2018-08-01  本文已影响5人  Rreply

定义:

大部分时候,类是一个独立的程序单元,但是在一些情况下,会把一个类放进另外一个类内部进行定义,这个定义在其他类中的类就叫做内部类。

作用:

  1. 内部类提供了更好的封装
  2. 内部类成员可以直接访问外部类的私有数据,外部类却不能够访问内部类的实现细节。
  3. 改善Java类单继承的缺陷。
  4. 匿名内部类可以用来创建那些只用一次的类对象

下面分别对这四个作用进行说明:

1.内部类提供了更好的封装
public class Cow {
    private class CowLeg{
        private void print() {
            System.out.println("这是一个cowLeg");
        }
    }
    
    public static void main(String[] args) {
        CowLeg cowLeg = new Cow().new CowLeg();
        cowLeg.print();
    }
}

如上述代码所示,CowLeg类只能在Cow外部类中被调用,在包中的其他类是不能够访问到的,这就提供了更好的封装。

2. 内部类成员可以直接访问外部类的私有数据,外部类却不能够访问内部类的实现细节
public class Cow {

    private int cowLegCount = 4;

    private class CowLeg{
        private int a = 5;
        private void print() {
            System.out.println("这是一个cowLeg");
            System.out.println("它有" + cowLegCount + "条腿");
        }
    }

    public static void main(String[] args) {
        CowLeg cowLeg = new Cow().new CowLeg();
        cowLeg.print();
        System.out.println(cowLeg.a);
    }
}

CowLeg的对象能够访问外部类中的私有成员变量,但是外部类不能够直接访问内部类的私有成员变量,必须通过内部类的对象来进行调用。

3. 改善Java类单继承的缺陷。

因为Java是只能实现单继承的,在有内部类出现之前,一般使用的是多接口实现。但是接口要求实现其内部所有的抽象方法,不是特别灵活,而且接口中不存在成员变量。

class A{
        public void testA() {
            System.out.println("这是A类中方法");
        }
        public void doSomething() {
              //do something
        }
    }
    class B{
        public void testB() {
            System.out.println("这是类B中的方法");
        }
    }
    public class test {
    
        private class subA extends A{
    
            private void testSubA() {
                super.testA();
            }
        }
    
        private class subB extends B{
    
            private void testSubB() {
                super.testB();
            }
        }
    
        public static void main(String[] args) {
            new test().new subA().testSubA();
            new test().new subB().testSubB();
        }
    }

如代码所示,通过让内部类继承需要的类,就可以间接达到多继承的效果。而且只需要关注需要使用的方法,如果是使用接口,那就必须考虑实现A类中的doSomething方法了。

内部类的分类

如下图所示


内部类 (1).png

成员内部类编译出的class文件的命名是OuterClass&InnerClass.class的方式。

1. 非静态内部类

非静态内部类就相当于是类中的一个普通的成员变量。但是这个成员变量有很多特殊之处。

public class test{

    private int a = 4;

    private class sub{
        private int a = 44;
        private void print() {
            int a = 444;
            System.out.println("内部类方法中a为:" +a);
            System.out.println("内部类中a为:" +this.a);
            System.out.println("外部类中方法为: " + test.this.a);
        }
    }
    public static void main(String[] args) {
        new test().new sub().print();
    }
}

非静态内部类对象与外部类对象的联系:
非静态内部类对象必须寄生在外部类对象中,有外部类对象,不一定有内部类对象,有内部类对象,一定有外部类对象。外部类访问内部类的数据必须通过内部类对象才能够获取。此外,非静态内部类中不允许有静态的方法、成员变量、初始化块等。因为内部类实际上是通过外部类对象联系起来的。

2. 静态内部类

3. 局部内部类

在方法中定义的内部类叫做局部内部类。
局部内部类只存活在方法生存的周期内,因此使用局部内部类定义变量,创建实例,派生子类的时候都只能在方法内部进行。

4.匿名内部类

匿名内部类的作用就是产生一个具有方法的对象,所以不需要修饰符。而引用局部变量必须使用final修饰则是因为匿名内部类是在堆内存中的,而局部变量是在方法栈中,当这个方法结束了,局部变量就会消失,而堆内存中的匿名内部类对象还引用着局部变量,这该怎么处理呢?答案就是在匿名内部类引用局部变量的同时,在匿名内部类内部对这个变量进行储存,但是如果这个局部变量的值或者储存的地址在方法中一直变化,让堆内存中对象的属性值也不断变化就会消耗资源。因此就使用final来保证局部变量在方法内是不变的。

内部类可能导致的内存泄漏问题

可以理解为内部类和外部类在一定程度上是一体的(匿名内部类除外),因为对于非静态内部类来说,如果内部类对象不被回收,外部类对象肯定也不能被回收,因为它被内部类对象引用。对于静态内部类来说,其引用着外部类,所以和非静态内部类一样。局部内部类在外部类的方法中就更不用说了。
因此解决这种可能存在的内存泄漏就是在回收外部类的时候,一定先把内部类回收掉。
在Android 中 Hanlder 作为内部类使用的时候其对象被系统主线程的 Looper 持有(当然这里也可是子线程手动创建的 Looper)掌管的消息队列 MessageQueue 中的 Hanlder 发送的 Message 持有,当消息队列中有大量消息处理的需要处理,或者延迟消息需要执行的时候,创建该 Handler 的 Activity 已经退出了,但是因为Message还在引用着Handler,导致创建Handler的Avtivity对象不能够被回收,就造成了内存泄漏。
那么 Hanlder 何时会被释放,当消息队列处理完 Hanlder 携带的 message 的时候就会调用 msg.recycleUnchecked()释放Message所持有的Handler引用。
在 Android 中要想处理 Hanlder 内存泄漏可以从两个方面着手:

   private static class MyHandler extends Handler {

        private final WeakReference<MainActivity> mActivity;

        public MyHandler(MainActivity activity) {
            mActivity = new WeakReference<MainActivity>(activity);
        }

        @Override
        public void handleMessage(Message msg) {
            MainActivity activity = mActivity.get();
            if (activity == null || activity.isFinishing()) {
               return;
            }
        }
    }

参考引用:搞懂 JAVA 内部类

上一篇 下一篇

猜你喜欢

热点阅读