昨天某二面,被怼了半天我一直逃避的内部类,只能在那一本正经地胡说八道…
1. 内部类的定义
内部类指的是定义在一个类或者一个方法内的类。具体又分成成员内部类、静态内部类、局部内部类和匿名内部类。
2. 成员内部类
顾名思义,成员内部类指的是内部类是作为外部类的一个成员存在的。这种情况下成员内部类可以无条件访问外部类的成员变量和方法,即使它们是private的。但是外部类要访问内部类的成员和方法时,需要通过内部类的对象进行间接调用。
- 举个例子::
public class ExternalClass {
private String name = "外部类";
public void sayHello() {
System.out.println("Hello! I'm " + name);
}
/*成员内部类*/
public class InnerClass{
private String iname = "内部类";
/*成员内部类访问外部类*/
public void accessExternalClass() {
System.out.println("I get " + name);//访问外部类的私有成员变量
sayHello();
}
}
/*外部类访问成员内部类:要通过内部类对象来访问*/
public void accessInnerClass() {
InnerClass ic = new InnerClass();
System.out.println("I get " + ic.iname);//访问内部类的私有成员变量
}
}
- 测试:
public class Main {
public static void main(String[] args) {
ExternalClass ec = new ExternalClass();//外部类对象
ExternalClass.InnerClass ic = ec.new InnerClass();内部类对象
ec.accessInnerClass();//外部类访问内部类的方法
ic.accessExternalClass();//内部类访问外部类的方法
}
}
- 测试结果:
- 小结:在成员内部类和外部类之间,即使是private的成员变量和方法都是可以被访问的,只是外部类需要通过内部类的对象来访问内部类的成员。
3. 静态内部类
和成员内部类相比,静态内部类就是多了一个static修饰符,和类的静态成员一样,静态内部类完全属于其外部类本身,而不是属于其实例对象。
-
静态内部类——>外部类:与静态方法和静态代码块一样,在静态内部类中,只能访问外部类的静态成员,而不能访问其实例成员。
-
外部类——>静态内部类:外部类既可以访问静态内部类的静态成员(外部类.内部类.成员名),又可以访问静态内部类的非静态成员(通过创建内部类的实例完成非静态成员的调用)
-
举个例子吧
- 还是上面的代码,我们尝试在内部类前加上static修饰符,会发现在内部类的accessExternalClass()方法中访问外部类的成员变量name和成员方法sayHello()时报错 (Cannot make a static reference to the non-static field name),也就是说在静态内部类中不能访问外部类的非静态成员。
- 然后将外部类的非静态成员变量name前加上static修饰,使其变成外部类的静态成员变量,报错就消失了。因此静态内部类中可以随意访问外部类的静态成员,即使是private的。
- 下一步测试外部类对内部类中成员的访问,首先在静态内部类中定义一个静态成员变量、一个静态成员方法和一个非静态成员方法,然后分别通过静态内部类的实例和类名完成对它们的访问。
- 具体代码如下:
public class ExternalClass {
private static String name = "外部类";
public void sayHello() {
System.out.println("Hello! I'm " + name);
}
/*静态内部类*/
public static class InnerClass{
private static String iname = "静态内部类";
/*静态内部类访问外部类*/
public void accessExternalClass() {
System.out.println("我是静态内部类,我可以访问外部类的静态成员 name = " + name);//可以访问外部类的静态成员变量
sayHello();//静态内部类中访问外部类的非静态成员方法报错
}
public static void hello() {
System.out.println("我是"+ iname);
}
}
/*外部类访问静态内部类:要通过内部类对象/类名来访问*/
public void accessInnerClass() {
InnerClass ic = new InnerClass();
System.out.println("I get " + ic.iname);//通过静态内部类的实例对象来访问静态内部类的静态成员
ExternalClass.InnerClass.hello();//通过类名访问静态内部类的静态成员
ic.accessExternalClass();//通过静态内部类的实例对象访问其本身的非静态成员
}
}
- 小结:静态内部类只能访问外部类的静态成员,外部类可以既可以访问静态内部类的静态成员(通过类名),又可以访问其非静态成员(通过实例对象)。
4. 局部内部类
局部内部类指的是定义在方法内部的类。其实就相当于方法中的局部变量,这个类的作用域也是方法的内部,同时也不能用public、private、protected、static等修饰。
- 举个例子吧
public class ExternalClass {
private String name = "外部类";
public void sayHello() {
System.out.println("Hello! I'm " + name);
class InnerClass { //定义局部内部类
public void say() { //定义局部内部类的成员方法
System.out.println("我是局部内部类");
}
}
InnerClass ic = new InnerClass(); //在外部类的成员方法中生成局部内部类的实例
ic.say();//完成对局部内部类中成员方法的调用
}
}
- 测试
public class Main {
public static void main(String[] args) {
ExternalClass ec = new ExternalClass();
ec.sayHello();
}
}
- 测试结果
- 小结:局部内部类是定义在外部类成员方法中的类,应该是为了实现方法的嵌套吧,不经常用!
5. 匿名内部类
匿名内部类指的是没有名字的内部类。这种内部类的应用场景是在类之间的继承或者接口的实现上通过匿名内部类来完成相应的方法重写或实现的功能。
- 举个例子—实现继承
public class ExternalClass {
private String name = "外部类";
public void sayHello() { //外部类的成员方法
System.out.println("Hello! I'm " + name);
}
}
- 内部类的定义及测试
public class Main {
public static void main(String[] args) {
/*实质上是生成了匿名内部类的实例对象*/
ExternalClass ec = new ExternalClass() { //定义匿名内部类,相当于是继承了ExternalClass的一个类
public void sayHello() { //相当于是对父类(外部类)ExternalClass的成员方法进行了重写
System.out.println("我是匿名内部类");
}
};
ec.sayHello(); //调用sayhello()方法
}
}
- 运行结果
- 小结:在生成实例对象的过程中针对外部类定义一个匿名内部类,并重写其外部类的成员方法,此时是生成了匿名内部类的一个实例对象,所以再调用成员方法时调用的是重写后的方法。
- 注意:因为生成的匿名内部类实例依然是ExternalClass类型的,所以不能实现对匿名内部类中自定义方法的访问,只能调用外部类中已经存在的成员方法。
- 另外一个例子——实现接口
首先定义一个待实现的接口:
public interface Animal {
public String sayHello(); //接口中待实现的方法
}
然后定义一个Animal接口的实现类并测试:
public class Dog {
/**/
public void speak(Animal animal) {
System.out.println(animal.sayHello()); //调用实现后的方法
}
public static void main(String[] args) {
// TODO Auto-generated method stub
Dog dog = new Dog();
dog.speak(new Animal() { //匿名内部类实现接口中的sayHello()方法
public String sayHello() {
return "汪汪!";
}
});
}
}
- 执行结果:
- 小结:可以利用匿名内部类完成对接口中方法的实现。
总结:
内部类分成成员内部类(作为外部类的成员存在)、静态内部类(外部类的静态成员)、局部内部类(在方法中定义的内部类,类似于局部变量)、匿名内部类(用于类与类的继承或者接口的实现)!