什么是内部类
在Java中,允许在一个类的内部定义类,这样的类称作内部类。如:
在实际情况中,根据内部类的位置、修饰符和定义方式的不同,内部类可以分为4种形式:成员内部类、局部内部类、静态内部类、匿名内部类。
1.成员内部类
成员内部类:内部类的位置与成员变量、成员方法相同。是外部类中的一个成员。
我们先来定义一个包含成员内部类的类:
class Outer{
int m = 0;
void text1(){
System.out.println("外部类成员方法");
}
class Inner{
int n = 1;
void show1(){
System.out.println("内部类成员方法,外部成员变量m="+m);
}
}
void text2(){
Inner i = new Inner();
System.out.println("外部类成员方法,内部类成员变量n="+i.n);
}
}
下面来定义一个测试类:
public class Text {
public static void main(String[] args) {
Outer o = new Outer();
Outer.Inner i = o.new Inner();
i.show1();
o.text2();
}
}
注意成员内部类对象的定义方法
外部类名.内部类名 变量名 = new 外部类名().new 内部类名();
我们来看一下运行结果
通过运行结果可以看出,成员内部类可以访问外部类的所有成员,同时外部类也可以访问成员内部类的所有成员。
2.局部内部类
局部内部类:也叫方法内部类,就是定义在某个局部范围中的类,它和局部变量一样,都是在方法中定义的,其有效范围也智局限在方法的内部。
我们先来定义一个包含局部内部类的类
public class Outer {
int m = 0;
void text1(){
System.out.println("外部类成员方法");
}
void text2(){
class Inner{
int n = 1;
void show(){
System.out.println("内部类成员方法,外部类变量m="+m);
}
}
Inner i = new Inner();
i.show();
}
}
在测试类中,仅定义一个外部类对象即可,用外部类对象来调用包含内部类的方法,即可实现内部类中的成员变量和成员方法。
运行成功,局部内部类成功调用了外部类中的变量
我们在外部类的其他方法中,创建内部类对象来调用show方法
这时编译器报错。 说明text2方法的外部不能定义内部类对象
局部内部类可以访问外部类的所有成员,但只在包含内部类的成员方法中,才可以访问内部类的所有成员。
3.静态内部类
静态内部类:静态内部类很简单,就是被static修饰的成员内部类
我们将上面的局部内部类稍作修改,把内部类的前面用 static 修饰。
这时我们发现,代码的第11行报错了。因为静态内部类只能访问外部类中被static修饰的变量
将变量int m也用static修饰
编译器不再报错
写一个测试类来测试一下运行结果
运行成功,调用了外部类中的静态成员。
静态内部类在创建内部类对象时,可直接跳过外部类对象
外部类名.静态内部类名 变量名 = new 外部类名.静态内部类名();
4.匿名内部类
匿名内部类:匿名内部类是局部内部类的一种。
格式:
new 类名或接口名(){
//匿名内部类
};
本质:
匿名内部类以new开头,其本质是一个继承了该类或实现了该接口的子类匿名对象。
下面我们就用代码来演示:
匿名内部类是一个继承了父类或实现了接口的子类匿名对象,那么我们就要先来定义一个父类或接口。我们在这里来定义一个接口,在接口中定义一个show方法
public interface Inter {
void show();
}
匿名内部类是局部内部类的一种,那么就要在成员方法内部来写成员内部类。我们来定义一个外部类,在外部类的成员方法中,定义匿名内部类。
public class Outer {
public void text(){
new Inter(){
@Override
public void show(){
System.out.println("匿名内部类重写的show方法");
}
};
}
}
我们再来写一个测试类,调用外部类中的Text方法,看看匿名内部类中的show方法会不会运行。
结果直接输出 运行结束,并没有调用匿名内部类中的show方法。
这是为什么呢?匿名内部类的本质是一个匿名对象,是一个没有名字的对象。我们在text方法中定义了一个匿名内部类,却并没有去调用它里面的show方法。
问题又来了,既然匿名内部类是一个没有名字的对象,那么我们就无法通过 对象名.方法名 的方式来调用匿名内部类中的方法。那我们应当如何来调用匿名内部类中的方法呢?这还要回到匿名内部类的本质,它是一个对象。那么我们是否可以通过 对象.方法名 ,来调用对象中的方法。我们来试验一下。
在匿名内部类的花括号外,紧跟一个.show();
编译器并没有报错,我们回到测试类中,不做任何修改,测试一下
show方法成功运行。
当然,我们也可以声明一个对象名,将对象赋值给它,在通过方法名调用方法,这样也是可以的。
内部类在开发中的应用是怎样的呢?
我们用代码来演示
public class Animal {
public void eat(InterEat i){
i.eat();
}
}
public interface InterEat {
void eat();
}
定义一个Animal类,Animal中有一个eat方法,eat方法需要传入一个接口类型的对象。我们要在Demo类中调用这个eat方法。根据我们之前学过的知识,要先创建一个接口实现类,实现接口
public class Cat implements InterEat{
@Override
public void eat(){
System.out.println("猫吃鱼");
}
}
在Dome类中创建实现类对象,即可调用eat方法。
如果我还想加一个狗的eat方法,那么就要创建一个狗类,让狗类实现接口类,再在Dome类中创建一个狗类对象,再将狗类对象的参数传入Animal类中的eat方法。这样做太麻烦。我们就可以用匿名内部类来简化
public class Dome {
public static void main(String[] args) {
Animal a = new Animal();
a.eat(new InterEat(){
@Override
public void eat(){
System.out.println("猫吃鱼");
}
});
}
}
运行成功
我们直接给eat方法传入一个匿名内部类。把匿名内部类放入到括号中。这样一来,我们就可以直接省去创建类、创建类的对象、传参的过程。
如果我们还想运行一个狗的eat方法,我们不需要再创建狗类,直接给eat方法传入一个内部类即可。
运行成功