java

关注公众号 jb51net

关闭
首页 > 软件编程 > java > Java内部类

Java内部类深入解析

作者:理想万岁万万岁

这篇文章主要介绍了Java内部类深入解析,在java中,我们被允许在编写一个类(外部类OuterClass)时,在其内部再嵌套一个类(嵌套类NestedClass),java将嵌套类分为两种,非静态内部类(简称内部类)和 静态内部,需要的朋友可以参考下

一、介绍

在java中,我们被允许在编写一个类(外部类OuterClass)时,在其内部再嵌套一个类(嵌套类NestedClass),java将嵌套类分为两种:非静态内部类(简称内部类) 和 静态内部类,如下所示

public class OuterClass {
    class InnerClass {
    }
    static class StaticInnerClass {
    }
}

嵌套类作为外部类的一个成员,无论其是否为静态内部类,我们都可以对其添加任何的访问修饰符(public、protected、private、default),如下所示

public class OuterClass {
    public class InnerClass {
    }
    protected static class StaticInnerClass {
    }
}

非静态内部类对其外部类中的所有成员变量和方法都具有访问权,即便它被private修饰也可以。但是静态内部类对外部类中的成员变量和方法没有访问权。

二、为什么要使用内部类

内部类为我们提供了以下便利:

如果一个类A.java只对另一个类B.java有用,那么将它嵌入到那个类中并把两个类放在一起岂不合理?即把A.java作为内部类,而将B.java作为外部类。

考虑两个类A.java 和 B.java,其中B.java需要访问A.java的被声明为private的成员变量或方法。通过将类B.java隐藏在类A.java中,A.java的成员变量或方法可以被声明为private,B.java也可以访问它们。另外,B.java本身也可以对外界隐藏。

将内部类嵌套在外部类中使代码更接近使用它的地方。

三、非静态内部类

非静态内部类有以下特点:

当我们实例化一个非静态内部类的对象时,使用以下方法

public class OuterClass {
    public void initInnerClass() {
        InnerClass innerClass = new InnerClass();
        innerClass.innerClassMethod_1();
    }
    class InnerClass {
        public void innerClassMethod_1() {
            System.out.println("调用内部类方法");
        }
    }
}
class InnerClassTest {
    public static void main(String[] args) {
        // 方式一:创建内部类innerClass实例
        OuterClass outerClass = new OuterClass();
        OuterClass.InnerClass innerClass1 = outerClass.new InnerClass();
        innerClass1.innerClassMethod_1();
        // 方式二:创建内部类innerClass实例
        OuterClass.InnerClass innerClass2 = new OuterClass().new InnerClass();
        innerClass2.innerClassMethod_1();
    }
}

非静态内部类还有两种特殊的类型:局部内部类 和 匿名内部类。后面细说。

四、静态内部类

静态内部类在行为上与其他类相似

当我们实例化一个静态内部类的对象时,使用以下方法

在外部类中实例化内部类

public class OuterClass {
    public void initStaticInnerClass() {
        StaticInnerClass staticInnerClass = new StaticInnerClass();
        staticInnerClass.staticInnerClassMethod_1();
    }
    static class StaticInnerClass {
        public void staticInnerClassMethod_1() {
            System.out.println("调用静态内部类方法");
        }
    }
}

在其他类中实例化内部类

class InnerClassTest {
    public static void main(String[] args) {
        OuterClass.StaticInnerClass staticInnerClass = new OuterClass.StaticInnerClass();
        staticInnerClass.staticInnerClassMethod_1();
    }
}

五、局部内部类

在一个代码块声明的类叫局部内部类。

此处的代码块指任何{}内部(如静态代码块、方法、for循环、if块)

创建局部内部类的方式如下:

public class OuterClass {
    public void localClassMethod() {
        for (int i=0; i<10; i++) {
            // for循环中的局部内部类
            class LocalClassInFor {
            }
        }
        if (true) {
            // if代码块中的局部内部类
            class LocalClassInIf {
            }
        }
        // 方法中的局部内部类
        class LocalClass {
        }
    }
}

六、匿名内部类

在一个方法内部声明的类但没有命名该类的名称叫局部内部类。匿名类使您的代码更加简洁。允许我们能够同时声明和实例化一个类。除了没有名字之外,它们类似于局部内部类。如果只需要使用一次局部内部类,就使用它们。

使用匿名内部类需要借助父类 或 接口实现,其本质相同,都是对方法的重写。如下所示

使用父类

public class AnonymousOuterClass {

    class InnerClass {
        private String innerField = "field in InnerClass";
        public void getField() {
            System.out.println(innerField);
        }
    }

    public void anonymousInnerMethod() {
        // 声明并实例化匿名内部类
        InnerClass innerClass = new InnerClass() {
            private String innerField = "a";
            @Override
            public void getField() {
                // 输出匿名内部类中的成员变量innerField
                System.out.println(innerField);
                // 输出父类类中的成员变量innerField
                super.getField();
            }
        };

        innerClass.getField();
    }
}

class AnonymousOuterClassTest {
    public static void main(String[] args) {
        AnonymousOuterClass anonymousOuterClass = new AnonymousOuterClass();
        anonymousOuterClass.anonymousInnerMethod();
    }
}

使用接口

public class AnonymousOuterClass {
    interface InnerInterface {
        void getField();
    }
    public void anonymousInnerInterfaceMethod() {
        InnerInterface innerInterface = new InnerInterface() {
            private String innerField = "field in inner interface";
            @Override
            public void getField() {
                // 输出匿名内部类中的成员变量innerField
                System.out.println(innerField);
            }
        };
        innerInterface.getField();
    }
}
class AnonymousOuterClassTest {
    public static void main(String[] args) {
        AnonymousOuterClass anonymousOuterClass = new AnonymousOuterClass();
        anonymousOuterClass.anonymousInnerInterfaceMethod();
    }
}

七、lambda表达式内部类

使用lambda表达式内部类的理由很简单:当匿名内部类的父类或接口中只有一个方法时,其实现代码最少也得五六行,为了使代码简化,所以使用lambda表达式内部类。

除了实例化方式不同,lambda表达式内部类和匿名内部类其余使用限制完全一致。

public class LambdaOuterClass {
    interface InnerInterface {
        void sayHello();
    }
    public void sayHello() {
        // lambda表达式内部类
        InnerInterface innerInterface = () -> System.out.println("hello world");
        innerInterface.sayHello();
    }
}
class LambdaOuterClassTest {
    public static void main(String[] args) {
        LambdaOuterClass lambdaOuterClass = new LambdaOuterClass();
        lambdaOuterClass.sayHello();
    }
}

八、成员重名

无论是静态内部类还是非静态内部类,当内部类中与外部类具有相同名称的成员变量的情况下,当我们在内部类中使用该变量时,默认采用就近原则的方式访问该变量。如果需要访问外部类中的重名变量时,则需要通过OuterClass.this.field访问,如下所示

public class OuterClass {
    private String sameNameField = "the same name field in OuterClass";
    class InnerClass {
        private String sameNameField = "the same name field in InnerClass";
        public void testSameNameField() {
            // 访问内部类的重名变量
            System.out.println(sameNameField);
            // 访问外部类的重名变量
            System.out.println(OuterClass.this.sameNameField);
        }
    }
}

九、序列化

java强烈建议不要序列化内部类,包括局部内部类和匿名类。当Java编译器编译某些结构时,比如内部类,它创建合成结构,这些是在源代码中没有相应构造的类、方法、字段和其他构造。合成结构使Java编译器能够在不改变JVM的情况下实现新的Java语言特性。然而,合成构造在不同的Java编译器实现中可能有所不同,这意味着在不同的实现中,类文件也可能不同。因此,如果我们序列化一个内部类,然后用不同的JRE实现反序列化它,可能会有兼容性问题。

十、如何选择内部类

非静态内部类

当需要访问外部类实例的非public修饰的成员变量和方法时。

静态内部类

当不需要访问外部类实例的成员变量和方法时。

局部内部类

当我们需要创建一个类的多个实例,请访问其构造函数,或者引入一个新的命名类型(例如,因为您稍后需要调用其他方法)。

匿名内部类

当我们需要同时声明和实例化一个类,并且只需要使用一次局部内部类时。

lambda表达式内部类

到此这篇关于Java内部类深入解析的文章就介绍到这了,更多相关Java内部类内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

您可能感兴趣的文章:
阅读全文