Java - 提高(7) - 内部类

内部类


为什么要使用内部类?
在《Think in java》中有这样一句话:
使用内部类最吸引人的原因是:每个内部类都能独立地继承一个(接口的)实现,所以无论外围类是否已经继承了某个(接口的)实现,对于内部类都没有影响

在我们程序设计中有时候会存在一些使用接口很难解决的问题,这个时候我们可以利用内部类提供的、可以继承多个具体的或者抽象的类的能力来解决这些程序设计问题。

可以这样说,接口只是解决了部分问题,而内部类使得多重继承的解决方案变得更加完整。


上面的文字可能太枯燥 , 估计大家不太想看..我们用代码看下.

//接口1
public interface Fatcher {
	
}
//接口2
public interface Mother {

}
//子类实现两个接口
public class Son implements Fatcher, Mother {
	
}
//或者这么写,通过内部类来实现
public class Daughter implements Fatcher {

	class Mother_ implements Mother {

	}
	
}
上面是接口,但如果是两个父类或者两个抽象类呢?
子类只能继承一个父类 , 这个时候怎么办呢? 不能继承两个父类呀...
这时候就可以使用内部类了..
通过内部类来继承第二个父类.


其实使用内部类最大的优点在于它能够非常好的解决多重继承问题 , 使用内部类还能够为我们带来如下特性:
1. 内部类可以用多个实例,每个实例都有自己的状态信息,并且与其他外围对象的信息相互独立
2. 在单个外围类中,可以让多个内部类以不同的方式实现同一个接口,或者继承同一个类
3. 创建内部类对象的时候并不依赖于外围类对象的创建
4. 内部类是一个独立的实体
5. 内部类提供了更好的封装,除了该外围类,其他类都不能访问


我们看一个最常用到的内部类例子:

File file = new File("");
file.listFiles(new FileFilter() {

@Override
public boolean accept(File pathname) {
	// TODO Auto-generated method stub
	return false;
}
});


下面我们一个一个看看有哪些内部类


内部类基础

当我们创建一个内部类时,他无形中就与外围类有了一种联系,依赖于这种联系,它可以无限制的访问外围类的元素。

public class OuterClass {

	private String name;
	private int age;

	public class InnerClass {

	public InnerClass() {
			name = "cyx";
			age = 10;
	}

	public void display() {
			System.out.println("name : " + getName() + " , age : " + getAge());
	}
	}

	public static void main(String[] args) {
			OuterClass o = new OuterClass();
			OuterClass.InnerClass innerClass = o.new InnerClass();
			innerClass.display();
	}

	public String getName() {
			return name;
	}
	public void setName(String name) {
			this.name = name;
	}
	public int getAge() {
			return age;
	}
	public void setAge(int age) {
			this.age = age;
	}
}
在上面代码中,我们可以看到内部类InnerClass可以对外围类OuterClass的属性进行无缝的访问,尽管是private修饰的。
因为当我们创建某个外围类的内部类对象时,此时内部类对象必定会捕获一个指向那个外围类对象的引用,只要我们在访问外围类的成员时,
就会用这个引用来选择外围类的成员。

其实通过上面的代码我们还看到了如何引用内部类:
引用内部类我们需要指明这个对象的类型,OuterClassName.InnerClassName.
同时如果我们需要创建某个内部类对象,必须利用外部类的对象 通过 .new来创建内部类
OuterClass.InnerClass innerClass = OuterClass.new InnerClass();

同时如果我们需要生成对外部类对象的引用,可以使用OuterClassName.this,这样就能够产生一个正确引用外部类的引用了。
public class OuterClass {

public void display() {
	System.out.println("OuterClass....");
}

public class InnerClass {
	public OuterClass getOuterClass() {
			return OuterClass.this;
	}
}

public static void main(String[] args) {
	OuterClass outer = new OuterClass();
	OuterClass.InnerClass innerClass = outer.new InnerClass();
	innerClass.getOuterClass().display();
}

}
输出:OuterClass....
到这里我们需要明确一点,内部类是个编译时的概念,一旦编译成功,它就与外围类属于两个完全不同的类。
对于一个一个名为OuterClass的外围类和一个名为InnerClass的内部类,在编译成功之后,会出现这样两个class文件:
OuterClass.class和OuterClass$InnerClass.class

这里再提供一个Thinking in Java中提供的例子,体会下内部类的使用:

public class Parcell {

	class Contents {
			private int i = 22;

	public int value() {
			return i;
	}
	}

	class Destination {

	private String label;

	public Destination(String whereTo) {
			label = whereTo;
	}

	String readLabel() {
			return label;
	}

	}

	public void ship(String dest) {
			Contents c = new Contents();
			System.out.println("c.value() : " + c.value());
			
			Destination d = new Destination(dest);
			System.out.println("d.readLabel() : " + d.readLabel());
	}

	public static void main(String[] args) {
			Parcell p = new Parcell();
			p.ship("CYX");
	}

}



Java 中内部类主要分为成员内部类,局部内部类,匿名内部类,静态内部类。


成员内部类
成员内部类是最普通的内部类,它是外围类的一个成员,所以它是可以无限制的访问外围类的所有成员属性和方法,尽管是private的
但是外围类要访问内部类的成员属性和方法则需要通过内部类实例来访问。

在成员内部类中需要注意两点:
1. 成员内部类中不能存在任何static的变量和方法
2. 成员内部类是依附于外围类的,所有只有先创建外围类才能创建内部类

public class OuterClass {

	private String str;

	public void outerDisplay() {
			System.out.println("outerClass...");
	}

	public class InnerClass {

	public void innerDisPlay() {
			// 使用外围类的属性
			str = "chenssy....";
			System.out.println("str : " + str);
			// 调用外围类的方法
			outerDisplay();
	}
	}

	public InnerClass getInnerClass() {
			return new InnerClass();
	}

	public static void main(String[] args) {
			OuterClass outer = new OuterClass();
			OuterClass.InnerClass innerClass = outer.getInnerClass();
			innerClass.innerDisPlay();
	}
}
推荐使用getxxx()来获取成员内部类,尤其是该内部类的构造函数无参数时


局部内部类
有种内部类,它是嵌套在方法和作用域中,对于这个类的使用主要是 应用/解决比较复杂的问题;
想创建一个类来辅助我们的解决方案,但又不想这个类是公共可用的。所以就产生了局部内部类;
局部内部类和成员内部类一样被编译,只是它的作用域发生了改变,它只能在该方法和属性中被使用,出了该方法和属性就会失效。

定义在方法里

public class Parcel {

	public Destionation destionation(String str) {
			class PDestionation implements Destionation {
					private String label;

	private PDestionation(String whereTo) {
			label = whereTo;
	}

	public String readLabel() {
			return label;
	}
	}
	return new PDestionation(str);
	}

	public static void main(String[] args) {

	Parcel p = new Parcel();
	Destionation d = p.destionation("CYX");
	
	}

}
定义在作用域内:

public class Pracel6 {

	private void internalTracking(boolean b) {

	if (b) {

	class TrackingSlip {
			private String id;

	TrackingSlip(String s) {
			id = s;
	}

	String getSlip() {
			return id;
	}
	}

	TrackingSlip ts = new TrackingSlip("cyx");
	String string = ts.getSlip();

	}
	}

	public void track() {
			internalTracking(true);
	}

	public static void main(String[] args) {
			Pracel6 p = new Pracel6();
			p.track();
	}

}


匿名内部类

匿名内部类的特点:
匿名内部类一定是跟在new后面的,用其隐含实现一个接口或一个类,没有类名,根据多态,我们使用其父类名。
因为匿名内部类属于局部类,所以局部类的所有限制对其生效。
匿名内部类是唯一一个无构造方法的类。
匿名内部类在编译时,系统自动起名Out$1.class。
如果一个对象编译时的类型是接口,那么其运行的类型为实现这个接口的类。

直接上实例

public static void main(String[] args) {

	new File("C:/Foxmail 7.2").listFiles(new FileFilter() {

	@Override
	public boolean accept(File pathname) {
		System.out.println(pathname);
		return false;
	}
	});

}

FileFilter 本身是一个接口


再来一个栗子

public abstract class Bird {

	private String name;

	public abstract int fly();

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}
}

//------------------------------------

public class Test {

	public void test(Bird bird) {
		System.out.println(bird.getName() + "能够飞 " + bird.fly() + " 米");
	}

	public static void main(String[] args) {
		Test test = new Test();
		test.test(new Bird() {
			@Override
			public int fly() {

				return 1000;
			}

			@Override
			public String getName() {
				return "大雁";
			}
		});
	}

}
在Test类中,test()方法接受一个Bird类型的参数,同时我们知道一个抽象类是没有办法直接new的,我们必须先有实现类才能new出
它的实例。所以在main方法中直接使用匿名内部类来创建一个Bird实例。

由于匿名内部类不能是抽象类,所以他必须要实现它的抽象父类或者接口里面所有抽象方法。

其实 匿名内部类是可以拆分的...
举个栗子:

这个是实现了FileFilter的实现类



然后是具体调用
1: 使用匿名内部类
2:没有使用匿名内部类,从代码角度来看,使用匿名内部类能够简化代码(少写类)



对于匿名内部类的使用它存在一个缺陷,就是它仅能被使用一次,创建匿名内部类时它会立即创建一个该类的实例,该类的定义
会立即消失,所以匿名内部类时不能够被重复使用。

注意事项:
a. 使用匿名内部类时,我们必须继承一个类或者实现一个接口,但是两者不可兼得,同时也只能继承一个类或者实现一个接口。(??)
b. 匿名内部类中是不能定义构造函数的。
c. 匿名内部类中不能存在任何静态成员变量和静态方法
d. 匿名内部类为局部内部类,所以局部内部类的所有限制同样对匿名内部类生效
e. 匿名内部类不能是抽象的,它必须要实现继承的类或者实现的接口的所有抽象/方法。


匿名内部类的优点:
a. 可以使命名变得简洁,使代码更加紧凑,简洁,封装性比内部类更优
b. 一个类用于继承其他类或是实现接口,无需增加其他的方法,只是对继承方法实现,覆盖。



静态内部类
关键字static中提到static可以修饰成员变量、方法、代码块,其实它还可以修饰内部类,使用static修饰的内部类我们称之为静态内部类,
不过更喜欢称之为"嵌套内部类"

静态内部类与非静态内部类之间存在一个最大的区别:
我们知道非静态内部类在编译完成之后会隐含的保存着一个引用,该引用是指向创建它的外围类,但是静态内部类却没有。
没有这个引用就意味着:
1. 它的创建是不需要依赖于外围类的。
2. 它不能使用任何外围的非static成员变量和方法。

来个栗子体会一下

/**
 * @author CYX
 * @create 2017-05-19-9:45
 */

public class OuterClass {

	private String sex;
	public static String name = "chenssy";

	/**
	 * 静态内部类
	 */
	static class InnerClass1 {

		public static String _name1 = "chenssy_static";

		public void display() {

			//静态内部类只能访问外围类的静态成员变量和方法,不能访问外围类的非静态成员变量和方法.
			System.out.println("InnerClass1 OuterClass name : " + name);
		}
	}

	/**
	 * 非静态内部类
	 */
	class InnerClass2 {
		public String _name2 = "chenssy_inner";

		public void display() {
			System.out.println("InnerClass2 OuterClass name : " + name);
		}

	}

	/**
	 * 外围方法
	 */
	public void display() {

		//外围类访问静态内部类:内部类
		System.out.println("InnerClass_1._name1 :" + InnerClass1._name1);
		//静态内部类,可以直接创建实例不需要依赖于外围类
		new InnerClass1().display();

		//非静态内部的创建的需要依赖于外围类
		OuterClass.InnerClass2 inner2 = new OuterClass().new InnerClass2();
		//访问非静态内部类的成员变量需要使用非静态内部类的实例
		System.out.println("inner2._name2 : " + inner2._name2);
		inner2.display();

	}

	public static void main(String[] args) {
		OuterClass outerClass = new OuterClass();
		outerClass.display();
	}

}

输出:
		InnerClass_1._name1 :chenssy_static
		InnerClass1 OuterClass name : chenssy
		inner2._name2 : chenssy_inner
		InnerClass2 OuterClass name : chenssy
		


参考资料:

http://www.cnblogs.com/chenssy/ 

《Thinking in Java》

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值