Java初窥门径
- Java万物皆对象,带你进入编程之旅!
- Java运算符,操纵数据全靠它!
- Java控制流程,保持身材尤为重要!
- Java初始化和清理,控制安全是首要!
- Java封装是为了更好的重构!
- JavaOOP优雅总是会有回报!
- Java接口和抽象类如何选择!
- Java集合,日常开发掌握这些就够了
- Java不要再问String为什么是不可变的!
前言
编写过代码的人都经历过,如果把一段代码搁置一段时间,回过头来在看,你可能发现更好的实现方式。这就是重构的原动力之一。一个软件或产品总是会产生这样一种糟糕的现象:软件产品最初制造出来,是经过精心的设计,具有良好架构的。但是随着时间的发展、需求的变化,必须不断的修改原有的功能、追加新的功能,还免不了有一些缺陷需要修改。为了实现变更,不可避免的要违反最初的设计构架。经过一段时间以后,软件的架构就千疮百孔了。
但是,在修改和完善代码的愿望下,也存在巨大的压力。通常,一些用户(基于软件开发功能的程序员)希望你的代码在某些方面保持不变。所以你想修改代码,但他们希望代码保持不变。由此引出了面向对象设计中的一个基本问题:“如何区分变动的事物和不变的事物”。
这个问题对于类库(library)而言尤其重要。类库的使用者必须依赖他们所使用的那部分类库,并且知道如果使用了类库的新版本,不需要改写代码。另一方面,类库的开发者必须有修改和改进类库的自由,并保证客户代码不会受这些改动影响。
为了解决这一问题,Java 提供了访问修饰符(access specifier)供类库开发者指明哪些对于客户端程序员是可用的,哪些是不可用的。访问控制权限的等级,从“最大权限”到“最小权限”依次是:public,protected,包访问权限(package access)(没有关键字)和 private。然而,类库组件的概念和对类库组件访问的控制仍然不完善。其中仍然存在问题就是如何将类库组件捆绑到一个内聚的类库单元中。Java 中通过 package 关键字加以控制,类在相同包下还是在不同包下,会影响访问修饰符。
包的概念
包内包含一组类,它们被组织在一个单独的命名空间(namespace)下。当创建一个包时,包名就隐含了目录结构。这个包必须位于包名指定的目录中,该目录必须在以 CLASSPATH 开始的目录中可以查询到。
如果你使用了 package 语句,它必须是文件中除了注释之外的第一行代码。当你如下这样写:
package com.fs;
你正在声明的编译单元中的 public 类名称位于名为 com.fs 的保护伞下。任何人想要使用该名称,必须指明完整的类名或者使用 import 关键字导入 com.fs 。(注意,Java 包名按惯例一律小写,即使中间的单词也需要小写,与驼峰命名不同)
例如,假设文件名是 MyClass.java ,这意味着文件中只能有一个 public 类,且类名必须是 MyClass(大小写也与文件名相同):
package com.fs;
public class MyClass {
// ...
}
如果有人想使用 MyClass 或 com.fs 中的其他 public 类,就必须使用关键字 import 来使 com.fs 中的名称可用。
import com.fs.*;
public class ImportedMyClass {
public static void main(String[] args) {
MyClass m = new MyClass();
}
}
还有一种选择是使用完整的名称:
public class ImportedMyClass {
public static void main(String[] args) {
com.fs.MyClass m = new MyClass();
}
}
访问权限修饰符
Java 访问权限修饰符 public,protected 和 private 位于定义的类名,属性名和方法名之前。每个访问权限修饰符只能控制它所修饰的对象。
访问权限 | 类 | 包 | 子类 | 其他包 |
---|---|---|---|---|
public | Y | Y | Y | Y |
protected | Y | Y | Y/N 不能修饰类(内部类除外) | N |
default | Y | Y | N | N |
private | Y | N | N | N |
包访问权限
包访问权限可以把相关类聚到一个包下,以便它们能轻易地相互访问。
- 使成员成为 public。那么无论是谁,无论在哪,都可以访问它。
- 赋予成员默认包访问权限,不用加任何访问修饰符,然后将其他类放在相同的包内。这样,其他类就可以访问该成员。
- 继承的类既可以访问 public 成员,也可以访问 protected 成员(但不能访问 private 成员)。只有当两个类处于同一个包内,它才可以访问包访问权限的成员。但现在不用担心继承和 protected。
- “get/set” 方法读取和改变值。
public: 接口访问权限
当你使用关键字 public,就意味着紧随 public 后声明的成员对于每个人都是可用的。
默认包
该目录中所有的其他文件都提供了包访问权限。
protected: 继承访问权限
关键字 protected 处理的是继承的概念,通过继承可以利用一个现有的类——我们称之为基类,然后添加新成员到现有类中而不必碰现有类。我们还可以改变类的现有成员的行为。为了从一个类中继承,需要声明新类 extends 一个现有类,像这样:
class student extends Person {}
private: 你无法访问
关键字 private 意味着除了包含该成员的类,其他任何类都无法访问这个成员。同一包中的其他类无法访问 private 成员,因此这等于说是自己隔离自己。另一方面,让许多人合作创建一个包也是有可能的。使用 private,你可以自由地修改那个被修饰的成员,无需担心会影响同一包下的其他类。
类访问权限
访问权限修饰符也可以用于确定类库中的哪些类对于类库的使用者是可用的。如果希望某个类可以被客户端程序员使用,就把关键字 public 作用于整个类的定义。这甚至控制着客户端程序员能否创建类的对象。
为了控制一个类的访问权限,修饰符必须出现在关键字 class 之前:
public class Person {}
注意,类既不能是 private 的(这样除了该类自身,任何类都不能访问它),也不能是 protected 的。所以对于类的访问权限只有两种选择:包访问权限或者 public。为了防止类被外界访问,可以将所有的构造器声明为 private,这样只有你自己能创建对象。
总结
控制成员访问权限有两个原因。第一个原因是使用户不要接触他们不该接触的部分,这部分对于类内部来说是必要的,但是不属于客户端程序员所需接口的一部分。因此将方法和属性声明为 private 对于客户端程序员来说是一种服务,可以让他们清楚地看到什么是重要的,什么可以忽略。这可以简化他们对类的理解。
第二个也是最重要的原因是为了让类库设计者更改类内部的工作方式,而不用担心会影响到客户端程序员。比如最初以某种方式创建一个类,随后发现如果更改代码结构可以极大地提高运行速度。如果接口与实现被明确地隔离和保护,你可以实现这一目的,而不必强制客户端程序员重新编写代码。访问权限控制确保客户端程序员不会依赖某个类的底层实现的任何部分。
最后的最后
为初学者提供学习指南,为从业者提供参考价值。我坚信码农也具有产生洞见的能力。扫描下图二维码关注,学习和交流!