20、Java基础教程之包及访问权限·上

本文深入讲解Java中包(package)的定义、导入(import)机制,详解javac、java、jar命令的使用,并介绍java.lang、java.util等常见系统包。掌握package和import关键字,解决类名冲突,提升代码组织与管理能力。

  • 本节学习目标
  • 1️⃣ 包的定义
  • 2️⃣ 包的导入
  • 3️⃣ 系统常见包
  • 4️⃣ jar命令
  • * 总结

 

本节学习目标

  • 掌握包的定义及使用;
  • 了解 Java 中的常用系统包;
  • 掌握 javac、java、jar 命令的使用;
  • 掌握 package 、import关键字的使用;

1️⃣ 包的定义

在Java 程序中的包主要用于将不同功能的文件进行分割。在前面介绍的代码开发中,所有编译后的 *.class 文件都保存在同一个目录中,这样一来就会带来一个问题:如果出现了同名文件,就会发生文件的覆盖问题,因为在同一个目录中不允许有重名文件。而要想解决同名文件冲突的问题,就必须设置不同的目录,因为在不同的目录下可以有重名文件。

所谓的包实际上指的就是文件夹,在 Java 中使用 package 关键字来定义包,此语句必须写在 *.java 文件的首行。

利用包也更方便组织多人开发,可以试想这样的一种情景,如果有多个开发人员共同开发同一个项目时,可能会出现类名称相同的情况,这样一来就会比较麻烦,此时就可以利用 package 关键字来解决此问题。

当然在实际的开发中,所有的开发者都会将程序提交到一个统一的服务器上进行保存,实际上如果要对程序进行管理,仅使用包是不够的,还要对程序的更新、上传进行统一的控制。这样在实际开发中通常会配置一个版本控制工具(如SVNGit),帮助管理代码。关于版本控制工具,后面文章会详细介绍。

//	范例 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”。

当然,在实际的开发中往往都会利用开发工具编写代码,例如:EclipseIDEA等工具。而在这些开发工具中都会存在自动编译的功能,所以在实际的开发中,很少会考虑到编译顺序或者是导包(有自动提示)等操作。

范例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中的包的定义和导入机制,以及常用的系统包。学习了javacjavajar命令的使用方法,并理解了packageimport关键字的重要性。

通过包的定义和导入,我们能够更好地组织和管理Java代码。包提供了一种层次结构来分类和组织类文件,使得代码更易于维护和扩展。采用合理的包名和正确的 import语句,能够避免命名冲突并提高代码的可读性。

同时,我们也介绍了常见的系统包,如java.langjava.util 等,它们提供了丰富的类和功能,可以帮助我们完成各种任务。

此外,我们学会了使用javacjavajar 命令来编译、运行和打包Java代码。通过正确使用这些命令,我们可以将源代码编译为可执行的字节码文件,并在Java虚拟机中运行。使用jar命令,我们还可以将多个类文件打包为一个JAR文件,方便共享和部署。

通过深入研究包的定义、导入机制,掌握常用系统包和命令的使用,以及合理运用packageimport关键字,我们可以更加高效、有序地进行Java开发,并构建出高质量的软件系统。


[  ]nbsp_nbsp 3