内部类是一种实现多继承效果的方式。
成员内部类的位置类似于成员变量,定义在类中方法外:
/**
* @author QHJ
* @date 2022/9/19 13:46
* @description: 成员内部类
*/
public class ConstructorInnerClass {
String name = "外部类";
class ConstructorInnerClass2{
String name = "成员内部类";
}
}
内部类 ConstructorInnerClass2 就好像 ConstructorInnerClass 的一个成员,成员内部类可以无限制访问外部类的所有成员属性和成员方法(包括私有成员和静态成员):
/**
* @author QHJ
* @date 2022/9/19 13:46
* @description: 成员内部类
*/
public class ConstructorInnerClass {
String name = "外部类";
boolean flag = true;
private int age = 22;
static double money = 100;
class ConstructorInnerClass2{
String name = "成员内部类";
public void print(){
System.out.println(flag);
System.out.println(age);
System.out.println(money);
}
}
}
不过需要注意的是,当成员内部类拥有和外部类同名的成员变量或者方法时,会发生隐藏现象,即默认情况下访问的是成员内部类的成员。如果需要访问外部类的同名成员,需要使用 外部类.this.成员变量
、外部类.this.成员方法
进行访问:
内部类可以随心所欲地访问外部类的成员,但外部类想要访问内部类的成员就不那么容易了。必须先创建一个成员内部类的对象,再通过这个对象来访问:
/**
* @author QHJ
* @date 2022/9/19 13:46
* @description: 成员内部类
*/
public class ConstructorInnerClass {
String name = "外部类";
boolean flag = true;
private int age = 22;
static double money = 100;
class ConstructorInnerClass2{
String name = "成员内部类";
public void print(){
System.out.println(flag);
System.out.println(age);
System.out.println(money);
}
}
public static void main(String[] args) {
ConstructorInnerClass constructorInnerClass = new ConstructorInnerClass();
// 先创建内部类的对象
ConstructorInnerClass2 constructorInnerClass2 = constructorInnerClass.new ConstructorInnerClass2();
// 访问内部类的方法
constructorInnerClass2.print();
}
}
这也就意味着,如果想要在静态方法中访问成员内部类,就必须先得创建一个外部类的对象,因为内部类是依附于外部类的。
但是这种创建内部类的方式在实际开发中并不常用,因为内部类和外部类紧紧地绑定在一起,使用起来非常不便。
内部类可以拥有 private 访问权限、protected 访问权限、public 访问权限及包访问权限。比如上面的例子,如果成员内部类 ConstructorInnerClass2 用 private 修饰,则只能在外部类的内部访问,如果用 public 修饰,则任何地方都能访问;如果用 protected 修饰,则只能在同一个包下或者继承外部类的情况下访问;如果是默认访问权限,则只能在同一个包下访问。这一点和外部类有一点不一样,外部类只能被public 和包访问两种权限修饰。我个人是这么理解的,由于成员内部类看起来像是外部类的一个成员,所以可以像类的成员一样拥有多种权限修饰。
局部内部类是定义在一个方法或者一个作用域里面的类,所有局部内部类的生命周期仅限于作用域内:
/**
* @author QHJ
* @date 2022/9/19 14:09
* @description: 局部内部类
*/
public class LocalInnerClass {
public void print() {
class LocalInnerClass2 {
private int age = 22;
public void print2() {
System.out.println(age);
}
}
LocalInnerClass2 localInnerClass2 = new LocalInnerClass2();
localInnerClass2.print2();
}
public static void main(String[] args) {
LocalInnerClass localInnerClass = new LocalInnerClass();
localInnerClass.print();
}
}
局部内部类就好像一个局部变量一样,它是不能被权限修饰符修饰的,比如 public
、protected
、private
和static
等:
匿名内部类是平常用的最多的,尤其是启动多线程的时候会经常用到,并且 IDE 也会帮我们自动生成:
/**
* @author QHJ
* @date 2022/9/19 14:25
* @description: 匿名内部类
*/
public class AnonymousInnerClass {
public static void main(String[] args) {
Thread t = new Thread(new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName());
}
});
t.start();
}
}
匿名内部类就好像是一个方法的参数一样,用完就没了,以至于我们都不需要为它专门写一个构造方法,它的名字也是由系统自动命名的。观察一下编译后的字节码文件也可以发现,匿名内部类连名字都没有,而是直接借用的外部类,然后 $1 就搞定了。
匿名内部类是唯一一种没有构造方法的类,不允许我们为其编写构造方法,因为它就像是直接通过 new 关键字创建出来的一个对象。
匿名内部类的作用主要是用来继承其他类或者实现接口,并不需要增加额外的方法,方便对继承的方法进行实现或者重写。
静态内部类和成员内部类类似,只是多了一个static
关键字:
/**
* @author QHJ
* @date 2022/9/19 14:43
* @description: 静态内部类
*/
public class StaticInnerClass {
static int age = 22;
double money;
static class StaticInnerClass2 {
public void print() {
// 只能访问静态变量
System.out.println(age);
}
}
}
由于 static
关键字的存在,静态内部类是不允许访问外部类中的非静态变量和方法的:
静态内部类是不需要依赖于外部类的,这点和类的静态成员属性有点类似,并且它不能使用外部类的非 static 成员变量或者方法。因为在没有外部类的情况下,可以创建静态内部类的对象,如果允许访问类的非静态成员就会产生矛盾,因为外部类的非静态成员是必须依附于具体的对象的。
为什么要使用内部类?
在《Think in java》中有这样一句话:
使用内部类最吸引人的原因是:每个内部类都能独立地继承一个(接口的)实现,所以无论外围类是否已经继承了某个(接口的)实现,对于内部类都没有影响。
在我们程序设计中有时候会存在一些使用接口很难解决的问题,这个时候我们可以利用内部类提供的、可以继承多个具体的或者抽象的类的能力来解决这些程序设计问题。可以这样说,接口只是解决了部分问题,而内部类使得多重继承的解决方案变得更加完整。
使用内部类还能够为我们带来如下特性(摘自《Think in java》):
1、内部类可以使用多个实例,每个实例都有自己的状态信息,并且与其他外围对象的信息相互独立。
2、在单个外部类中,可以让多个内部类以不同的方式实现同一个接口,或者继承同一个类。
3、创建内部类对象的时刻并不依赖于外部类对象的创建。
4、内部类并没有令人迷惑的 “is-a” 关系,他就是一个独立的实体。
5、内部类提供了更好的封装,除了该外围类,其他类都不能访问。