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

  • 本节学习目标
  • 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