java中的“包”与C#中的“命名空间
原文地址:http://www.cnblogs.com/lidabo/archive/2012/12/15/2819865.html
Package vs. Namespace
我们知道,重用性(reusebility)是软件工程中一个非常重要的目标。重用,不仅仅指自己所写的软件(代码、组件等等)可以被重复利用;更广义的重用是指不同的人,不同的团队,不同的公司之间可以互相利用别人的成果。另外,对于大型软件,往往是由多个团队共同开发的,这些团队有可能分布于不同的城市、地区、甚至国家。由于这些原因,名字管理成为一个非常重要的因素。
由于C语言本身不提供名字管理的机制(C语言的static命名解决的是可见性问题,这些名字不会输出给外部,但我们要讨论的名字空间和这个问题并不完全一样),为了解决名字冲突的问题,大家一般会选取加前缀的方法;而前缀规则往往是: ${project name}_${name};更加安全的命名规则将前缀分了更多的级别:${project name}_${module name}_${name}。
这种方案在现实生活中有大量类似的例子。比如:中国的很多城市都有滨河路,如果你谈话的对象都明白你所指的城市,你只需要说滨河路,大家都会明白你的所指。但如果情况不是这样,你就需要加上前缀,说明这到底是乐山的滨河路,还是成都的滨河路。你在邮寄信件的时候,这一点体现的最为直接。
所以,如果存在一个全局的管理,C语言的这种方案应该是非常有效的。但它的缺陷是,这可能会造成很长的名字,而每次在引用一个名字的时候你都必须给出全名。
这是一件非常麻烦的事情。你不妨想象一下,明明大家都知道你所谈的是乐山的滨河路,但你不得不在每次谈到它的时候,都要说“中国四川省乐山市滨河路”,你会多么的痛苦。
为了解决这类问题,并给出一个行之有效的管理方案。随后的编程语言,无论是C++,Java还是C#都提供了自己的名字管理的机制。这些方案在本质上有其统一的思想,但操作方式存在着一定的差异。
在前面C语言的方案里,本身体现了分级管理的方式。分级管理是一种非常自然而有效的手段。比如,互联网的域名。它通过分级的命名保证了一个名字的全局唯一性,其排列方式是从小范围到大范围(这既是因为西方的书写习惯,也是为了方便。其实从这一点上,我们可以发现,如果一个人的预读习惯是从左到右的话,从小到大的排布方式则非常便于节省时间,比如 “滨河路,乐山市,四川省,中国”。在我们从左边的信息已经知道我们的所指时,则可以跳过或忽略后面的信息。而从大到小的排布方式则可以避免错误,因为我们首先了解了限定条件,最后读到滨河路的时候,我们已经确定我们的所指了) 。我们可以在前面加上名字,指定更小的范围。比如:wsd.wmsg.sps.motorola.com 说明这是motorola公司的SPS部门的wmsg部门的wsd组。
Java使用这种方式来命名包(Package),只不过把书写方式反过来。这种方式可以非常有效的保证命名的统一。比如,一个名为mlca的项目的mmi模块包可以命名为:com.motorola.sps.wmsg.wsd.mlca.mmi,而其engine模块包可以命名为:com.motorola.sps.wmsg.wsd.mlca.engine。
这样,当不同的团队,公司之间的代码放在一起进行使用时,在一个名字不冲突的情况下,我们只需要简单的使用它。当引起冲突的时候,我们指定其全名就行了。比如,上述的两个包中都有一个名为Message的class,如果我们的另外一个package中的某个class要同时使用这两个包,在引用Message类的时候,我们需要指明它来自于哪个包。如下:
import com.motorola.sps.wmsg.wsd.mlca.mmi;
import com.motorola.sps.wmsg.wsd.mlca.engine;
// 我们需要指明class Message来自于哪个包.
public class Foo extends com.motorola.sps.wmsg.mlca.mlca.mmi.Message {
...
}
而C++和C#则提供了namespace的概念来支持这种方式。你可以在全局的空间内指定自己的namespace,然后还可以在某个namespace内制定更小范围的namespace。虽然C++和C#本身没有推荐任何namespace的命名方式(其实反域名的方式也是Java推荐的,并非强制),但我们也可以使用上述方式。比如下面的C# code:
namespace com.motorola.sps.wmsg.wsd.mlca.mmi
{
// MMI Stuff
...
}
namespace com.motorola.sps.wmsg.wsd.mlca.engine
{
// Engine Stuff
...
}
当我们同时使用这两个模块时,如果出现名字冲突,也许要通过指定namespace来指明。比如:
class Foo: com.motorola.sps.wmsg.wsd.mlca.mmi.Message
{
...
}
Java的package本身没有子包的概念,所有package都是并列的关系,没有谁包含谁的问题。比如:org.dominoo.action和org.dominoo.action.asl之间绝对没有包与子包的关系。它们是各自独立的包,各自拥有自己的class/interface的集合。在org.dominoo.action.asl的某个java文件里,如果想引用org.dominoo.action里的某个class/interface,则必须import org.dominoo.action。
C++/C#的namespace方案则不然,一个namespace可以有自己的sub-namespace,我们不妨将namespace也称为package,那么C++/C#的package之间就可能存在包与子包的关系. 比如:
namespace org.dominoo
{
namespace action
{
namespace asl
{
....
}
}
namespace constraint
{
namespace ocl
{
....
}
}
}
在这个例子中,action和constraint都是org.dominoo的子包,而它们又各自拥有自己的子包asl和ocl。
所以,对于一个全局的名字空间,C语言无法直接进行名字空间分离,而Java则可以从全局的名字空间里分离出独立的名字空间,但C++/C#则可以进一步将各个名字空间进行进一步分离。如下图:
|------------------|
| global namespace |
| |
| |
| |
|------------------|
C 语言
|-------------------|
| global namespace |
| |
| |---| |---| |---| |
| | A | | B | | C | |
| |---| |---| |---| |
|-------------------|
Java 语言
|----------------------------|
| global namespace |
| |
| |-------------| |--------| |
| | A | | B | |
| | |---| |---| | | |---| | |
| | | C | | D | | | | E | | |
| | |---| |---| | | |---| | |
| |-------------| |--------| |
|----------------------------|
C++/C# 语言
所以,Java的Package方案只对全局的名字空间进行了一次划分,本质上只是为语言提供了一个命名前缀方案,只是通过命名前缀的分级管理来保证名字的唯一性。它唯一的作用就是为了避免名字冲突。
而C++/C#则提供了对任何一个空间进行再次划分的能力。在Java中org.dominoo和org.dominoo.asl之间是完全没有包含关系的各自独立的包,但在C++/C#中,dominoo.asl则和明显是dominoo的一个子包。
事实上,如果仅仅为了避免命名冲突,像C++/C#这样复杂的方案并无必要,Java的方案就足够了。但C++/C#这种方案可以带来其它的便利:
1、软件开发的本质就是自上而下依次分解的,每一层都有自己的定义,并且这种定义可以作为下一层所有子系统的公共服务,多层次的树状结构符合这种逻辑。C++/C#方案用最自然的方式满足了这种划分关系。事实上,这种方案和文件管理的思路是一样的。
2、一个程序一旦using哪个namespace,就可以通过它向下访问它的子包,而无需指出全路经。比如,在上面的图中,如果一个程序写了using namespace A,则它在访问C包中的class Foo时,只需要写C::Foo,而不需要写全路径::A::C::Foo。在Java中,由于A,C是并列的关系,为了访问C中的内容,必须明确指出import C。然后在访问Foo而产生名字冲突的情况下,必须指出全路径。
3、当程序身处某个包的时候,在不产生名字冲突的情况下,可以直接访问外部包中的定义。由于Java包的层次只有一层,所以Java只能直接访问global namespace中的定义,任何其它包中的定义,必须通过import才能够访问。
毫无疑问,C++/C#的方案更加强大灵活,但也更复杂。而复杂的东西往往让使用者更容易犯错误。孰优孰劣,你自己判断吧。
java中的“包”与C#中的“命名空间的更多相关文章
- idea在src/main/java下新建包后项目中只显示src/main,后面的东西不显示,但在本地磁盘中是存在的
去掉图中的勾
- java 执行 jar 包中的 main 方法
java 执行 jar 包中的 main 方法 通过 OneJar 或 Maven 打包后 jar 文件,用命令: java -jar ****.jar执行后总是运行指定的主方法,如果 jar 中有多 ...
- hadoop+javaWeb的开发中遇到包冲突问题(java.lang.VerifyError)
1.HDFS + WEB 项目 报java.lang.VerifyError... 异常 抛异常: Exception in thread "main" java.lang.Ver ...
- java中的包以及内部类的介绍
1:形式参数和返回值的问题(理解) (1)形式参数: 类名:需要该类的对象 抽象类名:需要该类的子类对象 接口名:需要该接口的实现类对象 (2)返 ...
- java编程思想第四版中net.mindview.util包下载,及源码简单导入使用
在java编程思想第四版中需要使用net.mindview.util包,大家可以直接到http://www.mindviewinc.com/TIJ4/CodeInstructions.html 去下载 ...
- java中的包有那些 ???
java.util工具包java.sql数据库包java.io输入输出流包java.net网络包java.lang基础包这些是基本的包,还有一些其他的例如集合,反射等的工具包,你可以去查一下java ...
- Java中的包
包:定义包用package关键字. 1:对类文件进行分类管理. 2:给类文件提供多层名称空间. 如果生成的包不在当前目录下,需要最好执行classpath,将包所在父目录定义到classpath变量中 ...
- (转)java 从jar包中读取资源文件
(转)java 从jar包中读取资源文件 博客分类: java 源自:http://blog.csdn.net/b_h_l/article/details/7767829 在代码中读取一些资源文件 ...
- JAVA中反射机制五(java.lang.reflect包)
一.简介 java.lang.reflect包提供了用于获取类和对象的反射信息的类和接口.反射API允许对程序访问有关加载类的字段,方法和构造函数的信息进行编程访问.它允许在安全限制内使用反射的字段, ...
随机推荐
- jquery图片滚动
注:代码来自17sucai网,已去除部分冗余代码,只保留图片效果 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional// ...
- 深入探讨 java.lang.ref 包--转
概述 Java.lang.ref 是 Java 类库中比较特殊的一个包,它提供了与 Java 垃圾回收器密切相关的引用类.这些引用类对象可以指向其它对象,但它们不同于一般的引用,因为它们的存在并不防碍 ...
- [转] IPC之管道、FIFO、socketpair
管道和FIFO作为最初的UNIX IPC形式,现在已用得较少.SocketPair可作为全双工版本的管道,较为常用,这里简单做个笔记 管道 * 只用于有亲缘关系的进程间通信 * 单向,即半双工 (双向 ...
- Linux shell入门基础(一)
Linux shell入门基础(一): 01.增加删除用户: #useradd byf userdel byf(主目录未删除) userdel -r byf 该用户的属性:usermod 用 ...
- Avast注册以及更新
昨天无聊在网上看Avast,然后下了玩. Avast有三种版本,免费版,网络版,高级版. 下了个高级版,在贴吧和论坛找激活码,发现大部分都没用,最后找了一个许可文件 关于Avast的注册,有在线和离线 ...
- hdu 2304
题意: 插座插空问题 水题.....只要知道最后一个不需要插即可.... 直接贴代码.. AC代码: #include <iostream> using namespace std; in ...
- Hibernate 关联查询 相关错误
错误提示: could not resolve property: 确定有相关属性时,记得使用 criteria.createAlias @ManyToOne 若可能为null 要加上 @NotFou ...
- java的@see注释
@see注释用法 @see 类名 @see #方法名或属性名 @see 类名#方法名或属性名
- 在SPItemEventReceiver中使用BeforeProperties和AfterProperties
当你利用这些事件时,就很快会发现存在前(同步)后(异步)两种事件.其方法的后缀分别为“ing”(比如,ItemAdding)和“ed”(比如,ItemAdded),分别代表了变更发生前调用和发生后调用 ...
- 没有找到iertutil.dll怎么办?快速解决iertutil.dll丢失
iertutil.dll 是存放在 C:\Windows\System32 目录下的一个动态链接库文件,它提供函数给其他程序所调用.iertutil.dll 能够实现接到互联网,纪录输入,监控应用程序 ...