24、Java程序中各部分名称的共享

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代码。在实际开发中,根据具体需求选择合适的访问修饰符和包结构,将有助于提高代码的质量和可扩展性。

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符  | 博主筛选后可见
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值