计算机程序的思惟逻辑 (22) - 代码的组织机制

本系列文章经补充和完善,已修订整理成书《Java编程的逻辑》(马俊昌著),由机械工业出版社华章分社出版,于2018年1月上市热销,读者好评如潮!各大网店和书店有售,欢迎购买:京东自营连接 html

使用任何语言进行编程都有一个相似的问题,那就是如何组织代码,具体来讲,如何避免命名冲突?如何合理组织各类源文件?如何使用第三方库?各类代码和依赖库如何编译链接为一个完整的程序?java

本节就来讨论Java中的解决机制,具体包括包、jar包、程序的编译与链接,从包开始。apache

包的概念

使用任何语言进行编程都有一个相同的问题,就是命名冲突,程序通常不全是一我的写的,会调用系统提供的代码、第三方库中的代码、项目中其余人写的代码等,不一样的人就不一样的目的可能定义一样的类名/接口名,Java中解决这个问题的方法就是包。编程

即便代码都是一我的写的,将不少个关系不太大的类和接口都放在一块儿,也不便于理解和维护,Java中组织类和接口的方式也是包。微信

包是一个比较容易理解的概念,相似于电脑中的文件夹,正如咱们在电脑中管理文件,文件放在文件夹中同样,类和接口放在包中,为便于组织,文件夹通常是一个层次结构,包也相似。模块化

包有包名,这个名称以顿号(.)分隔表示层次结构。好比说,咱们以前经常使用的String类,就位于包java.lang下,其中java是上层包名, lang是下层包名,带完整包名的类名称为其彻底限定名,好比String类的彻底限定名为java.lang.String。Java API中全部的类和接口都位于包java或javax下,java是标准包,javax是扩展包。工具

接下来,咱们讨论包的细节,从声明类所在的包开始。spa

声明类所在的包

语法

咱们以前定义类的时候没有定义其所在的包,默认状况下,类位于默认包下,使用默认包是不建议的,文章中使用默认包只是简单起见。3d

定义类的时候,应该先使用关键字package,声明其包名,以下所示:code

package shuo.laoma;

public class Hello {
    //类的定义
}
复制代码

以上声明类Hello的包名为shuo.laoma,包声明语句应该位于源代码的最前面,前面不能有注释外的其余语句。

包名和文件目录结构必须匹配,若是源文件的根目录为 E:\src\,则上面的Hello类对应的文件Hello.java,其全路径就应该是E:\src\shuo\laoma\Hello.java。若是不匹配,Java会提示编译错误。

命名冲突

为避免命名冲突,Java中命名包名的一个惯例是使用域名做为前缀,由于域名是惟一的,通常按照域名的反序来定义包名,好比,域名是:apache.org,包名就以org.apache开头。

没有域名的,也不要紧,使用一个其余代码不太会用的包名便可,好比本文使用的"shuo.laoma",表示"老马说编程"中的例子。

若是代码须要公开给其余人用,最好有一个域名以确保惟一性,若是只是内部使用,则确保内部没有其余代码使用该包名便可。

组织代码

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

包能够方便模块化开发,不一样功能能够位于不一样包内,不一样开发人员负责不一样的包。包也能够方便封装,供外部使用的类能够放在包的上层,而内部的实现细节则能够放在比较底层的子包内。

经过包使用类

同一个包下的类之间互相引用是不须要包名的,能够直接使用。但若是类不在同一个包内,则必需要知道其所在的包,使用有两种方式,一种是经过类的彻底限定名,另一种是将用到的类引入到当前类。

只有一个例外,java.lang包下的类能够直接使用,不须要引入,也不须要使用彻底限定名,好比String类,System类,其余包内的类则不行。

好比说,使用Arrays类中的sort方法,经过彻底限定名,能够这样使用:

int[] arr = new int[]{1,4,2,3};
java.util.Arrays.sort(arr);
System.out.println(java.util.Arrays.toString(arr));
复制代码

显然,这样比较啰嗦,另一种就是将该类引入到当前类,引入的关键字是import,import须要放在package定义以后,类定义以前,以下所示:

package shuo.laoma;
import java.util.Arrays;

public class Hello {
    public static void main(String[] args) {
        int[] arr = new int[]{1,4,2,3};
        Arrays.sort(arr);
        System.out.println(Arrays.toString(arr));
    }
}
复制代码

import时,能够一次将某个包下的全部类引入,语法是使用.*,好比,将java.util包下的全部类引入,语法是:import java.util.*,须要注意的是,这个引入不能递归,它只会引入java.util包下的直接类,而不会引入java.util下嵌套包内的类,好比,不会引入包java.util.zip下面的类。试图嵌套引入的形式也是无效的,如import java.util.*.*。

在一个类内,对其余类的引用必须是惟一肯定的,不能有重名的类,若是有,则经过import只能引入其中的一个类,其余同名的类则必需要使用彻底限定名。

引入类是一个比较繁琐的工做,不过,大多数Java开发环境都提供工具自动作这件事,好比,在Eclipse中,经过菜单"Source->Organize Imports"或对应的快捷键ctrl+shift+O就能够自动管理引入类。

包范围可见性

前面几节咱们介绍过,对于类、变量和方法,均可以有一个可见性修饰符,public/private/protected,而上节,咱们提到能够不写修饰符。若是什么修饰符都不写,它的可见性范围就是同一个包内,同一个包内的其余类能够访问,而其余包内的类则不能够访问。

须要说明的是,同一个包指的是同一个直接包,子包下的类并不能访问,好比说,类shuo.laoma.Hello和shuo.laoma.inner.Test,其所在的包shuo.laoma和shuo.laoma.inner是两个彻底独立的包,并无逻辑上的联系,Hello类和Test类不能互相访问对方的包可见性方法和属性。

另外,须要说明的是protected修饰符,protected可见性包括包可见性,也就是说,声明为protected,不只代表子类能够访问,还代表同一个包内的其余类能够访问,即便这些类不是子类也能够。

总结来讲,可见性范围从小到大是:

private < 默认(包) < protected < public

jar包

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

在Java中,编译后的一个或多个包的Java class文件能够打包为一个文件,Java中打包命令为jar,打包后的文件后缀为.jar,通常称之为jar包。

可使用以下方式打包,首先到编译后的java class文件根目录,而后运行以下命令打包:

jar -cvf <包名>.jar <最上层包名>

好比,对前面介绍的类打包,若是Hello.class位于E:\bin\shuo\laoma\Hello.class,则能够到目录 E:\bin下,而后运行:

jar -cvf hello.jar shuo

hello.jar就是jar包,jar包其实就是一个压缩文件,可使用解压缩工具打开。

Java类库、第三方类库都是以jar包形式提供的。如何使用jar包呢?将其加入到类路径(classpath)中便可。类路径是什么呢?

程序的编译与链接

从Java源代码到运行的程序,有编译和链接两个步骤。编译是将源代码文件变成一种字节码,后缀是.class的文件,这个工做通常是由javac这个命令完成的。链接是在运行时动态执行的,.class文件不能直接运行,运行的是Java虚拟机,虚拟机听起来比较抽象,执行的就是java这个命令,这个命令解析.class文件,转换为机器能识别的二进制代码,而后运行,所谓链接就是根据引用到的类加载相应的字节码并执行。

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

在Java源代码编译时,Java编译器会肯定引用的每一个类的彻底限定名,肯定的方式是根据import语句和classpath。若是import的是彻底限定类名,则能够直接比较并肯定。若是是模糊导入(import带.*),则根据classpath找对应父包,再在父包下寻找是否有对应的类。若是多个模糊导入的包下都有一样的类名,则Java会提示编译错误,此时应该明确指定import哪一个类。

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

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

小结

本节介绍了Java中代码组织的机制,包和jar包,以及程序的编译和链接。将类和接口放在合适的具备层次结构的包内,避免命名冲突,代码能够更为清晰,便于实现封装和模块化开发,经过jar包使用第三方代码,将自身代码打包为jar包供其余程序使用,这些都是解决复杂问题所必需的。

咱们一直在说,程序主要就是对数据的操做,为表示和操做数据,咱们介绍了基本类型,类以及接口,下节,咱们介绍Java中表示和操做一种特殊数据的机制 - 枚举


未完待续,查看最新文章,敬请关注微信公众号“老马说编程”(扫描下方二维码),深刻浅出,老马和你一块儿探索Java编程及计算机技术的本质。用心原创,保留全部版权。

相关文章
相关标签/搜索