第六章 访问权限控制
开发者 必须 有权限 进行修改和 改进, 并确保客户代码不会以为这些改动而受到影响,这一目标可以通过约束达到
访问控制权限等级
- public
- protected
- 包访问权限
- private
类库设计人员 尽可能的将一切方法设置为 private 而 仅向客户端程序员 公开你 愿意让他们访问的方法。
6.1 包 : 库单元
包 内 含有一组类,它们在单一的名字空间之下被组织在一起
在编译 一个 Java 原代码文件的时,此文件通常被称为编译单元(.java)。编译单元可能有一个类 public类。
命名规则 小写字母
6.1.1 代码组织
编译一个 .Java文件时 , .Java 文件中 每个类都会有一个输出文件, 输出文件名于 类名相同 .class
在编译少量.Java文件后,会得到大量的.class文件,Java运行程序时一组可以打包并压缩为一个 Java 文档文件(JAR 使用Java的Jar 文档生成器)的class 文件。Java解释器负责这些文件的查找 ,装载和 解释
6.1.2 创建独一无二的包名
将 所有文件 利用 操作系统的层次 结构来收入不同的 子目录 中可以解决两个问题
- 怎样创建独一无二的名称
- 怎样查找有可能隐藏于目录结构中某处的类
- 通过域名来命名 package 名称 时 独一无二的
Java 解释器运行过程
- 找出环境变量 CLASSPATH (我的 CLASSPATH :%E:ProgramFileJavain% ;%E:ProgramFileJavajarin%) 用于查找 .class 文件的根目录。
- 从根目录开始 ,解释器 包的名称 并且将每个 句点替换成 / 例如 package.foo.bar.baz --> fooaraz
- 得到的路径 会于 CLASSPATH 中其余各个不同的项相互连接,解释器就在这些目录中查找与你所要创造的类的名称相关的 .class文件
6.1.3 定制工具类
我们可以创造自己的工具类 来 减少 或消除重复的代码了
6.1.4 用Import 改变行为
练习3
* package access.debugoff;
*
* public class Debug {
* public static void debug(String s) { }
* }
*/
package access.debug;
public class Debug {
public static void debug(String s) {
System.out.println(s);
}
}
6.2 Java 访问权限修饰符
- 包访问权限
- 没有使用任何访问权限修饰词,没有任何关键字,但通常指包访问权限
- 包 中的所有类对其他类的 包访问权限成员都有访问权限
- 包之外 的所有类,这个成员是却是 private
- 包访问权限 为把 类群 聚在 一个包中的做法提供了 意义和理由
- 获取对 某成员访问权的唯一途径是
- 使该成员为 public ,于是无论是谁,无论在哪都可以访问该成员
- 通过不加访问权限修饰词并将其他类放置于同一个包内的方式给成员赋予包访问权,包内其他类就可以访问该成员了
- 继承技术,继承而来的类既可以访问 public 成员 也可以访问 protected 成员(访问 private不行) ,只有两个类在一个包时 才可以访问包访问权限成员
- 提供 访问器 accessor 和 变异器 mutator 方法 (get/set 方法),以读取和改变数值
6.2.2 public : 接口访问权限
public : 自己对 每个人 都是可用的
6.2.3 private : 你无法访问
除了 包含该成员 的 类之外 ,其他类 无法访问这个成员
允许 随意改变 该成员,不必考虑这样做是否会影响到包内其他的类
用武之地
- 控制如何创建对象,并阻止别人直接访问某个特定的构造器(或全部构造器)
- 类的助手方法 的 方法,够可以把它指定为 private
- 不能因为 在 类的 某个对象的引用时 private ,就认为其他的对象 无法拥有该 对象 的 public 引用
6.2.4 protected : 继承访问权限
Protected 处理的是继承的概念,通过继承可以利用一个现有类(基类),然后将新成员添加到该现有类而不必碰现有类。还可以改变该类现有成员的行为。
基类的创造者 希望 有 某个 特定成员 ,把对它的访问权限赋予 派生类而并不是 所有类
需要 protected 来完成 工作: protected 也提供 包 访问权限,形同的包内的其他类可以访问 protected 元素。
练习四
* public class Cookie {
* public Cookie() {
* System.out.println("Cookie contstructor");
* }
* protected void bite() { System.out.println("bite"); }
* }
*/
import access.cookie2.*;
public class CookieThief {
public static void main(String[] args) {
Cookie x = new Cookie();
//! x.bite(); // access protected
}
}
练习五
* package access;
*
* public class FourWays {
* int a = 0;
* public int b = 1;
* protected int c = 2;
* private int d = 3;
* FourWays() { System.out.println("FourWays() constructor"); }
* void showa() { System.out.println(a); }
* public void showb() { System.out.println(b); }
* protected void showc() { System.out.println(c); }
* private void showd() { System.out.println(d); }
* }
*/
package access; // run command java access.AccessTest
public class AccessTest {
public static void main(String[] args) {
FourWays fw = new FourWays();
fw.showa();
fw.showb();
fw.showc();
fw.a = 10;
fw.b = 20;
fw.c = 30;
fw.showa();
fw.showb();
fw.showc();
//! fw.showd(); // private access, compiler can't touch
}
}
练习 六
class SomeData {
protected int a = 13;
}
class DataChanger {
static void change(SomeData sd, int i) { sd.a = i; }
}
public class ProtectedData {
public static void main(String[] args) {
SomeData x = new SomeData();
System.out.println(x.a);
DataChanger.change(x, 99);
System.out.println(x.a);
}
}
=============================================================
13
99
6.3 接口和实现
访问权限控制被称为 具体实现的隐藏
访问 权限控制 将权限边界 划在 了 数据类型内部 :
- 设定客户端程序员可以使用 和 不可以使用的界限
- 接口 和 具体实现进行分离 结构是用于 一组程序之中 , 客户端程序员 除了可以像接口 发送 信息之外 什么也不可以做的话,那么就可以 更改 所有 不是 public 的东西 (包访问 权限,protected 和 private 的 成员),而不会 破坏客户端代码。
6.4 类访问权限
- 每个 编译 单元 都只有 一个public 类
- 类不可以是 private 这样 出了它本身以外,其他任何类都访问不了它
- 也不可以是protected 的 (一个以上的内部类可以是 private 和 protected)
- 如果不希望其他任何人对该类拥有访问权限,可以把所有的构造器指定为 private,从而阻止任何人创建该类的对象
练习8
class Connection {
private static int count = 0;
private int i = 0;
private Connection() { System.out.println("Connection()");}
// Allow creation via static method:
static Connection makeConnection() {
count++;
return new Connection();
}
public static int howMany() { return count; }
public String toString() {
return ("Connection " + count);
}
}
public class ConnectionManager {
static int howManyLeft = 3;
static Connection[] ca = new Connection[3];
{
for(Connection x : ca)
x = Connection.makeConnection();
}
public static Connection getConnection() {
if(howManyLeft > 0)
return ca[--howManyLeft];
else {
System.out.println("No more connections");
return null;
}
}
public static void main(String[] args) {
ConnectionManager cm = new ConnectionManager();
System.out.println(cm.howManyLeft);
cm.getConnection();
System.out.println(howManyLeft);
cm.getConnection();
System.out.println(howManyLeft);
cm.getConnection();
System.out.println(cm.getConnection());
System.out.println(howManyLeft);
}
}
==========================================================
Connection()
Connection()
Connection()
3
2
1
No more connections
null
0
练习九
/* Create in access/local directory in your CLASSPATH:
* // access/local/PackagedClass.java
* package access.local;
*
* class PackagedClass {
* public PackagedClass() {
* System.out.println("Creating a packaged class");
* }
* }
* // Then, in another directrory create the file below and explain why compiler
* generates error. Would making Foreign class part of access.local change anything?
*/
// access/foreign/Foreign.java
package access.foreign;
import access.local.*;
public class Foreign {
public static void main(String[] args) {
PackagedClass pc = new PackagedClass();
}
}
/* Compiler error because: PackagedClass in not public, so no access outside of
* package. Moving Foreign to local would allow package access to PackagedClass.
*/