1. 程式人生 > >Java中內部類和靜態內部類的區別

Java中內部類和靜態內部類的區別

內部類和靜態內部類

示例

public class OuterClass {
    private int numPrivate = 1;
    public int numPublic = 2;
    public static int numPublicStatic = 3;
    private static int numPrivateStatic = 4;

    public void nonStaticPublicMethod(){
        System.out.println("using nonStaticPublicMethod");
    }

    private void nonStaticPrivateMethod(){
        System.out.println("using nonStaticPrivateMethod");
    }

    public static void staticPublicMethod(){
        System.out.println("using staticPublicMethod");
    }

    private static void staticPrivateMethod(){
        System.out.println("using staticPrivateMethod");
    }

    class InnerClass{

        //Inner class cannot have static declarations
        //static int numInnerClass = 4;
        //public static void test(){}

        int numNonStaticInnerClass = 5;

        public void print(){
            System.out.println("using InnerClass");
            System.out.println("access private field: "+numPrivate);
            System.out.println("access public field: "+numPublic);
            System.out.println("access public static field: "+numPublicStatic);
            System.out.println("access private static field: "+numPrivateStatic);
            System.out.println("access numNonStaticInnerClass: "+numNonStaticInnerClass);
            nonStaticPrivateMethod();
            nonStaticPublicMethod();
            staticPrivateMethod();
            staticPublicMethod();
        }
    }

    static class StaticNestedClass{

        static int numStaticNestedClass = 6;
        int numNonStaticNestedClass = 7;

        public void print(){
            System.out.println("using StaticNestedClass");
            System.out.println("access public static field: "+numPublicStatic);
            System.out.println("access private static field: "+numPrivateStatic);
            System.out.println("access numStaticNestedClass: "+numStaticNestedClass);
            System.out.println("access numNonStaticNestedClass: "+numNonStaticNestedClass);
            staticPrivateMethod();
            staticPublicMethod();
        }
    }

    public static void main(String[] args) {
        //內部類例項物件
        OuterClass outerClass = new OuterClass();
        OuterClass.InnerClass innerClass = outerClass.new InnerClass();
        innerClass.print();
        System.out.println("=====================");
        //靜態內部類例項化物件
        OuterClass.StaticNestedClass nestedClass = new OuterClass.StaticNestedClass();
        nestedClass.print();
    }
}

結果

using InnerClass
access private field: 1
access public field: 2
access public static field: 3
access private static field: 4
access numNonStaticInnerClass: 5
using nonStaticPrivateMethod
using nonStaticPublicMethod
using staticPrivateMethod
using staticPublicMethod
=====================
using StaticNestedClass
access public static field: 3
access private static field: 4
access numStaticNestedClass: 6
access numNonStaticNestedClass: 7
using staticPrivateMethod
using staticPublicMethod

靜態內部類使用方法

通過外部類訪問靜態內部類

OuterClass.StaticNestedClass

建立靜態內部類物件

OuterClass.StaticNestedClass nestedObject = new OuterClass.StaticNestedClass();

內部類的使用方法

必須先例項化外部類,才能例項化內部類

OuterClass outerClass = new OuterClass();
OuterClass.InnerClass innerClass = outerClass.new InnerClass();

兩者區別

  1. 內部類, 即便是私有的也能訪問,無論靜態還是非靜態都能訪問
    • 可以訪問封閉類(外部類)中所有的成員變數和方法
    • 封閉類(外部類)中的私有private成員變數和方法也可以訪問
    • 內部類中不可以有靜態的變數和靜態的方法
  2. 靜態內部類
    • 無權訪問封閉類(外部類)的中的非靜態變數或者非靜態方法
    • 封閉類(外部類)中的私有private的靜態static成員變數和方法也可以訪問
    • 靜態內部類中可以有靜態的變數和靜態的方法
  3. 內部類可以被宣告為private, public, protected, or package private. 但是封閉類(外部類)只能被宣告為public or package private

特殊情況

public class ShadowTest {

    public int x = 0;

    class FirstLevel {

        public int x = 1;

        void methodInFirstLevel(int x) {
            System.out.println("x = " + x);
            System.out.println("this.x = " + this.x);
            System.out.println("ShadowTest.this.x = " + ShadowTest.this.x);
        }
    }

    public static void main(String... args) {
        ShadowTest st = new ShadowTest();
        ShadowTest.FirstLevel fl = st.new FirstLevel();
        fl.methodInFirstLevel(23);
    }
}

輸出結果

x = 23
this.x = 1
ShadowTest.this.x = 0

結論

  1. ShadowTest類中定義了三個名字一樣的變數x

    • ShadowTest的成員變數x

    • FirstLevel內部類的成員變數x

    • methodInFirstLevel方法中的引數x

  2. methodInFirstLevel方法中的引數x在內部類FirstLevel的陰影下, 所以在方法methodInFirstLevel中使用x的時候, x指向的是方法的引數x, 此時x的結果為23

  3. 此時this指向的內部類FirstLevel的作用域, 所以this.x的結果是1

  4. ShadowTest.this指向的是ShadowTest的作用域, 此時ShadowTest.this.x的結果是1

為什麼使用內部類

  • 這是一種對僅在一個地方使用的類進行邏輯分組的方法:如果一個類僅對另一個類有用,那麼將其嵌入該類並將兩者保持在一起是合乎邏輯的。
  • 它增加了封裝:考慮兩個頂級類A和B,其中B需要訪問A的成員,如果將A的成員宣告為private則B無法訪問。通過將類B隱藏在類A中,可以將A的成員宣告為私有,而B可以訪問它們。另外,B本身可以對外界隱藏。
  • 這可能會導致程式碼更具可讀性和可維護性:在外部類中巢狀小類會使程式碼更靠近使用位置。

序列化

強烈建議不要對內部類(包括 本地和 匿名類)進行序列化。

如果序列化一個內部類,然後使用其他JRE實現對其進行反序列化,則可能會遇到相容性問題。

Serialization of inner classes, including local and anonymous classes, is strongly discouraged. When the Java compiler compiles certain constructs, such as inner classes, it creates synthetic constructs; these are classes, methods, fields, and other constructs that do not have a corresponding construct in the source code. Synthetic constructs enable Java compilers to implement new Java language features without changes to the JVM. However, synthetic constructs can vary among different Java compiler implementations, which means that .class files can vary among different implementations as well. Consequently, you may have compatibility issues if you serialize an inner class and then deserialize it with a different JRE implementation. See the section Implicit and Synthetic Parameters in the section Obtaining Names of Method Parameters for more information about the synthetic constructs generated when an inner class is compiled.