Loading...
墨滴

代码界的小白

2021/11/07  阅读:30  主题:前端之巅同款

Java中的内部类,类似俄罗斯套娃?

内部类

见名思意,内部类就是在一个类的内部中又嵌套了一个类,内部类根据不同的定义又可以分为成员内部类、静态内部类、局部内部类和匿名内部类。

不知道大家有没有思考过这个问题,为什么不在把内部类分离出来,单独一个新类呢?

其实引入内部类有它的原因所在的,大概有以下几点原因:

  • 内部类方法可以访问该类定义所在的作用域中的数据,包括私有数据。
  • 内部类可以对同一个包中的其他类有隐藏的作用。
  • 当想要定义一个回调函数且不想编写大量代码的时候,可以使用匿名内部类比较便捷。

成员内部类

定义在类内部的非静态类叫做成员内部类,成员内部类不能定义静态方法和变量(final修饰的除外),因为成员内部类是非静态的,而在Java的非静态代码块中不能定义静态方法和变量。

下面举个例子进行分析,

public class MemberClass {
    public static void main(String[] args) {
        OutClass outClass = new OutClass(10,true);
        outClass.test();
    }
}

class OutClass{
    private int high;
    private boolean flag;

    //有参构造方法
    public OutClass(int high, boolean flag) {
        this.high = high;
        this.flag = flag;
    }
    public void test(){
        MemberInnerClass memberInnerClass = new MemberInnerClass();
        memberInnerClass.print();
    }

    public  class MemberInnerClass{
        public void print(){
            //本类中并没有falg和high变量,但是可以访问创建本类的外围类的对象的数据域
            if(flag == true){
                System.out.println("我是内部类,我的高度是:"+high);
            }
        }
    }
}

可以从上面的代码中看到,falg和high都是OutClass这个外围类的数据,并且都是private的,但是成员内部类MemberInnerClass却可以访问,原因是内部类的对象总是有一个隐式引用,它指向了创建它的外部类对象。这个引用在内部类的定义中是不可见的。

因为MemberInnerClass这个内部类没有定义构造器,所以编译器为这个类生成一个默认 的构造器。

public MemberInnerClass(OutClass out){
    outClass = out;
}

其中outClass是外围类对象的引用。

public  class MemberInnerClass{
    public void print(){
        //本类中并没有falg和high变量,但是可以访问创建本类的外围类的对象的数据域   真实的flag其实是outClass.flag
        if(outClasss.flag == true){
            System.out.println("我是内部类,我的高度是:"+outClasss.high);
        }
    }
}

看到这里仿佛感觉成员内部类没什么实质性的作用吧,其实还是有一定的作用的

  1. 成员内部类可以无条件访问外部类的所有成员属性和成员方法(包括静态成员和私有成员)。
  2. 成员内部类和外部类的属性和方法名同名时,外部类的属性和方法会隐藏;但可以通过外部类.this.成员变量的方式访问外部类的属性和方法
  3. 外部类必须通过成员内部类的对象来访问内部类的属性和方法。
  4. 成员内部类对象会隐式的引用一个外部类对象。(可以解释第一点)
  5. 成员内部类可以有public\private\protected以及默认访问权限。

静态内部类

使用成员内部类只是为了把一个类隐藏在另外一个类的内部,并不需要内部类引用外围类对象。因此可以吧内部类声明为static,以便取消这种引用。

静态内部类是定义在类中的静态类!静态内部类可以访问外部类的静态变量和方法;静态内部类中可以定义静态变量、方法、构造函数等;静态内部类通过“外部类.静态内部类”的方式来调用。

下面是静态内部类最典型的一个例子,求数组中的最大值和最小值问题。当然我们可以编写两个方法,分别求最大和最小值,这样的话就需要遍历两次数组,效率自然没有遍历一次快。然而遍历一次同时返回最大和最小值,正常的返回只能返回一个值,所以这里返回一个包含两个值的对象Pair。

package Foundation;

public class StaticClass {
    public static void main(String[] args) {
        long startTime = System.currentTimeMillis();    //获取开始时间
        double []d = new double[200000];
        for (int i=0;i<d.length;i++){
            d[i] = 100 * Math.random();
        }

        //使用静态内部类的时间
/*        StaticClassDemo.Pair p = StaticClassDemo.Pair.MinMax(d);
        System.out.println("min = "+p.getFirst());
        System.out.println("max = "+p.getSecond());

        long endTime = System.currentTimeMillis();    //获取结束时间
        System.out.println("使用静态内部类的程序运行时间:" + (endTime - startTime) + "ms");    //输出程序运行时间*/



        //不适用静态内部类的时间
        System.out.println("min = "+getMin(d));
        System.out.println("max = "+getMax(d));
        long endTime = System.currentTimeMillis();    //获取结束时间
        System.out.println("不使用静态内部类的程序运行时间:" + (endTime - startTime) + "ms");    //输出程序运行时间
    }
    public static double getMin(double []value){
        double min = Double.POSITIVE_INFINITY;
        for (double v : value) {
            if(v<min){
                min = v;
            }
        }
        return min;
    }
    public static double getMax(double []value){
        double max = Double.NEGATIVE_INFINITY;
        for (double v : value) {
            if(v>max){
                max = v;
            }
        }
        return max;
    }

}
class StaticClassDemo{
    public static class Pair{
        private double first;
        private double second;
        //有参构造方法
        public Pair(double f,double s){
            first = f;
            second = s;
        }
        //返回值
        public double getFirst(){
            return first;
        }
        public double getSecond(){
            return second;
        }

        //通过遍历一次数组 找出最大和最小值
        public static Pair MinMax(double []value){
            double min = Double.POSITIVE_INFINITY;
            double max = Double.NEGATIVE_INFINITY;
            for (double num:value){
                if(min>num) min = num;
                if(max<num) max = num;
            }
            return new Pair(min,max);
        }

    }
}

输出:
min = 5.302138580631777E-4
max = 99.99903047200087
使用静态内部类的程序运行时间:11ms
    
min = 4.843133101894992E-4
max = 99.99957863430913
不使用静态内部类的程序运行时间:17ms

静态内部类除了没有对生成它的外围类对象的引用特权外,与其他的内部类完全一样!

局部内部类

局部内部类是指定义在方法中的类。

public class PartClass {
    private static int a;
    private int b;
    public void partClassTest(final int c){
        final int d = 1;
        //在partClassTest 方法中定义一个局部内部类PastClass
        class PastClass{
            public void print(){
                System.out.println(c);
            }
        }
    }
}

局部类不能用public或者private访问说明符进行声明。它的作用域被限定在声明这个局部类的块中。

匿名内部类

之前Java程序员习惯使用匿名内部类实现事件监听器和其他回调。如今大家都使用lambda表达式了,关于lambda表达式后序在进行详细的讲解吧。

匿名内部类指:通过继承一个父类或实现一个接口的方式直接定义并使用的类。匿名内部类没有class关键字,这是因为匿名内部类直接使用new生成一个对象的引用。

//定义一个抽象类Worker
abstract class Worker{
    private String name;
    public String getName(){
        return name;
    }
    public void setName(String name){
        this.name = name;
    }
    public abstract int workTime();
}

public class AnonymousClass {
    public void test(Worker worker){
        System.out.println(worker.getName() + "工作时间:"+worker.workTime());
    }

    public static void main(String[] args) {
        AnonymousClass anonymousClass = new AnonymousClass();
        //在方法中定义并使用匿名内部类
        anonymousClass.test(new Worker(){
            public int workTime(){
                return 8;
            }
            public String getName(){
                return "Test";
            }
        });
    }
}

在上面的代码中定义了一个抽象类Worker和一个抽象方法workTime,然后定义了一个AnonymousClass类,在AnonymousClass类中定义了一个方法,该方法接收一个Worker参数,这时匿名类需要的准备工作都已经准备好。在需要一个根据不同场景有不同的实现的匿名内部类时,直接在test方法中新建匿名内部类并重写相关方法即可!

匿名内部类的特点:

1、唯一一种没有构造器的类;匿名内部类在编译时,编译器会自动起名xxx$1.class;

2、 匿名内部类不能存在任何静态的变量、方法等;

3、 匿名内部类是局部内部类的特例;

4、大部分匿名内部类用于接口返回;

通过上面的介绍,想必大家对内部类应该有了一个比较初步的认识吧,当然仅凭该文章可能还不能让大家更加深度的理解,不过当你后期去看源代码的时候,或许就会发现有些地方用到了内部类哦!

面试问题:

内部类你知道有哪些?

说说他们之间的区别?

这块面试的时候可能不太会被问到,但也不一定,我当时美团面试的时候就被问到了,让我说其中两个内部类的区别。

推荐阅读

Java学习资料-电子版

Java面试宝典《阿里调优手册》,你值得拥有!

面试高频问题:Java反射你了解多少?

面试高频手撕代码:写个快排的代码吧。

Java中的基本数据类型有哪些?

Java中的String类型你真的掌握了吗?

代码界的小白

2021/11/07  阅读:30  主题:前端之巅同款

作者介绍

代码界的小白

公众号:代码界的小白