Java内部类机制

Java中提供了内部类机制(Innner Class),是指将一个类的定义放在另一个类的内部,其中内部类可以访问包含它们外部类的域,内部类技术主要用于设计具有相互协作关系的类集合,在编写GUI事件的代码时会经常使用到内部类。内部类和组合是完全不同的概念。

为什么需要内部类?

  • 内部类可以访问外部类的数据,包括私有的数据。
  • 内部类可以对同一个包中的其他类隐藏
  • 当要定义一个回调函数又不想编写大量的代码的时候,使用匿名(anonymous)内部类比较方便
为什么内部类拥有外部类的所有元素的访问权?
当某个外围类对象创建一个内部连对象时,内部类对象必定会捕获一个指向那个外围类对象的引用。内部类对象只能在与其外部类对象关联的情况下才能被创建(在内部类非static时),构建内部类需要一个外部类的引用,内部类正是利用这个引用去访问外部类的。

内部类的种类
按照内部类所在的位置不同,内部类可以分为以下几种:
  • 成员内部类
  • 方法内部类
  • 匿名内部类
  • 静态内部类

1.成员内部类的创建

a、内部类直接在类的内部进行声明。可以声明为private、protected、public或者默认访问权限,这个访问权限约定和外部类完全一样。 
b、内部类自动拥有对其外围类所有成员(方法、属性)的访问权。如果内部类和外部类成员的名字完全相同,在内部类方法中要访问外部类成员,则需要使用下面的方式来访问:外部类名.this.外部成员名,例如Outer.this.i++;  (看例子)
c、必须使用外部类对象来创建内部类对象,而不是直接去new一个。
格式为:外部对象名.new 内部类构造方法
d、成员内部类中不能定义静态的变量和静态方法

下面创建的Outer外部类包含了一个私有的内部类Inner,在内部类中访问外部类中的数据域,然后通过外部类去创建内部类
public class Outer {
	
	public int i = 10;
	private String str = "outer class";

	private class Inner {
		
		private int i = 11;
		
		public int getInner() {
			return i;
		}
		
		public int getOuter() {
			return Outer.this.i;
		}
		
		public String getOuterStr() {
			return Outer.this.str;
		}
		
	}
	
	public static void main(String[] args) {
		
		Outer outer = new Outer();
		
//		Inner inner = new Inner();  //这样创建内部类编译器会报错
		Inner inner = outer.new Inner();
		
		System.out.println(inner.getInner());
		System.out.println(inner.getOuter());
		System.out.println(inner.getOuterStr());
		
	}
	
}

需要说明的是,创建内部类必须要用外部类的引用去创建,虽然这里的内部类没有构造方法,但是之后编译器会给Inner加上一个构造方法,然后外部类的引用就作为参数传递给内部类,创建好的内部类就维护了这个外部类的引用,并用它访问外部类。并且即使Inner有构造方法,编译器也会修改那个构造方法加入一个外部类对象的类型参数。
public Inner(Outer outer) {
			this.outer = outer;
		}

如若像注释中直接创建内部类,编译器就会直接报如下错误:
No enclosing instance of type Outer is accessible. Must qualify the allocation with an enclosing instance of type Outer (e.g. x.new A() where x is an instance of Outer).

另外,可以发现这个Inner内部类是private,这就意味着Inner只在这个Outer类中可见,是一个私有类,即时在同包的其他类中通过Outer也无法访问。当然,如果不是private内部类,还是可以访问的。

上述Outer类经过javac编译后一共生成了3个Class文件



2.方法内部类
将一个类的定义放在方法体的内部,并且方法内部类只能在该方法内可见
public class MethodInner {

	private int i;
	
	public MethodInner(int i) {
		this.i = i;
	}
	
	public int method(int val) {
		class InnerClass {
			private int i;
			
			public InnerClass(int i) {
				this.i = i;
			}
			
			public int increment() {
				return ++i;
			}
			
		}
		
		InnerClass inner = new InnerClass(val);
		return inner.increment() + add();
		
	}
	
	public int add() {
		return ++i;
	}
	
	public static void main(String[] args) {
		MethodInner mi = new MethodInner(5);
		int i = mi.method(2);
		System.out.println(i);
	}
	
}

程序的输出:9
这里在method方法内定义了Inner内部类,Inner内部类只能在该方法内可见。

3.匿名内部类
所谓匿名内部类就是不给出类的名字,直接定义一个类,通常这个类实现了某个接口,在多线程编程中经常使用到匿名内部类
public class AnonymousClass {

	public static void main(String[] args) {
		Thread t = new Thread(new Runnable() {
			
			@Override
			public void run() {
				// TODO Auto-generated method stub
				System.out.println("It's runnable target...");
			}
		});
		
		t.start();
		
	}
	
}

其中new Thread(Runnable); 中Runnable就是一个匿名内部类,这样的写法简化了程序的编写。上述写法等价于下面的代码,很显然,上面的匿名类省去了Target类的命名
public class AnonymousClass {

	public static void main(String[] args) {
		Target target = new Target();
		Thread t = new Thread(target);
		t.start();
		
	}
	
}

class Target implements Runnable {
	@Override
	public void run() {
		// TODO Auto-generated method stub
		System.out.println("It's runnable target...");
	}
}

其中匿名内部类在经过javac编译之后生成的Class文件也只有两个:


4.静态内部类
如果不需要内部类和外部类对象之间有任何联系,即不让内部类访问外部类可以将内部类声明为static,称为静态内部类,也叫嵌套内部类,相比于其他内部类,嵌套内部类和外围类的关系没那么紧密。
这种内部类的特定是:它不能访问外部类的非静态成员;创建静态内部类对象的时候,也不需要外部类对象。
以下定义了一个静态内部类,并在静态内部类中访问外部类的静态成员,但是不能访问外部类的非静态成员,然后直接创建静态内部类,不需要传递外部类的引用。
public class StaticInner {

	private int i = 1;
	private static String str = "outer";
	
	private static class StaticInnerClass {
		private int i = 11;
		private String name;
		
		public StaticInnerClass(String str) {
			this.name = str;
		}
		
		public void say() {
			System.out.println("outer : " + StaticInner.str);
			System.out.println("inner : i=" + i + " -  name=" + name);
		}
		
	}
	
	public static void main(String[] args) {
		StaticInnerClass inner = new StaticInnerClass("inner");
		inner.say();
	}
	
}

还有就是,有时会分出一种接口内部类,但是接口内部类可以归结为静态内部类,在接口中声明的内部类默认就是public static的,所以可以参照静态内部类去学习接口内部类。


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值