桔子园
欢迎大家访问我的个人网站
萌萌的IT人
BlogJava
首页
新随笔
联系
聚合
管理
随笔-128 评论-55 文章-5 trackbacks-0
[转]内部类详解
内部类详解
1、定义
一个类的定义放在另一个类的内部,这个类就叫做内部类。
Java代码
public
class
First {
public
class
Contents{
public
void
f(){
System.out.println(
"In Class First's inner Class Contents method f()"
);
}
}
}
像这样的,Contents就叫做内部类
内部类了解外围类,并能与之通信(后面详细讲)
2、链接到外围类
创建了内部类对象时,它会与创造它的外围对象有了某种联系,于是能访问外围类的所有成员,不需任何特殊条件。
Java代码
public
class
First {
public
class
Contents{
public
void
getStr(){
System.out.println(
"First.str="
+str);
}
}
private
String str;
}
在内部类Contents中,可以使用外围类First的字段str。
那么,它是如何实现的呢?
是这样的,用外围类创建内部类对象时,此内部类对象会秘密的捕获一个指向外围类的引用,于是,可以通过这个引用来访问外围类的成员。
通常,这些都是编译器来处理,我们看不到,也不用关心这个。
正是因为如此,我们创建内部类对象时,必须与外围类对象相关联。
注:嵌套类(后面会讲到)除外。
3、使用关键字.this与.new
内部类中得到当前外围类对象的引用,可以使用.this关键字,注意与new的区别
Java代码
private
int
num ;
public
Test2(){
}
public
Test2(
int
num){
this
.num = num;
}
private
class
Inner{
public
Test2 getTest2(){
return
Test2.
this
;
}
public
Test2 newTest2(){
return
new
Test2();
}
}
public
static
void
main(String [] args){
Test2 test =
new
Test2(
5
);
Test2.Inner inner = test.
new
Inner();
Test2 test2 = inner.getTest2();
Test2 test3 = inner.newTest2();
System.out.println(test2.num);
System.out.println(test3.num);
}
输出结果为5 0
使用.this后,得到时创建该内部类时使用的外围类对象的引用,new则是创建了一个新的引用。
.new关键字
如果想直接创建一个内部类对象,而不是通过外围类对象的方法来得到,可以使用.new关键字
形式是这样的:
Java代码
OutClass.InnerClass obj = outClassInstance.
new
InnerClass();
必须是外围类对象.new,而不能是外围类.new
Java代码
public
class
First {
public
class
Contents{
public
void
f(){
System.out.println(
"In Class First's inner Class Contents method f()"
);
}
public
void
getStr(){
System.out.println(
"First.str="
+str);
}
}
public
static
void
main(String [] args){
First first =
new
First();
First.Contents contents = first.
new
Contents();
contents.f();
}
}
必须通过外围类First的对象first来创建一个内部类的对象
而且需要注意的是,在创建外围类对象之前,不可能创建内部类的对象(嵌套类除外)。
4、内部类与向上转型
将内部类向上转型为基类型,尤其是接口时,内部类就有了用武之地。
Java代码
public
interface
Shape {
public
void
paint();
}
public
class
Painter {
private
class
InnerShape
implements
Shape{
public
void
paint(){
System.out.println(
"painter paint() method"
);
}
}
public
Shape getShape(){
return
new
InnerShape();
}
public
static
void
main(String []args){
Painter painter =
new
Painter();
Shape shape = painter. getShape();
shape.paint();
}
}
此时,内部类是private的,可以它的外围类Painter以外,没人能访问。
这样,private内部类给累的设计者提供了一种途径,通过这种方式可以完全阻止任何依赖于类型的编码,并完全隐藏实现的细节。
5、方法内的类
可以在方法内创建一个类。
Java代码
public
void
test(){
ass Inner{
public
void
method(){
ystem.out.println(
"在方法内创建的类"
);
}
}
值得注意的是:方法内创建的类,不能加访问修饰符。
另外,方法内部的类也不是在调用方法时才会创建的,它们一样也被编译了(怎么知道的?后面会有讲解)。
6、匿名内部类
Java代码
public
class
Painter {
ublic Shape getShape(){
return
new
Shape(){
public
void
paint(){
System.out.println(
"painter paint() method"
);
}
};
public
static
void
main(String [] args){
Painter painter =
new
Painter();
Shape shape = painter.getShape();
shape.paint();
}
}
public
interface
Shape {
ublic
void
paint();
}
注意,匿名内部类后面的分号不可缺少!
匿名类,顾名思义,就是没有名称。
getShape()方法里,就使用了匿名内部类。
看上去很奇怪,不符合传统的写法?
第一眼看上去确实是这样的。
这样写,意思是创建了一个实现了Shape的匿名类的对象。
匿名类可以创建,接口,抽象类,与普通类的对象。创建接口时,必须实现接口中所有方法。
这是无参的,如果需要参数呢?
可以直接传。
Java代码
public
class
B {
public
A getA(
int
num){
return
new
A(num){
};
}
}
public
class
A {
private
int
num;
public
A(
int
num){
this
.num = num;
}
public
A(){
}
}
Ok,在这个例子中,可以为A的构造方法传入一个参数。在匿名内部类中,并没有使用到这个参数。
如果使用到了这个参数,那么这个参数就必须是final的。
Java代码
public
class
B {
public
A getA(
final
int
num){
return
new
A(num){
public
int
getNum(){
return
num;
}
};
}
}
public
class
A {
private
int
num;
public
A(
int
num){
this
.num = num;
}
public
A(){
}
}
如果不是final的,编译器就会提示出错。
另外,还可以在匿名内部类里定义属性
由于类是匿名的,自然没有构造器,如果想模仿构造器,可以采用实例初始化({})
Java代码
public
A getA(){
return
new
A(){
int
num =
0
;
String str;
{
str =
"javaeye"
;
System.out.println(
"hello robbin"
);
}
};
}
匿名内部类通过实例初始化,可以达到类似构造器的效果~
另外可以通过匿名内部类来改造工厂方法。
Java代码
public
interface
Service {
public
void
method1();
}
public
interface
ServiceFactory {
Service getService();
}
public
class
Implemention1
implements
Service{
public
void
method1(){
System.out.println(
"In Implemention1 method method1()"
);
}
public
static
ServiceFactory factory =
new
ServiceFactory(){
public
Service getService(){
return
new
Implemention1();
}
};
}
public
class
Implemention2
implements
Service {
public
void
method1(){
System.out.println(
"in Implemention2 method method1()"
);
}
public
static
ServiceFactory factory =
new
ServiceFactory(){
public
Service getService(){
return
new
Implemention2();
}
};
}
public
class
Test {
public
static
void
main(String []args){
service(Implemention1.factory);
service(Implemention2.factory);
ServiceFactory factory1 = Implemention1.factory;
Service service1 = factory1.getService();
service1.method1();
ServiceFactory factory2 = Implemention1.factory;
Service service2 = factory2.getService();
service2.method1();
}
}
在Implemention1和2中匿名内部类用在字段初始化地方。
这样定义的工厂方法,代码上看起来是不是优雅一些?
7、嵌套类
static的内部类就叫做嵌套类
前面提到了很多次,嵌套类是个例外
使用嵌套类时有两点需要注意:
a、创建嵌套类对象时,不需要外围类
b、在嵌套类中,不能像普通内部类一样访问外围类的非static成员
Java代码
public
class
StaticClass {
private
int
num;
private
static
int
sum =
2
;
private
static
class
StaticInnerClass{
public
int
getNum(){
//只能访问sum,不能访问num
return
sum;
}
}
}
public
class
Test {
public
static
void
main(String [] args){
//可以直接通过new来创建嵌套类对象
StaticClass.StaticInnerClass inner =
new
StaticClass.StaticInnerClass();
inner.getNum();
}
}
另外,嵌套类还有特殊之处,就是嵌套类中可以有static方法,static字段与嵌套类,而普通内部类中不能有这些。
8、内部类标识符
我们知道每个类会产生一个.class文件,文件名即为类名
同样,内部类也会产生这么一个.class文件,但是它的名称却不是内部类的类名,而是有着严格的限制:外围类的名字,加上$,再加上内部类名字。
前面说到得定义在方法内的内部类,不是在调用方法时生成,而是与外围类一同编译,就可以通过查看.class文件的方式来证明。
9、为何要内部类?
a、内部类提供了某种进入外围类的窗户。
b、也是最吸引人的原因,每个内部类都能独立地继承一个接口,而无论外围类是否已经继承了某个接口。
因此,内部类使多重继承的解决方案变得更加完整。
在项目中,需要多重继承,如果是两个接口,那么好办,接口支持多重继承。
如果是两个类呢?这时只有使用内部类了。
Java代码
public
interface
One {
public
void
inOne();
}
public
interface
Two {
public
void
inTwo();
}
//两个接口,用普通类就可实现多重继承
public
class
CommonClass
implements
One,Two {
public
void
inOne(){
System.out.println(
"CommonClass inOne() method"
);
}
public
void
inTwo(){
System.out.println(
"CommonClass inTwo() method"
);
}
}
public
abstract
class
Three {
public
abstract
void
inThree();
}
public
abstract
class
Four {
public
abstract
void
inFour();
}
//两个抽象类,使用普通类无法实现多重继承
//使用内部类可以实现
public
class
Contents
extends
Three {
public
void
inThree(){
System.out.println(
"In Contents inThress() method"
);
}
public
class
InnerFour
extends
Four{
public
void
inFour(){
System.out.println(
"In Contents"
);
}
}
}
另外,还有好多地方可以使用内部类。读过hibernate源代码的同学,应该可以发现,里面有好多内部类。
最常见的内部类,应该是Map.Entry了,可以看看源代码~
总结:
内部类的特性大致就是上述了,特性很直观,了解了之后,使用也很简单。
但是,何时使用我说的并不是很明确,因为本人知识有限,使用内部类也不是很多。项目中很少用,好像就是ActiveMQ那里用了一些。
不过,相信大家在了解了内部类的特性之后,再随着时间的推移,慢慢积累经验,应该会做出自己的判断,会在何时使用内部类,怎样应用了。
Author
: orangelizq
email: orangelizq@163.com
欢迎大家访问我的个人网站
萌萌的IT人
posted on 2010-04-21 15:03
桔子汁
阅读(484)
评论(0)
编辑
收藏
所属分类:
J2SE
新用户注册
刷新评论列表
只有注册用户
登录
后才能发表评论。
网站导航:
博客园
IT新闻
知识库
C++博客
博问
相关文章:
[转]处理线程泄露
[转]内部类详解
[转]领略内部类的“内部”
[转]Java中的内部类
[转]一个计算机专业学生几年的Java编程经验汇总
[转]共享内存在java中的实现
[转]Find a way out of the ClassLoader maze
[转]走出ClassLoader迷宫
对象初始化全过程
[转]Java泛型编程指南
<
2010年4月
>
日
一
二
三
四
五
六
28
29
30
31
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
1
2
3
4
5
6
7
8
常用链接
我的随笔
我的评论
我的参与
最新评论
留言簿
(9)
给我留言
查看公开留言
查看私人留言
随笔分类
(124)
Flex
J2EE(25)
J2SE(23)
OSGI
other(19)
SOA(11)
Web Service(33)
开源软件(3)
读后感(2)
随想(8)
随笔档案
(127)
2013年5月 (1)
2012年8月 (1)
2010年11月 (3)
2010年8月 (1)
2010年4月 (3)
2009年12月 (4)
2009年10月 (1)
2009年9月 (1)
2009年8月 (5)
2009年7月 (10)
2009年6月 (1)
2009年5月 (5)
2009年4月 (1)
2009年3月 (2)
2008年12月 (6)
2008年11月 (1)
2008年9月 (4)
2008年7月 (3)
2008年5月 (1)
2008年4月 (4)
2008年3月 (2)
2008年2月 (1)
2008年1月 (7)
2007年12月 (6)
2007年11月 (2)
2007年10月 (3)
2007年9月 (9)
2007年8月 (4)
2007年7月 (33)
2007年6月 (2)
文章分类
(5)
Java技术(2)
other
SOA
WebService 技术(3)
中间件相关
文章档案
(5)
2007年9月 (3)
2007年7月 (1)
2007年6月 (1)
Good Blog
beansoft
dreamstone
周爱民
周筠老师
江南白衣
技术网站
IBM DevelopWork
搜索
积分与排名
积分 - 265145
排名 - 215
最新评论
1. re: XML解析技术研究(一)[未登录]
我有份XML,我需要解开明码,用什么格式转换都可以,有偿价格。你看下能做不,报个价格我。我QQ;200879086 麻烦看见跟我联系
--小林
2. re: JNDI 学习
评论内容较长,点击标题查看
--didafor
3. re: 用Java实现基于SOAP的XML文档网络传输及远程过程调用(RPC)[未登录]
接受SOAP端代码中,Enumeration e = bodyEntries.elements();
//如果当前情况下,向量中有更多条目,怎么选择想要的条目
--Michael
4. re: [转]java中byte转换int时为何与0xff进行与运算
lz你是在害人阿~
“1000 0000代表的就是-1 ”
--javachow
5. re: XML解析技术研究(一)[未登录]
文章不错,收藏了。
--chchpd
阅读排行榜
1. [转]java中byte转换int时为何与0xff进行与运算(56424)
2. 消息中间件原理及JMS简介(2) (21400)
3. WSDL样式详解(17421)
4. XML解析技术研究(一)(17085)
5. 消息中间件原理及JMS简介(1)(13757)