Java 语言中的嵌入类,内部类,局部类,匿名类

文章目录

在代码中使用嵌入类,能增强代码的封装性和可读性,让代码更简洁,有效。

1. 嵌入类,内部类 ,外部类

嵌入类

在 Java 中,允许在一个类中定义另一个类,称之为嵌入类,其中 non-static 嵌入类又称为内部类(inner class)。外部类也可以成为包裹类。

class OuterClass{
    // outer class
    static class StaticNestedClass{
        // static nested class
    }
    class NestedClass{
        // inner class
    }
}

Static nested class

  • 静态嵌入类不可以访问外部类的实例成员(instance variables or methods);
  • 通过外部类名来引用,并实例化;
OuterClass.StaticNestedClass nestedObject = new OuterClass.StaticNestedClass();

Inner class 本质上就是外部类的实例成员,因此可以访问 Outer Class 实例(instance)的所有成员,包括私有成员。

要创建 Inner class 的实例,必须先创建一个包裹类的实例,也正因如此,内部类不能定义静态成员(不然,该怎么访问呢?):

OuterClass outerObject = new OuterClass();
OuterClass.InnerClass innerObject = outerObject.new InnerClass();

2. 变量的可见性

当具有相同名称的变量或方法出现在包裹类和内部类时,内部类的声明会覆盖掉包裹类的声明,这时将不能直接通过名称来访问外部类中被覆盖掉的成员,要使用详细的访问路径才行。

示例

public class ShadowTest {
    public int x = 0;
    class InnerClass {
        public int x = 1;

        void methodInInnerClass(int x) {
            System.out.println("x = " + x);             // input parameter: x
            System.out.println("this.x = " + this.x);   // innerClass.x
            System.out.println("ShadowTest.this.x = " + ShadowTest.this.x); // shadowTest.x
        }
    }

    public static void main(String... args) {
        ShadowTest st = new ShadowTest();
        ShadowTest.FirstLevel fl = st.new InnerClass(); // 内部类对象的实例化,注意用法
        fl.methodInInnerClass(23);
    }
}

特别注意!
在内部类调用外部类的实例变量,可以通过 OuterClass.this 进行访问,类似于命名空间。

3. 局部类

局部类(Local Classes)是定义在代码块(Block)中的类,可以理解为局部类(对应局部变量)。

Java 中的 Block 是指由 { } 所包裹的代码:

class BlockDemo {
     public static void main(String[] args) {
          boolean condition = true;
          if (condition) { // begin block 1
               System.out.println("Condition is true.");
          } // end block one
          else { // begin block 2
               System.out.println("Condition is false.");
          } // end block 2
     }
}

在编程语言中,代码块与变量作用域有很大的关系,但不同语言之间,有很大的差别。比如 JavaScript、Python 允许函数嵌套,可以产生嵌套的函数作用域——闭包;而 Java 中的方法是不能嵌套的,但是类和代码块可以嵌套,并产生嵌套作用域。

JavaScript 中的 { } 并不产生独立的作用域,与 c/c++/java 不同:

if (1){var xxx=111}
console.log(xxx) // 111

Java 则可以在 { } 中定义局部类和局部变量,比如在方法,循环语句,条件语句等的 { } 中:

public class OuterClass { 
    public static void method(String arg1, String arg2) {
        final int localVariable = 10;      
        class LocalClass {
            String localNum = null;
            LocalClass(String num){
                localNum = num;
            }

            public String getNumber() {
                return localNum;
            }
        }

        LocalClass myNumber1 = new LocalClass(arg1);
        LocalClass myNumber2 = new LocalClass(arg2);

        System.out.println("First number is " + myNumber1.getNumber());
        System.out.println("Second number is " + myNumber2.getNumber());
    }

    public static void main(String... args) {
        method("123-456-7890", "456-7890");
    }
}

局部类只能访问 final or effectively final 的外部变量,否则编译器会报错:local variables referenced from an inner class must be final or effectively final

4. 匿名类

匿名类(anonymous class)让代码更简洁,它没有名称,在声明的同时实例化。当局部类只使用一次时,可以考虑使用匿名类。

public class HelloWorldAnonymousClasses {
    interface HelloWorld {
        public void greet();
    }
  
    public void sayHello() {        
        HelloWorld frenchGreeting = new HelloWorld() {
            public void greet() {
                greetSomeone("tout le monde");
            }
        };
        
        frenchGreeting.greet();
    }

    public static void main(String... args) {
        HelloWorldAnonymousClasses myApp = new HelloWorldAnonymousClasses();
        myApp.sayHello();
    }            
}

5. 避免内部类的序列化

内部类、局部类和匿名类尽量避免进行序列化,因为编译期在编译这些特殊的结构时,会创建 synthetic constructs,包括 classses,methods,fields 等等,它们对应的字节码是由 Java 编译器在编译过程中生成的,不同的编译器创建的 synthetic constructs 很可能是不同的,因此在反序列化时会有兼容性问题。

比如,inner class 源码如下:

public class OuterClass {
    public class InnerClass { }
}

InnerClass 的构造函数是隐式声明(implicitly declared)的,但是它的构造函数需要包含特定的参数,当 Java 编译器在编译 InnerClass 时,就会生成相应的构造函数,大致如下:

public class OuterClass {
    public class InnerClass {
        final OuterClass parent;
        InnerClass(final OuterClass this$0) {
            parent = this$0; 
        }
    }
}

Java 编程语言在语法上允许变量名中包含 $,但是按照惯例,编码时尽量不要这样用。

synthetic 指的是那些由编译器生成,但在代码中既没有隐式声明,也没有显式声明的结构,比如:

public class OuterClass {
    enum Colors {
        RED, WHITE;
    }
}

在编译时,编译器会生成类似于下面的代码:

final class Colors extends java.lang.Enum<Colors> { 
    public final static Colors RED = new Colors("RED", 0);
    public final static Colors BLUE = new Colors("WHITE", 1);
 
    private final static values = new Colors[]{ RED, BLUE };
 
    
    private Colors(String name, int ordinal) {  // the formal parameters (name, ordinal) are synthetic.
        super(name, ordinal);
    }
 
    public static Colors[] values(){ // implicitly declared
        return values;
    }
 
    public static Colors valueOf(String name){ // implicitly declared
        return (Colors)java.lang.Enum.valueOf(Colors.class, name);
    }
}

synthetic constructs 跟编译器的行为有关,不同编译器间不保证兼容。

    原文作者:墨城之左
    原文地址: https://blog.csdn.net/antony1776/article/details/84968542
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞