Java Package

package 关键字

在 Java 中,package是一个非常重要的概念,用于组织和管理 Java 类。

定义与作用

  • 定义

    • package语句是 Java 源文件中的一个特殊语句,用于声明该文件中定义的类所属的包
    • 它必须是源文件中的第一条非注释语句
  • 作用

    • 组织代码结构:将相关的类、接口等组织在一起,形成一种层次化的命名空间,使代码更易于管理和维护。

      比如,所有与数据库操作相关的类可以放在一个名为com.example.db的包中。

    • 避免命名冲突:不同的包中可以有相同名称的类,通过包名来区分它们

      例如,java.util包和java.sql包中都有Date类,通过完整的包名来明确使用的是哪个类。

声明与命名规则

  • 声明语法

    在 Java 源文件的开头使用package关键字来声明包,例如package com.example.myapp;,表示该文件中的类都属于com.example.myapp包。

  • 命名规则:

    • 全小写:包名通常采用全小写字母,以提高可读性和遵循 Java 的命名规范。
    • 域名倒置:一般以公司或组织的域名倒置作为包名的开头,然后根据项目的结构和功能进行细分。例如,域名为example.com,那么包名可能是com.example.projectname.module
    • 避免使用关键字:不能使用 Java 中的关键字作为包名,如classpublic等。

示例

  1. 创建一个叫做animals的包。通常使用小写的字母来命名避免与类、接口名字的冲突。在 animals 包中加入一个接口(interface)

    Animal.java文件代码

    1
    2
    3
    4
    5
    6
    package animals;

    interface Animal {
    public void eat();
    public void travel();
    }
  2. 接下来,在同一个包中加入该接口的实现

    MammalInt.java文件代码

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

    public class MammalInt implements Animal{

    public void eat(){
    System.out.println("Mammal eats");
    }

    public void travel(){
    System.out.println("Mammal travels");
    }

    public int noOfLegs(){
    return 0;
    }

    public static void main(String args[]){
    MammalInt m = new MammalInt();
    m.eat();
    m.travel();
    }
    }
  3. 然后,编译这两个文件,并把他们放在一个叫做animals的子目录中。 用下面的命令来运行:

    1
    2
    3
    $ mkdir animals
    $ cp Animal.class MammalInt.class animals #将这两个文件放在animals文件夹,也就是animals包下。
    $ java animals/MammalInt #这是在命令行中执行MammalInt类。java是 Java 虚拟机(JVM)的启动命令,用于运行 Java 类。这里指定了animals/MammalInt,表示要运行animals目录下的MammalInt类。

    输出

    1
    2
    Mammal eats
    Mammal travel

    或者在把这两个文件放在 手动新建的animal文件夹 下,然后在IntelliJ IDEA Community Edition 下直接运行MammalInt.java即可,可实现相同功能。

    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
    PS C:\Users\19707\iCloudDrive\iCloud~md~obsidian\Happy\quzia_pac\Java_prac\包> tree /F
    卷 OS 的文件夹 PATH 列表
    卷序列号为 1511-5760
    C:.
    ├─.idea
    │ .gitignore
    │ misc.xml
    │ modules.xml
    │ vcs.xml
    │ workspace.xml
    │ 包.iml

    ├─animals
    │ Animal.java
    │ MammalInt.java

    └─out
    └─production
    └─包
    ├─.idea
    │ .gitignore
    │ misc.xml
    │ modules.xml
    │ vcs.xml
    │ 包.iml

    └─animals
    Animal.class
    MammalInt.class

上述代码中,MammalInt类实现了Animal接口,并实现了eattravel方法,在方法中分别打印出了Mammal eatsMammal travels

package 的目录结构设置

首先,在.java的文件中,如果想要使用package <name>语句,那么必须将该文件放到 ./<name> 文件夹 (项目根目录的name文件夹) 下。

示例1

通常,一个公司使用它互联网域名的颠倒形式来作为它的包名.例如:互联网域名是 Mihoyo.com,所有的包名都以 com.Mihoyo 开头。包名中的每一个部分对应一个子目录。

例如:有一个 com.Mihoyo.test 的包,这个包包含一个叫做 Mihoyo.java 的源文件,那么相应的,应该有如下面的一连串子目录:

1
....\com\Mihoyo\test\Mihoyo.java

那么文件Mihoyo.java如下:

1
2
3
4
5
6
7
package com.Mihoyo.test;
public class Mihoyo {

}
class Google {

}

如果使用javac -d . Mihoyo.java编译文件,就会产生如下字节码文件:

1
2
.\com\Mihoyo\test\Mihoyo.class
.\com\Mihoyo\test\Google.class

编译之后的 .class 文件应该和 .java 源文件一样,它们放置的目录应该跟包的名字对应起来。

但是,并不要求 .class 文件的路径跟相应的 .java 的路径一样。你可以分开来安排源码和类的目录。比如:

1
2
<path-one>\sources\com\Mihoyo\test\Mihoyo.java
<path-two>\classes\com\Mihoyo\test\Google.class

这样,你可以将你的类目录分享给其他的编程人员,而不用透露自己的源码。

字节码文件的加载路径

用这种方法管理源码和类文件可以让编译器和java 虚拟机(JVM)可以找到你程序中使用的所有类型:

  • 类目录的绝对路径叫做 class path。设置在系统变量 CLASSPATH 中。也可以在执行时使用java -classpath <class path>来指定或者直接更改CLASSPATH。(更改方法在下一个示例中)
  • 编译器和 java 虚拟机通过将 <package name> + <class path> 来构造 .class 文件的路径。

比如在上一个示例中:

  • <path- two>\classesclass path
  • <package name>com.Mihoyo.test
  • 所以最终 编译器和 JVM 会在 <path-two>\classes\com\Mihoyo\test 中找编译好的 .class 文件。

一个 class path 可能会包含好几个路径,多路径应该用分隔符(;)分开。

默认情况下,编译器和 JVM 查找 当前目录。JAR 文件按包含 Java 平台相关的类,所以他们的目录默认放在了 class path 中。

默认的CLASSPATH如下:

1
ClassPath=.;C:\Program Files\Java\jdk-17\lib\dt.jar;C:\Program Files\Java\jdk-17\lib\tools.jar;C:\Program Files\Java\jdk-17\lib;

示例2

使用集成开发环境IDE IntelliJ IDEA Community Edition运行java文件时,包以及java文件编译后,默认存放在./out/production/<根目录名称>下,比如:

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
PS C:\Users\19707\iCloudDrive\iCloud~md~obsidian\Happy\quzia_pac\Java_prac\包> tree /F
卷 OS 的文件夹 PATH 列表
卷序列号为 1511-5760
C:.
│ _Import.java

├─.idea
│ .gitignore
│ misc.xml
│ modules.xml
│ vcs.xml
│ workspace.xml
│ 包.iml

├─animals
│ Animal.java
│ MammalInt.java

└─out
└─production
└─包
│ _Import.class

├─.idea
│ .gitignore
│ misc.xml
│ modules.xml
│ vcs.xml
│ workspace.xml
│ 包.iml

└─animals
Animal.class
MammalInt.class #编译好的可被JVM加载执行的字节码文件

这是如果想要在另一个文件夹下使用这个包,有两种方法:

本地库的使用方法

方法一:命令行执行指定包路径

命令行执行,指定包路径C:\Users\19707\iCloudDrive\iCloud~md~obsidian\Happy\quzia_pac\Java_prac\包即可:

1
2
3
PS C:\Users\19707\iCloudDrive\iCloud~md~obsidian\Happy\quzia_pac\Java_prac\本地库和外部库> java -classpath C:\Users\19707\iCloudDrive\iCloud~md~obsidian\Happy\quzia_pac\Java_prac\包\out\production\包 .\OutLib.java
Mammal eats
Mammal travels

方法二:更改变量CLASSPATH

在 CMD 中追加 CLASSPATH

1
2
3
4
5
6
7
C:\Users\19707>set CLASSPATH
ClassPath=.;C:\Program Files\Java\jdk-17\lib\dt.jar;C:\Program Files\Java\jdk-17\lib\tools.jar;C:\Program Files\Java\jdk-17\lib;

C:\Users\19707>set CLASSPATH=%CLASSPATH%;C:\Users\19707\iCloudDrive\iCloud~md~obsidian\Happy\quzia_pac\Java_prac\包\out\production\包

C:\Users\19707>set CLASSPATH
ClassPath=.;C:\Program Files\Java\jdk-17\lib\dt.jar;C:\Program Files\Java\jdk-17\lib\tools.jar;C:\Program Files\Java\jdk-17\lib;;C:\Users\19707\iCloudDrive\iCloud~md~obsidian\Happy\quzia_pac\Java_prac\包\out\production\包

上述命令中,%CLASSPATH% 会获取当前 CLASSPATH 的值,然后通过分号 ; 将新的路径追加到后面。

或在 PowerShell 中追加 CLASSPATH

1
$env:CLASSPATH = "$env:CLASSPATH;C:\Users\19707\iCloudDrive\iCloud~md~obsidian\Happy\quzia_pac\Java_prac\包\out\production\包"

这里,$env:CLASSPATH 表示当前 CLASSPATH 的值,通过字符串拼接的方式将新的路径追加到后面。

效果如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
C:\Users\19707\iCloudDrive\iCloud~md~obsidian\Happy\quzia_pac\Java_prac\本地库和外部库>tree /f 
OS 的文件夹 PATH 列表
卷序列号为 1511-5760
C:.
OutLib.java

C:\Users\19707\iCloudDrive\iCloud~md~obsidian\Happy\quzia_pac\Java_prac\本地库和外部库>set CLASSPATH=%CLASSPATH%;C:\Users\19707\iCloudDrive\iCloud~md~obsidian\Happy\quzia_pac\Java_prac\包\out\production\包

C:\Users\19707\iCloudDrive\iCloud~md~obsidian\Happy\quzia_pac\Java_prac\本地库和外部库>set CLASSPATH
ClassPath=.;C:\Program Files\Java\jdk-17\lib\dt.jar;C:\Program Files\Java\jdk-17\lib\tools.jar;C:\Program Files\Java\jdk-17\lib;;C:\Users\19707\iCloudDrive\iCloud~md~obsidian\Happy\quzia_pac\Java_prac\包\out\production\包

C:\Users\19707\iCloudDrive\iCloud~md~obsidian\Happy\quzia_pac\Java_prac\本地库和外部库>java OutLib.java
Mammal eats
Mammal travels

import 关键字

Java包的导入与使用

  • 导入其他包中的类
    • 使用import语句导入其他包中的类,以便在当前类中可以直接使用这些类,而无需使用完整的包名
    • 例如,要使用java.util.Date类,可以在代码中添加import java.util.Date;,然后就可以直接使用Date来创建对象等操作。
    • import 语句应该位于 package 语句之后
  • 使用不同包中的同名类
    • 当需要使用不同包中的同名类时,可以使用完整的包名来明确指定要使用的类。
    • 例如,java.util.Datejava.sql.Date是两个不同的Date类,如果在代码中同时需要使用这两个类,可以通过完整的包名来区分它们。

示例

在项目下创建文件_Import.java如下:

1
2
3
4
5
6
7
8
9
10
import animals.MammalInt; //使用包中的类,该类是公共的,本文件对它有访问权限。

public class _Import {
public static void main(String args []){
MammalInt m = new MammalInt();
m.eat();
m.travel();

}
}

执行后也输出

1
2
Mammal eats
Mammal travels

包与访问控制

  • 访问修饰符与包的关系:Java 中的访问修饰符(public、private、protected和默认(没有修饰符))用于控制类、方法和变量在不同包中的访问权限。
    • public:被public修饰的 类、方法或变量 可以在 任何包中的任何类 中被访问。
    • privateprivate修饰的成员只能在 当前类内部 被访问,即使在同一个包中的其他类也无法访问
    • protectedprotected修饰的成员可以在 当前类、同一个包中的其他类以及不同包中的子类 中被访问。
    • 默认(没有修饰符):没有修饰符的成员具有包访问权限,即只能在同一个包中的类中被访问。