目录
一 概述
定义:一个类的内部完整的嵌套了另一个类结构,被嵌套的类称为内部类(inner class),嵌套其他类的类称为外部类(outer class)。
内部类是类的五大成员(属性、方法、构造器、代码块、内部类)之一。
特点:内部类最大的特点就是可以直接访问外部类的所有属性、方法,包括私有的,并且可以体现类与类之间的包含关系。
注意点:内部类内部不允许使用static修饰属性、方法,也就是不允许定义静态属性、静态方法。
//外部类
public class OuterClass {
//内部类
public class innerClass{
}
}
//外部其他类
class OuterOtherClass{
}
编译器编译时,会编译成3个字节码文件,内部类的字节码文件名称是(外部类名$内部类名)。
若是局部内部类,则生成外部类名$正整数+内部类名;
若是匿名内部类,则生成外部类名$正整数。
额外知识点:一个java文件可以定义多个外部类,外部类不写修饰符,默认是default权限。
若使用public修饰符修饰外部类,则只能有一个外部类可以使用public修饰符修饰,且java文件名称必须与public修饰的外部类相同。
二 成员内部类
2.1 定义
- 成员内部类定义在外部类的成员位置,且没有使用static修饰。
- 如同成员一样,可使用任意访问修饰符(public,protected,默认(default),private)修饰,因为它的地位就是一个成员。
- 成员内部类可以直接访问外部类的所有成员,包括私有的(静态、非静态)。
- 作用域:同外部类的其他成员一样,为整个类体。
- 成员内部类依赖于外部类而存在的,也就是说,如果要创建成员内部类的对象,前提是必须存在一个外部类的对象,将外部类对象和成员内部类对象关联起来。
2.2 成员内部类访问外部类
- 访问方式:直接访问。
- 若外部类和内部类的成员重名时,遵循就近原则,默认访问内部类的成员。如果想访问外部类的成员,则可以使用(外部类名.this.成员)去访问。(外部类名.this表示当前外部类对象)。
为什么内部类可直接访问外部类对象的任意成员,可参考Java内部类详解 - Matrix海子 - 博客园的第二章深入理解内部类。
//外部类
public class OuterClass {
private int num = 2;
public void test(){
System.out.println("外部类成员方法");
}
//成员内部类
public class InnerClass{
private int num = 1;
public void printNum(){
//访问外部类成员,直接访问
test();
//若与外部类成员重名,遵循就近原则,默认访问成员内部类的成员
System.out.println(num);
//访问外部类重名成员,通过外部类名.this.成员访问
System.out.println(OuterClass.this.num);
}
}
}
反编译成员内部类的class文件,也就是OuterClass$InnerClass.class文件。
可发现,内部类在构造器方法上默认添加了一个外部类类型的参数。创建内部类对象时,将外部类对象当作形参传进来,然后赋值给内部类对象的this$0(this$0是编译器会自动为成员内部类生成的外部类类型的属性),可通过this$0来访问外部类对象,所以成员内部类才可任意访问外部类的成员。
2.3 外部类访问内部类
- 访问方式:创建内部类对象,再访问。(外部类可以访问内部类任何成员,包括私有成员)
- 成员内部类依赖于外部类而存在的,也就是说,如果要创建成员内部类的对象,前提是必须存在一个外部类的对象。
- 若在外部类的静态方法中访问内部类,则需要先创建外部类对象,再通过外部类对象创建内部类对象,再访问。
- 若是定义内部类为非静态成员属性或者在非静态成员方法中访问内部类,可直接创建内部类对象。因为访问非静态成员,必须由类对象访问,所以已经存在了外部类对象,可直接创建内部类对象,JVM会将外部类对象注入到内部类对象中,将外部类对象和内部类对象关联起来。
//外部类
public class OuterClass {
private int num = 2;
//成员属性:创建内部类对象
private InnerClass innerClass = new InnerClass();
//成员内部类
public class InnerClass{
private int num = 1;
public void printNum(){
System.out.println(OuterClass.this.num);
}
}
//外部类的非静态方法须通过外部类对象才能访问,已经有了外部类对象,所以可直接在方法内部创建成员内部类对象
public void test(){
InnerClass innerClass = new InnerClass();
//可直接访问内部类的私有成员
System.out.println(innerClass.num);
}
//若是静态外部类方法,则需要在方法内部先创建外部类对象,再通过外部类对象创建成员内部类
public static void innerClassStatic(){
InnerClass innerClass = new OuterClass().new InnerClass();
//OuterClass outerClass = new OuterClass();
//InnerClass innerClass = outerClass.new InnerClass();
}
}
2.4 外部其他类访问成员内部类
- 外部其他类访问成员内部类的前提是成员内部类提供了对外访问权限。
- 先创建外部类对象,通过外部类对象创建成员内部类对象,如new 外部类().new 成员内部类(),再访问。
- 通过外部类的方法值返回,即在外部类的成员方法中编写一个专门用于对外提供访问内部类的方法。
//外部类
public class OuterClass {
private int num = 2;
//成员内部类
public class InnerClass{
private int num = 1;
public void printNum(){
System.out.println(OuterClass.this.num);
}
}
//通过非静态方法返回内部类对象
public InnerClass getInnerClass(){
return new InnerClass();
}
//通过静态方法返回内部类对象
public static InnerClass getInnerClassStatic(){
return new OuterClass().new InnerClass();
}
}
//外部其他类
class OuterOtherClass{
public static void main(String[] args) {
//先创建外部类对象,再通过外部类对象创建内部类对象
OuterClass.InnerClass innerClass = new OuterClass().new InnerClass();
innerClass.printNum();
//通过外部类方法获取内部类对象
OuterClass outerClass = new OuterClass();
OuterClass.InnerClass innerClass1 = outerClass.getInnerClass();
OuterClass.InnerClass innerClassStatic = OuterClass.getInnerClassStatic();
}
}
2.5 外部其他类继承成员内部类
- 外部其他类继承成员内部类的前提是成员内部类对外提供了访问权限
- 外部其他类的构造函数必须含有外部类类型的参数,且显示调用类型为外部类类型的参数.super();
//外部类
public class OuterClass {
//成员内部类
public class InnerClass{
private int num = 1;
public void printNum(){
}
}
}
//外部其他类
class ExtendInnerClass extends OuterClass.InnerClass{
public ExtendInnerClass(OuterClass outerClass){
//显示调用
outerClass.super();
}
}
三 静态内部类
3.1 定义
- 静态内部类是定义在外部类的成员位置,并且有static修饰。
- 可直接访问外部类的所有静态成员,包括私有的,但不能直接访问非静态成员。(可通过外部类对象访问非静态成员)。
- 如同其他成员一样,可添加任意访问修饰符(public,protected、默认(default)、private),因为它的地位就是一个成员。
- 作用域:同其他成员一样,为整个类体。
- 静态内部类不需要依赖于外部类,也就是说,可单独创建静态内部类对象,不需要通过外部类对象来创建。
3.2 静态内部类访问外部类
- 访问方式:直接访问外部类的所有静态成员(包括私有),不能直接访问非静态成员。
//外部类
public class OuterClass {
private int num = 2;
private static int numStatic = 3;
//静态内部类
public static class InnerClass{
private int numStatic = 1;
public void printNum(){
//能直接访问外部类的静态成员
//若与外部类成员重名,则遵循就近原则
System.out.println(numStatic);
//访问外部类静态成员,通过外部类名.成员
System.out.println(OuterClass.numStatic);
//若想访问外部类的非静态成员,则需要通过外部类对象访问
OuterClass outerClass = new OuterClass();
System.out.println(outerClass.num);
}
}
}
3.3 外部类访问静态内部类
- 访问方式:创建内部类对象,再访问静态内部类的所有成员(包括私有)。
//外部类
public class OuterClass {
private int num = 2;
private static int numStatic = 3;
//静态内部类
public static class InnerClass{
private int numStatic = 1;
}
public static void main(String[] args) {
InnerClass innerClass = new InnerClass();
//可访问内部类所有成员,包括所有
System.out.println(innerClass.numStatic);
}
}
3.4 外部其他类访问静态内部类
- 外部其他类访问静态内部类的前提是静态内部类提供了对外访问权限。
- 直接创建成员内部类对象,如new 外部类.静态内部类(),再访问。
- 通过外部类的方法值返回,即在外部类的成员方法中编写一个专门用于对外提供访问内部类的方法。
//外部类
public class OuterClass {
private int num = 2;
private static int numStatic = 3;
//静态内部类
public static class InnerClass{
private int numStatic = 1;
public void printNum(){
}
}
//通过非静态方法返回内部类对象
public InnerClass getInnerClass(){
return new InnerClass();
}
//通过静态方法返回内部类对象
public static InnerClass getInnerClassStatic(){
return new InnerClass();
}
}
//外部其他类
class OuterOtherClass{
public static void main(String[] args) {
//直接创建内部类对象访问
OuterClass.InnerClass innerClass = new OuterClass.InnerClass();
innerClass.printNum();
//通过外部类方法返回内部类对象访问
OuterClass outerClass = new OuterClass();
outerClass.getInnerClass().printNum();
OuterClass.getInnerClassStatic().printNum();
}
}
3.5 外部其他类继承静态内部类
- 外部其他类继承静态内部类的前提是静态内部类提供了对外访问权限。
//外部类
public class OuterClass {
public int num = 3;
public static class InnerClassStatic{
public void test(){
System.out.println("InnerClassStatic");
}
}
}
//外部其他类
class ExtendInnerClassStaic extends OuterClass.InnerClassStatic{
public static void main(String[] args) {
OuterClass.InnerClassStatic innerClassStatic = new ExtendInnerClassStaic();
innerClassStatic.test();
}
}
四 局部内部类
4.1 定义
- 局部内部类是定义在外部类的局部位置,比如方法、代码块中,并且有类名。
- 可直接访问外部类的所有成员,包括私有的。
- 定义在非静态局部位置,例如非静态成员方法中,可直接访问外部类的所有成员,包括非静态和静态成员。
- 定义在静态局部位置,例如静态成员方法中,则只能直接访问外部类的所有静态成员。
- 若外部类和内部类的成员重名时,遵循就近原则,默认访问内部类的成员。如果想访问外部类的成员,则可以使用(外部类名.this.成员)或(外部类名.成员)去访问。
- 局部内部类只能访问final的局部变量。(若访问的局部变量没加final修饰符,则编译器会自动加上)
public class OuterClass {
private int out;
public void test(){
final int k = 2;
//局部内部类
class C{
public void testK(){
//局部内部类访问局部变量k,若局部变量K不声明final,则编译器会默认加上final修饰符
System.out.println(k);
}
}
C c = new C();
c.testK();
}
}
- 不能添加访问修饰符,因为其地位就是一个局部变量,局部变量是不能使用修饰符的,但是可以使用final修饰,因为局部变量也可以使用final。
- 作用域:仅仅在定义它的方法或代码块中,作用域外不允许访问内部类。
- 局部内部类本质上仍是一个类。
- 外部类访问局部内部类成员:先创建局部内部类对象,再访问(注意:必须在作用域内)。
- 外部其他类不能访问局部内部类(因为其作用域是在定义它的局部内)。
4.2 局部内部类访问外部类
- 访问方式:直接访问。
//外部类
public class OuterClass {
private int num = 2;
private static int numStatic = 3;
public void test(){
//局部内部类,只能在定义的作用域使用
class InnerClass{
private int num = 4;
public void test(){
//若与外部类成员重名,遵循就近原则
System.out.println(num);
//通过外部类.this.成员访问外部类的成员
System.out.println(OuterClass.this.num);
//访问外部类的静态成员
System.out.println(OuterClass.numStatic);
}
}
}
public static void testStatic(){
//局部内部类,只能在定义的作用域使用
class InnerClass{
private int numStatic = 5;
public void test(){
//若与外部类静态成员重名,则遵循就近原则
System.out.println(numStatic);
//在静态方法中定义局部内部类,则只能直接访问外部类的静态成员
System.out.println(OuterClass.numStatic);
}
}
}
public static void main(String[] args) {
OuterClass outerClass = new OuterClass();
outerClass.test();
OuterClass.testStatic();
}
}
4.3 外部类访问局部类
- 访问方式:先创建局部内部类对象,再访问(注意:必须在作用域内)。
//外部类
public class OuterClass {
private int num = 2;
private static int numStatic = 3;
public void test(){
//局部内部类,只能在定义的作用域使用
class InnerClass{
private int num = 4;
public void test(){
}
}
//外部类访问局部内部类,只能在作用域内访问
InnerClass innerClass = new InnerClass();
innerClass.test();
}
public static void testStatic(){
//局部内部类,只能在定义的作用域使用
class InnerClass{
private int numStatic = 5;
public void test(){
}
}
//外部类访问局部内部类,只能在作用域内访问
InnerClass innerClass = new InnerClass();
innerClass.test();
}
public static void main(String[] args) {
OuterClass outerClass = new OuterClass();
outerClass.test();
OuterClass.testStatic();
}
}
五 匿名内部类
5.1 定义
- 匿名内部类定义在外部类的局部位置,没有类名。
- 匿名内部类是唯一一种没有构造器的类。
- 匿名内部类在编译的时候由系统自动起名为Outter$正整数.class。
- 一般来说,匿名内部类用于继承其他类或是实现接口,并不需要增加额外的方法,只是对继承方法的实现或是重写。
- 匿名内部类只能实例化一次对象。
- 匿名内部类只能访问final的局部变量。(若访问的局部变量没加final修饰符,则编译器会自动加上)
public void test(){
final int k = 2;
B b = new B() {
@Override
public void test1() {
//访问局部变量k,若局部变量k没声明final,编译器会自动加上final
System.out.println(k);
}
};
}
5.2 用法
语法:
//若是创建类的匿名内部类对象,则参数列表就是类的构造器方法的参数列表
new 类或接口(参数列表){
类体
};
若匿名内部类在外部类的非静态成员位置时,可直接访问外部类的所有静态、非静态成员,包括私有的成员。
因为通过反编译字节码文件,可发现创建匿名内部类对象时,会将外部类对象传给匿名内部类对象。
public class OuterClass {
private int out;
//非静态成员属性位置
private B b = new B() {
@Override
public void test1() {
//可直接访问外部类的成员
System.out.println(out);
}
};
//非静态成员方法位置
public void test(){
B b = new B() {
@Override
public void test1() {
//可直接访问外部类的成员
System.out.println(out);
}
};
}
}
interface B{
void test1();
}
反编译其中一个匿名内部类的字节码文件如图
若匿名内部类在外部类的静态成员位置时,则只能直接访问外部类的静态所有成员。
public class OuterClass {
private static int outStatic;
//静态成员位置
private static B b = new B() {
@Override
public void test1() {
}
};
}
interface B{
void test1();
}
反编译匿名内部类的字节码文件如图
实现接口的匿名内部类
package com.mzp.test.inner;
public class OuterClass {
public static void main(String[] args) {
TestInterface testInterface = new TestInterface() {
@Override
public void test() {
System.out.println("testInterface");
}
};
testInterface.test();
}
}
interface TestInterface {
void test();
}
反编译如图
抽象类的匿名内部类
package com.mzp.test.inner;
public class OuterClass {
public static void main(String[] args) {
TestAbstractClass testAbstractClass = new TestAbstractClass() {
@Override
public void test() {
System.out.println("testAbtractClass");
}
};
testAbstractClass.test();
}
}
abstract class TestAbstractClass{
public abstract void test();
}
反编译如图
普通类的匿名内部类
public class OuterClass {
public static void main(String[] args) {
TestClass testClass = new TestClass(){
public void test(){
System.out.println("testClass");
}
};
testClass.test();
}
}
class TestClass{
public void test(){
System.out.println("test");
}
}
反编译如图
参考
内部类系列1(JAVA基础)_chi_666的博客-CSDN博客