Java 大厂面试高频题:Java 非静态内部类和静态内部类有什么区别?
在学习 Java 内部类时,很多初学者会遇到两个非常相似的概念:
class Outer {
class Inner {
}
static class StaticInner {
}
}
其中,Inner 是非静态内部类,StaticInner 是静态内部类。
它们看起来只是相差一个 static,但在对象创建方式、成员访问范围、内存关系和使用场景上都有明显区别。
一句话概括:
非静态内部类依赖外部类对象创建,并且可以直接访问外部类的实例成员;静态内部类不依赖外部类对象创建,只能直接访问外部类的静态成员。
一、什么是内部类?
在一个类的内部定义另一个类,这个类就叫内部类。
例如:
class Outer {
class Inner {
}
}
这里:
Outer是外部类;Inner是内部类。
内部类通常用于表示一个只服务于外部类的辅助结构。把它放在外部类内部,可以让代码关系更加紧密,也可以隐藏不需要暴露给其他类的实现细节。
成员内部类主要可以分为两类:
非静态内部类
静态内部类
二、什么是非静态内部类?
没有使用 static 修饰的成员内部类,叫非静态内部类。
例如:
class Outer {
int age = 18;
class Inner {
void show() {
System.out.println(age);
}
}
}
这里的 Inner 就是非静态内部类。
它最大的特点是:必须依赖一个具体的外部类对象才能创建。
创建方式如下:
Outer outer = new Outer();
Outer.Inner inner = outer.new Inner();
inner.show();
不能直接写:
Outer.Inner inner = new Outer.Inner(); // 编译错误
因为非静态内部类属于某一个具体的外部类对象,而不是只属于外部类本身。
可以把外部类理解成一栋房子,把非静态内部类理解成房子里的房间。
必须先有一栋具体的房子,才能创建这栋房子里的房间。
所以需要先执行:
Outer outer = new Outer();
再执行:
Outer.Inner inner = outer.new Inner();
三、非静态内部类为什么能访问外部类实例成员?
看下面的代码:
class Outer {
private int age = 18;
class Inner {
void show() {
System.out.println(age);
}
}
}
age 是外部类的实例变量,但是内部类可以直接访问它,甚至不需要写:
Outer.this.age
原因是,每个非静态内部类对象都会关联一个具体的外部类对象。
例如:
Outer outer = new Outer();
Outer.Inner inner = outer.new Inner();
这里创建出的 inner 内部保存了对 outer 的关联,所以当它访问 age 时,实际访问的是这个 outer 对象的 age。
可以简单理解为:
非静态内部类对象身上带着一个外部类对象。
因此,非静态内部类可以直接访问外部类中的:
实例变量
实例方法
静态变量
静态方法
private 成员
示例:
class Outer {
private int age = 18;
private static int count = 100;
class Inner {
void show() {
System.out.println(age);
System.out.println(count);
}
}
}
这段代码是可以正常运行的。
四、什么是静态内部类?
使用 static 修饰的成员内部类,叫静态内部类,也常被称为静态嵌套类。
例如:
class Outer {
static class StaticInner {
void show() {
System.out.println("静态内部类");
}
}
}
静态内部类不依赖外部类对象,可以直接通过外部类名创建:
Outer.StaticInner inner = new Outer.StaticInner();
inner.show();
不需要先创建:
Outer outer = new Outer();
原因是,静态内部类属于外部类本身,而不是属于某个具体的外部类对象。
可以简单理解为:
非静态内部类属于外部类对象;
静态内部类属于外部类本身。
五、静态内部类为什么不能直接访问实例成员?
看下面的代码:
class Outer {
int age = 18;
static class StaticInner {
void show() {
System.out.println(age); // 编译错误
}
}
}
这里会报错,因为 age 是外部类的实例变量。
实例变量属于某一个具体对象,而静态内部类在创建时并没有关联任何外部类对象。
假设存在两个外部类对象:
Outer outer1 = new Outer();
Outer outer2 = new Outer();
outer1.age = 18;
outer2.age = 20;
当静态内部类访问 age 时,Java 无法判断它要访问的是:
outer1.age
还是:
outer2.age
所以静态内部类不能直接访问外部类的实例成员。
不过,它可以直接访问外部类的静态成员:
class Outer {
static int count = 100;
static class StaticInner {
void show() {
System.out.println(count);
}
}
}
因为静态变量属于类本身,不属于某一个具体对象。
如果静态内部类确实需要访问实例成员,可以手动创建或接收外部类对象:
class Outer {
int age = 18;
static class StaticInner {
void show(Outer outer) {
System.out.println(outer.age);
}
}
}
使用:
Outer outer = new Outer();
Outer.StaticInner inner = new Outer.StaticInner();
inner.show(outer);
六、完整代码对比
下面通过一个完整示例对比两者:
class Outer {
int instanceValue = 100;
static int staticValue = 200;
class NormalInner {
void show() {
System.out.println(instanceValue);
System.out.println(staticValue);
}
}
static class StaticInner {
void show() {
// 不能直接访问 instanceValue
System.out.println(staticValue);
}
}
}
创建非静态内部类:
Outer outer = new Outer();
Outer.NormalInner normalInner = outer.new NormalInner();
normalInner.show();
创建静态内部类:
Outer.StaticInner staticInner = new Outer.StaticInner();
staticInner.show();
两者的创建方式是最容易在考试和面试中被问到的区别。
七、两者的核心区别
| 对比项 | 非静态内部类 | 静态内部类 |
|---|---|---|
是否使用 static |
否 | 是 |
| 是否依赖外部类对象 | 依赖 | 不依赖 |
| 创建方式 | outer.new Inner() |
new Outer.StaticInner() |
| 能否直接访问外部类实例成员 | 可以 | 不可以 |
| 能否直接访问外部类静态成员 | 可以 | 可以 |
| 是否关联外部类对象 | 是 | 否 |
| 更接近普通类还是对象成员 | 对象成员 | 普通类 |
八、为什么静态内部类更节省关联?
非静态内部类对象会隐式关联一个外部类对象。
例如:
Outer outer = new Outer();
Outer.Inner inner = outer.new Inner();
只要 inner 还被使用,它关联的 outer 也可能无法被垃圾回收。
如果内部类实际上并不需要访问外部类的实例成员,却仍然被定义成非静态内部类,就可能产生不必要的对象关联。
静态内部类没有这种隐式关联。
因此,如果内部类不需要使用外部类对象的实例成员,一般更推荐定义为静态内部类。
九、常见使用场景
1. 非静态内部类
当内部类必须依赖外部类对象的数据时,可以使用非静态内部类。
例如,一个订单中的订单项:
class Order {
private String orderNo;
class OrderItem {
void printOrderNo() {
System.out.println(orderNo);
}
}
}
OrderItem 与某一个具体订单紧密关联,因此可以使用非静态内部类。
2. 静态内部类
当内部类只是为了组织代码,不需要访问外部类实例成员时,更适合使用静态内部类。
常见例子是建造者模式:
class User {
private String name;
private int age;
static class Builder {
private String name;
private int age;
Builder name(String name) {
this.name = name;
return this;
}
Builder age(int age) {
this.age = age;
return this;
}
User build() {
User user = new User();
user.name = name;
user.age = age;
return user;
}
}
}
使用:
User user = new User.Builder()
.name("Tom")
.age(18)
.build();
这里的 Builder 不需要依赖一个已经存在的 User 对象,所以通常定义为静态内部类。
十、初学者容易混淆的问题
1. 静态内部类能不能创建对象?
可以。
static 并不代表这个类不能创建对象,而是表示创建它时不需要外部类对象。
Outer.StaticInner inner = new Outer.StaticInner();
2. 非静态内部类自己能不能有成员变量?
可以。
class Outer {
class Inner {
int value = 10;
}
}
3. 静态内部类能不能访问外部类的 private 静态成员?
可以。
class Outer {
private static int count = 10;
static class Inner {
void show() {
System.out.println(count);
}
}
}
内部类和外部类之间可以互相访问对方的私有成员,只要访问方式符合静态和非静态规则。
十一、面试怎么回答?
面试时可以这样回答:
非静态内部类依赖外部类实例创建,内部会关联一个具体的外部类对象,因此可以直接访问外部类的实例成员和静态成员;静态内部类不依赖外部类实例,可以直接通过外部类名创建,只能直接访问外部类的静态成员。如果内部类不需要访问外部类实例成员,通常更推荐使用静态内部类。
也可以用一句话回答:
非静态内部类属于外部类对象,静态内部类属于外部类本身。
十二、总结
非静态内部类和静态内部类最核心的区别,不是有没有 static 这么简单,而是它们是否依赖外部类对象。
非静态内部类:
必须依赖外部类对象创建;
可以直接访问外部类实例成员;
内部会关联一个具体的外部类对象。
静态内部类:
不依赖外部类对象;
可以直接通过外部类名创建;
不能直接访问外部类实例成员;
不会隐式关联外部类对象。
最后记住这句口诀:
非静态内部类先有外部对象,再有内部对象;
静态内部类直接通过外部类名创建。
更多推荐




所有评论(0)