一、什么是内部类?
到底什么是内部类呢?通俗的讲,就是在类内部定义的类,包括定义在一个类的方法外面、方法里面或者代码块中。
二、为什么要使用内部类?
为什么我们要不走寻常路,把一个类定义在另一个类的内部呢?这样到底是出于什么目的呢?其实内部类是为了弥补java的单继承的特点,以实现多继承。内部类可以继承抽象类或实现接口。这样外部类也继承一个父类,就变相的实现了多继承。内部类可以访问外部类的属性,包括私有属性。
三、成员内部类
先上代码:
package noStaticInnerClass; public class OuterClass { private int outerPrivateParam = 1; public int outerPublicParam = 2; public static int outerStaticParam = 3; void outerFun() { System.out.println("function in outer class."); } public class InnerClass { public int innerParam = 4; //在非静态内部类中不可以定义静态变量,以下会报编译错 //public static innerStaticParam = 5; public void innerFun() { System.out.println("outerPrivateParam : " + outerPrivateParam + ", outerPublicParam : " + outerPublicParam + ", outerStaticParam : " + outerStaticParam); //访问外部类的方法 outerFun(); } } public void test() { InnerClass inner = new InnerClass(); inner.innerFun(); } public static void main(String[] args) { OuterClass outer = new OuterClass(); outer.test(); } }
成员内部类是一种个Field、方法、构造器和初始化块相似的类成员。
成员内部类又被称为非静态内部类,它具有以下几个特性:
- 在非静态内部类里可以直接访问外部类的成员。这是因为在非静态内部类对象里,保存了一个它寄存的外部类对象的引用。但是需要注意的是,外部类对象则不一定有非静态内部类对象寄存在其中。
- 在外部类中不能直接访问内部类的成员,必须通过创建内部类对象来访问。
在非静态内部类对象中不能定义静态方法、静态Field以及静态初始化块。不允许在外部类的静态方法或者静态代码块中直接使用非静态内部类。看下面代码:
package noStaticInnerClass; public class OuterClass { private int outerPrivateParam = 1; public int outerPublicParam = 2; public static int outerStaticParam = 3; public class InnerClass { public int innerParam = 4; //在非静态内部类中不可以定义静态变量,以下会报编译错 //public static innerStaticParam = 5; public void innerFun() { System.out.println("outerPrivateParam : " + outerPrivateParam + ", outerPublicParam : " + outerPublicParam + ", outerStaticParam : " + outerStaticParam); } } public static void test() { //不可以在外部类的静态方法中直接创建内部类对象 //InnerClass inner = new InnerClass(); OuterClass out = new OuterClass(); InnerClass inner = out.new InnerClass(); inner.innerFun(); } }
这是因为静态方法是类相关的,test方法不需要创建外部类的实例,就可以通过OuterClass.test()来调用,但是非静态内部类对象是依赖外部类的实例对象才能创建的,所以要先显式创建外部类对象。
非静态内部类的上一级是外部类,所以非静态内部类可以用private、protected、public以及默认修饰符来修饰内部类。如何在外部类以外的部分创建内部类对象呢?
外部类 外部类实例名 = new 外部类();
外部类.内部类 内部类实例名 = 外部类实例名.new 内部类();
四、静态内部类
继续看代码:
package noStaticInnerClass; public class OuterClass { private int outerPrivateParam = 1; public int outerPublicParam = 2; public static int outerStaticParam = 3; void outerFun() { //外部类可以通过内部类.静态成员来访问内部类的静态成员 System.out.println(InnerClass.innerStaticParam); } static void outerStaticFun() { InnerClass.innerStaticFun(); } public static class InnerClass { public static int innerStaticParam = 5; public int innerParam = 4; public void innerFun() { //在静态内部类中不能访问外部类的非静态成员,以下会报编译错。 //System.out.println("outerPrivateParam : " + outerPrivateParam); //ouerFun(); System.out.println("outerStaticParam : " + outerStaticParam); outerStaticFun(); } public static void innerStaticFun() { System.out.println(innerStaticParam); } } public void test() { InnerClass inner = new InnerClass(); System.out.println(inner.innerParam); } public static void main(String[] args) { OuterClass.InnerClass inner = new OuterClass.InnerClass(); inner.innerFun(); OuterClass.InnerClass.innerStaticFun(); } }
- 静态内部类属于类本身,不需要先创建外部类对象,再来创建内部类对象。
- 静态内部类只能访问外部类的类成员。
- 外部类不能直接访问静态内部类中的成员,可以通过创建对象或用类名来访问。
五、局部内部类
局部内部类放在方法中定义,局部内部类仅在方法内有效。局部类不能使用访问控制修饰符和static修饰符来修饰。
若需要用局部内部类来创建实例或派生子类,那么只能在局部内部类所在的方法或代码块内进行。
package noStaticInnerClass; public class OuterClass { private int outerPrivateParam = 1; public int outerPublicParam = 2; public static int outerStaticParam = 3; void outerFun() { class InnerClass { public int innerParam = 4; public void innerFun() { System.out.println("outerPrivateParam : " + outerPrivateParam + ", outerPublicParam : " + outerPublicParam + ",outerStaticParam : " + outerStaticParam); } } InnerClass inner = new InnerClass(); inner.innerFun(); System.out.println(inner.innerParam); } public static void main(String[] args) { OuterClass out = new OuterClass(); out.outerFun(); } }
我们查看以上代码生成的class文件,发现内部类的class文件名中多了一个1,那是因为同一个类中可能有两个以上同名的局部内部类。它们在不同的方法中定义。
六、匿名内部类
适合创建只需要一次使用的类。
格式 :
new 父类构造器(实参列表) | 实现接口()
{
//匿名内部类的类体部分。
}
代码如下 :
package innerClass; public class OutterClass { public TestInterface getInnerClass() { return new TestInterface() { @Override public void print(String name) { System.out.println(name); }; }; } public static void main(String[] args) { OutterClass out = new OutterClass(); out.getInnerClass().print("demo"); } }
创建匿名内部类时会立即创建一个类的实例,这个类定义会立即消失。
匿名内部类必须继承一个父类,或实现一个接口。
注意 :
- 匿名内部类不能是抽象类。
- 无法定义构造器,因为匿名内部类没有类名。但可定义初始化完成构造器的功能。
package innerClass; public class OutterClass { public TestInterface getInnerClass(final String sex) { final int age = 23; return new TestInterface() { @Override public void print(String name) { System.out.println(name + " is a " + sex + " and age is " + age); }; }; } public static void main(String[] args) { OutterClass out = new OutterClass(); out.getInnerClass("boy").print("demo"); } }
如果匿名内部类要访问外部类的局部变量,则必须使用final修饰符来修饰外部类的局部变量。否则编译部通过。
因为这里是拷贝引用,为了避免引用值发生改变,例如被外部类的方法修改等,而导致内部类得到的值不一致,于是用final来让该引用不可改变。