手机版
你好,游客 登录 注册 搜索
背景:
阅读新闻

Java Package 菜鸟快速入门教程

[日期:2018-02-02] 来源:Linux社区  作者:WreckBear [字体: ]

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结合的地方很不明白,通过这么从零推导,现在明白多了,顺便分享给大家,希望对大家有帮助。

本文永久更新链接地址http://www.linuxidc.com/Linux/2018-02/150731.htm

linux
相关资讯       Java Package 
本文评论   查看全部评论 (0)
表情: 表情 姓名: 字数

       

评论声明
  • 尊重网上道德,遵守中华人民共和国的各项有关法律法规
  • 承担一切因您的行为而直接或间接导致的民事或刑事法律责任
  • 本站管理人员有权保留或删除其管辖留言中的任意内容
  • 本站有权在网站内转载或引用您的评论
  • 参与本评论即表明您已经阅读并接受上述条款