探索JAVA——内部类

内部类大法

最近学到了内部类的范围,但是上课的老师只是一笔带过,所以很多内容还是很模糊,所以打开了JAVA编程思想,理解后还是写篇博客便于日后的查看,菜鸟要抓紧成长啊!加油,给自己打气!!!

在接下来的篇幅中主要会介绍普通的内部类和静态嵌套内部类,其实听名字能容易的联想到成员变量,同时它们也是非常相似的,可以借助成员变量来理解不同的内部类

内部类

什么是内部类呢?可以将一个类的定义放在另一个类的定义内部,这就是内部类。内部类是一种非常有用的特性,因为它允许你把一些逻辑相关的类组织在一起,并控制位于内部的类的可视性(它用来测试类和放在接口中的时候是真的妙极了啊~再次感叹JAVA相比于c、c++于操作上爽很多啊)

普通的内部类

如前文所说,可以将它按照实例变量理解。
1.如果想从外部类的非静态方法之外的任何位置创建某个内部类的对象,那么必须具体的致命这个对象的类型:OuterClassName.InnerClassName
2.普通的内部类不仅仅只是一种名字隐藏和组织代码的模式;它还能在生成一个内部类的对象时,与制造它的外围对象之间有一种联系,所以它能访问外围对象的所有成员,而且是不需要任何条件的访问!

下面我们先创建一个普通的内部类来感受一下:

例子一:

/*
 * 名字隐藏和组织代码的模式
 * */
import javax.xml.namespace.QName;

public class Parcel1 {
	class Contents {
		private int i = 11;
		public int value() { return i; }
	}
	class Destination {
		private String lable;
		public Destination(String whereTo) {
		lable = whereTo;
		}
		String readLabel() {return lable;}
	}
	//	Outer定义方法返回Inner实例
	public Contents contents() {
		return new Contents();
	} 
	public Destination to(String s) {
		return new Destination(s);
	}
	//	利用ship方法同时返回两个Inner的实例
	public void ship(String dest) {
		Contents contents = contents();
		Destination d = to(dest);
		System.out.println(d.readLabel());
	}
	public static void main(String[] args) {
		Parcel1 p = new Parcel1();
		p.ship("Tasmania");
		Parcel1 q = new Parcel1();
		Parcel1.Destination d = q.to("Birneo");
		Parcel1.Contents c = q.contents();
	}
}

例子二:

/*当生成一个内部类的对象时,次对象与制造它的外围对象之间就有了一种联系,所以他能访问外围对象 的所以成员,二不需要任何特殊条件
 * */

//	创建Selector接口,可以实现(end()、current()、next())
interface Selector{
	boolean end();
	Object current();
	void next();
}

public class Saquence {
	private Object[] items;
	private int next = 0;
	//	给数组一个初始化大小
	public Saquence(int size) { items = new Object[size]; }
	
	//	给数组中添加元素
	public void add(Object x) {
		if(next<items.length) 
			items[next++] = x; 
	}
	private class SeqenceSelector implements Selector {
		private int i;
		//	检查序列是否到末尾
		public boolean end(){
			return i == items.length;
		}
		public Object current() {
			return items[i];
		}
		public void next() {
			if(i<items.length)
				i++;
		}
	}
	//	外部类的方法,返回一个内部类
	public Selector selector() {
		return new SeqenceSelector();
	}
	public static void main(String[] args) {
		Saquence saquence = new Saquence(10);
		for (int i = 0; i < 10; i++) {
			saquence.add(Integer.toString(i));
		}
		//	利用外部类创建一个内部类
		Selector selector = saquence.selector();
		while(!selector.end()) {
			System.out.print(selector.current()+" ");
			selector.next();
		}
	}
}

在例二中,Sequence类只是一个固定大小的Object数组,以类的形式包装了起来,在还有空间的情况下可以调用add()在序列末尾添加新的Object。Selector允许你检查序列是否到末尾了(end())、访问当前对象(current())、以及移到序列中的下一个对象(next())。因此Selector是一个接口,所以别的类可以按他们自己的方式来实现接口,并且另外的方法能以此接口为参数,来生成更加通用的反码。内部类可以访问其外围类的方法和字段,就像自己拥有他们似的。

内部类是如何做到自动拥有对其外部类的访问权限的呢?
当外围类的对象创建了一个内部类对象时,此内部类对象必定会秘密地捕获一个纸箱那个外围类对象的引用。然后当你在访问外围类对象的成员时,就是用哪个引用来选择外围类的成员。(当然编译器会帮你处理这些细节)

使用.this 和 .new

.this
如果你需要生成对外部类对象的引用,可以使用外部类的名字后面紧跟.this。这样产生的引用自动地具有正确的类型。

public class DotThis {
	void f() { System.out.println("DotThis.f()"); }
	public class Inner {
		public DotThis outer() {
			//	生成对外部类对象的引用,产生的引用自动地具有正确的类型
			return DotThis.this;	
		}
	}
	public Inner inner() { return new Inner(); }
	public static void main(String[] args) {
		DotThis dt = new DotThis();
		DotThis.Inner dti= dt.inner();
		//	利用.this找到外部类的引用从而正确的使用控制外部类的遥控器
		dti.outer().f();
	}
}

你必须使用外部类的对象来创建该内部类对象,实际上,在拥有外部类对象之前是不可能创建内部类对象的,这是因为内部类对象会暗暗地连接到创建它的外部类对象上。(但是,如果你创建的是嵌套类(静态内部类)就不要对外部对象的引用)

.new

/*除了在外部类中用方法返回内部类对象外,还可以使用.new的方式去创建其某个内部类的对象
 * */
public class DotNew {
	public class Inner { }
	public static void main(String[] args) {
		DotNew dNew = new DotNew();
		DotNew.Inner dni = dNew.new Inner();
	}
}
//~OK,Good!
/*在拥有外部类对象之前是不可能创建内部类对象的——成员变量类
 * 嵌套类(静态内部类)不需要对外部类对象的引用——静态变量类
 * */
 

补充一个点:
private:表示除了外部类,其他类都不能访问该内部类
protected:表示只有外部类和它的子类能够访问

匿名类

先看段代码感受一下:


class Wrapping {
	private int i;
	public Wrapping(int x) {i = x;}

	public int value() {
		return i;
	}
}

public class Parcel7b {
	public Wrapping wrapping(int x) {
		return new Wrapping(x) {
			public int value(){
				return super.value()*47;
			}
		};
	}
	public static void main(String[] args) {
		Parcel7b p = new Parcel7b();
		Wrapping w = p.wrapping(10);
		System.out.println(w.value());
	}
}

wrapping(int x)方法将返回值的生成与表示这个返回值的类的定义结合在一起!另外,你可以发现这个类是没有名字的,它看起来似乎是你正在要创建一个Wrapping对象。但是然后(在到达语句结束的分号之前)你却说:“等一下,我想在这插入一个类的定义”。
你可以注意到,*匿名内部类末尾的分号,并不是用来标记次内部类结束的。是加上,它标记的事表达式的结束,只不过这个表达式正巧包含了匿名内部类。因此,这与别的地方使用的分号是一致的

因此上面的Parcel7b类等价于:

public class Parcel7b {
	public class ParWrapping extends Wrapping{
		public int value(){
			return super.value()*47;
		}
	}
	public Wrapping wrapping() {
		return new ParWrapping();
	}
	public static void main(String[] args) {
		Parcel7b p = new Parcel7b();
		Wrapping w = p.wrapping(10);
		System.out.println(w.value());
	}
}

使用匿名类其外部定义的对象需要final

interface Destination{
	String readLabel();
}
public class Parcel{
	public Destination destination(final String dest){
		return new Destination(){
			private String label = dest;
			public String readLabel(){
				return label;
			}
		};
	}
	public static void main(String[] args){
		Parcel p = new Parcel();
		Destination d = p.destination("hello");
	}
}

如果定义一个匿名类,并且希望它使用有个在其外部定义的对象,那么编译器会要求其参数引用是final的。

这里所说的“外部定义的对象”,指的是所有外来的对象,包括外部方法的形参、局部变量、基本类型或自定义类型等。

这里之所以只能用final修饰的参数,是变量作用域的原因。虽然匿名内部类被定义在方法内部,但匿名内部类是单独的个体,编译时随外部类一起被编译成为Outer$1.class文件,并不是方法被调用时才会被执行。方法中的局部变量只是在方法被调用时被创建在栈内存中,调用完毕会自动清空栈,所以,匿名内部类要想使用方法内部的变量,只能将该变量用final修饰,即定义为常量。

匿名类相比于正规的继承相比受限,因为匿名内部类既能扩展类,也能实现接口,但是两者不能兼得;如果是实现接口,也只能实现一个接口

嵌套类

如果不需要内部类对象与其外围类对象之间有联系,可以将内部类声明为static,通常被称为嵌套类或者静态类。当内部类是static时就不会保存指向外围对象的引用了。(普通的内部类不能有static数据和static字段,也不能含有嵌套类)。嵌套类类似于一个static方法。

public class TestBed {
	public void f() { System.out.println("f()"); }
	public static class Tester {
		public static void main(String[] args) {
			//	不能直接使用外部类的非static元素,需要先创建外部类的对象才能使用(感觉也不是很影响啦。。。)
			TestBed t = new TestBed();
			t.f();
		}
	}
}

接口内部的类

嘿嘿,在正常情况下,不能在接口内部放置任何代码,但嵌套类可以作为接口的一部分,你放到接口中的任何类都自动地施public和static的。因此类是static的,只是嵌套类之余接口的命名空间内。你甚至可以在内部类中实现其外围接口。

public interface ClassInInterface {
	void howdy();
	class Test implements ClassInInterface{
		public void howdy() {
			System.out.println("Howdy!");
		}
	}
	public static void main(String[] args) {
		new Test().howdy();
	}
}

什么时候能用到它呢?

1. 如果你想要创建某些公共代码,使得他们可以被某个及饿哦口的所以不同实现所共用,那么使用接口内部的嵌套类会很方便。
2. 每个类都做好写一个main()方法来测试类,但是那样会造成必须带着那些已编译过的额外代码。而使用嵌套类的话,虽然生成了一个独立的类(如:TestBed$ Tester)
,但是不必在发布的产品中包含它,在产品打包前可以简单地删除TestBed$ Tester.class

接口内部类还是很奇特的

interface ClassInInterfac {
	void howdy();
	class Test implements ClassInInterfac{
		public void howdy() {
			System.out.println("Howdy");
		}
	}
	public static void main(String[] args) {
		new Test().howdy();
	}
}
public class A {
	public static void main(String[] args) {
		ClassInInterfac.Test t = new ClassInInterfac.Test();
		t.howdy();
	}
}
发布了27 篇原创文章 · 获赞 28 · 访问量 4417
展开阅读全文

没有更多推荐了,返回首页

©️2019 CSDN 皮肤主题: 深蓝海洋 设计师: CSDN官方博客

分享到微信朋友圈

×

扫一扫,手机浏览