您的位置:首页 > 编程语言 > Java开发

Java编程思想:第六章:访问控制

2018-12-10 16:22 766 查看

第六章:访问控制


    访问控制(或者隐藏具体实现)与(最初的实现并不恰当)有关。

    所有的优秀作者,包括哪些编写软件的程序员,都清楚著作的某些部分直至重新创作的时候才变得完美,有时甚至要反复重写几次。如果你把一个代码段放到了某个位置,等过一会回头再看时,有可能会发现优更好的方式去实现相同的功能。这正是重构的原则之一。重构及重写代码,以使得它更好的可读,更易理解, 并因此而更具可维护性。

    但是,这种修改和完善代码的愿望之下,也存在着巨大的压力。通常总是会有一些消费者(客户端)需要你的代码在某些方面保持不变。因此,你想改变代码,而他们却像让代码保持不变,因此而产生了在面向对象编程中需要考虑的一个基本问题:“如何把变动的事务与保持不变的事务区分开来”。

    这对类库而言尤为重要。该类的消费者必须依赖他所使用的那部分类库,并且能够知道如何类库出现了新的版本,他们并不需要改写代码。从另一个方面来说,类库的开发者必须要有权限进行修改和改进,并确保客户代码不会因为这些改动而受到影响。

    这一目标可以通过约束来达到,为了解决这个问题Java提供了访问修饰符,以供类库开发人员向客户端程序员指明那些是可用的,那些是不可用的。

    访问权限控制的等级,从最大权限到最小权限依次为:

  • public

  • protected

  • private

    不过构建类库的概念以及对于谁有权取用该类库的构建的控制问题还是不完善的。其中仍旧存在着如何将构建捆绑到一个内聚的类库单元中的问题。对于这一点,Java用关键字package加以控制,而访问权限修饰词会因类是存在于一个相同的包,还是存在于一个单独的包而受到影响。


6.1 包:库单元

    包内包含有一组类,它们在单一的名字空间之下被组织在一起。导入一般使用:import java.util.*;

    我们之所以导入包,就是要提供一个管理名字空间的机制。所有类成员的名称都是彼此隔离的。由于名字之间的冲突,Java对名称空间进行了完全控制并为每个类创建唯一的标识符组合就称为了非常重要的事情。

    当编写一个Java源代码文件时,此文件通常被称为编译单元。每一个编译单元必须有一个后缀为.java,而在编译单元内则可以有一个public类,该类的名称必须与文件的名字相同。每一个编译单元只能有一个public类,否则编译器就不会接受。如果编译单元中有额外的类的话,那么在包之外的世界是无法看到这些类的,就是因为他们不是public的,而且它们主要用来为主poblic类提供支持。


代码组织

    当编译一个.java文件时,文件中的每一个类都会有一个输出文件,而且该输出文件的名字与.java文件中的每一个类的名称相同。只是多了一个后缀名.class。因此,在编译少量.java文件之后,会得到大量的.class文件。Java可运行程序是一组可以打包并解压为一个java文档文件的.class文件。Java解析器负责这些文件的查找,装载,解析。

    类库实际上是一组类文件。其中每一个文件都有一个public类,以及任意数量的非public类。因此,每个文件都有一个构建。如果希望这些构建从属于同一个群组,就可以使用关键字package。

    如果使用package关键字,它必须是文件中去除注释外的第一句程序代码。在文件起始处写。如下:package.utils; 表示该编译单元是名称未utils的类库的一部分。或者换种说话,你正在声明该编译单元中的public类名称是位于utils名称的保护伞下。任何想要使用该名称的人都必须使用前面给出的选择,指定全名或者与utils结合使用import。

    身为一个类库设计员,要记住,package和import关键字允许你做的,是将单一的全局名字空间分隔开,使得无论多少人使用Internet以及Java开会编写类,都不会出现名称冲突问题。


创建独一无二的包名

    特定包的所有.class文件都置于一个目录下。也就是,操作系统的层次化的文件系统结构来解决这个问题。这是Java解决混乱问题的解决方式。

    将所有的文件收入一个子目录还可以解决另外两个问题:

  • 怎样创建独一无二的名称以及怎样查找有可能隐藏于目录结构中的某些类。一般package就是倒过来的域名,是独一无二的。因此你的package名称也是独一无二的。所以不会出现冲突问题。

  • 此技巧的第二部分是,把package名称分解为你机子上的一个目录。

    Java解析器的运行过程如下:

  • 首先,找出环境变量CLASSPATH。CLASSPATH包含一个或多么目录,用作查找.class文件的根目录。从根目录开始,解析器获得包的名称并将每一个句点替换成反斜杠,已从CLASSPATH根中产生一个路径名称,得到的路径会与CLASSPaTH中各个不同的项相连接,解析器就在这个目录中查找与你所要创建的类名称相符的.class文件。

    使用import引入时注意冲突哦。


用import改变行为

    Java中没有条件编译,一般调试时可以用到次动能。比如:创建两个包dev,test。然后用import来切换。


对使用包的忠告

    务必记住,无论何时创建包,都已经在给定包的名称的时候隐含指定了目录结构。这个包必须位于名称所指定的目录中,而该目录必须在以CALASPATH开始的目录中可以查询到的。最初使用关键字package,可能会有一点不顺,因为除非遵守“包的名字对应目录路名”的规则,否则将会许多出乎意料的运行时信息。


6.2 Java的访问修饰符


6.3 接口与实现

    访问权限的控制常被称为具体实现的隐藏。把数据和方法包装进类中,以及具体实现的隐藏,常共同被称为封装。其结果是一个同时带有特征和行为的数据类型。

    出于两个很重要的原因,访问权限控制将权限的边界划在了数据类型的部分,如下:

  • 要指定客户端程序员可以使用和不可以使用的边界。可以在结构中建立自己的内部机制,而不必担心客户端程序员会偶然地将内部机制当做是他们可以使用的接口的一部分。

  • 将接口和具体实现分离。

    为了清晰起见,可能会采用一种将public成员置于开头,后面跟着protected,包访问权限和private成员的创建类的形式。这样做的好处是使用者可以从头读起,首选阅读对他而言最重要的部分,等到遇见作为内部实现细节的非public成员时停止阅读。


6.4 类的访问权限

    在Java中,访问权限修饰符也可以用于确定库中的哪些类对于该库的使用者是可用的。如果希望某个类可以为某个客户端程序员所用,就可以通过public关键字作用于整个类的定义来达到目的。这样做甚至可以控制客户端程序员是否能创建一个该类的对象。

    这里有一些额外的限制:如下:

  • 每个编译单元都只能有一个public类。这表示,每个编译单元都有单一的公共接口,用public类来表示。该接口可以按要求包含众多的支持包访问权限的类。如果再某个编译单元内有一个以上的public类,编译器就给出错信息。

  • public类的名称必须完全与含有编译单元的文件名相匹配,包括大小写。

  • 虽然不是很常见,但编译单元内完全不带public类也是可能的。在这种情况下,可以随意对文件命名。

    比如,内部类,去掉public就包权限了,而且客户端程序员也不会放到问,隐藏了细节。

    类是不可以设置成private的,所以类而言只有两种选择:

  • 包权限

  • public

    如果不希望别人对类访问权限,可以把构造器改成private来防止new出对象。但是有一个例外,就是你在该类的static成员内部可以创建。

    正如前面所提到的,如果没能为类访问权限指定一个访问修饰符,它就会默认得到包访问权限。这意味着该类的对象可以由包内任何其他类来创建。但在包外则是不行的。然而,如果该类的某个static成员是public的,则客户端程序员仍旧可以调用该static成员。尽管他们并不能生成该类的对象。


内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息