Chapter6 访问控制(Access Control)
1. Package: library单元
-
Java提供了多种权限控制关键字(access keyword):public, protected, package access(no keyword), private权限依次降低。Package access即默认权限(可以把package理解为一个文件夹),如果类名或method等无前缀时,即表示只有当前package中的部分可以访问。如果把package看成是一个library单元的话,那么这个单元里面可以包括多个class。以java.util为例,这是java的一个package名称,而ArrayList是这个package中的一个class。在调用时我们可以直接调用完整的class名(包括package名)。
示例如下:
public class PackageTest1 { public static void main(String[] argv){ java.util.ArrayList a = new java.util.ArrayList(); } }
可以看到此时并不需要import。当然,也可以采用import的形式,如import java.util.ArrayList或者import packagename.*,表示把该package下的class都添加进来。
1.1 代码组成
- 我们在用java进行编程时,会保存一个后缀为.java的源文件。java编译器会把这个.java文件看成一个编译单元,在这个编译单元中可以有多个class声明,但只能有一个(可以没有)是public类型的,且这个public的类名必须和文件名相同。编译之后会根据这个.java文件的类生成多个.class文件,多个.class文件也可以压缩成一个.jar文件(java archive)
1.2 创建唯一的package名
- 想象一下,如果我们把所有的.java文件放在同一个文件夹中,那么就不允许重名,这在程序代码量较大或者多人共同开发的情况下,维护性和扩展性将会变得极差。于是对java文件进行归类就显得十分自然,而这跟文件系统又有几分相似。因此java的package也就是对应os中的文件,这使得定位java文件变得非常容易。而且我们可以把需要放在一起的文件放在同一个package目录下,如之前提到过的java.util。java为了让不同用户之间能够互相独立,采用取反的域名加上类地址的方式使得外部程序可以导入指定library,因为每个用户的域名都是独一无二的。比如我的域名是 www.pppeter. org,创建的Test类在thinkingjava文件目录下,那么org.pppeter.www.thinkingjava.Test.class就可以作为我的library地址被其它程序import。
1.3 命名冲突
- 之前提到过,我们可以通过 * import一整个package下的所有.class文件,但这时候可能会出现一个问题:也许不同package中存在相同的class名。比如java.util中一个类名是ArrayList,而import的另一个package中也有一个叫做ArrayList的类。当然如果我的程序中并没有用到这个名字冲突的类那么完全没有问题,但当我需要使用时,就会报错。解决方法是我可以具体指定类名,如之前的java.util.ArrayList,或者尽量在import时尽可能不要使用*,当然这视具体情况而定。
1.4 自定义library
-
我们可以自己定义一个library来简化System.out.print这个比较长的输出。当别的程序需要使用这个library时,同样需要import自定义的library。这里采用的import static XXX.*的形式表示导入XXX类中所有静态方法。
示例如下:
public class Print { public static void println() { System.out.println(); } public static void println(Object obj) { System.out.println(obj); } public static void print(Object obj) { System.out.print(obj); } }
import static lib.Print.*; public class Test1 { public static void main(String[] argv){ println("hello world"); } }
2. java access specifier
2.1 package access
- 前面说过,package访问权限也就是默认权限,不需要关键字,可以让我们更方便地管理类。
2.2 public
-
public顾名思义,即所有人都可以访问。如下所示,首先在package dessert下创建一个Cookie类;然后在另一个package dinner下创建一个Dinner 类,因为Cookie类和它的构造器是public的,因此我们可以在Dinner中创建cookie对象。但因为Cookie的bite方法没有关键字,也就是package access,所以我们不能在另一个package中调用该方法。
示例如下:
package dessert; public class Cookie { public Cookie() { System.out.println("cookie constructor is successful!"); } void bite() { System.out.println("bite cookie"); } }
package dinner; import dessert.Cookie; public class Dinner { public static void main(String[] args) { Cookie cookie = new Cookie(); //! cookie.bite(); } }
2.3 private
-
一般来说,package access已经保证了足够的隐蔽性,因为对于用户来说,package access的内容是不可见的,因此我们只需要考虑该把什么部分公开给用户就可以了。但private在实际应用中还是有很重要的作用的,比如对于多线程来说(之后的多线程章节会具体讲到)。此外,对于明确不想暴露给用户的method或者field,我们也可以添加static,以免出现意外的情况。如下所示,我们不能直接创建对象,只能通过调用指定的method来创建对象,这可以保证用户只能以指定的方式创建对象,保证了安全性。
如下所示:
扫描二维码关注公众号,回复: 5362390 查看本文章package dessert; public class Cookie { private Cookie() { System.out.println("cookie constructor is successful!"); } public static Cookie createACookie() { return new Cookie(); } void bite() { System.out.println("bite cookie"); } }
package dinner; import dessert.Cookie; public class Dinner { public static void main(String[] args) { Cookie cookie = Cookie.createACookie(); // ! cookie.bite(); } }
2.4 Protected
-
protected关键字用于继承(inheritance),具体之后的继承章节会专门介绍,现在先大致了解一下。继承值得是新创建一个类,它可以以已经存在的一个类作为基类,使用extends关键字进行继承,如 public class NewClass extends BaseClass{}。而protected关键字引导的method或者field可以让被继承得到的新类同样具有访问权限。当然,protected关键字同样也支持package access。其中,super指的就是被继承的父类。
示例如下:
package dessert; public class Cookie { public Cookie() { } public static Cookie createACookie() { return new Cookie(); } protected void bite() { System.out.println("bite cookie"); } }
package dinner; import dessert.Cookie; public class Dinner { public static void main(String[] args) { Cookie cookie = Cookie.createACookie(); //! cookie.bite(); ChocolateChip cc = new ChocolateChip(); cc.print(); } } class ChocolateChip extends Cookie{ void print() { super.bite(); } }
Output:
bite cookie
3. class access
-
对于class访问权限需要注意,private(否则除了class本身任何其他对象都无法访问)和protected不能作为class的前缀。如果不想任何人能够访问该class,可以创建private构造器,并通过静态method创建对象。示例见上面private部分。
-
还有一种非常特殊的用法,叫做 Singleton,是指某个类只允许创建一个对象。不妨先想一下如何实现。当然,这里的实现肯定不是创建一个静态变量然后判断是否为0。实际上,我们需要首先对构造器用private进行限制,使得client不能直接创建对象。那么就想到之前用method调用的方法创建对象,但这样每一次创建的对象都会返回一个引用,显然不满足要求。因此我们只要稍作修改,使得每次调用方法返回的只能是同一个引用就成功了。
示例如下:
class Test1 { public static void main(String[] argv){ Cookie cookie = Cookie.createACookie(); } } class Cookie { private Cookie() {} private static Cookie cookie = new Cookie(); public static Cookie createACookie() { return cookie; } }