提vJava内部c(Inner ClassQ可能很多h不太熟悉Q实际上cM的概念在C++里也有,那就是嵌套类QNested ClassQ,关于q两者的区别与联p,在下文中会有Ҏ。内部类从表面上看,是在类中又定义了一个类Q下文会看到Q内部类可以在很多地方定义)Q而实际上q没有那么简单,乍看上去内部cM乎有些多余,它的用处对于初学者来说可能ƈ不是那么显著Q但是随着对它的深入了解,你会发现Java的设计者在内部cn上的是用心良苦。学会用内部类Q是掌握Java高~程的一部分Q它可以让你更优雅地设计你的E序l构。下面从以下几个斚w来介l:
W一ơ见?/strong>
public interface Contents {
int value();
}
public interface Destination {
String readLabel();
}
public class Goods {
private class Content implements Contents {
private int i = 11;
public int value() {
return i;
}
}
protected class GDestination implements Destination {
private String label;
private GDestination(String whereTo) {
label = whereTo;
}
public String readLabel() {
return label;
}
}
public Destination dest(String s) {
return new GDestination(s);
}
public Contents cont() {
return new Content();
}
}
class TestGoods {
public static void main(String[] args) {
Goods p = new Goods();
Contents c = p.cont();
Destination d = p.dest("Beijing");
}
}
在这个例子里cContent和GDestination被定义在了类Goods内部Qƈ且分别有着protected和private修饰W来控制讉KU别。Content代表着Goods的内容,而GDestination代表着Goods的目的地。它们分别实C两个接口Content和Destination。在后面的mainҎ里,直接?Contents c和Destination dq行操作Q你甚至q这两个内部cȝ名字都没有看见!q样Q内部类的第一个好处就体现出来了――隐藏你不想让别人知道的操作Q也卛_装性?
同时Q我们也发现了在外部cM用范围之外得到内部类对象的第一个方法,那就是利用其外部cȝҎ创徏q返回。上例中的cont()和dest()Ҏ是q么做的。那么还有没有别的方法呢Q当然有Q其语法格式如下Q?/p>
outerObject=new outerClass(Constructor Parameters);
outerClass.innerClass innerObject=outerObject.new InnerClass(Constructor Parameters);
注意在创建非静态内部类对象Ӟ一定要先创v相应的外部类对象。至于原因,也就引出了我们下一个话题―?/p>
非静态内部类对象有着指向其外部类对象的引?/strong>
对刚才的例子E作修改Q?/p>
public class Goods {
private valueRate=2;
private class Content implements Contents {
private int i = 11*valueRate;
public int value() {
return i;
}
}
protected class GDestination implements Destination {
private String label;
private GDestination(String whereTo) {
label = whereTo;
}
public String readLabel() {
return label;
}
}
public Destination dest(String s) {
return new GDestination(s);
}
public Contents cont() {
return new Content();
}
}
修改的部分用蓝色昄了。在q里我们lGoodscd加了一个private成员变量valueRateQ意义是货物的h值系敎ͼ在内部类Content的方法value()计算价值时把它乘上。我们发玎ͼvalue()可以讉KvalueRateQ这也是内部cȝW二个好处――一个内部类对象可以讉K创徏它的外部cd象的内容Q甚臛_括私有变量!q是一个非常有用的Ҏ,为我们在设计时提供了更多的思\和捷径。要惛_现这个功能,内部cd象就必须有指向外部类对象的引用。Java~译器在创徏内部cd象时Q隐式的把其外部cd象的引用也传了进dƈ一直保存着。这样就使得内部cd象始l可以访问其外部cd象,同时q也是ؓ什么在外部cM用范围之外向要创建内部类对象必须先创建其外部cd象的原因?/p>
有h会问Q如果内部类里的一个成员变量与外部cȝ一个成员变量同名,也即外部cȝ同名成员变量被屏蔽了Q怎么办?没事QJava里用如下格式表达外部cȝ引用Q?/p>
outerClass.this
有了它,我们׃怕这U屏蔽的情况了?/p>
静态内部类
和普通的cMP内部cM可以有静态的。不q和非静态内部类相比Q区别就在于静态内部类没有了指向外部的引用。这实际上和C++中的嵌套cd相像了,Java内部cMC++嵌套cL大的不同在于是否有指向外部的引用这一点上Q当然从设计的角度以及以它一些细节来讲还有区别?/p>
除此之外Q在M非静态内部类中,都不能有静态数据,静态方法或者又一个静态内部类Q内部类的嵌套可以不止一层)。不q静态内部类中却可以拥有q一切。这也算是两者的W二个区别吧?/p>
局部内部类
是的QJava内部cM可以是局部的Q它可以定义在一个方法甚至一个代码块之内?/p>
public class Goods1 {
public Destination dest(String s) {
class GDestination implements Destination {
private String label;
private GDestination(String whereTo) {
label = whereTo;
}
public String readLabel() { return label; }
}
return new GDestination(s);
}
public static void main(String[] args) {
Goods1 g= new Goods1();
Destination d = g.dest("Beijing");
}
}
上面是q样一个例子。在Ҏdest中我们定义了一个内部类Q最后由q个Ҏq回q个内部cȝ对象。如果我们在用一个内部类的时候仅需要创建它的一个对象ƈ创给外部Q就可以q样做。当Ӟ定义在方法中的内部类可以使设计多样化Q用途绝不仅仅在q一炏V?/p>
下面有一个更怪的例子Q?/p>
public class Goods2{
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("slip");
String s = ts.getSlip();
}
}
public void track() { internalTracking(true); }
public static void main(String[] args) {
Goods2 g= new Goods2();
g.track();
}
}
你不能在if之外创徏q个内部cȝ对象Q因已经出了它的作用域。不q在~译的时候,内部cTrackingSlip和其他类一样同时被~译Q只不过它由它自q作用域,出了这个范围就无效Q除此之外它和其他内部类q没有区别?/p>
匿名内部c?
java的匿名内部类的语法规则看上去有些古怪,不过如同匿名数组一P当你只需要创Z个类的对象而且用不上它的名字时Q用内部类可以使代码看上去z清楚。它的语法规则是q样的:
new interfacename(){......}; ?new superclassname(){......};
下面接着前面l箋举例子:
public class Goods3 {
public Contents cont(){
return new Contents(){
private int i = 11;
public int value() {
return i;
}
};
}
}
q里Ҏcont()使用匿名内部cȝ接返回了一个实C接口Contents的类的对象,看上ȝ十分简z?/p>
在java的事件处理的匿名适配器中Q匿名内部类被大量的使用。例如在惛_闭窗口时加上q样一句代码:
frame.addWindowListener(new WindowAdapter(){
public void windowClosing(WindowEvent e){
System.exit(0);
}
});
有一炚w要注意的是,匿名内部cȝ于没有名字,所以它没有构造函敎ͼ但是如果q个匿名内部cȝ承了一个只含有带参数构造函数的父类Q创建它的时候必d上这些参敎ͼq在实现的过E中使用super关键字调用相应的内容Q。如果你惌初始化它的成员变量,有下面几U方法:
1.如果是在一个方法的匿名内部c,可以利用q个Ҏ传进你想要的参数Q不q记住,q些参数必须被声明ؓfinal?
2.匿名内部类攚w成有名字的局部内部类Q这样它可以拥有构造函C?
3.在这个匿名内部类中用初始化代码块?
Z么需要内部类Q?/strong>
java内部cL什么好处?Z么需要内部类Q?/p>
首先举一个简单的例子Q如果你惛_C个接口,但是q个接口中的一个方法和你构想的q个cM的一个方法的名称Q参数相同,你应该怎么办?q时候,你可以徏一个内部类实现q个接口。由于内部类对外部类的所有内定w是可讉K的,所以这样做可以完成所有你直接实现q个接口的功能?/p>
不过你可能要质疑Q更改一下方法的不就行了吗?
的确Q以此作计内部类的理由,实在没有说服力?/p>
真正的原因是q样的,java中的内部cd接口加在一P可以的解军_被C++E序员抱怨java中存在的一个问题――没有多l承。实际上QC++的多l承设计h很复杂,而java通过内部cd上接口,可以很好的实现多l承的效果?br />
早在Java 1.2推出之时QJavaq_中就引入了一个新的支持:java.lang.ThreadLocalQ给我们在编写多U程E序时提供了一U新的选择。用这个工L可以很简z地~写Z的多线E程序,虽然ThreadLocal非常有用Q但是似乎现在了解它、用它的朋友还不多? ThreadLocal是什?/strong> ThreadLocal是什么呢Q其实ThreadLocalq是一个线E的本地实现版本Q它q不是一个ThreadQ而是thread local variableQ线E局部变量)。也许把它命名ؓThreadLocalVar更加合适。线E局部变量(ThreadLocalQ其实的功用非常单,是为每一个用该变量的线E都提供一个变量值的副本Q是每一个线E都可以独立地改变自q副本Q而不会和其它U程的副本冲H。从U程的角度看Q就好像每一个线E都完全拥有该变量。线E局部变量ƈ不是Java的新发明Q在其它的一些语a~译器实玎ͼ如IBM XL FORTRANQ中Q它在语a的层ơ提供了直接的支持。因为Java中没有提供在语言层次的直接支持,而是提供了一个ThreadLocal的类来提供支持,所以,在Java中编写线E局部变量的代码相对比较W拙Q这也许是线E局部变量没有在Java中得到很好的普及的一个原因吧?/p> ThreadLocal的设?/strong> 首先看看ThreadLocal的接口: Object get() ; // q回当前U程的线E局部变量副?br /> protected Object initialValue(); // q回该线E局部变量的当前U程的初始?br /> void set(Object value); // 讄当前U程的线E局部变量副本的?/p> ThreadLocal?个方法,其中值得注意的是initialValue()Q该Ҏ是一个protected的方法,昄是ؓ了子c重写而特意实现的。该Ҏq回当前U程在该U程局部变量的初始|q个Ҏ是一个gq调用方法,在一个线E第1ơ调用get()或者set(Object)时才执行Qƈ且仅执行1ơ。ThreadLocal中的实实现直接q回一个nullQ?/p> protected Object initialValue() { return null; } ThreadLocal是如何做Cؓ每一个线E维护变量的副本的呢Q其实实现的思\很简单,在ThreadLocalcM有一个MapQ用于存储每一个线E的变量的副本。比如下面的CZ实现Q?/p> public class ThreadLocal public void set(Object newValue) public Object initialValue() 当然Q这q不是一个工业强度的实现Q但JDK中的ThreadLocal的实现M思\也类g此?/p> ThreadLocal的?/strong> 如果希望U程局部变量初始化其它|那么需要自己实现ThreadLocal的子cdƈ重写该方法,通常使用一个内部匿名类对ThreadLocalq行子类化,比如下面的例子,SerialNumcMؓ每一个类分配一个序P public class SerialNum private static int nextSerialNum = 0; public static int get() SerialNumcȝ使用非常地单,因ؓget()Ҏ是static的,所以在需要获取当前线E的序号Ӟ单地调用Q?/p> int serial = SerialNum.get(); 卛_?/p> 在线E是zd的ƈ且ThreadLocal对象是可讉K的时Q该U程持有一个到该线E局部变量副本的隐含引用Q当该线E运行结束后Q该U程拥有的所以线E局部变量的副本都将失效Qƈ{待垃圾攉器收集?/p> ThreadLocal与其它同步机制的比较 ThreadLocal和其它同步机制相比有什么优势呢QThreadLocal和其它所有的同步机制都是Z解决多线E中的对同一变量的访问冲H,在普通的同步机制中,是通过对象加锁来实现多个线E对同一变量的安全访问的。这时该变量是多个线E共享的Q用这U同步机刉要很l致地分析在什么时候对变量q行dQ什么时候需要锁定某个对象,什么时候释放该对象的锁{等很多。所有这些都是因为多个线E共享了资源造成的。ThreadLocal׃另一个角度来解决多线E的q发讉KQThreadLocal会ؓ每一个线E维护一个和该线E绑定的变量的副本,从而隔M多个U程的数据,每一个线E都拥有自己的变量副本,从而也没有必要对该变量进行同步了。ThreadLocal提供了线E安全的׃n对象Q在~写多线E代码时Q可以把不安全的整个变量装qThreadLocalQ或者把该对象的特定于线E的状态封装进ThreadLocal?/p> ׃ThreadLocal中可以持有Q何类型的对象Q所以用ThreadLocal get当前U程的值是需要进行强制类型{换。但随着新的Java版本Q?.5Q将模版的引入,新的支持模版参数的ThreadLocal<T>cd从中受益。也可以减少强制cd转换Qƈ一些错误检查提前到了编译期Q将一定程度地化ThreadLocal的用?/p> ȝ 当然ThreadLocalq不能替代同步机Ӟ两者面向的问题领域不同。同步机制是Z同步多个U程对相同资源的q发讉KQ是Z多个U程之间q行通信的有效方式;而ThreadLocal是隔d个线E的数据׃nQ从Ҏ上就不在多个U程之间׃n资源Q变量)Q这样当然不需要对多个U程q行同步了。所以,如果你需要进行多个线E之间进行通信Q则使用同步机制Q如果需要隔d个线E之间的׃n冲突Q可以用ThreadLocalQ这极大地化你的程序,使程序更加易诅R简z?/p> |