Java程序中各部分名称的共享
1. 引言
在日常交流中,我们常常会谈到个人隐私被侵犯的问题。有人说“他们能读取你的电子邮件”,还有人提到“他们知晓你访问的每一个网站,了解你购买的产品、晚餐吃什么、穿什么、想什么,甚至知道你最深藏的秘密”。这就如同在Java编程里,我们也需要考虑如何保护代码中的信息,避免被随意访问。
2. 访问修饰符
在面向对象编程中,隐藏细节是非常重要的。这并非是为了安全和保密,而是为了实现模块化。当我们隐藏细节时,可以防止一个代码片段中的复杂内容被另一个代码片段破坏,使代码更易于管理,减少错误,节省成本,甚至有助于促进世界和平(夸张说法,强调其重要性)。
Java中有几种访问修饰符,下面为你详细介绍:
-
private
:当一个字段被声明为private时,它会对所有外部访问隐藏。例如:
class SomeClass {
private int myField = 10;
}
class SomeOtherClass {
public static void main(String args[]) {
SomeClass someObject = new SomeClass();
// 这行代码会出错,因为myField是private的
System.out.println(someObject.myField);
}
}
- public :一个被声明为public的字段就像公众人物一样,完全开放,任何代码都可以直接引用。例如:
class SomeClass {
public int myField = 10;
}
class SomeOtherClass {
public static void main(String args[]) {
SomeClass someObject = new SomeClass();
// 这行代码可以正常工作
System.out.println(someObject.myField);
}
}
- default :如果一个字段或方法在声明时没有使用访问修饰符,它就具有默认访问权限。默认访问权限是包级别的访问权限,即只有在同一个包中的代码才能访问。
- protected :这是另一种访问修饰符,在之前的例子中可能未使用过,后续会详细介绍其相关内容。
下面用表格总结不同访问修饰符的访问范围:
| 访问修饰符 | 同一类中 | 同一包中 | 不同包的子类 | 不同包的非子类 |
| ---- | ---- | ---- | ---- | ---- |
| private | 可以 | 不可以 | 不可以 | 不可以 |
| default | 可以 | 可以 | 不可以 | 不可以 |
| protected | 可以 | 可以 | 可以 | 不可以 |
| public | 可以 | 可以 | 可以 | 可以 |
3. 类、访问和多部分程序
3.1 基本概念
在Java中,有字段和方法局部变量两种变量。这里主要讨论的是方法和字段,它们被统称为类的成员。例如下面的代码:
class MyClass {
int myField; // 一个字段(成员)
void myMethod() { // 一个方法(另一个成员)
int myOtherField; // 一个方法局部变量(不是成员)
}
}
3.2 成员与类的区别
Java中的
public
关键字有两种不同的含义,一种用于成员,另一种用于类。例如:
- 用于成员:
public static void main(String args[]) {
public amountInAccount = 50.22;
- 用于类:
public class Drawing {
// 你的代码写在这里
}
大部分情况下,我们主要关注
public
(以及其他类似关键字)对成员的含义,而关于类的访问修饰符含义会在后面单独介绍。
3.3 成员的访问修饰符
判断是否可以在代码的特定位置使用某个成员名称,首先要检查该位置是在成员所在类的内部还是外部:
- 如果成员是private,只有成员所在类内部的代码才能直接引用该成员的名称。
- 如果成员是public,任何代码都可以直接引用该成员的名称。
下面是类和子类的关系图(mermaid格式):
graph LR
class1 --> classA
classA --> classX
class2 --> classB
classB --> classY
class3 --> classC
classC --> classZ
4. 在框架上绘制图形示例
4.1 显示框架
下面的代码用于显示一个ArtFrame,ArtFrame上有一个Drawing。
import com.burdbrain.drawings.Drawing;
import com.burdbrain.frames.ArtFrame;
class ShowFrame {
public static void main(String args[]) {
ArtFrame artFrame = new ArtFrame(new Drawing());
artFrame.setSize(200, 100);
artFrame.setVisible(true);
}
}
代码说明:
- 代码开头的两个import声明,分别用于缩写
com.burdbrain.drawings
包中的
Drawing
类和
com.burdbrain.frames
包中的
ArtFrame
类。
- 创建一个新的
ArtFrame
实例,并调用
setSize
和
setVisible
方法使其显示。
4.2 绘图类
下面是
Drawing
类的代码:
package com.burdbrain.drawings;
import java.awt.Graphics;
public class Drawing {
public int x = 40, y = 40, width = 40, height = 40;
public void paint(Graphics g) {
g.drawOval(x, y, width, height);
}
}
代码说明:
- 代码顶部的包声明表明
Drawing
类属于
com.burdbrain.drawings
包。包名的命名遵循Java的约定,先反转域名,再添加描述性名称。
-
Drawing
类被声明为public,因为如果不这样做,不在
com.burdbrain.drawings
包中的类将无法使用该类。
-
paint
方法使用
Graphics
对象在屏幕上绘制一个椭圆。
4.3 目录结构
当把一个类放入一个包中时,需要创建一个与包名对应的目录结构。对于
com.burdbrain.drawings
包,需要创建
com
、
burdbrain
和
drawings
三个目录。如果代码没有放在正确的目录中,会出现
NoClassDefFoundError
错误。
4.4 创建框架类
下面是
ArtFrame
类的代码:
package com.burdbrain.frames;
import java.awt.Graphics;
import javax.swing.JFrame;
import com.burdbrain.drawings.Drawing;
public class ArtFrame extends JFrame {
private static final long serialVersionUID = 1L;
Drawing drawing;
public ArtFrame(Drawing drawing) {
this.drawing = drawing;
setTitle("Abstract Art");
setDefaultCloseOperation(EXIT_ON_CLOSE);
}
public void paint(Graphics g) {
drawing.paint(g);
}
}
代码说明:
-
ArtFrame
类继承自
JFrame
,用于创建一个浮动窗口。
- 构造函数接收一个
Drawing
对象,并将其赋值给
drawing
字段。
-
paint
方法调用
Drawing
对象的
paint
方法,在窗口中绘制图形。
4.5 修改绘图类
如果想修改椭圆的外观,使其更宽,可以创建一个
Drawing
类的子类
DrawingWide
:
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);
}
}
同时,需要修改
ShowFrame
类中的代码:
ArtFrame artFrame = new ArtFrame(new DrawingWide());
并移除
com.burdbrain.drawings.Drawing
的导入声明。
4.6 默认访问权限
如果
Drawing
类中的字段没有声明为public,而是使用默认访问权限,会出现问题。例如:
package com.burdbrain.drawings;
import java.awt.Graphics;
public class Drawing {
int x = 40, y = 40, width = 40, height = 40;
public void paint(Graphics g) {
g.drawOval(x, y, width, height);
}
}
当创建
DrawingWide
子类时:
import com.burdbrain.drawings.*;
import java.awt.Graphics;
public class DrawingWide extends Drawing {
int width = 100, height = 30;
public void paint(Graphics g) {
g.drawOval(x, y, width, height);
}
}
代码将无法编译,会出现以下错误信息:
x is not public in com.burdbrain.drawings.Drawing; cannot be accessed from outside package
y is not public in com.burdbrain.drawings.Drawing; cannot be accessed from outside package
这是因为具有默认访问权限的字段不能在其所在包之外直接引用,即使是子类也不行。
下面用mermaid图展示包和子类的关系:
graph LR
class1 --> classA
classA --> classX
class2 --> classB
classB --> classY
class3 --> classC
classC --> classZ
style class1 fill:#E5F6FF,stroke:#73A6FF,stroke-width:2px
style classA fill:#E5F6FF,stroke:#73A6FF,stroke-width:2px
style classX fill:#E5F6FF,stroke:#73A6FF,stroke-width:2px
style class2 fill:#E5F6FF,stroke:#73A6FF,stroke-width:2px
style classB fill:#E5F6FF,stroke:#73A6FF,stroke-width:2px
style classY fill:#E5F6FF,stroke:#73A6FF,stroke-width:2px
style class3 fill:#E5F6FF,stroke:#73A6FF,stroke-width:2px
style classC fill:#E5F6FF,stroke:#73A6FF,stroke-width:2px
style classZ fill:#E5F6FF,stroke:#73A6FF,stroke-width:2px
subgraph 包1
class1
classA
classX
end
subgraph 包2
class2
classB
classY
end
subgraph 包3
class3
classC
classZ
end
综上所述,在Java编程中,合理使用访问修饰符对于代码的模块化和可维护性至关重要。我们需要根据实际需求选择合适的访问修饰符,以确保代码的安全性和可扩展性。
5. 包和类路径的重要性
5.1 类路径的作用
在编译和运行Java程序时,编译器和Java虚拟机(JVM)需要知道在哪里找到所需的类文件。为了解决这个问题,Java定义了一个名为
CLASSPATH
的概念,它是一个包含编译器和JVM查找代码的位置列表。
如果没有设置
CLASSPATH
,编译器和JVM将不知道去哪里查找类,会导致
cannot find symbol
或
NoClassDefFoundError
错误。设置
CLASSPATH
有多种方式,一些程序员在每次运行Java程序时创建新的
CLASSPATH
,而另一些则创建系统范围的
CLASSPATH
变量。
5.2 包的使用
包可以帮助组织代码,避免命名冲突。在前面的例子中,
Drawing
类属于
com.burdbrain.drawings
包,
ArtFrame
类属于
com.burdbrain.frames
包。使用包时,需要遵循以下规则:
- 包名通常是反转的域名加上描述性名称,例如
com.burdbrain.drawings
。
- 类文件需要放在与包名对应的目录结构中,例如
com/burdbrain/drawings
。
下面是一个总结包和类路径相关要点的表格:
| 要点 | 描述 |
| ---- | ---- |
| 包名约定 | 反转域名 + 描述性名称 |
| 目录结构 | 与包名对应 |
| 类路径 | 编译器和JVM查找代码的位置列表 |
| 错误处理 | 未设置CLASSPATH会导致找不到类的错误 |
6. 受保护的访问修饰符
6.1 受保护访问的概念
除了
private
、
public
和默认访问修饰符外,Java还有
protected
访问修饰符。当一个成员被声明为
protected
时,它可以被同一包中的所有类访问,也可以被不同包中的子类访问。
6.2 示例代码
以下是一个使用
protected
访问修饰符的示例:
package com.example.parent;
public class ParentClass {
protected int protectedField = 10;
protected void protectedMethod() {
System.out.println("This is a protected method.");
}
}
package com.example.child;
import com.example.parent.ParentClass;
public class ChildClass extends ParentClass {
public void accessProtectedMembers() {
System.out.println(protectedField);
protectedMethod();
}
}
package com.example.other;
import com.example.parent.ParentClass;
public class OtherClass {
public static void main(String[] args) {
ParentClass parent = new ParentClass();
// 下面这行代码会报错,因为在不同包的非子类中不能直接访问protected成员
// System.out.println(parent.protectedField);
}
}
在这个示例中,
ChildClass
是
ParentClass
的子类,它可以访问
ParentClass
的
protected
成员。而
OtherClass
不是
ParentClass
的子类,并且在不同的包中,所以不能直接访问
ParentClass
的
protected
成员。
下面用mermaid图展示受保护访问的范围:
graph LR
classDef samePackage fill:#E5F6FF,stroke:#73A6FF,stroke-width:2px
classDef differentPackageSubclass fill:#FFF6CC,stroke:#FFBC52,stroke-width:2px
classDef differentPackageNonSubclass fill:#FFEBEB,stroke:#E68994,stroke-width:2px
A(ParentClass):::samePackage --> B(ChildClass - 同一包):::samePackage
A --> C(ChildClass - 不同包):::differentPackageSubclass
A --> D(OtherClass - 不同包非子类):::differentPackageNonSubclass
linkStyle 0 stroke:#00FF00,stroke-width:2px
linkStyle 1 stroke:#00FF00,stroke-width:2px
linkStyle 2 stroke:#FF0000,stroke-width:2px
7. 访问修饰符的最佳实践
7.1 最小化访问权限
为了提高代码的安全性和可维护性,应该尽量使用最小的访问权限。例如,如果一个字段或方法只需要在类内部使用,就应该声明为
private
;如果只需要在同一包中使用,就使用默认访问权限;如果需要被不同包中的子类访问,使用
protected
;只有在确实需要被所有类访问时,才使用
public
。
7.2 遵循封装原则
封装是面向对象编程的重要原则之一,它要求将数据和操作数据的方法封装在一起,并通过访问修饰符控制对数据的访问。通过合理使用访问修饰符,可以隐藏类的内部实现细节,只暴露必要的接口给外部使用。
7.3 代码示例
以下是一个遵循最佳实践的代码示例:
public class BankAccount {
private double balance;
public BankAccount(double initialBalance) {
this.balance = initialBalance;
}
public double getBalance() {
return balance;
}
public void deposit(double amount) {
if (amount > 0) {
balance += amount;
}
}
public boolean withdraw(double amount) {
if (amount > 0 && amount <= balance) {
balance -= amount;
return true;
}
return false;
}
}
在这个示例中,
balance
字段被声明为
private
,外部类不能直接访问它。通过
getBalance
、
deposit
和
withdraw
方法,外部类可以安全地操作账户余额。
8. 总结
8.1 访问修饰符总结
| 访问修饰符 | 同一类中 | 同一包中 | 不同包的子类 | 不同包的非子类 |
|---|---|---|---|---|
| private | 可以 | 不可以 | 不可以 | 不可以 |
| default | 可以 | 可以 | 不可以 | 不可以 |
| protected | 可以 | 可以 | 可以 | 不可以 |
| public | 可以 | 可以 | 可以 | 可以 |
8.2 关键要点回顾
- 合理使用访问修饰符可以提高代码的模块化和可维护性。
- 包和类路径对于组织代码和查找类文件非常重要。
- 遵循最小化访问权限和封装原则是良好的编程实践。
通过掌握这些知识,你可以编写更安全、更易于维护的Java代码。在实际开发中,根据具体需求选择合适的访问修饰符和包结构,将有助于提高代码的质量和可扩展性。
超级会员免费看
170万+

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



