读书笔记——Java中的内部类
(看完Java核心技术卷一第六章关于内部类一节,特做记录)
内部类(inner class)是定义在另一个类中的类,为什么要使用内部类呢?其主要原因有以下三点:
1.内部类方法可以访问该类定义所在的作用域中的数据,包括私有数据。
2.内部类可以对同一个包中的其他类隐藏起来。
3.当想要定义一个回调函数且不想编写大量代码时,使用匿名内部类比较便捷。
普通的内部类:
一个简单的内部类, 代码如下:
package com.chen;
import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.Date;
//import java.swing.Timer;
/**
* @author 淡
* @create 2020-03-02 0:18
*/
public class InterClassTest {
public static void main(String[] args) {
TalkingClock clock = new TalkingClock(1000, true);
clock.start();
//注释代码一:
// ActionListener listener = new TalkingClock.TimePrinter();
// Timer t = new Timer(1000, listener);
// t.start();
// 注释代码二:
// TalkingClock jabberer = new TalkingClock(100, true);
// TalkingClock.TimePrinter listener = jabberer.new TimePrinter();
JOptionPane.showConfirmDialog(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;
}
//TimePrinter 可以声明为私有类(private),也可以声明为公有类(public)
// 当声明为public时,可以通过 main中的注释代码二访问
public class TimePrinter implements ActionListener{
@Override
public void actionPerformed(ActionEvent e) {
System.out.println(" At the tone, the time is " + new Date());
if (beep)
Toolkit.getDefaultToolkit().beep();
}
}
public void start(){
ActionListener listener = new TimePrinter();
Timer t = new Timer(interval, listener);
t.start();
}
}
如果有一个TimePrinter类是一个常规类,它就需要通过TalkingClock类的公有方法访问beep标志,而使用内部类可以给予改进,即不用提供用于访问其他类的访问器
特殊的语法规则:
可以在外部类的作用域之外,这样引用内部类:OuterClass.InnerClass
如上述代码中,若内部类声明为:public(private时则不可以),可以通过如下代码,在main中构建一个TimePrinter对象
TalkingClock jabberer = new TalkingClock(100, true);
TalkingClock.TimePrinter listener = jabberer.new TimePrinter();
局部内部类:
从上文代码中,可以了解到,TimePrinter这个类名字只使用了一次(在start方法中创建这个类型的对象中使用过一次),此时可以在start方法中定义局部类,代码如下:
public void start(){
class TimePrinter implements ActionListener{
@Override
public void actionPerformed(ActionEvent e) {
System.out.println(" At the tone, the time is " + new Date());
if (beep)
Toolkit.getDefaultToolkit().beep();
}
}
ActionListener listener = this.new TimePrinter();
Timer t = new Timer(interval, listener);
t.start();
}
优势:将TimePrinter类完全隐藏起来。
除了start方法外,没有任何方法知道TimePrinter类的存在,即使是TalkingClock类中的其他代码也不能访问它
注意:1、此时TimePrinter类前面不能有public、private修饰,否则报错;
2、start方法中创建多个TimePrinter对象,注明以此与匿名内部类做区分。
匿名内部类:
假如只创建这个类的一个对象,甚至连名字都不用命名,这种没有名字的类,称为匿名内部类(annoymous inner class)
start方法代码,可改为如下所示:
public void start(){
ActionListener listener = new ActionListener()
{
public void actionPerformed(ActionEvent e) {
System.out.println(" At the tone, the time is " + new Date());
if (beep)
Toolkit.getDefaultToolkit().beep();
}
};
Timer t = new Timer(interval, listener);
t.start();
}
含义是:创建了一个实现ActionListener接口的类的新对象,需要实现的方法actionPerformed定义在括号{}内
注意:由于构造器的名字必须和类名相同,而匿名类没有名字,所以匿名类不能有构造器,尤其是在内部类中实现接口时,不能有任何构造参数。
如果构造参数的闭圆括号跟一个开花括号,正在定义的就是匿名内部类,如下代码:
Person cout = new Person("Dracula")
{
匿名内部类的内容;
};
静态内部类:
- 有时候,使用内部类只是为了把一个类隐藏在另外一个类的内部,并不需要内部类引用外部类对象。所以可以把内部类声明为static,以便取消产生的引用
- 静态内部类的对象除了没有对生成它的外围类对象的引用特权外,与其他的所有内部类完全一样(但静态内部类可以有静态域和方法,详见下例)
- 在内部类不需要访问外围类对象时(即:都是外围类调用它,它不用调用外围类的东西),应该使用静态内部类
注意:当内部类对象是由静态方法中构造出来时,必须使用静态内部类(若该类声明无static,则编译器会报错),如下例代码所示(注:懒得写了,直接拿书上源码来用了):
/**
* 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
{
/**
* A pair of floating-point numbers
*/
public static class Pair
{
private double first;
private double second;
/**
* Constructs a pair from two floating-point numbers
* @param f the first number
* @param s the second number
*/
public Pair(double f, double s)
{
first = f;
second = s;
}
/**
* Returns the first number of the pair
* @return the first number
*/
public double getFirst()
{
return first;
}
/**
* Returns the second number of the pair
* @return the second number
*/
public double getSecond()
{
return second;
}
}
/**
* Computes both the minimum and the maximum of an array
* @param values an array of floating-point numbers
* @return a pair whose first element is the minimum and whose second element
* is the maximum
*/
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类不需要访问外围类对象(即:不需要引用任何其他的对象),且Pair对象是由外部类的静态方法public static Pair minmax(double[] values)构造,故此Pair应该使用static,声明为内部静态类。
书中有一句“声明在接口中的内部类自动成为static和public类”,不太理解!!!