文章目录
1. 什么是内部类?
Java 类中不仅可以定义变量和方法,还可以定义类,在类内部定义的类就被称为内部类。根据定义的方式不同,可以将内部类分为四种:
- 成员内部类
- 局部(方法)内部类
- 静态内部类
- 匿名内部类
2. 为什么要使用内部类?
使用内部类最吸引人的原因是:每个内部类都能独立地继承一个(接口的)实现,所以无论外围类是否已经继承了某个(接口的)实现,对于内部类都没有影响。——《Think in java》
使用内部类的优势:
- 内部类可以继承父类、实现接口
- 内部类可以用多个实例,每个实例都有自己的状态信息,并且与其他外围对象的信息相互独立
- 内部类并没有令人迷惑的“is-a”关系,他就是一个独立的实体
- 内部类提供了更好的封装,除了该外围类,其他类都不能访问
- 创建内部类对象的时刻并不依赖于外围类对象的创建
具体来说,内部类信息(属性、方法)可以和外部类重名;内部类是具有类的基本特征的独立实体;可以利用访问修饰符隐藏内部类的实施细节,提供了更好的封装;静态内部类使用时可直接使用,不需先创造外部类。
3. 如何使用内部类?
说明:
- 非静态内部类不能拥有 静态变量、静态代码块、静态方法(暂无法解释)
- 静态内部类无需外部类实例即可调用
- 非静态内部类需要外部类实例调用
3.1 成员内部类
3.1.1 什么是成员内部类?
定义在类内部的非静态类,就是成员内部类。
成员内部类的实例化过程:
- 首先生成外部类的实例对象
- 然后生成绑定到外部类实例对象的成员内部类实例对象
成员内部类实例对象的生成是依赖于外部类实例对象
【注意】:成员内部类不能定义静态方法和变量(IDEA中直接报错),但可以定义static final/final 的变量(常量)。
至于成员内部类为什么不能定义静态方法和变量这个问题,我暂无法解释(网上的答案五花八门,暂还没有找到能够说服我的理由的资料),以后有机会的话再来补坑。
3.1.2 如何使用成员内部类?
demo:
public class OuterClass {
private void doSomething() {
System.out.println("OuterClass doSomething ...");
}
public class InnerClass {
public void doSomething() {
OuterClass.this.doSomething();
System.out.println("InnerClass doSomething ...");
}
}
public static void main(String[] args) {
// 在外部类中访问内部类
InnerClass innerClass = new InnerClass();
innerClass.doSomething();
}
}
public class Test {
public static void main(String[] args) {
// 在其他类中访问内部类
OuterClass.InnerClass innerClass = new OuterClass.InnerClass();
innerClass.doSomething();
// 这种形式也行
//OuterClass outerClass = new OuterClass();
//OuterClass.InnerClass innerClass = outerClass.new InnerClass();
//innerClass.doSomething();
}
}
3.1.3 小结
1)、成员内部类可以是任意修饰符(当为private修饰时,不能直接在其他类中实例化)
2)、在其他类(不是外部类)中访问成员内部类时,必须要是这种形式:外部类.成员内部类
3)、成员内部类可以随意的访问外部类的成员变量/方法,且成员变量名/方法名可以同名,但如果这样的话,要想访问外部类的成员变量/方法,必须是这种形式:外部类.this.变量名/方法名
3.2 局部(方法)内部类
3.2.1 什么是局部内部类?
定义在方法中的类,就是局部类。如果一个类只在某个方法中使用,则可以考虑使用局部类。
3.2.2 如何使用局部内部类?
public class OuterClass {
private void doSomething() {
System.out.println("OuterClass doSomething ...");
}
public void test() {
int i = 1;
class InnerClass {
private void doSomething() {
OuterClass.this.doSomething();
System.out.println("i = " + i);
System.out.println("InnerClass doSomething ...");
}
}
InnerClass innerClass = new InnerClass();
innerClass.doSomething();
}
public static void main(String[] args) {
OuterClass outerClass = new OuterClass();
outerClass.test();
}
}
3.2.3 小结
1)、类前不能有访问修饰符
2)、仅在此方法内使用
3)、无法创造静态信息
4)、可以随意的访问外部类的任何信息
5)、可以直接访问方法内的局部变量和参数(有限制)
3.2.4 局部内部类访问局部变量的限制
变量名从内部类中访问,需要final或有效的final
具体限制如下:
- 直接被final修饰的变量
- 已被赋值且始终未改变的变量(有且仅有赋值一次),引用指向不能改变。JDK8以前(不包括8)只能访问被final修饰的变量
如:修改变量i的值
3.3 静态内部类
3.3.1 什么是静态内部类?
定义在类内部的静态类,就是静态内部类。
3.3.2 如何使用静态内部类?
public class OuterClass {
private static int i = 1;
private int j = 1;
public static void doSomething() {
System.out.println("OuterClass doSomething ...");
}
public static class InnerClass {
private static int i = 100;
private int j = 100;
public static void doSomething() {
OuterClass.doSomething();
System.out.println("InnerClass doSomething ...");
System.out.println(OuterClass.i);
}
public void test() {
System.out.println("InnerClass test ...");
}
}
}
public class Test {
public static void main(String[] args) {
// 1.通过静态内部类名直接访问它的静态方法
OuterClass.InnerClass.doSomething();
// 2.实例化静态内部类,并访问它的实例方法
OuterClass.InnerClass innerClass = new OuterClass.InnerClass();
innerClass.test();
}
}
3.3.3 小结
1)、静态内部类中可以包含任意的信息(static/非static)
2)、静态内部类只能直接访问外部类的所有的static的成员变量及方法
3)、用 外部类.内部类 引用=new 外部类.内部类(); 然后利用 引用.成员信息(属性、方法)调用
4)、访问内部类的静态信息,直接外部类.内部类.静态信息就可以了
5)、静态内部类可以独立存在,不依赖于其他外围类
3.4 匿名内部类
3.4.1 什么是匿名内部类?
说简单点:就是没有名字的内部类
匿名内部类必须要继承一个父类或者实现一个接口,同时它也是没有class关键字,这是因为匿名内部类是直接使用new来生成一个对象的引用。
3.4.3 如何使用匿名内部类?
demo1:
public interface IAnimal {
void bark();
}
public class OuterClass {
public IAnimal getInnerInstance(String s) {
return new IAnimal() {
@Override
public void bark() {
System.out.println(s);
}
};
}
public static void main(String[] args) {
OuterClass outerClass = new OuterClass();
IAnimal iAnimal = outerClass.getInnerInstance("汪汪汪...");
iAnimal.bark();
}
}
demo2:把匿名内部类作为方法的参数
public abstract class User {
private String name;
public abstract int run();
public String getName() {
return name;
}
}
public class OuterClass {
public void test(User user) {
System.out.println(user.getName() + "能够一口气跑 " + user.run() + "米");
}
public static void main(String[] args) {
OuterClass outerClass = new OuterClass();
outerClass.test(new User() {
@Override
public int run() {
return 500;
}
@Override
public String getName() {
return "zzc";
}
});
}
}
调用test()方法时,要传入一个User对象。但是由于User是一个接口,无法创建对象,所以要实现该接口。因此此处采用匿名内部类的方式进行,并实现接口中全部的抽象方法。
【参考资料】
浅谈Java内部类(超详细代码示例)