一、使用上对比
1、声明区别
(1)只有内部类才能声明为静态,外部类不行、局部类也不行。
屏幕快照 2018-07-17 下午3.40.01.png(2) 变量声明
外部类既可以声明静态变量、方法,也可以声明普通变量、方法。内部类不能声明静态变量和静态方法(不能有静态声明)。静态内部类中可以生成静态成员和非静态成员。
屏幕快照 2018-07-17 下午3.54.05.png 屏幕快照 2018-07-17 下午3.54.28.png2、互相使用变量、调用方法
(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函数去获得了它的私有静态变量
网友评论