> 文章列表 > Java 代码组织机制

Java 代码组织机制

Java 代码组织机制

使用任何语言进行编程都有一个相同的问题,就是命名冲突。

包类似于计算机中的文件夹,正如文件放在文件夹中,类和接口放在包中,为便于组织,文件夹一般是一个层次结构,包也类似。

包有包名,以(.)分隔表示层次结构。比如,String 类就位于 java.lang 包下,其中 java 是上层包名,lang 是下层包名。

带完整包名的类名称为其完全限定名,String 类的完全限定名为 java.lang.String。

Java API 中所有的类和接口都位于包 java 或 javax 下,java 是标准包,javax 是扩展包。

package 声明类所在的包

默认情况下,类位于默认包下,使用默认包是不建议的。

定义类时,使用关键字 package 声明类所在的包,为避免命名冲突,一般按照域名的反序来定义包名。

// // 包声明语句应该位于源代码的最前面,前面不能有注释外的其他语句。
package com.cai.base;public class Hello {// 类的定义
}

Hello 类的包名为 com.cai.base;,包声明语句应该位于源代码的最前面,前面不能有注释外的其他语句。

包名和文件目录结构必须匹配,如果源文件的根目录为 E:\\src\\,则 Hello 类对应的文件 Hello.java,其全路径是 E:\\src\\com\\cai\\base\\Hello.java,如果不匹配,Java 会提示编译错误。

除了避免命名冲突,包也是一种方便组织代码的机制。一般而言,同一个项目下的所有代码都有一个相同的包前缀,这个前缀是唯一的,不会与其他代码重名,在项目内部,根据不同目的再细分为子包,子包可能又会分为下一级子包,形成层次结构,内部实现一般位于比较底层的包。

import 通过包使用类

同一个包下的类之间互相引用是不需要包名的,可以直接使用。但如果类不在同一个包内,则必须要知道其所在的包。

有两种方式:一种是通过类的完全限定名;另外一种是使用 import 将用到的类引入当前类,import 需要放在 package 定义之后,类定义之前。

特别地,java.lang 包下的类可以直接使用,不需要引入,也不需要使用完全限定名。

package com.base.oop;
import java.util.Arrays; // 引入后,可以在当前类中使用public class Human {// 类定义
}

import 操作时,可以一次将某个包下的所有类引入,语法是使用 .*,比如,将 java.util 包下的所有类引入,语法是:import java.util.*

注意:引入不能递归,import java.util.* 只会引入 java.util 包下的直接类,而不会引入 java.util 下嵌套包内的类,比如不会引入包 java.util.zip 下面的类。

在一个类内,对其他类的引用必须是唯一确定的,不能有重名的类,如果有,通过 import 只能引入其中的一个类,其他同名的类则必须要使用完全限定名。

注意:同一个包指的是同一个直接包,子包和父包是不同的包。

jar 包

为方便使用第三方代码,也为了方便我们写的代码给其他人使用,各种程序语言大多有打包的概念,打包的一般不是源代码,而是编译后的代码。打包将多个编译后的文件打包为一个文件,方便其他程序调用。

在 Java 中,编译后的一个或多个包的 Java class 文件可以打包为一个文件,Java 中打包命令为 jar,打包后的文件扩展名为 .jar,一般称之为 jar 包,实质就是一个压缩文件,可以使用解压缩工具打开。

到编译后的 Java class 文件根目录下执行如下指令:

jar -cvf <jar包名>.jar <最上层包名(目录名)>

比如:对 E:\\src\\com\\base\\oop\\Human.java 编译后的 Human.class 打包,在根目录 E:\\src 下执行

jar -cvf HumanDemo.jar com

如何使用 jar 包呢?将其加入类路径(classpath)中即可。

Java 编译和链接

从Java 源代码到运行的程序,有编译和链接两个步骤。

编译是将源代码文件变成 .class 字节码文件,一般是由 javac 命令完成。

链接是在运行时动态执行的,所谓链接就是根据引用到的类加载相应的字节码并执行。

Java 编译和运行时,都需要以参数指定一个 classpath,即类路径。类路径可以有多个,对于直接的 class 文件,路径是 class 文件的根目录;对于 jar 包,路径是 jar 包的完整名称(jar 包全路径)。在 Windows 系统中,多个路径用分号“; ”分隔;在其他系统中,以冒号“:”分隔。

在 Java 源代码编译时,Java 编译器会确定引用的每个类的完全限定名,确定的方式是根据 import 语句和 classpath。

如果导入的是完全限定类名,则可以直接比较并确定;如果是模糊导入(import 带 .*),则根据 classpath 找对应父包,再在父包下寻找是否有对应的类,如果多个模糊导入的包下都有同样的类名,则 Java 会提示编译错误,此时应该明确指定导入哪个类。

Java 运行时,会根据类的完全限定名寻找并加载类,寻找的方式就是在类路径中寻找,如果是 class 文件的根目录,则直接查看是否有对应的子目录及文件,如果是 jar 文件,则首先在内存中解压文件,然后再查看是否有对应的类。

总结来说,import 是编译时概念,用于确定完全限定名,在运行时,只根据完全限定名寻找并加载类,编译和运行时都依赖类路径,类路径中的 jar 文件会被解压缩用于寻找和加载类。

模块

在Java 9中,清晰地引入了模块的概念,JDK 和JRE 都按模块化进行了重构,传统的组织机制依然是支持的,但新的应用可以使用模块。

一个应用可由多个模块组成,一个模块可由多个包组成。模块之间可以有一定的依赖关系,一个模块可以导出包给其他模块用,可以提供服务给其他模块用,也可以使用其他模块提供的包,调用其他模块提供的服务。

参考:《Java 编程的逻辑》 马俊昌