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

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

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

相关文章

  • Java 内部类、静态内部类、方法内部类(未完待续)

    内部类 什么是内部类?内部类其实就是在一个类中创建的类。内部类有四种: 成员内部类 静态内部类 局部内部类 匿名内...

  • Java学习——内部类

    内部类 一,成员内部类(包括静态内部类和非静态内部类) 非静态内部类可以直接访问外部类的成员,反之则不行 非静态内...

  • Java 内部类

    1. 内部类 概念:在一个类的内部再定义一个完整的类内部类分为 4 类:成员内部类、静态内部类、局部内部类、匿名内...

  • Java 内部类

    内部类包括成员内部类、方法内部类、*静态内部类、匿名内部类*。 内部类的作用 由于内部类的实现和外部类没有关系,内...

  • Java自学-接口与继承 内部类

    Java 内部类 内部类分为四种: 非静态内部类静态内部类匿名类本地类 步骤 1 : 非静态内部类 非静态内部类 ...

  • java-内部类

    内部类与类的属性没有什么区别。静态内部类,非静态内部类在new时有区别。静态内部类类似于外部类的类变量,非静态内部...

  • Java内部类

    Java内部类,简单来说就是将一个类定义在另一个类或者方法里面,主要分为成员内部类,局部内部类,匿名内部类和静态内...

  • 进阶:匿名内部类

    匿名内部类:内部类:在类的内部又定义了一个新类。内部类分类:静态内部类:类似于静态变量实例内部类:类似于实例变量局...

  • 内部类

    成员内部类 局部内部类(定义在方法内和定义在作用域内的类) 匿名内部类

  • java 内部类

    内部类分为以下四种: 静态内部类 成员内部类 局部内部类 匿名内部类 一、静态内部类 静态内部类: 在类中用sta...

网友评论

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

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