[Java] Java中的内部类

基本概念

  • 1.内部类定义:将一个类的定义放在另一个类的内部,称为内部类,外面这个类称为外围类。

  • 2.内部类持有外部类的引用,因此,在内部类中可以访问外围对象的所有成员,而不需要特殊条件。这里需要注意,并非是内部类对象可以访问外围类对象的成员。

.this 和.new 的使用

3.在外围类中,可以直接通过new 获取内部类引用;在内部类中,可以通过.this获得外部类的引用:

//外围类
public class Outer {

	private String name="Outer";
	
	/**
	 * 在外围类中,可以直接使用new生成一个内部类的引用
	 */
	Inner getInner(){
		return new Inner();
	}

	//内部类
	class Inner{
		
		void innerF(){
			System.out.println("innerF()...");
		}
		/**
		 * 在内部类中,通过.this获取生成一个外围类的引用
		 */
		Outer outer(){
			return Outer.this;
		}
	}

	void outerF(){
		System.out.println("OuterF()...");
	}
}

在其他类中,如果要获取内部类对象,必须使用外部类的对象来创建该内部类对象,在拥有外部类对象之前是不可能创建内部类对象的(静态内部类除外)。创建时通过.new关键字来创建:

public class Demo1 {

	public static void main(String[] args) {
		//创建外部类对象
		Outer outer = new Outer();
		outer.outerF();
		//通过外部类对象的引用.new来创建内部类对象
		Outer.Inner inner = outer.new Inner();
		inner.innerF();
		inner.outer().outerF();
	}
}

内部类用于隐藏实现细节

将内部类以private修饰,则在除外部类的任何类中,都无法获取该内部类实例,但可以向上转型为该内部类的基类,如下示例所示:

public interface Destination {

	String readLabel();
}

public interface Contents {

	int value();
}

public class Parcel {

	//PContent作为Parcel的外部类,被private修饰
	private class PContent implements Contents{
		@Override
		public int value() {
			return 0;
		}	
	}
	//PContent作为Parcel的外部类,被private修饰
	protected class PDestination implements Destination{
		private String lable;
		@Override
		public String readLabel() {
			return "PDestination";
		}
		private PDestination(String s){
			lable = s;
		}
	}
	//在其他类中将通过Parcel对象调用该方法获取PDestination实例
	public Destination destination(String s) {
		return new PDestination(s);
	}
	//在其他类中将通过Parcel对象调用该方法获取PContent实例
	public Contents contents(){
		return new PContent();
	}
}

public class Demo1 {

	public static void main(String[] args) {

		Parcel p = new Parcel();
		//通过Parcel对象方法可以获取到Parcel中被private修饰的内部类,且向上转型为其基类,因此隐藏了实现的细节。
		Contents content = p.contents();
		Destination  d = p.destination("Hello");
		//由于Parcel.PContent被private修饰,因此即使构造方法为public,也无法通过该方式获取其实例。
		//Contents c = p.new PContent();
	}
}

方法体中的局部内部类

在一个方法体中定义的类,称为局部内部类,如下面示例:

public class Demo1 {

	public static void main(String[] args) {
		
		Demo1 d = new Demo1();
		Destination ds = d.destination("Hello");
		ds.readLabel();
	}
	public Destination destination(String s) {
		//PDestination位于destination(String s)方法体中,因此对外部不可见
		class PDestination implements Destination{
			private String label;
			@Override
			public String readLabel() {
				// TODO Auto-generated method stub
				System.out.println(label);
				return label;
			}
			private PDestination(String s){
				label = s;
			}
		}
		//返回PDestination实例,但返回值为Destination,进行了向上转型
		return new PDestination(s);
	}
}

从该例中得知:

  • 1.局部内部类对方法之外不可见,它不是外部类的一部分,而是方法的一部分;
  • 2.并不意味着一旦方法执行完毕,方法体中的内部类就不可用了(依然可以使用该内部类实例)。

作用域内的内部类

除了在方法中定义一个内部类之外,还可以在任意的代码块中定义一个内部类,如:

/**
 * 定义在代码块中的局部内部类
 */
public class Demo3 {

	public static void main(String[] args) {
		Demo3 demo3 = new Demo3();
		demo3.test(true);
	}
	
	private void test(boolean b){
		if (b){
			//定义在代码块中的局部内部类,并不是该类的创建是有条件的,而是在if()之外,它是不可用的
			class LocalInnerClass{
				private String id;
				LocalInnerClass(String id){
					this.id = id;
				}
				String getId(){
					System.out.println("id:"+id);
					return id;
				}
			}
			LocalInnerClass lic = new LocalInnerClass("no.1");
			lic.getId();
		}
	}
}

这种形式的内部类,其设计目的不是说该类的创建是有条件的,它在编译时就已经加载了,其目的在于,只有在定义该类的作用域内可用,超过其作用域,它是不可用的,除此之外,它和其他普通类无差别。

局部内部类中不能有访问说明符,因为它不是外部类的一部分。

匿名内部类

匿名内部类,即没有名字的内部类,它将返回值的生成和表示这个返回值的类的定义结合在了一起。比如以下示例中表示了匿名内部类的语法:

public class AnonymoutInnerClass {

	public static void main(String[] args) {
		AnonymoutInnerClass aic = new AnonymoutInnerClass();
		Animal animal = aic.getAnimal();
		System.out.println(animal.getName());
	}
	
	public Animal getAnimal(){
		//表示创建一个继承自Animal的匿名类的对象
		return new Animal("Dog"){
			public String getName(){
				return super.getName()+" is Animal";
			}
		};
	}
	
    class Animal{
		private String name;
		public Animal(String name){
			this.name = name;
		}
		public String getName(){
			return name;
		}
	}

用一句话来概括匿名内部类的语法:创建一个继承自xxx的匿名类的实例!(经典啊)

如果要在匿名内部类中使用外部类中定义的变量时,那么这个变量必须是final类型的。如果仅仅是传递给匿名内部类基类构造器的变量,那么不要求该变量一定是final类型的,因为没有使用它。如:

	public Animal getAnimal(String name){
		//表示创建一个继承自Animal的匿名类的对象
		return new Animal(name){
			//private String str = name;如果使得该句编译通过,则需要将参数name定义为final类型的
			public String getName(){
				return super.getName()+" is Animal";
			}
		};
	}

匿名内部类用途

匿名内部类经常用于定义一个接口或者抽象类的子类,比如在接口回调中,就可以通过匿名内部类得到子类,示例如下:

public class AnonymoutInnerClass2 {

	public static void main(String[] args) {
		AnonymoutInnerClass2 aic2 = new AnonymoutInnerClass2();
		//创建了一个实现了Listener接口的匿名类
		aic2.setListener(new Listener(){
			@Override
			public void onChange() {
				System.out.println("onChange...");
			}});
	}
	
	//参数为一个接口类型的对象
	public void setListener(Listener l){
		l.onChange();
	}
	
	//定义一个接口
	interface Listener{
		void onChange();
	}
}

静态内部类

被static关键字修饰的内部类是静态内部类,也叫作嵌套类,因为它和外部类对象之前没有关系。普通内部类和静态内部类区别如下:

  • 1.创建普通内部类时,必须首先创建其外部类对象;创建静态内部类,不需要外部类对象;
  • 2.普通内部类持有创建它的外部类的引用(.this);静态内部类不持有外部类引用,因此不能使用(.this),也不能访问外部类中的非静态数据;
  • 3.普通内部类中,不能有static数据和字段;静态内部类中可以是static,也可以是非static的数据;
package com.chapter10.innerclass;

public class StaticInnerClass {

	public static void main(String[] args) {
		//嵌套内部类
		ParcelContents p = new ParcelContents();
		ParcelDestination d = new ParcelDestination("lable");	
		//普通内部类
		ParcelContents2 p2 = new StaticInnerClass().new ParcelContents2();
	}
	
	//嵌套类
	private static class ParcelContents implements Contents{
		@Override
		public int value() {
			return 0;
		}
		
	}
	
	private class ParcelContents2 implements Contents{
		@Override
		public int value() {
			return 0;
		}
		
	}

	//嵌套类
	protected static class ParcelDestination implements Destination {

		private String label;
		private ParcelDestination(String label){
			this.label = label;
		}
		@Override
		public String readLabel() {
			return label;
		}
		static int x = 10;
		//嵌套类
		static class AnotherLevel{
			public static void f(){}
			static int x = 10;
		}
	}
}

接口内部的类

正常情况下,接口内部不能放置任何代码,但静态内部类(嵌套类)可以作为接口的一部分,并且,放置在接口中的内部类自动地是static final的,如:

public interface ClassInInterface {
	
	void doSomething();
	
	class Test implements ClassInInterface{

		@Override
		public void doSomething() {
			System.out.println("doSomething");
		}
		
		public static void main(String args[]){
			new Test().doSomething();
		}
		
	}
}

内部类的继承

内部类也可以被其他类继承,只不过内部类的构造器必须连接到只想其外部类对象的引用,并且,内部类持有的外围类对象的引用必须初始化。因此,继承内部类时,必须在构造器中使用特殊的语法:

enclosingClassReference.super();//外部类引用.super()

如下示例:


public class WithInner {

	class Inner{
		
	}
}

public class InheritInner extends WithInner.Inner{

    //必须这样,否则编译报错
	public InheritInner(WithInner wi){
		wi.super();
	}
	public static void main(String[] args) {
		WithInner wi = new WithInner();
		InheritInner ii = new InheritInner(wi);
	}
}

内部类标识符

每个类都会产生一个.class文件,包括内部类,内部类的类文件命名规则为外部类$内部类。如果内部类是匿名的,编译器会简单产生一个数字作为标识符。如:

StaticInnerClass$ParcelContents
StaticInnerClass$1

为什么要使用内部类?

一般来说,内部类继承于某个类或者实现某个接口。而在Java中,没有多重继承,而通过内部类,则可以实现”多重继承“,我们看下面的例子就会明白:

package com.chapter10.innerclass.mutiextends;
//定义一个接口
public interface Incrementable {

	void increment();
}

//定义一个抽象类
public abstract class MyIncrement{
	
	public abstract void increment();
}

public class Client implements Incrementable{


	public static void main(String[] args) {
		// TODO Auto-generated method stub
		Client c = new Client();
		c.increment();

	}

	@Override
	public void increment() {
		// TODO Auto-generated method stub
		System.out.println("Incrementable.increment()...");
		new Inner().increment();
		
	}
	
	class Inner extends MyIncrement{
		@Override
		public void increment() {
			System.out.println("MyIncrement.increment()...");
		}
		
	}
}
/**
 * Incrementable.increment()...
 * MyIncrement.increment()...
 */

该例中,接口和抽象类中存在同名方法increment(),因此,不能同时继承和实现它们,这时通过创建一个内部类来继承或实现接口,巧妙地解决了这个问题。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值