[14] 内部类


1、内部类

什么是内部类,定义在其他类中的类称之为内部类。内部类也可以有访问权限修饰符,甚至可以标记为abstract或final。内部类与外部类实例有特殊的关系,这种关系即允许内部类访问外部类的程序,也包括私有成员

内部类分为下面四种:
  • 成员内部类
  • 局部内部类
  • 匿名内部类
  • 静态内部类

1.1 成员内部类

正如其名,内部类声明在某个类中,如果要实例化一个内部类实例,需要一个外部类的实例作为依托,内部类的实例只能通过外部类的实例来访问
  • 不能存在static的变量和方法
  • 必须先创建外部类才能创建内部类
  • 普通类只有public可用修饰符,但是内部类可以使用private和protected
public class Demo { 

    private int value = 1;
    
    public class InnerDemo {
        public void seeOuterValue() {
            System.out.println("Value is:" + value);
        }
    }
    
}
public class Test {
    public static void main(String[] args) {
        Demo.InnerDemo inner = new Demo().new InnerDemo();
        inner.seeOuterValue();
    }
}

1.2 局部内部类

局部内部类被定义在外部类的方法中,我们说,内部类必须依托于内部类,这就意味着对于局部内部类来说:
  • 如果想使用这个局部内部类,那么你必须在这个方法中实例化这个内部类
  • 只有abstract和final可以修饰局部内部类
  • 方法的局部变量为final或参数是effectively final,局部内部类才能使用

当变量或参数在初始化之后,值再也没改变过,则说明其为effectively final
public class Demo {
    private int value = 1;

    public void doSth() {
        //局部内部类
        class InnerDemo {
            public void seeOuterValue() {
                System.out.println("Value is:" + value);
            }
        }
        //在方法体中实例化,才能使用局部内部类
        InnerDemo inner = new InnerDemo();
        inner.seeOuterValue();
    }

}
public class Test {
    public static void main(String[] args) {
        Demo demo = new Demo();
        demo.doSth();
    }
}

为什么必须是final或effectively final,因为从表面上看这些变量是同一个东西,实际上在编译时外部类和内部类是两个class文件,也就是说两者是独立的。内部类并不是直接调用方法传递的参数,而是利用自身的构造函数对传入的参数进行了备份,也就是实际上是调用的自身属性,其值是从外部参数拷贝过来的。那么假如不是final,方法参数发生了变化,而内部类的属性还是原来的值,显然是不合理的,因为在我们眼里,他们俩是一个整体,为了避免出现这种情况,就使用final来让该引用的参数不可变。

1.3 匿名内部类

从学习以来不论是知识点还是面试常见的问题,总会问到诸如接口和抽象类可以被实例化吗?显然是不能。很多时候我们采用的new然后直接跟接口或抽象类的形式,这又算什么呢?这就是匿名内部类
  • 没有名字(表面上似乎有名字,但那并不是它们的名字,定义时直接创建该类的对象)
  • 只能被实例化一次(无法重复使用)
    • 创建匿名内部类时会立即创建一个该类的实例,该类的定义会立即消失
    • 匿名内部类没有类名,无法“new 类名”来声明新的对象,所以就当前这一次
    • 没有名字和只能实例化一次,我仍然有点迷糊
  • 通常被申明在方法或代码块的内部
  • 没有构造函数
  • 不能是静态static的,也不能包括静态变量和方法
  • 匿名内部类也是一种局部内部类,所以局部内部类的显示对匿名内部类适用

匿名类相当于在定义类的同时再新建这个类的实例。一个匿名类的创建有下面几个部分:
  • new:操作符
  • 接口名:或抽象类/普通类
  • () :这个括号表示构造函数的参数列表,接口没有构造函数所以一般填写空括号
  • {...}:大括号中间代码表示类的内部结构,也可以定义变量方法等,和普通类一样

public abstract class Animal {
    public abstract void run();
}
public class Test {
    public static void main(String[] args) {

        Animal animal = new Animal() {
            @Override
            public void run() {
                System.out.println("run!");
            }
        };

        animal.run();
    }
}

内部类是可以有构造方法的,但由于匿名内部类的特殊性,其没有构造函数!那么很多初始化工作我们如何完成?使用代码块
public class Test {
    public static void main(String[] args) {

        Animal animal = new Animal() {
            {
                System.out.println("start init");

            }

            @Override
            public void run() {
                System.out.println("run!");
            }
        };

        animal.run();
    }
}

1.4 静态内部类(嵌套内部类)

使用static修饰的内部类就是静态内部类,那么:
  • 创建不再依托于外部类
  • 不能使用外部类的非static成员变量和方法

(感觉和静态方法差不多)


2、小结

成员内部类:
  • 必须有外部类作为依托
  • 不能有static变量和方法
  • 可以由private和protected修饰

局部内部类:
  • 在方法中实例化后才能使用
  • 方法局部变量必须是final

匿名内部类:
  • 不能有static变量和方法
  • 没有名字,只能使用一次
  • 没有构造函数
  • 方法变量必须是final

静态内部类:
  • 和静态方法差不多
  • 不再依托外部类
  • 不能使用外部类的非静态成员变量和方法


3、参考链接



posted @ 2018-01-04 11:42  Dulk  阅读(182)  评论(0编辑  收藏  举报