Inner Classes 在平常的应用中虽然不是很多,但是仍然有很多细节问题值得注意,需要掌握。因此,这里对这方面内容做一小结。
Inner Classes,顾名思义,就是定义在另一个类中的类,之所以出现这一概念,主要是因为它具有如下两个特点:
- inner classes中的方法可以访问类定义域内的所有数据, 哪怕数据是private的。
- inner classes对同包内的其它类是不可见的。
常规内部类
下面,我将针对一段具体代码,对Inner Classes的特性和应用作具体的阐述。
public class TimerTestForInnerClass {
public static void main(String[] args) {
TalkingClock clock = new TalkingClock(100, true);
clock.start();
JOptionPane.showMessageDialog(null, "Quit Program?");
System.exit(0);
}
}
class TalkingClock {
private int interval;
private boolean beep;
public TalkingClock(int interval, boolean beep) {
this.interval = interval;
this.beep = beep;
}
public void start() {
ActionListener listener = new TimePrinter();
Timer t = new Timer(interval, listener);
t.start();
}
public class TimePrinter implements ActionListener {
public void actionPerformed(ActionEvent event) {
Date now = new Date();
System.out.println("At the tone, the time is " + now);
if (beep) Toolkit.getDefaultToolkit().beep();
}
}
}
在上面的代码中,TimePrinter是TalkingClock的内部类。仔细观察,发现actionPerformed方法中引用的beep变量在TimePrinter中并没有定义,它是外部类TalkingClock的成员变量。这就是inner classes特点一的展示,之所以可以如此,是因为inner class在被创建时获得了外部类的一个引用,这个引用是由编译器自动传给内部类的构造函数的,因此,内部类中使用的beep变量的完整形式其实是:
if (TalkingClock.this.beep) Toolkit.getDefaultToolkit().beep();
上述代码中的OuterClass.this代表的就是外部类的引用。
如果内部类是public的,那么我们也可以在外部类以外来创建内部类,方法如下:
TalkingClock jabberer = new TalkingClock(1000, true);
TalkingClock.TimePrinter listener = jabberer.new TimePrinter();
Local Inner Classes 局部内部类
内部类除了可以作为外部类的成员,还可以定义在外部类的方法中,这样的内部类称为:Local Inner Classes。Local Inner Classes不需要访问权限修饰符(access specifier),因为它们的作用域受它们被定义的块域的限制。Local Inner Classes的一个重要的优点是,除了定义它们的方法以外,外界任何类或方法都无法访问它们。因此,当一个内部类只需要被一个方法使用,并且想屏蔽任何其他方法时,就可以将它们定义为Local的。
public void start() {
class TimePrinter implements ActionListener {
public void actionPerformed(ActionEvent event) {
Date now = new Date();
System.out.println("At the tone, the time is " + now);
if (beep) Toolkit.getDefaultToolkit().beep();
}
}
ActionListener listener = new TimePrinter();
Timer t = new Timer(interval, listener);
t.start();
}
上面的代码就是Local Inner Class的应用实例。
Local Inner Class不光可以使用Outer Class的成员变量,还可使用它所在方法的参数以及方法中的局部变量。但要注意一点,这些外部引用变量都必须是final型,否则会收到编译器错误。见下面的代码:
public void start(int interval, final boolean beep) {
class TimePrinter implements ActionListener {
public void actionPerformed(ActionEvent event) {
Date now = new Date();
System.out.println("At the tone, the time is " + now);
if (beep) Toolkit.getDefaultToolkit().beep();
}
}
ActionListener listener = new TimePrinter();
Timer t = new Timer(interval, listener);
t.start();
}
我们来简单了解下beep参数是如何传入内部类的方法中。直观来看,beep变量是无法存留到actionPerformed方法执行之时的,它在start()方法执行结束之后就被回收了,而actionPerformed则要过短时间才执行,因此,为了正常使用beep变量,我们需要为它做一个copy,并将这个copy传进内部类中。所以,内部类中所谓的beep,其实是原有变量的副本。
TIP:为什么只有final型的参数或局部变量可以传入inner class?(整理自Core Java,如有不妥,敬请指正)
在很多时候,这个final的限制其实为我们带来了很多不便,因为我们有的时候确实是想要在inner class中改变局部变量的,如下面这段代码:
int counter = 0;
Date[] dates = new Date[100];
for (int i = 0; i < dates.length; i++)
dates[i] = new Date() {
public int compareTo(Date other) {
counter++; // ERROR
return super.compareTo(other);
}
};
Arrays.sort(dates);
System.out.println(counter + " comparisons.");
我们只是想获取比较次数,但是由于内部类的final限制,这样的代码显然是不合法的。但如果直接加上final,我们的update目标又无法达到,所以,只能做如下的转换:
final int[] counter = new int[1];
for (int i = 0; i < dates.length; i++)
dates[i] = new Date() {
public int compareTo(Date other) {
counter[0]++;
return super.compareTo(other);
}
};
这样,虽然counter是final的,但是我们却可以随意改变它内部的值,从而变相的达到了update的目标。
事实上,在inner class起初创建时,这种从primitive型到array型的转换最开始是由编译器自动完成的,但是开发者们很害怕编译器在“背着他们”创建那么些对象,因此,最后还是决定加上final这个限制,如果需要在inner class中改变变量值,那就让programer自己去做转换。而且,也并不排除,随着Java语言的不断完善,会有更好的方式来替代final限制,达到原有的安全效果。
分享到:
相关推荐
在Android开发过程中,有时会遇到导入项目时出现错误警告,比如"Ignoring InnerClasses attribute for an anonymous inner class"。这个问题并不会阻止项目在Windows系统上运行,但可能会导致在其他平台如OS X上无法...
静态内部类 Static Inner 马克-to-win java视频的详细介绍
### SQL Server 中 DELETE 语句结合 INNER JOIN 的应用 #### 背景介绍 在数据库管理与维护过程中,经常会遇到需要删除表中的某些记录的情况。简单地使用 `DELETE` 语句可以删除单个表中的数据,但在多表关联的情况...
3. **连接顺序**:连接表的顺序可能会影响查询性能,通常先连接较小的表可以提高执行速度。 4. **多表连接**:当涉及到多个表的连接时,合理的连接顺序和分组方式对于优化查询至关重要。 #### 五、总结 通过以上...
SQLInner是一种专门用于防止SQL注入的工具或库,它的目标是确保应用程序的SQL查询不会被恶意输入所利用。在这个源码中,我们可以深入学习如何在编程中实现这样的防御机制。 SQL注入的常见方式包括通过HTTP请求传递...
彦舜原创,CSDN首发:希望对你有所帮助,仅此而已。内容工整规范,是作者本人,逐句敲出来,同时也含有个人的一些独见。
场景:有三个List变量,分别为list1、list2、list3 List,Object>> list1 = new ...在sql中就是"select * from list1 inner join list2 on list1.column1=list2.column2 inner join list3 on list1.column3=list
INNER JOIN table2 t2 ON t1.fid = t2.fid) INNER JOIN table3 t3 ON t1.mid = t3.mid; 这里使用了3表关联,对于多表关联的 INNER JOIN 写法有一个技巧 1. 先写最简单的2表关联 INNER JOIN 2. 然后使用 () 从 FROM ...
### inner join、left join、right join、outer join之间的区别 在数据库操作中,连接(Join)是一种非常重要的操作,用于组合两个或多个表中的数据。根据连接的方式不同,可以分为几种类型:`INNER JOIN`、`LEFT ...
对于高IoU样本,使用较小的辅助边界框计算损失可以加速收敛,而较大的辅助边界框适合于低IoU样本。然后,我们提出了Inner-IoU损失,通过辅助边界框计算IoU损失。对于不同的数据集和检测器,我们引入一个缩放因子比来...
SQL 外链接操作小结 inner join left join right join SQL 外链接操作是关系型数据库管理系统中的一种基本操作,用于从多个表中检索数据。外链接操作可以分为三种:inner join、left join 和 right join。 inner ...
内部类(Inner Classes)是Java编程语言中的一个特色特性,它允许我们在一个类的内部定义另一个类。这种设计模式在处理一些特定问题时非常有用,比如实现事件监听、封装复杂逻辑或者创建私有辅助类。内部类有四种...
此资源是Binner轮播图,可应用于广告位,类似于淘宝主页轮播。此资源用java写的,我还有kotlin版本在我的下载资源中。
利用inner join 查找出ERP系统中某销售订单所关联的生产任务单、入库单、领料单等单据情况。
Java 内部类(Inner Class)是 Java 语言的一个特性,它允许我们在一个类的内部定义另一个类。这种设计模式提供了更高的代码封装性和复用性,同时也为解决某些特定问题提供了方便。在“java_innerclass_instance.rar...
内部类分为几种类型,包括静态成员类(Static member class)、局部内部类(Local inner class)、匿名内部类(Anonymous inner class)以及嵌套接口(Nested interface)。在本讨论中,我们将主要聚焦于静态成员类...
### SQL中的INNER JOIN详解 #### 一、INNER JOIN的基本概念 **INNER JOIN** 是SQL中最常用的连接类型之一,主要用于从两个或多个表中提取数据,其中仅返回那些满足连接条件的记录。简单来说,INNER JOIN返回的是两...
Java中的内部类(Inner Class)是Java语言特性中一个独特且强大的工具,它允许在一个类的内部定义另一个类。这种设计模式提供了许多便利,包括封装、代码复用、以及对匿名类的支持。本文将深入探讨Java内部类的概念...
**Binner轮播图**是一种常见的用户界面组件,尤其在电商、新闻和其他需要展示一系列可滑动内容的应用中。在Android开发中,实现这样的功能通常需要利用到自定义视图或者第三方库。在这个资源中,开发者提供了一个用...