Java中内部类总结

定义在一个类内部的类称为内部类,《Java 核心技术(卷1,第10版)》一书对使用内部类的主要原因概括为以下三点:

  1. 内部类方法可以访问该类定义所在的作用域中的数据,包括私有的(private)数据
  2. 内部类可以对同一个包中的其他类隐藏起来
  3. 当想要定义一个回调函数且不想编写大量代码时,使用匿名内部类比较便捷

另外,Java中的类是单继承的,但是使用内部类可以巧妙的实现“多继承”的效果。

前言

先来看一个包含内部类的类的例子:

package com.itomcat.test.innner_class;
    
    public class OuterClass {
    	private String name;
    	public String getName() {
    		return name;
    	}
    
    	public void setName(String name) {
    		this.name = name;
    	}
    	
    	public void testMethod(String param){
    	}
    	class InnerClass{
    		
    		public InnerClass() {
    			// TODO Auto-generated constructor stub
    			name="test";
    		}
    		
    		public void innerMethod(){
    			testMethod("test");
    		}
    	}
    	class InnerOtherClass{
    		
    	}
    }

上面的外部类OuterClass类里定义了两个内部类,从编译后的class文件可以看出每个内部类都在外部类的同一包下重新生成了一个外部类,命名方式为“外部类$内部类”:

img

内部类可以无限制访问外部类的成员,即使该成员是private的,而外部类访问内部类成员则需要先获得一个该内部类的实例对象,然后外部类通过该内部类实例对象访问它的成员。

用“javap -c OuterClass.class”命令打开OuterClass类编译后的类,可以看到编译器自动给外部类添加了一个静态的方法access$0方法,参数为之一就是外部类:

    static void access$0(com.itomcat.test.innner_class.OuterClass, java.lang.String);
        Code:
           0: aload_0
           1: aload_1
           2: putfield      #18                 // Field name:Ljava/lang/String;
           5: return

内部类通过此静态方法访问外部类的数据。

同样用此命令打开OuterClass$InnerClass.class文件,可以看到编译器自动给内部类添加了一个final类型的“this$0”变量:

final com.itomcat.test.innner_class.OuterClass this$0;

查看外部类编译后的class文件,我们在内部类中重新定义"name"变量,编译器自动添加了“.this”指向:

public InnerClass()
        {
          OuterClass.this.name = "test";
        }

包括方法。

为了更详细的了解“this”在内部类的使用,可以先看看内部类的print()方法的输出结果什么:

 package com.itomcat.test.innner_class;
    
    public class OuterClassVarTest {
        private int a = 1;
    	public static void main(String[] args) {
    		// TODO Auto-generated method stub
    		OuterClassVarTest ocv=new OuterClassVarTest();
    		OuterClassVarTest.InnerClass ic=ocv.new InnerClass();
    		ic.print();
    	}
        class InnerClass {
            private int a = 2;
            public void print() {
                int a = 3;
                System.out.println(a);
                System.out.println(this.a);
                System.out.println(OuterClassVarTest.this.a);
            }
        }
    }

打印结果如下:

3 2 1

从上面例子可以看出,在“外部类.this”则调用的是外部类。内部类中调用“this”引用的是内部类,在class文件则是用“this.this$0”表示:

      public void innerMethod() {
        this.this$0.testMethod("test");
      }

内部类主要分为成员内部类、局部内部类、静态内部类、匿名内部类,下面一一详解这4中内部类。

一、成员内部类

成员内部类是比较普通的类,就是在一个类中创建另一个类,上面的例子就是成员内部类,他就像是外部类的一个变量或方法似的存在。

成员内部类需要注意的地方是: 成员内部类不能包含static的方法或变量。由于成员内部类是依赖于外部类而存在,而static修饰的变量或方法在JVM运行时就已经确定。

二、局部内部类

局部内部类是定义在一个方法或者一个作用域里面的类,它和成员内部类的区别在于局部内部类的访问仅限于方法内或者该作用域内,不能用任何权限修饰符修饰该内部类:

public User getMan(){
    		class Man extends User {
    			private static final long serialVersionUID = 1L;
    			public Man() {
    				// TODO Auto-generated constructor stub
    				//构造函数
    			}
    			public User getUser(){
    				return new User();
    			}
    		}
    		return new Man().getUser();
    	}

三、匿名内部类

匿名内部类是一个没有名字的内部类,所以匿名内部类是唯一没有构造函数的类。同理它也不能被任何权限修饰符修饰。

下面是一个匿名内部类的用法示例:

public static void main(String[] args) {
    		
    		AbstractDemo ad=new AbstractDemo() {
    			
    			@Override
    			public void test1() {
    				// TODO Auto-generated method stub
    				
    			}
    			
    			@Override
    			public void test() {
    				// TODO Auto-generated method stub
    				
    			}
    		};
    		ad.test1();
    		ad.test2();
    	}

匿名内部类不能是抽象类,所以它必须要实现它的抽象类或者接口里的所有方法。但是两者只能选其一,即要么实现一个抽象类中的抽象方法,要么实现一个接口中的方法,不能同时实现一个抽象类和一个接口中的抽象方法。

下面的实例使用的是一个abstract类的匿名类的写法,接口也可以写成匿名内部类,例如多线程中使用匿名内部类实现一个Runnable实例:

Runnable a=new Runnable() {
    		
    		@Override
    		public void run() {
    			// TODO Auto-generated method stub
    			
    		}
    };
    new Thread(a).start();

匿名内部类比较简洁,适用于只使用一次的类,如果为了方便管理还是尽量用普通类的方式进行。

四、静态内部类

用static修饰的内部类称为静态内部类。静态内部类的创建不依赖于外部类,也不能使用外部类非static的变量或方法。

package com.itomcat.test.innner_class;
    
    public class InnerStaticClass {
    	
    	static class InnnerClass{
    		private int a=0;
    		public void meth(){
    			
    		}
    	}
    }

使用静态内部类时如下所示:

InnerStaticClass.InnnerClass ic=new InnerStaticClass.InnnerClass();
    ic.meth();

内部类较普通类来说没有普通类用的频率那么高,但是却是面试常问的知识点,所以得好好掌握下。

评论

Your browser is out-of-date!

Update your browser to view this website correctly. Update my browser now

×