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