内部类
1. 概述
内部类即在一个类中声明的其他Java类,这些类也称之为嵌套类,内属类;当一个类(B)只为另一个类(A)提供服务,其他类(CDEF…)不可能使用到该类(B)时,此时没有必要单独创建一个java文件来存储这个类(B),因此可以将类(B)在另一个类(A)中直接声明,减少不必要的文件创建。
Java中内部类的种类包含以下四种:
- 成员内部类
- 局部内部类
- 静态内部类
- 匿名内部类
2. 成员内部类
成员内部类,顾名思义,类似成员变量,成员方法;即直接在外部类结构中定义的一个java类,与成员变量和成员方法同一级别,例如:
public class Outter {
private String name1;
public void outerTest(){
System.out.println("外部类的普通方法");
Inner i = this.new Inner();
i.innerTest();
}
//成员内部类
private class Inner{
private String name2;
public void innerTest(){
System.out.println("内部类的测试方法");
System.out.println(Outter.this);
}
}
public static void main(String[] args) {
Outter ot = new Outter();
//创建内部类的实例必须通过外部类的对象创建
Outter.Inner i = ot.new Inner();
i.innerTest();
}
}
注意事项:
- 成员内部类与成员变量,成员方法同一级别
- 创建成员内部类需要由外部类的对象完成
- 可以在内部类中直接访问外部类的成员
- 多数时候的成员内部类都是
private
3. 局部内部类
局部内部类类似局部变量,一般在一个方法或者语句块中出现,适用范围也只有当前声明区域,若一个类只为一个方法或者一个语句块提供服务,此时没有必要声明全局内部类,更无必要创建一个单独java文件存储,因此,可以将该内部类声明到方法或者语句块中:
public class LocalOuter {
private String name;
public void testOutter(){
int i = 100;
//创建局部内部类
class InnerClass{
private int n = 10;
public void testInner(){
System.out.println(n);
//编译错误,i是方法的局部变量,只能使用不能修改(final)
//i = 1000;
System.out.println(i);
}
}
//由于局部内部类在方法内部创建,因此使用范围也只能是方法内部
InnerClass ic = new InnerClass();
ic.testInner();
}
public static void main(String[] args) {
LocalOuter lo = new LocalOuter();
lo.testOutter();
}
}
注意事项:
- 局部内部类不能使用任何的访问修饰符修饰
- 局部内部类中可以使用方法或者语句块中声明局部变量,但是只能用,不能修改(该变量会自动标记为
final
,在java8以前需要手动声明)
4. 静态内部类
静态内部类与静态变量,静态方法的加载顺序,静态内部类理解为使用static
修饰的成员内部类;
public class StaticOutter {
private String name;
public void testOutter(){
System.out.println("外部类方法。。。");
new InnerClass().testInner();
}
static class InnerClass{
private int age;
public void testInner(){
System.out.println("静态内部类的方法");
}
}
public static void main(String[] args) {
//创建静态内部类对象
StaticOutter.InnerClass si = new StaticOutter.InnerClass();
}
}
注意事项:
- 静态内部类一般用于在一个类中缓存数据,参考
java.lang.Integer
类中IntegerCache
类
5. 匿名内部类(重要)
匿名内部类即没有类名名称的内部类,为什么需要匿名内部类:对于一些内部类只需要执行一次(例如:局部内部类),但是根据类的定义方式有需要设置类名称,并根据类名称创建对象,然后执行类中的方法,等于说做了一些不要的事情(类名,创建对象),所以针对以上问题才有了匿名内部类一说。
大多数时候,匿名内部类一般针对抽象类和接口使用。
创建语法:
public class TestInnerClass {
public void t(){
}
public static void main(String[] args) {
//对接口创建匿名内部类对象
Flyable f = new Flyable(){
@Override
public void fly(){
System.out.println("fly....");
}
};
f.fly();
//对普通类创建匿名内部类对象
User u = new User(){
@Override
public void sayHi() {
System.out.println("hello,laotie!!!");
}
};
u.sayHi();
}
}
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-2fBLNW0i-1626698218574)(assets/image-20210717133210695.png)]
注意事项
- 匿名内部类即没有名字的内部类
- 创建匿名内部类时就是在创建匿名内部类的对象
- 匿名内部类没有构造器,可以使用
初始化块
实现构造器的功能
6. 内部类访问当前对象问题
之前有学习过this
关键字,this
表示调用当前成员的对象;但是如果在内部类中直接使用this
,此时的this
表示内部类的当前对象,如果需要访问外部类的this
,则应该使用外部类的类名称.this
,例如:
public class Outter {
public void outerTest(){
System.out.println("外部类的普通方法");
}
//成员内部类
private class Inner{
public void innerTest(){
System.out.println("内部类的测试方法");
//访问外部类的当前对象
System.out.println(Outter.this);
//访问当前内部类的对象
System.out.println(this);
}
}
}
7. 内部类使用场景
public class Button {
private String name;
public Button(String name) {
this.name = name;
}
/**按钮被点击时执行*/
public void onClick(ClickListener c){
c.click();
}
interface ClickListener{
void click();
}
}
public class TestButton {
public static void main(String[] args) {
Button btn = new Button("发送");
//使用匿名内部类对象
btn.onClick(new Button.ClickListener() {
//回调函数(钩子函数)
@Override
public void click() {
System.out.println("发送成功");
}
});
}
}
8. 内部类的好处
- 内部类提供了比外方法更好封装性
- 使用内部类可以解决外部类只能单继承的问题(内部类也能继承其他类)