第7章 面向对象进阶 7.2 内部类

7.2 内部类

实例066 普通内部类的简单应用

实例说明

在使用图形界面程序时,用户总是希望界面是丰富多彩的,这就要求程序员根据不同的情况为界面设置不同的颜色。本实例定义了3个按钮,用户通过单击不同的按钮,可以为面板设置不同的颜色。运行本实例,将显示如图7.9所示的效果,单击“红色”按钮,即可将背景设置为红色;单击“绿色”按钮,即可将背景设置为绿色;单击“蓝色”按钮,即可将背景设置为蓝色,如图7.10所示。

实现过程

(1)在Eclipse中创建项目066,并在该项目中创建com.Mingrisoft包。
(2)在com.Mingrisoft包中编写类ButtonTest,该类继承自JFrame。在该窗体中添加3个按钮,分别用来为面板设置不同的颜色。
(3)在com.Mingrisoft包中再编写类ColorAtion,该类继承自ActionListener接口。在该类的构造方法中,需要为其指定一种颜色,在actionPerformed方法中将面板设置成指定的颜色。关键代码如下:

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
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
package com.mingrisoft;

import java.awt.Color;
import java.awt.EventQueue;
import java.awt.FlowLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.border.EmptyBorder;

public class ButtonTest extends JFrame
{
private final class BackgroundColorAction implements ActionListener
{
private Color backgroundColor;
public BackgroundColorAction(Color color) {
this.backgroundColor = color;
}
public void actionPerformed(ActionEvent e)
{
contentPane.setBackground(backgroundColor);
}
}

private static final long serialVersionUID = -7473358778079684939L;
private JPanel contentPane;

/**
* Launch the application.
*/
public static void main(String[] args)
{
EventQueue.invokeLater(new Runnable() {
public void run()
{
try
{
ButtonTest frame = new ButtonTest();
frame.setVisible(true);
} catch (Exception e)
{
e.printStackTrace();
}
}
});
}

/**
* Create the frame.
*/
public ButtonTest() {
setTitle("普通内部类的简单应用");
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setBounds(100, 100, 450, 300);
contentPane = new JPanel();
contentPane.setBorder(new EmptyBorder(5, 5, 5, 5));
setContentPane(contentPane);
contentPane.setLayout(new FlowLayout(FlowLayout.CENTER, 5, 5));

JButton btnNewButton = new JButton("红色");
btnNewButton.addActionListener(new BackgroundColorAction(Color.red));
contentPane.add(btnNewButton);

JButton btnNewButton_1 = new JButton("绿色");
btnNewButton_1.addActionListener(new BackgroundColorAction(Color.green));
contentPane.add(btnNewButton_1);

JButton btnNewButton_2 = new JButton("蓝色");
btnNewButton_2.addActionListener(new BackgroundColorAction(Color.blue));
contentPane.add(btnNewButton_2);
}

}

指点迷津:contentPane是在外部类ButtonTest中定义的域,但是在内部类中却可以直接使用。
技术要点:在类中,除了可以定义域、方法和块,还可以定义类,这种类称为内部类。声明一个最简单的内部类的语法如下:

1
2
3
4
5
public class Outter{
class Inner{

}
}

内部类可以使用外部类中定义的属性和方法,即使它们都是私有的。编译器在编译内部类时,将内部类命名为Outter$Inner的形式,虚拟机并不知道有内部类。

实例067 局部内部类的简单应用

实例说明

日常生活中,闹钟的应用非常广泛。使用它可以更好地帮助人们安排时间。本实例将实现个非常简单的闹钟。运行本实例,控制台会不断输出当前的时间,并且每隔一秒钟会发出提示音。用户可以单击“确定”按钮来退出程序。

实现过程

(1)在Eclipse中创建项目067,并在该项目中创建com.Mingrisoft
(2)在com.Mingrisoft包中编写Java类,名称为AlarmClock。在该类中,首先定义两个属性,一个是delay,表示延迟的时间;另一个是flag,表示是否需要发出提示声音。然后在start()方法中,使用Timer类来安排动作发出事件。关键代码如下:

局部内部类 Swing定时器的使用

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
31
32
33
34
35
36
37
38
39
40
41
42
43
package com.mingrisoft;
import java.awt.Toolkit;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.text.SimpleDateFormat;
import java.util.Date;
import javax.swing.Timer;

public class AlarmClock
{

private int delay;
private boolean flag;

public AlarmClock(int delay, boolean flag) {
this.delay = delay;
this.flag = flag;
}

public void start()
{
// 局部内部类
// 1. 创建定时器的监听器
class TimerListener implements ActionListener
{
@Override
public void actionPerformed(ActionEvent e)
{
SimpleDateFormat format = new SimpleDateFormat("k:m:s");
String result = format.format(new Date());
System.out.println("当前的时间是:" + result);
if (flag)
{
Toolkit.getDefaultToolkit().beep();
}
}
}
// 2. 创建定时器,指定多久触发一次,指定要触发的操作(监听器)
Timer timer = new Timer(delay, new TimerListener());
// 3. 启动定时器
timer.start();
}
}

(3)编写类Test进行测试,在该类的main()方法中创建AlarmClock对象,并调用其start方法。使用对话框提示用户是否要退出程序。关键代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
package com.mingrisoft;
import javax.swing.JOptionPane;

public class Test
{
public static void main(String[] args)
{
AlarmClock clock = new AlarmClock(1000, true);
clock.start();
JOptionPane.showMessageDialog(null, "是否退出?");
System.exit(0);
}
}

技术要点
本实例的技术要点就是局部内部类。Java中可以将类定义在方法的内部,称为局部内部类。这种类不能使用publicprivate修饰,它的作用域被限定在声明这个类的方法中。局部内部类比其他内部类还有一个优点,就是可以访问方法参数。一个最简单的局部内部类代码如下:

1
2
3
4
5
public void book(){
public class JavaProgrammingBook{

}
}

脚下留神:被局部内部类使用的方法参数必须是final

Swing定时器的使用示例

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
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
package com.mingrisoft;

import java.awt.Toolkit;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JTextField;
import javax.swing.Timer;

public class CountdownClock
{
private JTextField textField;
// 1.创建计时器的监听器
private class CountdownAction implements ActionListener
{
public void actionPerformed(ActionEvent e)
{
String text = textField.getText();
// 判断是否有输入
if (!"".equals(text))
{
int num = Integer.parseInt(text);
if (num-- > 0)
{
textField.setText(String.valueOf(num));
}
if (num == 0)
{
System.out.println("0");
// 响铃一下
Toolkit.getDefaultToolkit().beep();
}
}
}
};
// 2.创建计时器,指定周期触发的时间,监听器。
private Timer timer = new Timer(1000, new CountdownAction());
public CountdownClock(JTextField textField) {
this.textField = textField;
}
public void start()
{
// 3. 启动定时器
timer.start();
}
public void stop()
{
// 3. 停止定时器
timer.stop();
}
}

调用界面

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
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
package com.mingrisoft;

import java.awt.Color;
import java.awt.EventQueue;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.GridLayout;
import java.awt.Insets;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JTextField;
import javax.swing.UIManager;
import javax.swing.border.EmptyBorder;
import javax.swing.border.TitledBorder;
import java.awt.FlowLayout;
import javax.swing.SwingConstants;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;

public class CountdownFrame extends JFrame
{
private static final long serialVersionUID = 2291192626975502319L;
private JPanel contentPane;
private JTextField textField;

/**
* Launch the application.
*/
public static void main(String[] args)
{
EventQueue.invokeLater(new Runnable() {
public void run()
{
try
{
CountdownFrame frame = new CountdownFrame();
frame.setVisible(true);
} catch (Exception e)
{
e.printStackTrace();
}
}
});
}

/**
* Create the frame.
*/
public CountdownFrame() {
setTitle("倒计时器");
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
// setBounds(100, 100, 450, 200);
contentPane = new JPanel();
contentPane.setBorder(new EmptyBorder(5, 5, 5, 5));
setContentPane(contentPane);
GridBagLayout gbl_contentPane = new GridBagLayout();
gbl_contentPane.columnWidths = new int[]{424, 0};
gbl_contentPane.rowHeights = new int[]{50, 40, 0};
gbl_contentPane.columnWeights = new double[]{1.0, Double.MIN_VALUE};
gbl_contentPane.rowWeights = new double[]{0.0, 0.0, Double.MIN_VALUE};
contentPane.setLayout(gbl_contentPane);

JPanel panel0 = new JPanel();
panel0.setToolTipText("");
panel0.setBorder(new TitledBorder(UIManager.getBorder("TitledBorder.border"), "请输入需要倒数的时间(秒数)", TitledBorder.LEADING,
TitledBorder.TOP, null, new Color(0, 0, 0)));
GridBagConstraints gbc_panel0 = new GridBagConstraints();
gbc_panel0.fill = GridBagConstraints.BOTH;
gbc_panel0.insets = new Insets(0, 0, 5, 0);
gbc_panel0.gridx = 0;
gbc_panel0.gridy = 0;
contentPane.add(panel0, gbc_panel0);
panel0.setLayout(new GridLayout(0, 1, 0, 0));

textField = new JTextField();
// 定义定时器
final CountdownClock textClock = new CountdownClock(textField);
textField.addKeyListener(new KeyAdapter() {
@Override
public void keyPressed(KeyEvent e)
{
if (e.getKeyCode() == KeyEvent.VK_ENTER)
{
System.out.println("你按下了enter键");
textClock.start();
}
}
});
textField.setHorizontalAlignment(SwingConstants.LEFT);
textField.setText("60");
textField.setToolTipText("在这里输入时间(单位:秒)");
panel0.add(textField);
textField.setColumns(10);

JPanel panel1 = new JPanel();
GridBagConstraints gbc_panel1 = new GridBagConstraints();
gbc_panel1.gridx = 0;
gbc_panel1.gridy = 1;
contentPane.add(panel1, gbc_panel1);

JButton btnNewButton = new JButton("开始");
btnNewButton.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e)
{
textClock.start();
}
});
panel1.setLayout(new FlowLayout(FlowLayout.CENTER, 5, 5));
panel1.add(btnNewButton);

JButton btnNewButton_1 = new JButton("暂停");
btnNewButton_1.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e)
{
textClock.stop();
}
});
panel1.add(btnNewButton_1);

JButton btnNewButton_2 = new JButton("清零");
btnNewButton_2.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e)
{
textClock.stop();
textField.setText("0");
}
});
panel1.add(btnNewButton_2);

// 窗体大小自适应
this.pack();
}

}

实例068 匿名内部类的简单应用

实例说明

在査看数码相片时,通常会使用一款图片査看软件,该软件应该能够遍历文件夹下的所有图片并进行显示。本实例将编写一个非常简单的图片查看软件,它可以支持6张图片。通过单击不同的按钮就可以查看不同的图片。运行本实例,单击不同的按钮将显示不同的图片,例如,单击“图片4”按钮,将显示如图712所示的图片;单击“图片5”按钮,将显示如图7.13所示的图片。

实现过程

(1)在Eclipse中创建项目068,并在该项目中创建com. mingrisoft
(2)在com.mingrisoft包中编写类,名称为ImageViewer,该类继承自JFrame。在窗体中添加6个按钮和一个标签,单击不同的按钮可以在标签上显示不同的图片。关键代码如下:

项目结构

1
2
3
4
5
6
7
8
9
10
11
12
G:\Desktop\书籍\Java\Java 实战\Java经典编程300例\Java经典编程300例\SL\07\068
└─src\
├─com\
│ └─mingrisoft\
│ └─ImageViewer.java
└─images\
├─1.png
├─2.png
├─3.png
├─4.png
├─5.png
└─6.png
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
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
package com.mingrisoft;

import java.awt.BorderLayout;
import java.awt.EventQueue;
import java.awt.GridLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.SwingConstants;
import javax.swing.border.EmptyBorder;

public class ImageViewer extends JFrame
{
private static final long serialVersionUID = 6317071842177275862L;
private JPanel contentPane;

/**
* Launch the application.
*/
public static void main(String[] args)
{
EventQueue.invokeLater(new Runnable() {
public void run()
{
try
{
ImageViewer frame = new ImageViewer();
frame.setVisible(true);
} catch (Exception e)
{
e.printStackTrace();
}
}
});
}

/**
* Create the frame.
*/
public ImageViewer() {
setTitle("匿名内部类的简单应用");
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setBounds(100, 100, 450, 300);
contentPane = new JPanel();
contentPane.setBorder(new EmptyBorder(5, 5, 5, 5));
contentPane.setLayout(new BorderLayout(0, 0));
setContentPane(contentPane);

final JLabel label = new JLabel("");
label.setHorizontalAlignment(SwingConstants.CENTER);
label.setIcon(new ImageIcon(ImageViewer.class.getResource("/images/1.png")));
contentPane.add(label, BorderLayout.CENTER);

JPanel panel = new JPanel();
contentPane.add(panel, BorderLayout.NORTH);

JButton button1 = new JButton("图片1");
button1.setIcon(null);
button1.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e)
{
// label.setIcon(new ImageIcon("src/images/1.png"));
label.setIcon(new ImageIcon(ImageViewer.class.getResource("/images/1.png")));
}
});
panel.setLayout(new GridLayout(2, 3, 0, 0));
panel.add(button1);

JButton button2 = new JButton("图片2");
button2.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e)
{
label.setIcon(new ImageIcon(ImageViewer.class.getResource("/images/2.png")));
}
});
panel.add(button2);

JButton button3 = new JButton("图片3");
button3.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e)
{
label.setIcon(new ImageIcon(ImageViewer.class.getResource("/images/3.png")));
}
});
panel.add(button3);

JButton button4 = new JButton("图片4");
button4.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e)
{
label.setIcon(new ImageIcon(ImageViewer.class.getResource("/images/4.png")));
}
});

panel.add(button4);

JButton button5 = new JButton("图片5");
button5.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e)
{
label.setIcon(new ImageIcon(ImageViewer.class.getResource("/images/5.png")));
}
});
panel.add(button5);

JButton button6 = new JButton("图片6");
button6.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e)
{
label.setIcon(new ImageIcon(ImageViewer.class.getResource("/images/6.png")));

}
});
panel.add(button6);
}

}

技术要点

本实例的技术要点就是匿名内部类。当只需要创建类的一个对象时,可以使用匿名内部类。ActionListenerSwing中动作事件的监听器,如果创建该接口的匿名内部类,可以使用以下代码:

1
2
3
4
5
6
ActionListener listener=new ActionListener() {
public void actionPerformed(ActionEvent e)
{
......
}
};

实例069 静态内部类的简单应用

实例说明

当对元素进行排序时,需要明确各个元素如何比较大小。使用既定的比较方式,就可以求出一个数组中的最大值和最小值。本实例使用静态内部类来实现使用一次遍历求最大值和最小值。实例的运行效果如图714所示。

实现过程

(1)在Eclipse中创建项目069,并在该项目中创建com.mingrisoft包。
(2)在com.Mingrisoft包中编写Java类,名称为MaxMin,在该类中,首先定义一个静态内部类Result,然后在该类中定义两个浮点型属性,一个是max,表示最大值;另一个是min,表示最小值。再使用构造方法为其初始化,并提供getXXX()方法来获得这两个值。最后定义个静态方法getResult(),该方法的返回值是Result类型,这样就可以既保存最大值,又保存最小值。关键代码如下

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
31
32
33
34
35
36
37
38
39
40
41
42
43
44
package com.mingrisoft;

public class MaxMin
{
// 保存最大最小值的静态内部类
public static class Result
{
private double max;
private double min;

public Result(double max, double min) {
this.max = max;
this.min = min;
}

public double getMax()
{
return max;
}

public double getMin()
{
return min;
}
}
// 遍历一次找出最大值和最小值
public static Result getResult(double[] array)
{
double max = Double.MIN_VALUE;
double min = Double.MAX_VALUE;
for (double i : array)
{
if (i > max)
{
max = i;
}
if (i < min)
{
min = i;
}
}
return new Result(max, min);
}
}

(3)在com.Mingrisoft包中再编写类Test进行测试,在该类的main()方法中,使用随机数初始化了一个长度为5的数组,并求得该数组的最大值和最小值。关键代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
package com.mingrisoft;

import com.mingrisoft.MaxMin.Result;

public class Test
{
public static void main(String[] args)
{
double[] array = new double[5];
for (int i = 0; i < array.length; i++)
{
array[i] = 100 * Math.random();
}
System.out.println("源数组:");
for (int i = 0; i < array.length; i++)
{
System.out.println(array[i]);
}
Result result = MaxMin.getResult(array);
System.out.println("最大值:" + result.getMax());
System.out.println("最小值:" + result.getMin());
}
}

运行效果

1
2
3
4
5
6
7
8
源数组:
33.76939597721237
62.481609746228216
11.735585952472894
54.27160232334431
94.32873841657997
最大值:94.32873841657997
最小值:11.735585952472894

技术要点

本实例的技术要点就是静态内部类。静态内部类是使用static修饰的内部类,在静态内部类中,可以使用外部类定义的静态域,但是不能使用非静态域。这是静态内部类与非静态内部类的重要区别。定义一个最简单的静态内部类的代码如下:

1
2
3
public void book(){
public static class Inner{}
}

注意,不要将静态内部类声明成private的,否则不能使用其中定义的方法