Java基础之内部类

77 阅读7分钟

Java为是强类型语言class则成为Java编程中重要的组成部分,Java类有普通类和内部类,今天这篇博客就主要说明一下Java的内部类。

Java的内部类分为以下几种:

  1. 成员内部类
  2. 局部内部类
  3. 匿名内部类
  4. 静态内部类

在Java中定义一个类很简单:

public class Car {
    //...
}

以下内容则会在此类基础上进行展开说明以上内部类。

成员内部类

通过上面的代码就可以轻松的创建一个类,这种类是在编程过程中经常书写的一种方式,一般写在.java文件中。然而内部类顾名思义从名称上可以看出。内部类处于某一个类的内部。继续沿用上面的代码,在Car类中添加一个内部类。

public class Car {

    private String name;
    
    Car(String name){
        this.name = name;
    }
    
    public class Engine {
        private String type;
        
        Engine(String type){
            this.type = type;
        }
        
        public void inof(){
            System.out.println("type is "+ type);
        }
        
    }
    
    public void info (){
        System.out.println("name is "+ name);
        Engine engine = new Engine("汽油机");
        engine.info();
    }
    
    public static void main(String [] args){
        Car car = new Car("奔驰");
        car.info();
    }

}

上述代码中在Car中定义了一个Engine的类,这个类则属于内部类。当我们需要调用内部类的时候则需要在静态类所属的类的方法中去实例化这个内部类则可以访问内部类中的方法即可。

可以看到Engine类是使用public进行修饰的,那么Engine类能否在外部被实例化呢?实际上是可以但是需要通过一些特殊的方式。定义一个Hunderd类,为了代表清晰一些,在Hunderd类中对Engine进行实例化。

public class Hunderd {

    private String name;

    public Hunderd(String name) {
        this.name = name;
    }

    public static void main(String[] args) {
        Car.Engine carEngine = new Car("奥迪").new Engine("汽油机");
        carEngine.info();
    }

}

当想要实例化Car中的Engine这个内部类的时候,需要先初始化一下Car的类之后继续使用new操作符对Engine进行二次实例化。可以看出是在Car实例上new了Engine这个类,那么既然是Car实例上的Engine类。这里需要注意的是不可以将Car实例化之后使用变量接收,通过该变量再去实例化Engine,这样是不可以的。如下:

public class Hunderd {

    private String name;

    public Hunderd(String name) {
        this.name = name;
    }

    public static void main(String[] args) {
        // 这样是不可以的,编译不通过。
        Car car = new Car("奥拓");
        Car.Engine carEngine = car.new Engine("汽油机");
        carEngine.info();
    }

}

Engine是使用public进行修饰的如果是使用private进行修饰的则这个为私有内部类,私有内部类只能在当前类中进行访问。

public class Car {

    private String name;
    
    Car(String name){
        this.name = name;
    }
    // 私有内部类。
    private class Seat {
        private byte count = 5;
        
        protected void info (){
            System.out.println(count);
        }
    }

}

如果我们想访问内部类外面类里面的属性的时候该如何访问呢?其实也会有这样的情况的,可以直接通过外部类的属性名直接访问到对应的属性或者方法。但有的时候可能会出现一种情况就是,外部类和内部类有相同的属性会怎样?如果内部类和外部类存有相同的属性的时候则会优先使用内部类,如果内部类没有则会使用外部类中对应的属性。

但是内部类和外部类存在相同的属性,并且非要使用外部类中的属性的时候该如何直接访问到外部类里面的属性呢?

public class Car {

    private String name;
    
    Car(String name){
        this.name = name;
    }
    
    public class Engine {
        private String type;
        
        private String name;
        
        Engine(String type, String name){
            this.type = type;
            this.name = name;
        }
        
        public void inof(){
            System.out.println("内部类的name是"+ name);
            System.out.println("外部类的name是"+ Car.this.name);
        }
        
    }

}

访问外部类同名属性的时候需要通过外部类.this.属性的方式访问到外部类的属性。

局部内部类

局部内部类则是定义在某一个范围之内的,更确切的说是定义在某一个方法里面的类。局部内部类是无法在任何地方访问到的,只能在当前方法中使用。

public class Car {

    public final String name;


    public Car(String name) {
        this.name = name;
    }

    //  匿名内部类
    public void sum (){
        Sum sum = new Sum() {
            public int sum(int... ints) {
                int result = 0;
                for(int num : ints){
                    result += num;
                }
                return result;
            }
        };
        int result = sum.sum(1,2,3,4,5,6,7,8,9,10);
        System.out.println("匿名内部类计算结果:" + result);
    }
}

局部内部类是定义在一个方法或者一个作用域里面的类,它和成员内部类的区别在于局部内部类的访问仅限于方法内或者该作用域内。局部内部类不可使用权限修饰符 静态修饰符进行修饰同局部变量相同,局部内部类可以直接访问方法中的属性,局部内部类可以直接访问方法外部类中属性和方法,局部内部类创建对象要在方法内部局部内部类的外部声明。

匿名内部类

匿名内部类不能定义任何静态成员、方法和类,只能创建匿名内部类的一个实例。一个匿名内部类一定是在new的后面,用其隐含实现一个接口或实现一个类。

定义一个interface名为Sum:

public interface Sum {
    int sum(int... ints);
}

接着在Car中也定义一个方法,并在这个方法中实现Sum接口对应的匿名内部类:

public class Car {

    public final String name;

    public Car(String name) {
        this.name = name;
    }

    //  匿名内部类
    public void sum (){
        Sum sum = new Sum() {
            @Override
            public int sum(int... ints) {
                int result = 0;
                for(int num : ints){
                    result += num;
                }
                return result;
            }
        };
        int result = sum.sum(1,2,3,4,5,6,7,8,9,10);
        System.out.println("匿名内部类计算结果:" + result);
    }

}

静态内部类

在类中编写的以static修饰的类称为静态内部类,静态内部类也是定义在另一个类里面的类,只不过在类的前面多了一个关键字static。静态内部类是不需要依赖于外部类的,这点和类的静态成员属性有点类似,并且它不能使用外部类的非static成员变量或者方法。静态内部类中即能声明静态成员也可以声明非静态成员。

public class Car {
    
    public static byte count = 12;

    public final String name;

    public Car(String name) {
        this.name = name;
    }


    //  公用静态内部类
    public static class Wheel {
        byte count = 4;

        public Wheel(byte count) {
            this.count = count;
        }

        public void info (){
            System.out.println("我有 "+count+" 个轮子");
        }
    }

}

与成员内部类相类似,若想访问外部类中的静态属性的时候也可以直接通过属性名直接去读取,若存在相同属性名的时候则使用外部类.属性名即可。

public class Car {
    
    public static byte count = 12;

    public final String name;

    public Car(String name) {
        this.name = name;
    }


    //  公用静态内部类
    public static class Wheel {
        byte count = 4;

        public Wheel(byte count) {
            this.count = count;
        }

        public void info (){
            System.out.println("我有 "+count+" 个轮子");
            System.out.println("我有 "+Car.count+" 个轮子");
        }
    }

}

总结

我们可以通俗按下面的方式理解:

内部类:就是我是你的一部分,我了解你,我知道你的全部,没有你就没有我。(所以内部类对象是以外部类对象存在为前提的)
静态内部类:就是我跟你没关系,自己可以完全独立存在,但是我就借你的壳用一下,来隐藏自己。

从字节码角度上看,静态内部类和非静态内部类最大的区别是:非静态内部类编译后隐式保存着外部类的引用但是静态内部类没有。

  1. 内部类方法可以访问该类定义所在的作用域中的数据,包括私有的数据。
  2. 内部类可以对同一个包中的其他类隐藏起来。

其实内部类更多的时候,不想把这个类暴露出去,它可能只是外部类的一个逻辑组成部分,不需要其他地方知道什么,这时候我们用内部类更加的清楚。