《Java编程思想》Chapter 6 : 访问权限控制
- 1. 前言
- 1.1. 访问权限控制的等级
- 1.2.
package
关键字的引入
- 2. 包:库单元
- 2.1. 代码组织
- 2.2. 包名的创建
- 3. Java访问权限修饰词
- 3.1. 包访问权限
- 3.2.
public
: 接口访问权限 - 3.3.
private
: 你无法访问 - 3.4.
protected
: 继承访问权限
- 4. 接口和实现
- 4.1. 封装
- 5. 类的访问权限
- 5.1. 部分额外限制
- 5.2. 作用
- 5.3. 一些特殊用法
1. 前言
我们总是会发现我们希望对于我们的代码进行重写以改进
使得其更可读、更易理解,因而更具有可维护性
这便是重构
但是我们改变代码,就可能会引发它的变化
但对于使用者来说,会希望代码保持不变
这边产生一个问题:如何将代码中变动的事物与不变的事物区分开来?
这对于类库尤其重要
这便是访问权限控制的作用
Java提供 访问权限修饰词
用于向代码使用者指明,哪部分是可以用的,哪部分是不可以用的
1.1. 访问权限控制的等级
从最大权限到最小权限
- public
任何地方都可以访问 - protected
同一个包内,或者子类可以访问 - 包访问权限(no keyword, default)
同一个包内可以访问 - private
只有类内可以访问
1.2. package
关键字的引入
对于 访问权限修饰词
还存在着如何将构件捆绑到一个内聚的类库单元中的问题
Java使用 package
关键字来解决这个问题
2. 包:库单元
**包(package)**内包含有一组类
被组织在单一的名字空间之下
也就是包名
我们一直使用的导入,就是提供一个管理名字空间的机制
我们平时编写的Java源文件,通常被称为编译单元(或转译单元)
每个编译单元只能由不超过一个public
类
编译单元中的public
类对包外可见
2.1. 代码组织
我们可以发现,我们对一个编译单元进行编译后
其中的每个类都会有一个输出文件,有着.class
的后缀
他们可以被打包并压缩为一个java文档文件(JAR,使用Java的jar文档生成器)
而类库则实际上是一组类文件
其中每个文件都有一个public
类,以及任意数量的非public
类
public
类的名字必须与文件名相同
关键字package
用于将类库中的类组织在一起:
// 在一个包中的类
package PackageTest;
若使用package
语句,则其必须是文件中除注释外的第一句代码
包的命名规则:
package
的命名全部使用小写字母,包括中间的字
对于package
与import
的理解:
将单一的全局名字空间分割
使得不论多少人进行编写,也不会出现名称冲突的问题
2.2. 包名的创建
包从未整被真正打包为单一的文件
且一个包可以有多个.class
文件构成
这可能造成复杂的情况
为了避免这种情况,我们将.class
文件都置于一个目录之下
利用操作系统的层次化化文件结构来解决此问题
这是Java解决混乱问题采用的一种方式
同时另外两个问题也可以被解决:
- 如何创建独一无二的名称?
- 如何查找可能隐藏于目录结构中某处的类?
包名的创建:
依据惯例,报名使用创建者的反顺序域名
(域名是第一无二的,那么包名因而也是独一无二的)
没有域名,也可以采用不太可能重复的组合
例子top.thesumst.lab
寻址方法:
把package
名称分解为系统上的一个目录
然后在该目录下寻找.class
文件
Java解释器寻找.class
运行过程大致图解:
graph LRA[Java解释器] --> B[寻找CLASSPATH环境变量]B --> C[寻找包名]C --> |包名重构,将点替换为路径|D[寻找包名对应的目录]D --> E[寻找类文件]E --> F[加载类]
p.s. 使用jar文件时,必须在类路径中将jar文件的实际名称写清楚
(实际上可以理解为一个目录的根目录)
冲突处理:
可以想象,还是哟可能出现几个包中出现重复类名的情况
如果我们使用通配符*
导入包,那么可能会出现问题
此时我们需要指明具体的包名
或者我们可以使用import
语句的全名形式,指定导入类
从而大大降低了冲突的可能性
p.s. 默认包:当没有使用package
语句时,类位于默认包中,同样目录下的默认包中的类之间具有包访问权限
3. Java访问权限修饰词
Java中的四种Java访问权限修饰词:
public
protected
包访问权限(no keyword, default)
private
他们用于在类中每个实例变量或方法前进行修饰
并仅作用于这个定义
其中,不提供任何访问权限修饰词,意味着包访问权限
3.1. 包访问权限
包访问权限意味着对当前包中的所有其他类都可见
但是包之外的所有类,都没有这个成员的访问权限
因而希望取得某成员的访问权,我们只有几个途径:
public
声明- 不加访问权限修饰词,将其他类至于同一个包中
protected
声明,继承此类的子类可以访问- 对于
private
声明,提供访问器(getter
)和修改器(setter
)方法
使得其他类可以通过这两个方法访问
OOP中最优雅的方式
3.2. public
: 接口访问权限
public
关键字修饰成员,表示其对每个人都是可用的
3.3. private
: 你无法访问
private
关键字修饰成员
表示除了成员所示类之外,其他任何类都无法访问
使用场景举例:
- 控制类的对象的创建
显示定义构造器,并将其声明为private
此时我们可以通过提供一个public
的静态方法来创建对象
这有利于我们控制对象的创建
(还会组织对于此类的继承) - "助手"方法
如果一个方法,我们确定它只会被用于辅助类中的别的方法
我们可以将它指定为private
可以组织我们在包内的其他地方对其进行误用 - 域一般应该指定为
private
通过提供public
方法来访问域
使得我们可以控制对域的访问
除非需要公开底层的实现细节(不太常见),否则一般推荐将所有域都指定为private
3.4. protected
: 继承访问权限
protected
关键字修饰成员
主要用于处理集成概念
说明这个成员对于继承的子类是可见的
此外,还提供包访问权限
4. 接口和实现
4.1. 封装
对访问权限的控制,常被称为具体实现的隐藏
将数据和方法包装进类中,以及具体是实现的隐藏,常共同被称为封装
结果是得到的同时带有特征和行为数据类型
访问权限控制将权限控制于数据类型的内部
两个重要原因:
- 要设定代码中,可以被使用和不可以被使用的界限
一般我们称使用者为客户端程序员 - 接口和具体实现进行分离
如果我们限制客户端程序员除了向接口发送信息之外不可以进行别的操作
那么我们可以在不破坏客户端代码的情况下
随意修改任何不是public
的东西(也就是接口之外的东西)
常用的实践模式:
将public
成员至于开头
随后耕者protected
、包访问权限、private
成员
这样便于类的使用者抓住重点
因为这样他们只需要阅读public
部分,也就是他们需要同时也是可以访问的部分
5. 类的访问权限
不同于类的成员
类的访问权限只有两种:public
和包访问权限
5.1. 部分额外限制
- 每个编译单元只能有一个
public
类 public
类的必须完全与其所处的编译单元的文件名相同
包括大小写- 编译单元内可以不带
public
类(不常用)
5.2. 作用
确保客户端程序员只使用我们希望提供给外部使用的类
而非我们可能知识用于内部实现,或者后续很可能更改或删除的类
5.3. 一些特殊用法
如果我们不希望其他任何人拥有某个类的访问权限
我们可以将其所有的构造器都指定为private
如此,除了该类static
成员内可以创建
其他任何人都无法创建该类的对象
这种操作有几种可能的用途:
- 返回引用之前对对象做一些额外的工作
- 记录对象的创建次数,可以限制对象的数量