Java Package为何被设计?如果你没想过,我这里或许可以提供一种视角。

想象一下,作为一个语言的设计者,你一定会考虑一个问题:变量名的冲突。为了解决这个问题,C++引入了命名空间(namespace),而Java引入了package。

1.变量名冲突的情况

我们平常接触的所有软件编写,基本都是以文件为基本单位存储的,所以下面以文件为维度进行讨论。

  • 同一文件内:在同一个文件中的变量名冲突,是完全可以通过编译去控制的,如果编译阶段检测到两个变量重复声明,可以报出错误给开发者。
  • 不同文件间:当小A和小B分别编辑两个文件A.cpp和B.cpp时,就无法保证这两个文件中的变量名无重复,比如这两个文件中都存在int b变量,则小A和小B都可以正常的分别编译A.cpp和B.cpp,所以在这里小A和小B都不会即时发现问题;但当小C引用了A.cpp和B.cpp,这两个文件合并编译的时候就会出错,可能这时小A和小B已经出去玩了。。。而这和单文件编译报错不同,这是两个不同的开发者开发的源码,小C最好不要乱改。

2.提出解决冲突的方案

现在摆在我们面前的,就是要解决小C的困扰,我们有以下两种方案:

  1. 把小A和小B叫回来,告诉他们他们再次声明变量的时候,需要互相通知,并且现在马上改一下他们造成的问题,这样用人工的方式避免掉变量名的冲突。
  2. 引入一种编译机制,编译文件的时候,给编译的文件内的变量名加上"文件名.",例如A.cpp里面的int b编译时标识成int A.b,这样,只要保证文件不重名,就不用担心这两个文件中的所有变量会有重复了,至于文件名就交给OS的文件系统去判断重复不重复了。

这个方案的选择不是很难,正常人都会选第2种,但其实第2种方案还是有待完善的,不过我们的大方向走对了。

3.解决方案的优化

在变量名前面加上一个标识前缀(文件名.)确实是一种办法,我们暂时称为前缀法。但上面的方案只是会在编译阶段自动追加前缀,这样会引出一个问题:我如果在c.cpp中想引用A.cpp的int b变量,我又该如何?

所以,我们可以将前缀法运用在代码编写阶段,而不是编译阶段,什么意思呢?举个例子:

A.cpp编写的时候所有的变量都要加一个前缀(暂时约定为文件名),所以之前编写的int b应该改为int A.b,注意,这里是在编辑时改为了int A.b,而不是编译时,要区分出这个时机。

这样,我便可以在C.cpp文件中,直接用A.b这个变量了。通过将前缀法转移到了编辑阶段,实现了多文件之间可以互相引用变量和方法而且不会引发变量名或方法名冲突了。

4.解决方案的实例

前缀法现在稍有成就了,解决了多文件之间的命名冲突,但还是有一些问题的。

前缀约定为"文件名."其实并不安全,因为我们知道同一目录下文件系统会要求不能存在重名的文件,但是不同目录下就可以,所以可能存在/Usr/A.cpp和/Dev/A.cpp两个文件合并编译的时候会发生错误。

同样,显而易见我们可以给出N种方案,这里给出三种:

  1. 我们不要把前缀和文件名划等号,我们可以给每一个文件的前缀指定不同的值,怎么做呢?在每份代码文件中用一个关键字(例如namespace)来标识这个文件的前缀,写法是这样的namespace devA或者namespace usrA,这样就给两个相同文件名的文件赋予了不同的前缀,而他们之间也可以互相引用。

  2. 我们还是把前缀和文件名绑在一起,但是这次狠一点,把文件夹也绑进来,什么意思呢?就是/Usr/A.cpp的变量都写成int usr.A.b,这样其实前缀就和文件层次结合起来了,这样也可以完美解决问题。

  3. 我们不要考虑前缀,文件内的前缀也不要,只要在跨文件引用的地方动态指定前缀就可以了,什么意思呢?就是/usr/A.cpp和/usr/A.cpp文件里变量int b还是写int b,但是当C.cpp引用他们两个的时候,再他们指定前缀,看下面:

imort /usr/A.cpp userA;

imort /dev/A.cpp decA;

print userA.b -- 此处引用变量

print devA.b -- 此处引用变量

其实这三种也是分别对应的C++命名空间、Java Packge机制、Nodejs命名空间的解决方案的实例。

注意:C++ 命名空间和Java Package的区别在这里也可以看出来,在命名空间里只是每个文件中的namespace不同,和物理磁盘的文件名、路径无关;而Java Pakage是和文件层次绑在一起的,所以是和物理存储层次有关的。

4.package机制总结

既然我们的题目是Java Package,那么继续在第二种方案上继续往前。

对于Java的某个类,它唯一的标识符是package+类名,比方说com.test.Test,而我们编写的时候是通过package关键字,指明了Test类的的Package前缀为com.test。编译的时候,我们通过下面命令进行编译:

/ > javac Test.java

通过这行命令,在根目录下会生成一个Test.class文件,这时候大家注意到了,Test的Package编译完了并没有和文件系统的层次有对应关系,是的,确实没有,package的层次关系会指示出Test类应该所在的路径,以便可以让jvm找到。这里,你完全可以自己新建目录 /com/test/ 并将Test.class 放到这个目录里面,在回到根目录,执行 java com.test.Test,你会发现正常执行。

另外,在Test 里面的package已经指明层次关系了,其实是可以让javac自动生成对应的文件层次,并把Test.class放进去,免去手动移动的麻烦,就是javac后面加一个-d 目录名。

javac -d ./ com.test.Test

5.结尾

之前是对javac和package结合的地方很不明白,通过这么从零推导,现在明白多了,顺便分享给大家,希望对大家有帮助。

从零认识Java Package的更多相关文章

  1. JAVA package与import机制

    JAVA package与import机制 http://files.cnblogs.com/files/misybing/JAVA-package-and-import.pdf import org ...

  2. java package(包)的用法

    一般来说都用eclipse自动化图形工具搞定,我用的是ubuntu,所以需要自己打包引入. 什么是包? 这是对java源代码的组织和管理的一种方式,比如:当操作系统某个目录的文件非常多的时候,我们一般 ...

  3. Atitit.软件命名空间  包的命名统计 及命名表(2000个名称) 方案java package

    Atitit.软件命名空间  包的命名统计 及命名表(2000个名称) 方案java package 1. 统计的lib jar 列表1 2. Code3 3. 常用包名按找字母排序(2000个)4 ...

  4. 从零讲Java,给你一条清晰地学习道路!该学什么就学什么!

                                             从零讲JAVA ,给你一条 清晰地学习道路!该学什么就学什么! 1.计算机基础: 1.1数据机构基础: 主要学习:1. ...

  5. java package 命名空间

    原文: http://www.studytonight.com/java/package-in-java.php 创建一个简单的maven 项目的命令是: mvn  archetype:generat ...

  6. Java package 包的命名规范。

    Java的包名都有小写单词组成,类名首字母大写:包的路径符合所开发的 系统模块的 定义,比如生产对生产,物资对物资,基础类对基础类.以便看了包名就明白是哪个模块,从而直接到对应包里找相应的实现. 由于 ...

  7. java package 包 学习笔记

    编译命令示例: javac -d . Main.java 注:带参数-d自动建立文件目录, 只使用javac 则需要手工创建目录 把 class文件打包 jar命令 jar cvf T.jar *; ...

  8. 从零构建Java项目(Maven+SpringBoot+Git) #02 奥斯丁项目

    前两天我说要写个项目来持续迭代,有好多小伙伴都表示支持和鼓励,项目的第一篇这不就来了么~我给项目取了个名字,英文名叫做:austin,中文名叫做:奥斯丁 名字倒没有什么特别的含义,我单纯觉得这个名字好 ...

  9. java package一些试验

    目录如上图,当前目录下,b是文件夹, a是文件夹. B.java 源码如下: package b; import a.*; public class B{ public static void mai ...

随机推荐

  1. 【三分模板】洛谷P3382三分模板

    题目描述 如题,给出一个N次函数,保证在范围[l,r]内存在一点x,使得[l,x]上单调增,[x,r]上单调减.试求出x的值. 输入输出格式 输入格式: 第一行一次包含一个正整数N和两个实数l.r,含 ...

  2. COM学习(四)——COM中的数据类型

    上一次说到,COM为了跨语言,有一套完整的规则,只要COM组件按照规则编写,而不同的语言也按照对应的规则调用,那么就可以实现不同语言间相互调用.但是根据那套规则,只能识别接口,并调用没有参数和返回类型 ...

  3. Nginx配置反向代理

    Nginx可做web服务器,也可做负载均衡使用. 反向代理:应用服务器不直接提供服务,通过nginx服务器处理请求, 转发到代理服务器(Tomcat,Nginx,Apache等) 获取响应交给客户端, ...

  4. css3 移动端 开关效果

    展示效果: 首先是html <div class="container"> <div class="bg_con"> <input ...

  5. 【三十】php之PDO抽象层

    1.PDO介绍(php data object) PHP 数据对象 (PDO) 扩展为PHP访问数据库定义了一个轻量级的一致接口. PDO 提供了一个数据访问抽象层,这意味着,不管使用哪种数据库,都可 ...

  6. python基础的输入字符串的格式化

    name = input("name:") age = input ("age:") job = input ("job") info = ...

  7. JavaScript基础知识(正则表达式、字符串)

    23.正则表达式 作用:定义一个特定的验证字符串内容规则的表达式 注:正则表达式并不是JavaScript独有的:JavaScript支持正则表达式 var a = { };  // 定义一个空对象  ...

  8. 一个标准的WebView示例

    xml <?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android= ...

  9. django中数据库操作——in操作符

    django一对多关系中,为了实现按照一端查询多端,之前都是用extra操作符,经过查看官方文档还有in这个操作符,用这个实现更简单.直接上代码: 根据查询条件模糊查询所有符合条件的机架列表,然后根据 ...

  10. 一步一步从原理跟我学邮件收取及发送 2.邮箱的登录和绕不开的base64

    一步一步从原理跟我学邮件收取及发送 2.邮箱的登录和绕不开的base64 好了,经过本系列上一篇文章 "1.网络命令的发送",假设大家已经掌握了 email 电子邮件的命令发送的方 ...