#Java 内部类# 定义内部类的四种方式及其差异

106 阅读3分钟

内部类可以使用外部类的所有成员变量和成员方法,包括私有的。

内部类分四种

  • 成员内部类(以成员的方式定义在类内部)
  • 局部内部类(在方法中定义)
  • 匿名内部类(没有命名)
  • 静态内部类

成员内部类

定义:

public class Main {
	public static void main(String[] args) {
	    // 方式一:通过成员方法访问内部类
            Main main = new Main();
            main.useInnerClass();
		
            // 方式二:直接使用内部类
            Main.InnerClass mIn = new Main().new InnerClass();
            mIn.printCount();
	}
	
	private int count = 2;
	protected String name = "James";
	public double monney = 2.33;
	
	/** 定义成员内部类 **/
        /** 
        内部类使用不同的限定符,会存在差别 
        public 公共内部类可以在任何地方被访问,包括其它类
        private 私用内部类,只能被其所定义的外部类访问,其它类均不可访问,包括子类
        protected 受保护的内部类,只能被其定义的外部类及其子类访问
        default 默认内部类,可以被本包内的类访问,其它类不能访问
        **/
	public class InnerClass{
	    
	    private int innerCount;
	    public InnerClass(){
	        // 访问外部类的私有成员 count
	        this.innerCount = count;
	    }
	    
	    public void printCount(){
	        System.out.println(innerCount);
	    }
	}
	
	public void useInnerClass(){
	    InnerClass in = new InnerClass();
	    in.printCount();
	}
}

局部内部类

定义:

public class Main {
	public static void main(String[] args) {
	    
	    // 只能通过外部类的成员方法调用内部类
	    Main main = new Main();
	    main.useInnerClass();
	}
	
	private int count = 2;
	protected String name = "James";
	public double monney = 2.33;
	
	public void useInnerClass(){
	    
	    int methodCount = 3;
	    /** 定义局部内部类 **/
	    /** 
	        局部内部类不能使用 public private protected 等限定符 **/
            class InnerClass{

                private int innerCount;
                public InnerClass(){
                    // 访问外部类的私有成员 count
                    this.innerCount = count;
                }

                public void printCount(){
                    System.out.println(innerCount);
                    // 使用方法内局部变量
                    System.out.println(methodCount);
                }
            }
	    InnerClass in = new InnerClass();
	    in.printCount();
	}
}

匿名内部类

定义:

public class Main {
	public static void main(String[] args) {
	    
	    new Thread(new Runnable(){
	        @Override
	        public void run(){
	          System.out.println("使用匿名内部类实现多线程");  
	        }
	        
	    }).start();
	}
}

静态内部类

静态内部类不能访问外部类的非静态成员变量和成员方法,只能访问外部类的类成员(静态)变量和成员(静态)方法。

定义:

public class Main {
    private static int a = 10;
    private int c = 12;

    // 静态内部类
    public static class InnerClass {
        private int b = 20;

        public void show() {
            System.out.println("外部类的a值:" + a);
            // 无法访问成员变量 c :报错误 non-static variable c cannot be referenced from a static context
            //System.out.println("外部类的c值:" + c);
        }
    }
    
	public static void main(String[] args) {
	    
	   Main.InnerClass mIn  = new Main.InnerClass();
	   mIn.show();
	    
	}
	
	
}

静态内部类的几种用法:

  1. 使用静态内部类实现单例模式
public class Singleton {
    private Singleton(){}
    private static class SingletonHolder {
        private static final Singleton INSTANCE = new Singleton();
    }
    public static Singleton getInstance() {
        return SingletonHolder.INSTANCE;
    }
}
  1. 使用静态内部类实现工厂模式
public class Factory {
    private Factory(){}
    public static class FactoryHolder {
        public static final Factory INSTANCE = new Factory();
    }
    public static Factory getInstance() {
        return FactoryHolder.INSTANCE;
    }
}

  1. 使用静态内部类实现多层次结构
public class OuterClass {
    private OuterClass(){}
    public static class InnerClass {
        private InnerClass(){}
        public static class InnerClass2 {
            private InnerClass2(){}
        }
    }
}
  1. 使用静态内部类实现枚举类
public enum EnumClass {
    A, B, C;
    public static class EnumClassHolder {
        public static final EnumClass[] values = EnumClass.values();
    }
}

内部类初始化的时机

静态内部类

在外部类被加载的时候,静态内部类不会被加载。只有在调用静态内部类的静态方法或创建静态内部类的实例时,才会被加载,而且只会加载一次。 在以下几种情况下,内部类会被初始化

  1. 首次调用静态内部类的静态属性或方法时;
  2. 创建一个静态内部类的实例时;
  3. 使用反射来获取静态内部类的信息时;
  4. 加载外部类时,会自动加载内部类;
  5. 当虚拟机启动时,如果某个类是静态内部类,也会被加载。

成员内部类

在外部类被加载的时候,成员内部类不会被加载。只有在创建成员内部类的实例时,才会被加载,而且每次创建实例时,都会加载一次。

局部内部类

在外部类被加载的时候,局部内部类也不会被加载。只有进入局部内部类所在的方法体时,才会被加载,每次进入方法体时,都会被加载一次。