Java 编程中的包、访问修饰符与事件处理
1. 包内代码的使用
有时收到邮件是件令人开心的事,可能会收到有用的东西。比如有人从 Burd Brain Consulting 发来了
Drawing
类的子类
DrawingWideBB
,代码如下:
package com.burdbrain.drawings;
import java.awt.Graphics;
public class DrawingWideBB extends Drawing {
int width = 100, height = 30;
public void paint(Graphics g) {
g.drawOval(x, y, width, height);
}
}
当运行这个类和
Drawing
类时,一切正常,因为它们在同一个包中。
DrawingWideBB
类有权使用
Drawing
类中默认访问权限的
x
和
y
字段。
若要使用
DrawingWideBB
类,需要对代码做两处修改:
1. 将导入声明改为
import com.burdbrain.drawings.DrawingWideBB
。
2. 修改
ArtFrame
对象的构造函数调用为
new ArtFrame(new DrawingWideBB())
。
2. 受保护的访问权限
起初,很多人认为 Java 中
protected
意味着安全、难以访问,但事实并非如此。在 Java 中,受保护的成员比默认访问权限的成员更易使用。
默认访问权限的字段只能在其所在的包内访问,而加上
protected
后,该字段的子类(无论是否在同一包中)都可以访问它。例如:
// Listing 13-8
package com.burdbrain.drawings;
import java.awt.Graphics;
public class Drawing {
protected int x = 40, y = 40, width = 40, height = 40;
public void paint(Graphics g) {
g.drawOval(x, y, width, height);
}
}
// Listing 13-9
import java.awt.Graphics;
import com.burdbrain.drawings.Drawing;
public class DrawingWide extends Drawing {
int width = 100, height = 30;
public void paint(Graphics g) {
g.drawOval(x, y, width, height);
}
}
在
Drawing
类中,
x
、
y
、
width
和
height
字段是受保护的。
DrawingWide
类虽然不在同一包中,但可以引用
Drawing
类中的
x
和
y
字段。
受保护的访问权限在团队协作中很有用,当外部团队使用你的代码并创建子类时,他们可以直接引用你代码中的受保护字段或方法。
3. 非子类在同一包中的情况
Burd Brain Consulting 又发来了
ShowFrame
类的替代类
ShowFrameWideBB
,代码如下:
package com.burdbrain.drawings;
import com.burdbrain.frames.ArtFrame;
class ShowFrameWideBB {
public static void main(String args[]) {
Drawing drawing = new Drawing();
drawing.width = 100;
drawing.height = 30;
ArtFrame artFrame = new ArtFrame(drawing);
artFrame.setSize(200, 100);
artFrame.setVisible(true);
}
}
ShowFrameWideBB
类与
Drawing
类在同一包中,但它不是
Drawing
类的子类。当编译
ShowFrameWideBB
类和包含受保护字段的
Drawing
类时,一切正常,因为受保护的成员在其所在包内的代码(无论是否为子类)中可用。
如果从命令行运行程序,可能需要输入全限定类名,例如运行
ShowFrameWideBB
类的代码时,需要输入
java com.burdbrain.drawings.ShowFrameWideBB
。
4. Java 类的访问修饰符
Java 类的访问修饰符相对简单,类可以是
public
或非
public
的。
-
公共类
:如果一个类是
public
的,你可以在代码的任何地方引用它,但需要遵循目录结构规则并正确引用打包的类。例如:
import com.burdbrain.drawings.Drawing;
import com.burdbrain.frames.ArtFrame;
...
ArtFrame artFrame = new ArtFrame(new Drawing());
或者不使用导入声明:
com.burdbrain.frames.ArtFrame artFrame =
new com.burdbrain.frames.ArtFrame
(new com.burdbrain.drawings.Drawing());
-
非公共类
:如果一个类不是
public的,你只能在该类所在的包内引用它。例如,将Drawing类的public关键字删除后:
package com.burdbrain.drawings;
import java.awt.Graphics;
class Drawing {
public int x = 40, y = 40, width = 40, height = 40;
public void paint(Graphics g) {
g.drawOval(x, y, width, height);
}
}
在同一包中的
DrawingWideBB
类可以正常访问它,但不在同一包中的代码引用时会报错。
不过,Java 中的内部类遵循不同的规则,但对于初学者来说,接触内部类的机会较少。
下面是一个简单的总结表格:
| 访问修饰符 | 类的访问范围 |
| ---- | ---- |
| public | 代码的任何地方(需遵循目录结构和引用规则) |
| 非 public | 所在包内 |
5. 响应按键和鼠标点击
在图形用户界面(GUI)中,用户的操作(如按键、移动鼠标、点击鼠标等)称为事件,响应这些操作的代码称为事件处理代码。
下面是一个处理鼠标点击事件的示例代码:
// Listing 14-1
import java.awt.FlowLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.Random;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JTextField;
class GameFrame extends JFrame implements ActionListener {
private static final long serialVersionUID = 1L;
int randomNumber = new Random().nextInt(10) + 1;
int numGuesses = 0;
JTextField textField = new JTextField(5);
JButton button = new JButton("Guess");
JLabel label = new JLabel(numGuesses + " guesses");
public GameFrame() {
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setLayout(new FlowLayout());
add(textField);
add(button);
add(label);
button.addActionListener(this);
pack();
setVisible(true);
}
@Override
public void actionPerformed(ActionEvent e) {
String textFieldText = textField.getText();
if (Integer.parseInt(textFieldText)==randomNumber) {
button.setEnabled(false);
textField.setText(textField.getText() + " Yes!");
textField.setEnabled(false);
} else {
textField.setText("");
textField.requestFocus();
}
numGuesses++;
String guessWord =
(numGuesses == 1) ? " guess" : " guesses";
label.setText(numGuesses + guessWord);
}
}
// Listing 14-2
class ShowGameFrame {
public static void main(String args[]) {
new GameFrame();
}
}
这个示例实现了一个猜数字游戏,用户在文本框中输入数字并点击按钮,程序会根据输入给出相应的反馈。
6. 事件处理的实现
Listing 14-1
通过以下三个部分处理按钮点击事件:
1.
GameFrame
类声明实现
ActionListener
接口。
2.
GameFrame
类的构造函数将
this
添加到按钮的动作监听器列表中。
3.
GameFrame
类有一个
actionPerformed
方法。
这三个部分共同使
GameFrame
类能够处理按钮点击事件。
7. Java 接口
在 Java 中,一个类只能继承一个父类,但可以实现多个接口。接口类似于类,但有以下特点:
- 接口的方法没有具体的实现体,称为抽象方法。例如
ActionListener
接口:
package java.awt.event;
import java.util.EventListener;
public interface ActionListener
extends EventListener {
public void actionPerformed(ActionEvent e);
}
-
当一个类实现接口时,必须为接口的所有方法提供具体的实现。例如
GameFrame类实现ActionListener接口,就必须实现actionPerformed方法。
8. 线程执行
Java 程序是多线程的,当运行 Java 程序时,多个线程会同时执行。例如,Java 有一个事件处理线程,它在后台监听鼠标点击事件,并在用户点击鼠标时调用相应的
actionPerformed
方法。
当用户点击按钮时,事件处理线程会查找要调用的
actionPerformed
方法,这就是为什么要调用
addActionListener
方法将
actionPerformed
方法添加到待调用列表中。
9.
this
关键字
this
关键字在
GameFrame
类中代表实例本身。当
GameFrame
类的实例调用
button.addActionListener(this)
时,就是将该实例的
actionPerformed
方法添加到按钮的动作监听器列表中。
10.
actionPerformed
方法内部
actionPerformed
方法使用了 Java API 中的一些技巧:
-
JTextField
和
JLabel
实例有自己的
getText
和
setText
方法,用于获取和设置组件中的文本。
-
javax.swing
包中的组件有
setEnabled
方法,用于启用或禁用组件。
-
javax.swing
包中的组件有
requestFocus
方法,用于获取用户的下一次输入焦点。
下面是一个简单的流程图,展示了事件处理的流程:
graph TD;
A[用户点击按钮] --> B[事件处理线程检测到点击];
B --> C[查找要调用的actionPerformed方法];
C --> D[调用actionPerformed方法];
D --> E{输入是否正确};
E -- 是 --> F[禁用按钮和文本框,显示Yes!];
E -- 否 --> G[清空文本框,获取焦点,增加猜测次数];
综上所述,Java 中的包、访问修饰符、事件处理和接口等概念是 Java 编程中的重要组成部分,掌握这些概念可以帮助开发者更好地编写和组织代码。
Java 编程中的包、访问修饰符与事件处理
11. 事件处理的详细流程分析
为了更清晰地理解事件处理的过程,我们可以将其拆分为更详细的步骤。以下是事件处理流程的详细列表说明:
1.
用户操作触发事件
:用户在图形用户界面上进行操作,如点击按钮、按下键盘按键等,这些操作会触发相应的事件。
2.
事件被捕获
:Java 的事件处理线程在后台持续监听各种事件,当用户操作触发事件时,事件处理线程会捕获该事件。
3.
查找监听器
:事件处理线程会查找与该事件相关的监听器。在我们的猜数字游戏示例中,按钮被点击后,事件处理线程会查找按钮的动作监听器列表。
4.
调用监听器方法
:找到相应的监听器后,事件处理线程会调用监听器的
actionPerformed
方法。在
GameFrame
类中,就是调用
actionPerformed
方法来处理按钮点击事件。
5.
处理事件逻辑
:在
actionPerformed
方法中,编写了处理事件的具体逻辑。例如,在猜数字游戏中,会比较用户输入的数字和随机生成的数字,根据比较结果进行相应的操作。
6.
更新界面
:根据事件处理的结果,更新图形用户界面。如猜对时禁用按钮和文本框,显示 “Yes!”;猜错时清空文本框,获取焦点并更新猜测次数显示。
下面是一个更详细的 mermaid 流程图,展示事件处理的详细流程:
graph LR
A[用户操作(点击按钮等)] --> B[事件处理线程捕获事件]
B --> C[查找事件监听器]
C --> D{是否找到监听器}
D -- 是 --> E[调用监听器的actionPerformed方法]
D -- 否 --> F[事件忽略]
E --> G[执行actionPerformed方法中的逻辑]
G --> H{输入是否正确}
H -- 是 --> I[禁用按钮和文本框,显示Yes!]
H -- 否 --> J[清空文本框,获取焦点,增加猜测次数]
I --> K[界面更新完成]
J --> K
12. 接口的实际应用拓展
在实际编程中,接口的应用非常广泛。除了处理事件,接口还可以用于实现多态性和代码的模块化。例如,我们可以定义一个
Shape
接口,用于表示各种形状:
public interface Shape {
double getArea();
double getPerimeter();
}
然后,不同的形状类可以实现这个接口:
public class Circle implements Shape {
private double radius;
public Circle(double radius) {
this.radius = radius;
}
@Override
public double getArea() {
return Math.PI * radius * radius;
}
@Override
public double getPerimeter() {
return 2 * Math.PI * radius;
}
}
public class Rectangle implements Shape {
private double length;
private double width;
public Rectangle(double length, double width) {
this.length = length;
this.width = width;
}
@Override
public double getArea() {
return length * width;
}
@Override
public double getPerimeter() {
return 2 * (length + width);
}
}
这样,我们可以通过接口类型来引用不同的形状对象,实现多态性:
public class ShapeTest {
public static void main(String[] args) {
Shape circle = new Circle(5);
Shape rectangle = new Rectangle(3, 4);
System.out.println("Circle area: " + circle.getArea());
System.out.println("Rectangle area: " + rectangle.getArea());
}
}
13. 包和访问修饰符的最佳实践
在使用包和访问修饰符时,遵循一些最佳实践可以提高代码的可维护性和安全性。以下是一些建议:
-
合理使用包
:将相关的类放在同一个包中,便于管理和组织代码。例如,将所有与绘图相关的类放在
com.burdbrain.drawings
包中。
-
使用公共类
:对于需要在多个地方引用的类,将其声明为
public
类。但要注意遵循目录结构和引用规则。
-
使用受保护的访问权限
:当希望外部团队或子类能够直接访问某些字段或方法时,使用
protected
访问权限。但要谨慎使用,避免过度暴露内部实现。
-
使用默认访问权限
:对于只在同一个包内使用的类和成员,使用默认访问权限,提高代码的封装性。
下面是一个包和访问修饰符使用的总结表格:
| 场景 | 建议的访问修饰符 |
| ---- | ---- |
| 公共类库 | public |
| 内部工具类 | 默认访问权限 |
| 可被子类扩展的类成员 | protected |
| 不希望外部访问的类成员 | private |
14. 多线程编程的注意事项
虽然 Java 程序默认是多线程的,但在多线程编程中需要注意一些问题,以避免出现线程安全问题。
-
共享资源的同步
:当多个线程访问共享资源时,可能会出现数据不一致的问题。可以使用
synchronized
关键字来保证线程安全。例如:
public class Counter {
private int count = 0;
public synchronized void increment() {
count++;
}
public synchronized int getCount() {
return count;
}
}
- 避免死锁 :死锁是指两个或多个线程相互等待对方释放资源,导致程序无法继续执行。要避免死锁,需要合理设计线程的执行顺序和资源分配。
- 线程的生命周期管理 :了解线程的生命周期,包括创建、启动、运行、阻塞和终止等状态,合理管理线程的生命周期可以提高程序的性能和稳定性。
15.
this
关键字的更多应用场景
this
关键字除了在事件处理中代表实例本身外,还有其他应用场景。
-
区分成员变量和局部变量
:当方法中的局部变量和类的成员变量同名时,可以使用
this
关键字来区分。例如:
public class Person {
private String name;
public Person(String name) {
this.name = name;
}
}
-
调用本类的其他构造方法
:在一个构造方法中,可以使用
this关键字调用本类的其他构造方法。例如:
public class Rectangle {
private int width;
private int height;
public Rectangle() {
this(0, 0);
}
public Rectangle(int width, int height) {
this.width = width;
this.height = height;
}
}
16. 总结与回顾
通过以上内容的学习,我们深入了解了 Java 编程中的包、访问修饰符、事件处理、接口、线程执行和
this
关键字等重要概念。
- 包和访问修饰符帮助我们组织和管理代码,提高代码的可维护性和安全性。
- 事件处理和接口使我们能够实现交互式的图形用户界面,处理用户的各种操作。
- 多线程编程让 Java 程序能够同时处理多个任务,提高程序的性能。
-
this
关键字在不同场景下有不同的用途,帮助我们更好地编写和理解代码。
在实际编程中,要灵活运用这些概念,根据具体需求选择合适的方法和技术,不断提高自己的编程能力。
超级会员免费看

被折叠的 条评论
为什么被折叠?



