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

一、使用上对比

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函数去获得了它的私有静态变量

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容

  • 1. Java基础部分 基础部分的顺序:基本语法,类相关的语法,内部类的语法,继承相关的语法,异常的语法,线程的语...
    子非鱼_t_阅读 31,742评论 18 399
  • 一、Java 简介 Java是由Sun Microsystems公司于1995年5月推出的Java面向对象程序设计...
    子非鱼_t_阅读 4,240评论 1 44
  • 二超四月/ 有一天 眼泪变成了珍珠 大海忽涌上沙漠 窗外的叶子停停摆摆 风似有似无的过 那穿花裙的阿妹慢慢穿过小径...
    二超四月阅读 245评论 0 2