Java基础(十三)内部类(inner class)
1.内部类是定义在另一个类中的类。使用内部类的原因有:
- 内部类方法可以访问该类定义所在的作用域中的数据,包括私有的数据
- 内部类可以对同一个包中的其他类隐藏起来
- 当想要定义一个回调函数且不想编写大量代码时,使用匿名内部类比较便捷
2.使用内部类访问对象状态
内部类既可以访问自身的数据域,也可以访问创建它的外围类对象的数据域。
例如,外部类TalkingClock中有数据域beep,而内部类中没有定义数据域beep,但是却可以直接通过if (beep) Toolkit.getDefaultToolkit().beep();来引用外部类TalkingClock的对象的数据域。
public class InnerClassTest
{
public static void main(String[] args)
{
TalkingClock clock = new TalkingClock(1000, true);
clock.start();
}
} 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)
{
System.out.println("At the tone, the time is " + new Date());
if (beep) Toolkit.getDefaultToolkit().beep();
}
}
}
3.使用内部类访问外部类的对象的作用域的具体实现
(1)内部类对象总有一个隐式引用,它指向了创建它的外部类对象,这个引用在内部类中的定义是不可见的,将外围类对象的引用称为outer,则上面的引用语句可以修改为:
if (outer.beep) Toolkit.getDefaultToolkit().beep();
(2)编译器修改了所有的内部类的构造器,添加了一个外围类引用的参数,例如:编译器为TimePrinter类生成了一个默认的构造器,用来将外围类对象传递进来
public TimerPrinter(Talking clock){
outer = clock;
}
(3)在start方法中,执行ActionListener listener = new TimePrinter();时编译器实际上会将外围类对象this传递到TimerPrinter的构造器中:
ActionListener listener = new TimePrinter(this);
到此,就实现了内部类访问外围类的对象的数据域。
使用正规语法在内部类中引用外部对象的数据域:
if (TalkingClock.this.beep) Toolkit.getDefaultToolkit().beep();
使用正规语法在内部类中编写内部对象的构造器
ActionListener listener = this.new TimePrinter();
显式地命名将外围类引用设置为其他的对象
TalkingClock jabberer = new TalkingClock(100,true);
TalkingClock.TimerPrinter listener = jabberer.new TimerPrinter();
4.局部内部类
(1)观察上述代码可以看到,TimerPrinter这个类只在start方法中创建这个类型的对象时使用了一次,这时就可以将TimerPrinter类定义成start方法的局部类,局部类不能用public或private声明,它的作用域被限制在这个局部类的块中。局部类的优势就是可以对外部世界完全隐藏起来,即使TalkingClock类中的其他代码也不能访问它,除了start方法,其他没有任何方法知道这个类的存在。
public void start()
{
class TimePrinter implements ActionListener
{
public void actionPerformed(ActionEvent event)
{
System.out.println("At the tone, the time is " + new Date());
if (beep) Toolkit.getDefaultToolkit().beep();
}
}
ActionListener listener = new TimePrinter();
Timer t = new Timer(interval, listener);
t.start();
}
(2)局部类还有一个优点:它们不仅能够访问包含它们的外部类,还可以访问局部变量,但是,局部类访问的局部变量必须为final,即一旦赋值就不可变。
class TalkingClock
{
private int interval;
private final boolean beep;
public void start(int interval, boolean beep)
{
...
}
}
5.匿名内部类
如果只创建内部类的一个对象,那么这个内部类就不用命名了,可以直接使用代码块替代,这种类被称为匿名内部类
public void start(int interval, boolean beep)
{
ActionListener listener = new ActionListener()
{
public void actionPerformed(ActionEvent event)
{
System.out.println("At the tone, the time is " + new Date());
if (beep) Toolkit.getDefaultToolkit().beep();
}
};
Timer t = new Timer(interval, listener);
t.start();
}
6.静态内部类
如果使用内部类只是为了把一个类隐藏在另外一个类的内部,而不需要在内部类中引用外围类对象,这个时候可以将内部类声明为static,以便取消产生的引用。
例如,如果想要计算一下数组中最大值和最小值,可以有常规方法和静态内部类方法两种。
(1)常规实现
- 首先需要写一个包含两个值的Pair类:
package test;
public class Pair {
private double first;
private double second;
public Pair(double f, double s)
{
first = f;
second = s;
}
public double getFirst()
{
return first;
}
public double getSecond()
{
return second;
}
}
- 然后需要写一个比较数组中的元素然后得到最大值和最小值的类,并返回一个Pari类型的对象
package test;
public class ArrayAlg {
public static Pair minmax(double[] values)
{
double min = Double.POSITIVE_INFINITY;
double max = Double.NEGATIVE_INFINITY;
for (double v : values)
{
if (min > v) min = v;
if (max < v) max = v;
}
return new Pair(min, max);
}
}
- 最后可以这么来调用:
package test;
public class Main {
public static void main(String[] args)
{
double[] d = new double[20];
for (int i = 0; i < d.length; i++)
d[i] = 100 * Math.random();
Pair p = ArrayAlg.minmax(d);
System.out.println("min = " + p.getFirst());
System.out.println("max = " + p.getSecond());
}
}
这样一来确实是实现了,没有问题。但是,由于在大型项目中很可能会有类名和Pair冲突的情况,同时,两个类也显得很麻烦,并且最为重要的是,在Pari对象中不需要引用任何其他的对象,因此可以使用静态内部类来解决这些问题。
(2)静态内部类实现
package staticInnerClass; /**
* This program demonstrates the use of static inner classes.
* @version 1.02 2015-05-12
* @author Cay Horstmann
*/
public class StaticInnerClassTest
{
public static void main(String[] args)
{
double[] d = new double[20];
for (int i = 0; i < d.length; i++)
d[i] = 100 * Math.random();
ArrayAlg.Pair p = ArrayAlg.minmax(d);
System.out.println("min = " + p.getFirst());
System.out.println("max = " + p.getSecond());
}
} class ArrayAlg
{ public static class Pair
{
private double first;
private double second; public Pair(double f, double s)
{
first = f;
second = s;
} public double getFirst()
{
return first;
} public double getSecond()
{
return second;
}
} public static Pair minmax(double[] values)
{
double min = Double.POSITIVE_INFINITY;
double max = Double.NEGATIVE_INFINITY;
for (double v : values)
{
if (min > v) min = v;
if (max < v) max = v;
}
return new Pair(min, max);
}
}
将Pair类放在ArrayAlg类中并声明为static,然后就可以通过下面的代码同时调用这两个类:使用ArrayAlg.Pair也不会出现重名的困扰。
ArrayAlg.Pair p = ArrayAlg.minmax(d);
System.out.println("min = " + p.getFirst());
System.out.println("max = " + p.getSecond());
如果不将Pair声明为static,由于没有可用的隐式(静态)ArrayAlg类型对象初始化内部类对象,编译器就会报错
Exception in thread "main" java.lang.Error: Unresolved compilation problem:
No enclosing instance of type ArrayAlg is accessible. Must qualify the allocation with an enclosing instance of type ArrayAlg (e.g. x.new A() where x is an instance of ArrayAlg). at staticInnerClass.ArrayAlg.minmax(StaticInnerClassTest.java:76)
at staticInnerClass.StaticInnerClassTest.main(StaticInnerClassTest.java:15)
静态类的对象在代码编译前就已经存在于内存中的对象,如果Pair类中不加static,在静态方法minmax中会return new Pair(min, max);此时,由于Pari不是静态类,所以在程序代码编译前无法执行,因此就会出现无法编译的错误。
Java基础(十三)内部类(inner class)的更多相关文章
- java基础之 内部类
Java中的内部类共分为四种: 静态内部类static inner class (also called nested class) 成员内部类member inner class 局部内部类loca ...
- Java基础(53):内部类(转)
java中的内部类总结 内部类不是很好理解,但说白了其实也就是一个类中还包含着另外一个类 如同一个人是由大脑.肢体.器官等身体结果组成,而内部类相当于其中的某个器官之一,例如心脏:它也有自己的属性和行 ...
- Java基础(十三)--深拷贝和浅拷贝
在上篇文章:Java基础(十二)--clone()方法,我们简单介绍了clone()的使用 clone()对于基本数据类型的拷贝是完全没问题的,但是如果是引用数据类型呢? @Data @NoArgsC ...
- Java基础(五)--内部类
内部类简单来说就是把一个类的定义放到另一个类的定义内部 内部类分为:成员内部类.局部内部类.匿名内部类.静态内部类 成员内部类:最常见的内部类 public class Outter { privat ...
- java基础之 内部类 & 嵌套类
参考文档: 内部类的应用场景 http://blog.csdn.net/hivon/article/details/606312 http://wwty.iteye.com/blog/338628 定 ...
- java基础(十三)-----详解内部类——Java高级开发必须懂的
可以将一个类的定义放在另一个类的定义内部,这就是内部类. 为什么要使用内部类 为什么要使用内部类?在<Think in java>中有这样一句话:使用内部类最吸引人的原因是:每个内部类都能 ...
- java基础随笔-内部类
今天来复习下内部类的一些基础知识. 首先是内部类的分类: 1.成员内部类 2.静态内部类 3.匿名内部类 4.局部内部类 下面逐一来介绍下. 首先是成员内部类,就是将内部类作为一个成员变量来处理.具体 ...
- java基础语法-内部类与匿名内部类
1.成员内部类(声明在类内部&&方法之外) class Person{ String name = "韩梅梅"; int age; class Bird{ Stri ...
- JAVA基础知识|内部类
一.什么是内部类? 内部类(inner class)是定义在另一个类中的类 为什么使用内部类? 1)内部类方法可以访问该类定义所在的作用域中的数据,包括私有数据 2)内部类可以对同一个包中的其他类隐藏 ...
- Java基础加强-内部类及代理
/*内部类是一个编译时的概念,*/ 常规内部类.静态内部类.局部内部类.匿名内部类 1.常规内部类(常规内部类没有static修饰且定义在外部类类体中) 1.常规内部类中的方法可以直接使用外部类的实例 ...
随机推荐
- golang1.13中重要的新特新
本文索引 语言变化 数字字面量 越界索引报错的完善 工具链改进 GOPROXY GOSUMDB GOPRIVATE 标准库的新功能 判断变量是否为0值 错误处理的革新 Unwrap Is As gol ...
- setInterval、setTimeout之遗忘的第三个参数
今天看阮一峰老师的ES6入门,在一个关于promise的小demo里,老师用到了setTimeout的第三个参数,惊了有没有,定时器还有第三个参数? 喏就是下面这个demo: function tim ...
- 一个selenium简单案例自动添加数据
//本来想着用execl来录入数据的,但是为了尽快完成所以直接搞了个数组 package aldtest; import org.openqa.selenium.*; import org.openq ...
- GStreamer基础教程08 - 多线程
摘要 GStreamer框架会自动处理多线程的逻辑,但在某些情况下,我们仍然需要根据实际的情况自己将部分Pipeline在单独的线程中执行,本文将介绍如何处理这种情况. GStreamer多线程 GS ...
- grep、正则表达式
1.grep :文本搜索工具 -i:忽略大小写--color:匹配到的内容高亮显示-v:显示没有被模式匹配到的行-o:只显示被模式匹配到的字符串-n:显示匹配到行的行号-E:使用扩展正则表达式-A:后 ...
- Spring IOC(3)----bean实例化
前面一节说到invokeBeanFactoryPostProcessors方法的调用来注册bean定义,这次来看看finishBeanFactoryInitialization这个方法实例化非懒加载的 ...
- Redis分布式锁的一点小理解
1.在分布式系统中,我们使用锁机制只能保证同一个JVM中一次只有一个线程访问,但是在分布式的系统中锁就不起作用了,这时候就要用到分布式锁(有多种,这里指 redis) 2.在 redis当中可以使用命 ...
- Spring Boot 2.X(一):入门篇
什么是 Spring Boot Spring Boot 是由 Pivotal 团队提供的全新框架,其设计目的是用来简化新 Spring 应用的初始搭建以及开发过程.该框架遵循"约定优于配置& ...
- B-线性代数-矩阵转置
[TOC] 更新.更全的<机器学习>的更新网站,更有python.go.数据结构与算法.爬虫.人工智能教学等着你:https://www.cnblogs.com/nickchen121/ ...
- Mac上Charles抓包iOS的https请求
介绍一款抓包工具,一般我在windows下使用Fiddler抓包,Fiddler使用教程这里就不讲了,重点介绍使用mac时的抓包工具----Charles. 进入官网 :Charles官网地址官网下载 ...