美文网首页
类、静态内部类与内部类、局部类

类、静态内部类与内部类、局部类

作者: hdychi | 来源:发表于2018-07-17 15:48 被阅读0次

    一、使用上对比

    1、声明区别

    (1)只有内部类才能声明为静态,外部类不行、局部类也不行。

    屏幕快照 2018-07-17 下午3.40.01.png

    (2) 变量声明

    外部类既可以声明静态变量、方法,也可以声明普通变量、方法。内部类不能声明静态变量和静态方法(不能有静态声明)。静态内部类中可以生成静态成员和非静态成员。

    屏幕快照 2018-07-17 下午3.54.05.png 屏幕快照 2018-07-17 下午3.54.28.png

    2、互相使用变量、调用方法

    (1)外部类使用其他类

    外部类可以声明私有内部类对象、调用私有内部类对象的私有变量、方法。

    外部类可以声明私有静态内部类对象、使用私有静态内部类的私有非静态变量、私有静态变量。如下所示:

    package com.meituan.huangdanyang;
    
    public class OutsideClass {
        private int outProperty;
        private static int staticOutProperty;
    
        public OutsideClass() {
            System.out.println("Outside class init");
        }
        public void innerClassTest(){
            InnerClass innerClass = new InnerClass();
            System.out.println(innerClass.innerProperty);
            System.out.println(StaticInnerClass.staticInnerProperty);
            System.out.println(new StaticInnerClass().innerProperty);
        }
    
        private void method() {
            class localClass {
                private int innerProperty;
    
            }
        }
    
        private static void staticMethod() {
    
        }
    
        private class InnerClass {
            private int innerProperty = 1;
    
        }
    
        private static class StaticInnerClass {
            private int innerProperty = 2;
            private static int staticInnerProperty = 3;
        }
    }
    
    

    调用innerClassTest运行结果如下:

    屏幕快照 2018-07-17 下午4.19.59.png

    (2)内部类使用外部类、静态内部类

    内部类可以直接使用外部类的静态非静态对象、方法。也能调用同一外部类下的私有静态内部类的私有静态变量、私有静态内部类对象的私有非静态变量。

    代码示例:

     private class InnerClass {
            private int innerProperty = 1;
            private void test(){
                System.out.println(outProperty);
                System.out.println(staticOutProperty);
                method();
                System.out.println(StaticInnerClass.staticInnerProperty);
                System.out.println(new StaticInnerClass().innerProperty);
            }
        }
    

    (3)静态内部类使用外部类、非静态内部类

    静态内部类不能调用外部类的非静态变量和非静态方法,同理也不能声明内部类的对象。

    屏幕快照 2018-07-17 下午4.34.14.png

    (4)三种类的声明

    私有静态内部类和私有内部类不能从外部类以外的测试类声明。

    内部类的对象要从外部类的对象上new一个内部类来声明。

    静态内部类的声明则外部类名.静态内部类名()来声明即可。

    从其他类声明三种类的对象的代码如下:

    package com.meituan.huangdanyang;
    
    public class Main {
        public static void main(String[] args){
            OutsideClass outsideClass = new OutsideClass();
            OutsideClass.InnerClass innerClass = outsideClass.new InnerClass();
            OutsideClass.StaticInnerClass staticInnerClass = new OutsideClass.StaticInnerClass();
        }
    }
    
    

    二、初始化顺序

    在一个类中,对于静态变量、静态初始化块、变量、初始化块、构造器,它们的初始化顺序依次是(静态变量、静态初始化块)>(变量、初始化块)>构造器

    在外部类加载时,内部类、静态内部类并不会作为外部类的静态/非静态成员而加载:

    package com.meituan.huangdanyang;
    
    public class Main {
        public static void main(String[] args){
            OutsideClass outsideClass = new OutsideClass();
            // OutsideClass.InnerClass innerClass = outsideClass.new InnerClass();
            // OutsideClass.StaticInnerClass staticInnerClass = new OutsideClass.StaticInnerClass();
        }
    }
    
    
    package com.meituan.huangdanyang;
    
    public class OutsideClass {
        private int outProperty = -1;
        private static int staticOutProperty = -2;
        static {
            System.out.println("Outside class static Property " + staticOutProperty);
        }
        {
            System.out.println("Outside class Property " + outProperty);
        }
        public OutsideClass() {
            System.out.println("Outside class Constructor");
        }
        public void innerClassTest(){
          /*  InnerClass innerClass = new InnerClass();
            System.out.println(innerClass.innerProperty);
            System.out.println(StaticInnerClass.staticInnerProperty);
            System.out.println(new StaticInnerClass().innerProperty);*/
    
        }
    
        public class InnerClass {
            public InnerClass(){
                System.out.println("Inner Class Constructor");
            }
    
            {
                System.out.println("Inner class Property" + outProperty);
            }
    
        }
    
        public static class StaticInnerClass {
            private int innerProperty = 2;
            private static int staticInnerProperty = 3;
            public StaticInnerClass(){
                System.out.println("Static Inner Class Constructor");
            }
            static {
                System.out.println("Outside class static Property " + staticOutProperty);
            }
            {
                System.out.println("Outside class Property" + innerProperty);
            }
    
        }
    }
    
    

    运行结果为:

    屏幕快照 2018-07-17 下午5.06.51.png

    可以看到静态内部类和内部类没有被加载。

    内部类应当在外部类对象调用了初始化对象时才会加载,并且调用一次类就加载一次。

    
    package com.meituan.huangdanyang;
    
    public class Main {
        public static void main(String[] args){
            OutsideClass outsideClass = new OutsideClass();
            OutsideClass.InnerClass innerClass1 = outsideClass.new InnerClass();
            OutsideClass.InnerClass innerClass2 = outsideClass.new InnerClass();
            // OutsideClass.StaticInnerClass staticInnerClass = new OutsideClass.StaticInnerClass();
        }
    }
    
    
    package com.meituan.huangdanyang;
    
    public class OutsideClass {
        private int outProperty = -1;
        private static int staticOutProperty = -2;
        static {
            System.out.println("Outside class static Property " + staticOutProperty);
        }
        {
            System.out.println("Outside class Property " + outProperty);
        }
        public OutsideClass() {
            System.out.println("Outside class Constructor");
        }
        public void innerClassTest(){
          /*  InnerClass innerClass = new InnerClass();
            System.out.println(innerClass.innerProperty);
            System.out.println(StaticInnerClass.staticInnerProperty);
            System.out.println(new StaticInnerClass().innerProperty);*/
    
        }
    
        public class InnerClass {
            private String innerProperty = "inner class non-static";
            public InnerClass(){
                System.out.println("Inner Class Constructor");
            }
    
            {
                System.out.println("Inner class Property" + innerProperty);
            }
    
        }
    
        public static class StaticInnerClass {
            private int innerProperty = 2;
            private static int staticInnerProperty = 3;
            public StaticInnerClass(){
                System.out.println("Static Inner Class Constructor");
            }
            static {
                System.out.println("Outside class static Property " + staticOutProperty);
            }
            {
                System.out.println("Outside class Property" + innerProperty);
            }
    
        }
    }
    
    

    运行结果如下:

    屏幕快照 2018-07-17 下午5.17.01.png

    静态内部类的初始化,也是在有静态内部对象的声明时才会初始化。同时,在静态内部对象声明时,会先加载外部类的静态块,然后是静态内部类的静态块:

    package com.meituan.huangdanyang;
    
    public class Main {
        public static void main(String[] args){
            //sOutsideClass outsideClass = new OutsideClass();
            // OutsideClass.InnerClass innerClass1 = outsideClass.new InnerClass();
           //  OutsideClass.InnerClass innerClass2 = outsideClass.new InnerClass();
            OutsideClass.StaticInnerClass staticInnerClass1 = new OutsideClass.StaticInnerClass();
            OutsideClass.StaticInnerClass staticInnerClass2 = new OutsideClass.StaticInnerClass();
        }
    }
    
    

    运行结果如下:

    屏幕快照 2018-07-17 下午5.46.45.png

    对于初始化顺序的猜想,在类调用静态方法/静态变量时会对类的静态区进行加载,由于静态块会被加载到Java内存结构中的方法区上,在再次调用这个类的静态方法或声明这个类的对象时,静态块不会加载第二次。而非静态块,存储在堆上,因此随着对象每一次的初始化,它们都会被分配新的内存空间,非静态初始化块都会被加载一次。

    关于类加载机制,详见博客:https://blog.csdn.net/ns_code/article/details/17881581

    三、内部类直接访问外部类的对象

    代码为:

    package com.meituan.huangdanyang;
    
    public class OutsideClass {
        protected int outProperty = -1;
    
        public class InnerClass {
            private String innerProperty = "inner class non-static";
            public InnerClass(){
                System.out.println("Inner Class Constructor");
            }
            public void test(){
                System.out.println(outProperty);
            }
    
        }
    
        public static class StaticInnerClass {
            private int innerProperty = 2;
            private static int staticInnerProperty = 3;
    
        }
    }
    
    

    使用jd-gui将内部类的class文件反编译后,得到如下图:

    屏幕快照 2018-07-18 上午10.38.19.png

    可以大致猜测出,内部类在编译时持有它所依赖的外部类对象this0,在访问外部类对象的protected属性时,就使用的是this0的outProperty.

    但当访问外部类的私有属性时,得到的反编译代码是:

    屏幕快照 2018-07-18 上午10.42.53.png

    编译器生成了一个access000方法去访问this0的私有变量,这个方法可以越过privarte的权限限制去获得私有变量。
    四、静态内部类访问外部类静态变量

    源码为:

    package com.meituan.huangdanyang;
    
    
    public class OutsideClass {
        private int outProperty = -1;
        private static int outStaticProperty = 0;
    
        public class InnerClass {
            private String innerProperty = "inner class non-static";
            public InnerClass(){
                System.out.println("Inner Class Constructor");
            }
            public void test(){
                System.out.println(outProperty);
            }
    
        }
    
        public static class StaticInnerClass {
            private int innerProperty = 2;
            private static int staticInnerProperty = 3;
            public void test(){
                System.out.println(outStaticProperty);
            }
    
        }
    
        public int getOutProperty() {
    
            return outProperty;
        }
    
        public void setOutProperty(int outProperty) {
            this.outProperty = outProperty;
        }
    }
    
    

    反编译后的文件为:

    package com.meituan.huangdanyang;
    
    import java.io.PrintStream;
    
    public class OutsideClass$StaticInnerClass
    {
      private int innerProperty = 2;
      private static int staticInnerProperty = 3;
      
      public void test()
      {
        System.out.println(OutsideClass.access$100());
      }
    }
    

    可以看到静态内部类没有持有外部类的强引用,而是直接利用了OutsideClass的静态区,编译器生成了access$100函数去获得了它的私有静态变量

    相关文章

      网友评论

          本文标题:类、静态内部类与内部类、局部类

          本文链接:https://www.haomeiwen.com/subject/nxcgpftx.html