Java内部类

在Java中可以将一个类定义在另一个类或者方法的内部,前者称为内部类,后者称为外部类。内部类也是封装的一种体现。

public class OutClass {
	class InnerClass{ 
	
	} 
}
// OutClass是外部类 
// InnerClass是内部类
public class Out {
    private int a;
    int b;
    
    public void fun(){
        class Inner{
            String name = "内部类";
        }
    }
}

注意:内部类和外部类虽然共用一个java源文件,但是经过编译后,内部类会形成单独的字节码文件:
在这里插入图片描述

1 实例内部类

实例内部类对比于静态内部类,就是不加static的成员内部类。

public class Out {
    int a = 1;
    int b = 2;
    public int c = 3;

    public void fun(){
        System.out.println("这是外部类");
    }

    class Inner{
        int a = 11;
        int b = 22;
        public int c = 33;

        public void fun(){
            System.out.println("内部类");
        }
    }
}

以上就是一个实例内部类的例子,那么我们要怎么实例化这个实例内部类呢?

Out out = new Out();
Out.Inner inner = out.new Inner();
//先实例化外部类 用过外部类引用实例化内部类
Out.Inner inner1 = new Out().new Inner();//直接new

思考一下在内部类中的字段是否能被static修饰呢?
答案是不可以 直接编译不通过 :
在这里插入图片描述
原因很简单 被static修饰后 成员将伴随类的加载而生成 但是要访问实例内部类中成员 必须要创建实例内部类的对象 所以就编译失败。当我们偏偏要想用static修饰的时候 该怎么办?我们可以用static final 修饰 public static final int A = 11; 此时A变量定义成一个常量。

现在我们的内部类声明好了 在内部类当中怎么访问外部类和自己的成员变量呢?

public class Out {
    int a = 1;
    int b = 2;
    public int c = 3;

    public void fun(){
        System.out.println("这是外部类");
    }

    class Inner{
        public static final int d = 11;
        int e = 22;
        public int f = 33;

        public void fun(){
            System.out.println("内部类");
            //在内部类中可以直接访问 外部类的成员
            System.out.println(a);
            System.out.println(b);
            System.out.println(c);
            System.out.println(d);
            System.out.println(e);
            System.out.println(f);
        }
    }
}

思考:当外部类的成员变量名和内部类的相同时访问情况是什么?

public class Out {
    int a = 1;
    int b = 2;
    public int c = 3;

    public void fun(){
        System.out.println("这是外部类");
    }

    class Inner{
        public static final int a = 11;
        int b = 22;
        public int c = 33;

        public void fun(){
            System.out.println("内部类");
            System.out.println(a);
            System.out.println(b);
            System.out.println(c);

        }
    }
}

此时会按照就近原则访问到的是我们的内部类的成员。那我们想要访问外部类的成员该怎么办? System.out.println(Out.this.a);
获取到外部类的this即可。

注意事项:

  1. 外部类中的任何成员都可以在实例内部类方法中直接访问
  2. 实例内部类所处的位置与外部类成员位置相同,因此也受public、private等访问限定符的约束
  3. 在实例内部类方法中访问同名的成员时,优先访问自己的,如果要访问外部类同名的成员,必须:外部类名
    称.this.同名成员 来访问
  4. 实例内部类对象必须在先有外部类对象前提下才能创建
  5. 实例内部类的非静态方法中包含了一个指向外部类对象的引用
  6. 外部类中,不能直接访问实例内部类中的成员,如果要访问必须先要创建内部类的对象。

2 静态内部类

被static修饰的内部成员类称为静态内部类。
那么静态内部类该怎么实例化呢?

Out.InnerClass innerClass = new Out.InnerClass();

在内部类当中怎么访问外部类的成员变量呢?

public class Out {
    private int a;
    static int b;

    public void methodA() {
        a = 10;
        System.out.println(a);
    }

    public static void methodB() {
        System.out.println(b);
    }

    // 静态内部类:被static修饰的成员内部类
    static class InnerClass {
        public void methodInner() { // 在内部类中只能访问外部类的静态成员
            a = 100; // 编译失败,因为a不是类成员变量
            b =200; 
            methodA(); //编译失败,因为methodA()不是类成员方法
            methodB();
        }
    }
    
}

注意:

  1. 在静态内部类中只能直接访问外部类中的静态成员
    如果确实想访问,我们可以通过通过外部类的引用(先实例化外部类)
  2. 创建静态内部类对象时,不需要先创建外部类对象
  3. 静态成员的应用就直接用类名 不能再乱加this 语法不允许

3 局部内部类

局部内部类就是定义在在外部类的方法体或者{}中,该种内部类只能在其定义的位置使用,一般使用的非常少。

public class OutClass {
    int a = 10;

    public void method() {
        int b = 10; // 局部内部类:定义在方法体内部 不能被public、static等访问限定符修饰
        class InnerClass {
            public void methodInnerClass() {
                System.out.println(a);
                System.out.println(b);
            }
        }
        
        // 只能在该方法体内部使用,其他位置都不能用
        InnerClass innerClass = new InnerClass();
        innerClass.methodInnerClass();
    }

    public static void main(String[] args) {
        // OutClass.InnerClass innerClass = null; 编译失败 
    }
}

注意:

  1. 局部内部类只能在所定义的方法体内部使用
  2. 不能被public、static等修饰符修饰
  3. 编译器也有自己独立的字节码文件,命名格式:外部类名字$内部类名字.class

4 匿名内部类

匿名内部类,就是没有名字的一种嵌套类。它是Java对类的定义方式之一。

我们为什么要使用匿名内部类呢?难道就是一个语法?一个花架子吗?当然不是了。

在实际开发中,我们常常遇到这样的情况:一个接口/类的方法的某个实现方式在程序中只会执行一次,但为了使用它,我们需要创建它的实现类/子类去实现/重写。此时可以使用匿名内部类的方式,可以无需创建新的类,减少代码冗余。

现在有一个IRun接口 接口中有一个run方法


public interface IRun {
    public void run();
}

我们现在如果想使用这个接口 就必须创建一个类并且实现IRun接口 重写里面的run方法

public class IRunImpl implements IRun{
    @Override
    public void run() {
        System.out.println("IRunImpl 实现了 IRun接口...");
    }
}

然后实例化实现接口的类即可使用接口

public class Main {
    public static void main(String[] args) {
        IRunImpl iRun = new IRunImpl();
        iRun.run();
    }
}

但是如果实现类IRunImpl全程只使用一次,那么为了这一次的使用去创建一个类,未免太过麻烦。我们需要一个方式来帮助我们摆脱这个困境。匿名内部类则可以很好的解决这个问题。

我们使用匿名内部类对上面的例子进行优化

public class Main {
    public static void main(String[] args) {
        IRun iRun = new IRun(){
            @Override
            public void run() {
                System.out.println("IRunImpl 实现了 IRun接口...利用了匿名内部类");
            }
        };
        iRun.run();//利用接口的引用调用接口方法
    }
}

或者匿名内部类直接作为参数

public interface Bell {
    public void ring();
}

public class Cellphone {
    public void alarmcolck(Bell bell){
        bell.ring();
    }
}

/**
 * 匿名内部类直接作为参数
 */
public class Test {
    public static void main(String[] args) {
        Cellphone cellphone = new Cellphone();
        cellphone.alarmcolck(new Bell(){
            @Override
            public void ring() {
                System.out.println("懒猪起床了...");
            }
        });
        cellphone.alarmcolck(new Bell(){
            @Override
            public void ring() {
                System.out.println("小伙上课了...");
            }
        });
    }
}

通常,我们也习惯用这样的方式创建并启动线程

new Thread(new Runnable() {
    @Override
    public void run() {
        System.out.println("我是一个线程");
    }
}).start();

通过上面例子我们大概了解了 匿名内部类的使用方式和场景

注意:

  1. 匿名内部类,在【创建对象】的时候,只能使用唯一一次。
    如果希望多次创建对象,而且类的内容一样的话,那么就需要使用单独定义的实现类了。
  2. 匿名对象,在【调用方法】的时候,只能调用唯一一次。
    如果希望同一个对象,调用多次方法,那么必须给对象起个名字。
  3. 匿名内部类是省略了【实现类/子类名称】,但是匿名对象是省略了【对象名称】
    强调:匿名内部类和匿名对象不是一回事!!!
  • 5
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 5
    评论
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

小小小小关同学

你的支持就是我的动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值