- 本节学习目标
- 1️⃣ 包的定义
- 2️⃣ 包的导入
- 3️⃣ 系统常见包
- 4️⃣ jar命令
- * 总结
本节学习目标
- 掌握包的定义及使用;
- 了解 Java 中的常用系统包;
- 掌握 javac、java、jar 命令的使用;
- 掌握 package 、import关键字的使用;
1️⃣ 包的定义
在Java 程序中的包主要用于将不同功能的文件进行分割。在前面介绍的代码开发中,所有编译后的 *.class
文件都保存在同一个目录中,这样一来就会带来一个问题:如果出现了同名文件,就会发生文件的覆盖问题,因为在同一个目录中不允许有重名文件。而要想解决同名文件冲突的问题,就必须设置不同的目录,因为在不同的目录下可以有重名文件。
所谓的包实际上指的就是文件夹,在 Java 中使用 package
关键字来定义包,此语句必须写在 *.java
文件的首行。
利用包也更方便组织多人开发,可以试想这样的一种情景,如果有多个开发人员共同开发同一个项目时,可能会出现类名称相同的情况,这样一来就会比较麻烦,此时就可以利用 package
关键字来解决此问题。
当然在实际的开发中,所有的开发者都会将程序提交到一个统一的服务器上进行保存,实际上如果要对程序进行管理,仅使用包是不够的,还要对程序的更新、上传进行统一的控制。这样在实际开发中通常会配置一个版本控制工具(如SVN
、Git
),帮助管理代码。关于版本控制工具,后面文章会详细介绍。
// 范例 1: 定义包
package com.xiaoshan.demo; //定义程序所在包,此语句必须放在首行
public class Hello {
public static void main(String args[]){
System.out.println("Hello World!");
}
}
此程序代码的功能就是在屏幕上输出一个字符串信息,但是唯一的区别是将 Hello
程序类定义在了一个“com.xiaoshan.demo
” 的包中。在定义包时出现 “.
”,就表示子目录。
当程序中出现包的定义后,在编译程序时,就必须使生成的 Hello.class
保存在指定的目录下(此时应该保存在 com\xiaoshan\demo
目录下,与包名称结构相同)。在JDK 中提供了以下两种自动的打包编译指令。
-
打包编译: javac -d .Hello.java;
-
“
-d
”: 生成目录,根据package
的定义生成; -
“
.
”: 设置保存的路径,如果为".
"表示在当前所在路径下生成。 -
在解释程序的时候不需要进入到包里面,应该在包外面输入类的完整名称(包.类)
-
输入指令:
java com.yootk.Hello
。
关于以上俩个Java的可执行指令的详细情况,可以参考我的另外文章:
为了方便程序的管理,在实际的项目开发中,所有的类都一定要放在一个包中, 而完整的类名称永远都是“包.类
”,而没有包的类不应该在开发中出现。
2️⃣ 包的导入
使用包可以将一个完整的程序拆分为不同的文件进行分别保存,这样就会造成一个问题,不同包的类之间有可能要进行互相访问,此时就需要使用包的导入 (import
语句) 操作。
如果一个程序定义在包中,并且需要引入其他程序类,那么import
语句必须写在 package
语句之后、类的定义之前。
// 范例 2: 定义一个 com.xiaoshan.util.Message 的类
package com.xiaoshan.util;
public class Message{
public void print(){
System.out.println("Hello World!");
}
}
此程序定义了一个 Message
类,并且在类中定义了一个 print()
打印信息的方法。
// 范例 3: 定义一个 com.xiaoshan.test.TestMessage
package com.xiaoshan.test;
import com.xiaoshan.util.Message; //导入所需要的类
public class TestMessage{
public static void main(String args[]){
Message msg = new Message(); // 实例化对象
msg.print(); //调用方法
}
}
程序执行结果:
Hello World!
此程序由于要使用到 Message
类,所以首先使用了 import
语句根据类的名称(包.类
)进行导入,导入后直接实例化 Message
类对象,然后调用 print()
方法。
Java 编译器考虑到作为大型程序开发时有可能会存在多个 *.java
文件中的类互相引用的情况,为了解决编译顺序的问题,提供了通配符 “*
” 操作:javac -d . *.java
,这样就会自动根据代码的调用顺序进行程序编译。
而如果不使用"*
",就需要按照顺序编译。如果现在使用纯粹的手工方式进行开发,最好用的编译方式一定是“*.java
”。如果不想这样自动顺序进行编译,以范例3为例可以按照以下顺序完成。
第一步:首先编译 Message.java
文件,执行:“javac -d . Message.java
”;
第二步:编译TestMessage.java
文件,执行:“javac -d . TestMessage.java
”。
当然,在实际的开发中往往都会利用开发工具编写代码,例如:Eclipse
、IDEA
等工具。而在这些开发工具中都会存在自动编译的功能,所以在实际的开发中,很少会考虑到编译顺序或者是导包(有自动提示)等操作。
范例3的程序由于要在 TestMessage
程序类中使用其他包中的类,所以在进行包导入操作时要编写 “import 包.类
”的语句,但是如果要在一个类中导入同一个包中许多类时,则这样每次都重复编写 “import 包.类
”语句会很麻烦,所以可以使用“包.*
”的方式来代替一个包中多个类的导入操作。
// 范例 4: 导入一个包中的多个类
package com.xiaoshan.test;
import com.xiaoshan.util.*; //自动导入指定包中所需要的类
public class TestMessage {
public static void main(String args[]){
Message msg = new Message(); //实例化对象
msg.print(); //调用方法
}
}
此程序假设需要导入 com.xiaoshan.util
包中的多个类,所以使用 “import com.xiaoshan.util.*
” 语句简化导入操作。
有的朋友可能怀疑如果写上 “import com.xiaoshan.util.*
” 这样的语句会不会将包中的所有类 (假设包中定义有100个类)都导入进来,那么是不是直接写上 “import com.xiaoshan.util.Message
” 准确地导入某一个类性能会更好?
但实际上即便代码中使用了 “import 包.*
” 的操作,也不会将本包中的所有类都导入进来,类加载时也只是加载所需要的类,不使用的类不会被加载,所以两种写法的性能是一样的。
既然出现了导包操作,那么就必须有一个重要的问题需要注意,有可能同一个代码里面会同时导入不同的包,并且不同的包里面有可能会存在同名类。
例如,现在有两个类:
(1)com.xiaoshan.util.Message
, 此类继续使用之前的代码;
(2)org.lxh.Messge
, 其定义如下。
// 范例 5: 增加一个新的Message 类
package org.Ixh;
public class Message{
public void get(){
System.out.println("世界,你好!");
}
}
此程序由于某种需要,要同时导入以上两个包,而为了方便,一定会在代码中编写两个导入操作。
// 范例 6: 导入程序包
package com.xiaoshan.test;
import org.lxh.*; //包中存在 Message 类
import com.yootk.util.*; //包中存在 Message 类
public class TestMessage {
public static void main(String args[]){
Message msg = new Message(); //实例化对象,出现错误
msg.print(); //调用方法
}
}
程序编译结果:
TestMessage.java:8: 错误:对Message 的引用不明确
Message msg = new Message();
org.lxh 中的类 org.lxh.Message 和 com.xiaoshan.util 中的类 com.xiaoshan.util.Message 都匹配
TestMessage.java:8: 错误:对Message的引用不明确
Message msg = new Message();
org.lxh 中的类 org.Ixh.Message 和 com.xiaoshan.util 中的类 com.xiaoshan.util.Message 都匹配
2个错误
此时,编译结果明确地告诉用户,现在两个包中都有同样的类名称,所以不确定该使用哪一个类。 那么在这种情况下为了可以明确地找到所需要的类,就需要在使用类时加上包名称。
// 范例 7: 解决多个包下存在同名类的冲突
package com.xiaoshan.test;
import org.lxh.* //包中存在Message 类,本程序中无用
import com.xiaoshan.util.*; //包中存在Message类
public class TestMessage{
public static void main(String args[]){
//由于类名称冲突,所以为了准确地描述使用的类,必须使用类的完整名称
com.xiaoshan.util.Message msg = new com.xiaoshan.util.Message();
msg.print(); //调用方法
}
}
此时已经不再需要关心包的导入操作了,而是在实例化 Message
类对象时, 明确地给出了类的完整名称。所以在以后发生类名称冲突时, 一定要写上类的完整名称。
3️⃣ 系统常见包
Java 本身提供了大量的程序开发包 (除了 Java 自己提供的,还有许多第三方提供的开发包)。在Java 开发里面有许多的常见系统包,如下表所示。
包名称 | 作用 |
---|---|
java.lang | 基本的包,像String这样的类就都保存在此包中,在JDK 1.0时如果想编写程序,则必须手工导入此包,但是随后的JDK版本解决了此问题,所以此包现在为自动导入 |
java.lang.reflect | 反射机制的包,是java.lang的子包,在Java反射机制中将会介绍 |
java.util | 工具包, 一些常用的类库、日期操作等都在此包中 |
java.text | 提供了一些文本的处理类库 |
java.sql | 数据库操作包,提供了各种数据库操作的类和接口 |
java.net | 完成网络编程 |
java.io | 输入、输出及文件处理操作处理 |
java.awt | 包含构成抽象窗口工具集(abstract window toolkits)的多个类,这些类 被用来构建和管理应用程序的图形用户界面(GUI) |
javax.swing | 用于建立图形用户界面,此包中的组件相对于java.awt包而言是轻量级组件 |
java.applet | 小应用程序开发包 |
关于Java的JDK内置包的更多全面且详细的情况,可以前往我的专栏学习:
《我的专栏——JAR包解析》
4️⃣ jar命令
在任何一个项目里,编译之后一定会存在大量的 *.class
字节码文件,如果将这些 *.class
文件直接交给用户使用,就会造成文件过多,并且会导致程序没有结构。所以在交付用户使用之前,会使用 jar
命令针对 *.class
文件进行压缩,最终交付用户使用的往往是 Java 归档 (Java Archive, jar
) 文件。
JDK已经为用户默认提供了生成 jar
文件的工具 (jar.exe
), 直接在命令行方式下就可以看见这个命令的使用格式,如下图所示。
在上图所示的操作之中,可以看到有许多参数,在实际运用中最常使用以下3个参数。
- -c: 创建一个新的文件;
- -v: 生成标准的压缩信息;
- -f: 由用户自己指定一个 *.jar 的文件名称。
// 范例 8: 定义一个 Message.java 文件
package com.xiaoshan.util;
public class Message{
public void print(){
System.out.println("Hello World!");
}
}
定义完程序类后,打包编译此文件: “javac -d . Message.java
”,此时会形成 “包.类
”的形式。随后假设这里面有很多 *.class
文件,并且要交付用户使用,那么将这个包的代码打包压缩,输入指令:“jar -cvf my.jar com
”, 这样就会将生成的 com
目录(包的根名称)及其中所有文件一并打包成一个压缩的 jar
文件。生成的 my.jar
文件并不能直接使用,必须配置 CLASSPATH
才可以正常被其他程序类加载。
SET CLASSPATH=.;E:\mydemo\my.jar
此时就可以直接使用 import
语句导入“com.*
” 包中的类,例如可以实例化 Message
类对象调用方法。在以后的开发中需要大量使用第三方的 jar
文件,那么所有的 jar
文件应该配置 CLASSPATH
, 否则就不能使用。但是如果每次都在命令行中配置会比较麻烦,所以一种更好的配置方式就是直接在环境属性中完成配置,第一篇章中详细介绍过此环境变量的配置与作用,此处不再过多介绍。
关于jar
这个Java的可执行指令的详细情况,可以参考我的另外文章:
* 总结
本文探讨了Java中的包的定义和导入机制,以及常用的系统包。学习了javac
、java
和 jar
命令的使用方法,并理解了package
和 import
关键字的重要性。
通过包的定义和导入,我们能够更好地组织和管理Java代码。包提供了一种层次结构来分类和组织类文件,使得代码更易于维护和扩展。采用合理的包名和正确的 import
语句,能够避免命名冲突并提高代码的可读性。
同时,我们也介绍了常见的系统包,如java.lang
、java.util
等,它们提供了丰富的类和功能,可以帮助我们完成各种任务。
此外,我们学会了使用javac
、java
和 jar
命令来编译、运行和打包Java代码。通过正确使用这些命令,我们可以将源代码编译为可执行的字节码文件,并在Java虚拟机中运行。使用jar
命令,我们还可以将多个类文件打包为一个JAR文件,方便共享和部署。
通过深入研究包的定义、导入机制,掌握常用系统包和命令的使用,以及合理运用package
和 import
关键字,我们可以更加高效、有序地进行Java开发,并构建出高质量的软件系统。
[ ]nbsp_nbsp 3