1.成员内部类:应用率低:
(1)类中套类,外面的称为Outer外部类,里面的称为Inner内部类
(2)内部类通常只服务于外部类,对外不具备可见性。
(3)内部类对象通常是在外部类中创建。
(4)内部类中可以直接访问外部类成员(包括私有的)
内部类中有个隐私的引用指向了创建它的外部类对象
eg:外部类名.this
class Circle {
private double radius = 0;
public static int count =1;
public Circle(double radius) {
this.radius = radius;
}
class Draw { //内部类无条件的访问外部类成员
public void drawSahpe() {
System.out.println(radius); //外部类的private成员
System.out.println(count); //外部类的静态成员
}
}
}
在外部类中访问成员内部类的成员,必须要通过内部类的对象才能访问,而不能直接访问。
class Circle { private double radius = 0; public Circle(double radius) { this.radius = radius; getDrawInstance().drawSahpe(); //必须先创建成员内部类的对象,再进行访问 } private Draw getDrawInstance() { return new Draw(); } class Draw { //内部类 public void drawSahpe() { System.out.println(radius); //外部类的private成员 } } }
2.匿名内部类:
(1)若想创建一个类(派生类)的对象,并且对象只被创建一次,此时该类不必命名,称为匿名内部类。
(2)匿名内部类若想访问外部的变量,该变量必须是final的
--------在JDK1.8之前有这样的要求。
package demo;
//匿名内部类的演示:
public class NstInnerClassDemo {
public static void main(String[] args) {
/*
* 1.创建了Coo的一个派生类,但是没有名字
* 2.为该派生类创建了一个对象,名为o1
* Doo o1=new Doo();
* 3.大括号中的为派生类的类体。
*/
Coo o1=new Coo(){
};
final int num=5;
/*
* 1.创建了Doo的一个派生类,但是没有名字
* 2.为该派生类创建了一个对象,名为o3
* 3.大括号中的为派生类的类体。
*/
Doo o3=new Doo(){
void show(){
System.out.println("showshow");
System.out.println(num);
}
};
o3.show();
}
}
/*
class Doo extends Coo{
}*/
abstract class Doo{
abstract void show();
}
abstract class Coo{
}
3.静态内部类
若一个类定义在另一个类的内部且加了static关键字作为静态成员,我们把这个作为静态成员的类称之为静态内部类。
静态内部类是不需要依赖于外部类实例的,这点和类的其它静态成员一样,作为静态成员他只能访问外部类的静态成员 ,而不能访问外部类的实例成员。
public class OuterClass { private String outerClass1="我是外部类的实例成员";
private static String outerClass2="我是外部类的静态成员";
private static class StaticInnerClass{
public void printOuterClassMumber(){ //静态内部类里面只能访问外部类的静态成员 //System.out.println(outerClass1);此句需被注释掉否则会报错 System.out.println(outerClass2); } } }
同成员内部类一样,外部类中也不能直接访问静态内部类成员,必须要通过内部类的对象才能访问。但是和成员内部类不同的是,静态内部类中可以定义静态成员,若外部类访问的是静态内部类的静态成员,则可以通过类名来完成,就无需创建静态内部类对象。
public class OuterClass { private static class StaticInnerClass{ private String staticInnerClass1="我是静态内部类的实例成员变量"; private static String staticInnerClass2="我是静态内部类的静态成员变量"; } public void staticInnerClassTest(){ //必须要通过内部类对象才能访问实例变量 System.out.println(new StaticInnerClass().staticInnerClass1); //可以通过类名来访问静态变量 System.out.println(StaticInnerClass.staticInnerClass2); } }
4.局部内部类
局部内部类是定义在一个方法或者一个作用域里面的类,它和成员内部类的区别在于局部内部类的访问仅限于方法或者该作用域内。局部内部类同局部变量一样,不能加如何访问修饰符,在其作用域外不能被访问。
class OuterClass { public void localInnerClassTest(){ System.out.println("<<<<<<<<<<<<<<<<测试局部内部类>>>>>>>>>>>>>>>>>"); class LocalInnerClass{ public void pritTest(){ System.out.println("我是局部内部类"); } } new LocalInnerClass().pritTest(); }
}
深入理解内部类
成员内部类为什么可以无条件的访问外部类的所有成员
我们通过查看编译后的文件可知,编译后内部类和外部类不再是嵌套关系,而是在同一个包下的两个类,并将内部类命名为"外部类类名$内部类类名.class"。
不仅如此,通过反编译成员内部类.class文件可知,编译器为内部类生成了一个指向外部类对象的引用this$0和一个以外部类对象引用作为形参的构造函数。当创建内部类对象时,通过构造函数给对象引用this$0赋值,更为重要的是针对外部类的私有成员还会创建访问他们的accesso方法。结合指向外部类的对象和访问私有成员的方法,才实现了内部类对外部类的无条件访问。这里也间接说明了成员内部类是依赖于外部类的,如果没有创建外部类的对象,则无法对Outter this&0进行赋值,也就无法创建成员内部类对象了。
下面是经编译后的成员内部类字节码文件:
class OuterClass$MumberInnerClass { private String mumberInnerClass1; private OuterClass$MumberInnerClass(OuterClass var1) { this.this$0 = var1; this.mumberInnerClass1 = "我是成员内部类的实例变量"; } public void printOuterClassMumber() { System.out.println(OuterClass.access$000(this.this$0)); System.out.println(OuterClass.access$100()); } }
外部类中为什么要创建内部类的对象才能访问内部类成员
成员内部类中对外部类的访问是无条件的,但是外部类中访问内部类成员需要通过内部类对象才能完成。原因在于编译器并未在外部类中自动创建内部类对象,所以需要自己去创建。
下面是编译后的外部类字节码文件,可以看出外部类经编译后没有自动创建内部类对象。
package innerClassTest; public class OuterClass { private String outerClass1 = "我是外部类的实例成员"; private static String outerClass2 = "我是外部类的静态成员"; public OuterClass() { } public void mumberInnerClassTest() { System.out.println("<<<<<<<<<<<<<<<<1.测试成员内部类>>>>>>>>>>>>>>>>>"); System.out.println((new OuterClass.MumberInnerClass(null)).mumberInnerClass1); (new OuterClass.MumberInnerClass(null)).printOuterClassMumber(); } public void staticInnerClassTest() { System.out.println("<<<<<<<<<<<<<<<<2.测试静态内部类>>>>>>>>>>>>>>>>>"); System.out.println((new OuterClass.StaticInnerClass(null)).staticInnerClass1); System.out.println(OuterClass.StaticInnerClass.staticInnerClass2); (new OuterClass.StaticInnerClass(null)).printOuterClassMumber(); } public void localInnerClassTest() { System.out.println("<<<<<<<<<<<<<<<<3.测试局部内部类>>>>>>>>>>>>>>>>>"); class LocalInnerClass { LocalInnerClass() { } public void pritTest() { System.out.println("我是局部内部类"); } } (new LocalInnerClass()).pritTest(); } public void anonymousInnerClassTest() { System.out.println("<<<<<<<<<<<<<<<<测试匿名内部类>>>>>>>>>>>>>>>>>"); (new AnonymousClass() { public void printAnonymousClass() { System.out.println("我是生成的匿名内部类"); } }).printAnonymousClass(); } private static class StaticInnerClass { private String staticInnerClass1; private static String staticInnerClass2 = "我是静态内部类的静态成员变量"; private StaticInnerClass() { this.staticInnerClass1 = "我是静态内部类的实例成员变量"; } public void printOuterClassMumber() { System.out.println(OuterClass.outerClass2); } } private class MumberInnerClass { private String mumberInnerClass1; private MumberInnerClass() { this.mumberInnerClass1 = "我是成员内部类的实例变量"; } public void printOuterClassMumber() { System.out.println(OuterClass.this.outerClass1); System.out.println(OuterClass.outerClass2); } } }
四种内部类的区别
1.四种内部类经编译后均会产生独立的class文件,除了静态内部类以外编译器均会为内部类添加一个指向外部类的引用,使得内部类能无条件访问外部类成员。
2.虽然内部类经编译后会产生独立的字节码文件,但是仍然要满足作为外部类成员的要求,即成员内部类中不能定义静态成员、静态内部类中可以定义静态成员、局部内部类同局部变量一样不能被其作用域以外的地方访问。
3.在外部类的外面创建内部类对象
a.由于成员内部类是依赖于外部类的,所以创建成员内部类对象必须先创建外部类对象。
b.静态内部类是不依赖外部类的,因此可以无需创建外部类对象而直接创建静态内部类对象。
下面是外部类之外创建内部类对象的案例:
public class Test { public static void main(String[] args) { OuterClass outerClass=new OuterClass(); OuterClass.MumberInnerClass mumberInnerClassInstance=outerClass.new MumberInnerClass(); mumberInnerClassInstance.printOuterClassMumber(); OuterClass.StaticInnerClass staticInnerClassInstance=new OuterClass.StaticInnerClass(); staticInnerClassInstance.printOuterClassMumber(); } }
面试题:
问:内部类有独立的.class 吗?
答:有