内部类
一个定义在另一个类中的类,叫做内部类
public class JavaClass {
public static void main(String[] args) {
OuterClass outerClass = new OuterClass();
// 第一种访问内部类的方法,通过在Outer内部实例化Inner
outerClass.show();
// 第二种访问内部类方法,直接实例化内部类对象
OuterClass.InnerClass innerClass = outerClass.new InnerClass();
innerClass.show();
}
}
class OuterClass {
private String tag = "OuterTag";
class InnerClass {
public void show() {
System.out.println(tag);
}
}
public void show() {
InnerClass innerClass = new InnerClass();
innerClass.show();
}
}
为什么使用内部类
也许,搞清楚为什么使用这种技术比会使用某项技术更能让人愿意去了解。内部类给开发带来了什么便利?什么情况下使用内部类?我的理解内部类解决了:
单继承问题,如果没有内部类提供的、可以继承多个具体的或抽象类的能力,一些设计与编程问题就很难解决。换个角度说,内部类有效地实现了“多重继承”。也就是说,内部类允许继承多个非接口类型(类或抽象类)。
简化多余代码,比如匿名内部类,有时对于某个接口的实现,并不常用接口中的方法,或者只调用一次,这时我们就可以使用匿名内部类来代替声明类实现接口的操作,这肯定会带来冗余的代码,而前面所讲的lambda表达式也是一种代替匿名内部类很好地方案,但是并不是所有的接口抽象方法都可以使用lambda表达式(SAM才可以使用),所以这时候最好就是使用匿名内部类。
内部类方法可以访问该类定义所在的作用域中的数据,包括私有的数据。
内部类可以对同一个包中的其他类隐藏起来。
内部类的创建
语法:记住一个原则,非静态内部类默认会有一个外部类的引用,即依赖外部对象的创建,静态内部类独立于外部类而存在,不需要与外部类对象建立关联。
//在外部类外部(静态方法内|非静态方法内),创建非静态内部类对象
Outer.Inner in=new Outer().new Inner();
//在外部类外部(静态方法内|非静态方法内),创建静态内部对象
Outer.Inner in=new Outer.Inner();
//----------------------------------------------------------------------
//在外部类内部(非静态方法内)创建成员内部类对象,就像普通对象一样直接创建
Inner in=new Inner();
//在外部类内部(静态方法内)创建成员内部类对象
Outer.Inner in=new Outer().new Inner();
//在外部内部类(静态方法内|非静态方法内)创建静态内部类对象
Inner in=new Inner();
内部类的分类
成员内部类
成员内部类既可以访问自身的数据域,也可以访问它的外围类对象的数据域,对于外围类来说,要想访问内部类属性,必须先创建内部类对象。你可能会有疑问了这是为什么?一个重要的原因是内部类的对象会保留一个指向创建它的外部类的引用。
1)成员内部类内部不允许存在任何static变量或方法正如成员方法中不能有任何静态属性 (成员方法与对象相关、静态属性与类有关)!!!
为什么不允许存在任何static变量或方法?
静态变量是属于类的,成员变量和方法是属于对象的,非静态内部类可以看成是一个外部类的成员属性,依赖于对象的,因为静态属性随类而加载而不是随对象加载,所以如果内部类中存在静态属性,由于静态属性会随类加载,而内部类又是外部类的一个成员属性随外部类对象而加载,这样就会在没有外部类对象的情况下,却加载了内部类的静态成员属性,产生了矛盾。
在类加载的时候,静态属性和代码块会先加载,那么按照这个逻辑,非静态内部类里面的静态属性也要优先于这个内部类加载,但这个时候这个内部类都还没有初始化,这就出现矛盾了。
2)成员内部类是依附外部类的,只有创建了外部类才能创建内部类。
静态内部类(嵌套类)
关键字static可以修饰成员变量、方法、代码块、其实还可以修饰内部类,使用static修饰的内部类我们称之为静态内部类,静态内部类和非静态内部类之间存在一个最大的区别,非静态内部类在编译完成之后会隐含的保存着一个引用,该引用是指向创建它的外围类,但是静态类没有。没有这个引用就意味着:
1)静态内部类的创建不需要依赖外部类可以直接创建。
2)静态内部类不可以使用任何外部类的非static类(包括属性和方法),但可以存在自己的成员变量。
局部内部类
局部内部类使用的比较少,其声明在一个方法体 / 一段代码块的内部,而且不在定义类的定义域之内便无法使用,其提供的功能使用匿名内部类都可以实现,而本身匿名内部类可以写得比它更简洁,因此局部内部类用的比较少。来看一个局部内部类的小例子:
public class InnerClassTest {
public int field1 = 1;
protected int field2 = 2;
int field3 = 3;
private int field4 = 4;
public InnerClassTest() {
System.out.println("创建 " + this.getClass().getSimpleName() + " 对象");
}
private void localInnerClassTest() {
// 局部内部类 A,只能在当前方法中使用
class A {
// static int field = 1; // 编译错误!局部内部类中不能定义 static 字段
public A() {
System.out.println("创建 " + A.class.getSimpleName() + " 对象");
System.out.println("其外部类的 field1 字段的值为: " + field1);
System.out.println("其外部类的 field2 字段的值为: " + field2);
System.out.println("其外部类的 field3 字段的值为: " + field3);
System.out.println("其外部类的 field4 字段的值为: " + field4);
}
}
A a = new A();
if (true) {
// 局部内部类 B,只能在当前代码块中使用
class B {
public B() {
System.out.println("创建 " + B.class.getSimpleName() + " 对象");
System.out.println("其外部类的 field1 字段的值为: " + field1);
System.out.println("其外部类的 field2 字段的值为: " + field2);
System.out.println("其外部类的 field3 字段的值为: " + field3);
System.out.println("其外部类的 field4 字段的值为: " + field4);
}
}
B b = new B();
}
// B b1 = new B(); // 编译错误!不在类 B 的定义域内,找不到类 B,
}
public static void main(String[] args) {
InnerClassTest outObj = new InnerClassTest();
outObj.localInnerClassTest();
}
}
匿名内部类
匿名内部类是一个没有名字的方法内部类,它的特点有
1)匿名内部类必须继承一个类或者实现一个接口(类可以是抽象类,也可以是具体类)
2)匿名内部类没有类名,因此没有构造方法
个人觉得匿名内部类其实就是对接口简单快速的实现
推荐比较好的两篇文章:详解 Java 内部类、内部类定义
// 附加:具体类也可以用内部类实现
// 下面定义了一个Object的匿名内部类,新增了一个方法并调用
// 编译通过,并能运行
Object obj = new Object() {
public void func() {...}
}.func();
// 下面就不能通过编译
Object obj = new Object() {
public void func() {...}
};
obj.func();//compile error 没有该方法
第二个编译失败,因为匿名内部类是一个 子类对象,Object类型的obj指向了一个匿名子类内部,向上转型了,所以编译时会检查Object里面是否有func方法。