访问权限控制(1)package、import

一、简介

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

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

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

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

    这一目标可以通过约定来达到。例如,类库开发者必须同意在改动类库中的类时不得删除任何现有方法,因为那样会破坏客户端程序员的代码。但是,与之相反的情况会更加棘手。在有域(即数据成员)存在的情况下类库开发者要怎样才能知道究竟都有哪些域已经被客户端程序员所调用了呢?这对于方法仅为类的实现的一部分,因此并不想让客户端程序员直接使用的情况来说同样如此。如果程序开发者想要移除旧的实现而要添加新的实现时,结果将会怎样呢?改动任何一个成员都有可能破坏客户端程序员的代码。于是类库开发者会手脚被缚,无法对任何事物进行改动。

    为了解决这一问题,java提供了访问权限修饰词,以供类库开发人员向客户端程序员指明哪些是可用的,哪些是不可用的。访问权限控制的等级,从最大权限到最小权限依次为:public、protected、包访问权限(没有关键字)、private。作为一名类库设计员,你应尽可能将一切方法都定为private,而仅向客户端程序员公开你愿意让他们使用的方法。

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

二、包:库单元

    包内包含有一组类,它们在单一的名字空间之下被组织在了一起。

    例如,在java的标准发布中有一个工具库,它被组织在java.util名字空间之下。java.util中有一个叫做ArrayList的类,使用ArrayList的一种方式是用其全名java.util.ArrayList来指定。

public class FullQualification {
	public static void main(String[] args) {
		java.util.ArrayList list = new java.util.ArrayList();
	}
}

    这立刻就使程序变得很冗长了,因此你可能想转而使用import关键字。如果你想要导入单个类,可以在import语句中命名该类:

import java.util.ArrayList;

public class SingleImport {
	public static void main(String[] args) {
		ArrayList list = new ArrayList();
	}
}

    现在,就可以不用限定地使用ArrayList了。但是,这样做java.util中的其他类仍旧是都不可用的。要想导入其中所有的类,只需要使用“*”,就可以了。例如:import java.util.*。

    我们之所以要导入,就是要提供一个管理名字空间的机制。所有类成员的名称都是彼此隔离的。A类中的方法f()与B类中具有相同特征标记(参数列表)的方法f()不会彼此冲突。但是如果类名称互相冲突又该怎么办呢?假设你编写了一个Stack类并安装到了一台机器上,而该机器上已经有了一个别人编写的Stack类,我们该如何解决呢?由于名字之间的潜在冲突,在java中对名称空间进行完全控制并为每个类创建唯一的标识符组合就成为了非常重要的事情。

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

三、代码组织

    当编译一个.java文件时,在.java文件中的每个类都会有一个输出文件,而该输出文件的名称与.java文件中每个类的名称相同,只是多了一个后缀名.class。因此,在编译少量的.java文件之后,会得到大量的.class文件。如果用编译型语言编写过程序,那么对于编译器产生一个中间文件(通常是obj文件),然后再与通过连接器(用以创建一个可执行文件)或类库产生器(librarian,用以创建一个类库)产生的其他同类文件捆绑在一起的情况,可能早已司空见惯。但这并不是java的工作方式。java可运行程序是一组可以打包并压缩为一个java文档文件(JAR,使用java的jar文档生成器)的.class文件。java解释器负责这些文件的查找、装载和解释。

    类库实际上是一组类文件。其中每个文件都有一个public类,以及任意数量的非public类。因此每个文件都有一个构件。如果希望这些构件(每一个都有它们自己的独立的.java和.class文件)从属于同一个群组,就可以使用关键字package。

    如果使用package语句,它必须是文件中除注释以外的第一句程序代码。在文件起始处写:package access;就表示你在声明该编译单元是位于access名称的保护伞下。任何想要使用该名称的人都必须使用前面给出的选择,指定全名或者与access结合使用关键字import。(请注意,java包的命名规则全部使用小写字母,包括中间的字也是如此。)

    例如,假设文件的名称是MyClass.java,这就意味着在该文件中有且只有一个public类,该类的名称必须是MyClass(注意大小写):

package access.mypackage;

public class MyClass {

}

    现在,如果有人想用MyClass或者是access中的任何其他public类就必须使用关键字import来使access中的名称可用。另一个选择是给出完整的名称:

public class QualifiedMyClass {
	public static void main(String[] args) {
		access.mypackage.MyClass m = new access.mypackage.MyClass();
	}
}

    关键字import可使之更加简洁:

import access.mypackage.MyClass;

public class ImportedMyClass {
	public static void main(String[] args) {
		MyClass m = new MyClass();
	}
}

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

四、定制工具库

    具备了这些知识后,现在就可以创建自己的工具库来减少或消除重复的程序代码了。 例如,我们用到的System.out.println()通过封装可以减少输出负担,这种机制可以用于名为Print的类中,这样,我们在使用该类时可以用一个更具可读性的静态import语句来导入:

public class Print {
	public static void println(Object obj) {
		System.out.println(obj);
	}

	public static void println() {
		System.out.println();
	}

	public static void print(Object obj) {
		System.out.print(obj);
	}

}

    可以使用打印便捷工具来打印String,无论是需要换行(println())还是不需要换行(print())。这个文件一般放在util包下。编译完成后,就可以用import static语句在你的系统上使用静态的print()和println()方法了。

import static com.buba.util.Print.*;

public class PrintTest {
	public static void main(String[] args) {
		println("Available from now on!");
		println(100);
		println(100L);
		println(3.14159);
	}
}

    从现在开始,你无论何时创建了有用的新工具,都可以将其添加到你自己的类库中。

发布了72 篇原创文章 · 获赞 2 · 访问量 7868

猜你喜欢

转载自blog.csdn.net/qq_40298351/article/details/104082918