面向对象设计的2个基本问题:
- 如何把变动的部分与保持不变的部分区别开来?
- 开发者如何约定权限来修改和改进代码,并确保客户代码不会因为这些改动收到影响?
Java使用访问权限修饰符来供开发人员向客户端程序员指明可用和不可用的,以及访问权限控制等级:
- 包访问权限(没有修饰符关键词)
- public - 公开的
- protected - 保护的
- private - 私有的
Java中构建类库的概念以及对于谁有权限取用该类库的控制问题,使用 package 方式控制。
1. 包:库的单元
包内有一组类,他们在单一的名字空间之下组织在一起。
如Java在标准发布中有一个工具库,被组织在java.util名字空间下,java.util中有一个叫ArrayList的类,使用ArrayList的方式有两种:
- 全限定名:
java.util.ArrayList list = new java.util.ArrayList();
- 导入包名:
import java.util.*; // 导入util下所有工具类(通配符 * )
import java.util.ArrayList; // 导入util下ArrayList工具类
为了防止类名称之间的冲突问题:
一个java源代码文件通常被称为编译单元,.java为后缀名,该类名必须与文件名相同,且编译单元内只能有一个public类,否则Java编译器不会通过编译。
该public类在包之外是无法被看到和使用。
Java的解释器负责编译器生成的.class文件的查找、装载、和解释。
如果希望构件(独立成对的.java和.class)从属于同一个群组,就可以使用关键字:
package access;
// 必须文件第一行,表示声明该文件是名为access的类库的一部分
// 在该文件下的public类使用时,必须使用全限定名或者import导入包
Java包的命名规则为:全小写字母。
2. Java访问权限修饰词
Java 有三个显式关键字来设置类中的访问权限:public(公开),private(私有)和protected(受保护)。这些访问修饰符决定了谁能使用它们修饰的方法、变量或类。
- public(公开)表示任何人都可以访问和使用该元素;
- private(私有)除了类本身和类内部的方法,外界无法直接访问该元素。private 是类和调用者之间的屏障。任何试图访问私有成员的行为都会报编译时错误;
- protected(受保护)类似于 private,区别是子类(下一节就会引入继承的概念)可以访问 protected 的成员,但不能访问 private 成员;
- default(默认)如果你不使用前面的三者,默认就是 default 访问权限。default 被称为包访问,因为该权限下的资源可以被同一包(库组件)中其他类的成员访问。
2.1 public
声明之后紧跟着的成员时每个人都可用的,尤其是使用类库的客户程序员更是如此。
package access.dessert;
public class Cookie {
public Cookie() { //公开的
System.out.println("Cookie constructor");
}
void bite() { //非公开,默认的
System.out.println("bite");
}
}
import access.dessert.*;
public class Dinner {
public static void main(String[] args) {
Cookie x = new Cookie(); //可以访问
// x.bite(); // 不能访问,因为权限非public
}
}
- Cookie.java必须位于名为access下的dessert目录中
- 不同目录下创建了Dinner.java就可以通过import导包的形式方位到 Cookie
- Dinner.java对于非public的bite()方法是不能访问的,编译器也禁止使用它
2.2 private
除了包含该成员的类以外的地方都无法直接访问这个修饰符修饰的成员。
- 修饰成员变量:只能通过get/set成员方法访问
- 修饰成员方法:该成员方法仅类内可以访问
- 修饰构造方法:该类不允许使用该私有构造方法来创建对象
public class Test01 {
public static void main(String[] args) {
//MyClass m = new MyClass(); // Error: private MyClass() {}
MyClass m = new MyClass(10);
m.setA(20);
//System.out.println(m.getA()); // Error
}
}
class MyClass {
private int a; //私有
private MyClass() {} //私有
public MyClass (int n) {
this.a = n;
}
private int getA() { //私有
System.out.println(this.a);
return a;
}
public void setA(int a) {
this.a = a;
this.getA();
}
}
2.3 protected
关键字protected处理的是继承的概念,通过继承可以利用一个现有类(父类),然后将新成员添加到该父类而不必去修改父类,还可以改变父类的现有成员的行为。——方法覆盖。
- 如果创建了一个新包,并再另一个包中继承类,那么唯一可以访问的成员就是源包的public成员;
- 如果父类的创建者会希望有某个特定的成员,把对它的访问权限赋予派生类而不是所有类,这就需要protected修饰。
- protected提供包访问权限,即相同包内的其他类可以访问protected元素
3. 接口和实现中的访问权限
封装思想,即把数据和方法包装进类内,以及具体实现的隐藏,共同被称作封装。
访问权限的边界放在了数据类型内部的原因:
- 要设定客户端程序员可以使用和不可以使用的界限;
- 将接口和具体实现进行分离。
【常用做法】将public成员置于类的开头部分,后面跟着protected、包访问权限和private成员的创建类形式。
(好处:类的使用者可以首先阅读到重要的部分)
class Access {
public void m1() { }
public void m2() { }
protected void o1() { }
protected void o2() { }
private void p1() { }
private void p2() { }
private int a;
private int b;
//...
}
4. 类的访问权限
原则:
- 每个.java源文件必须只能有一个public类;
- public类的名称必须与该源文件的文件名一致;
- .java的源文件中不带public类也是可以的,此时可以随意对文件命名(但随意命名会导致可读性变得非常差!谨慎)且文件中没有访问权限修饰符的类默认为 包访问权限,这也意味着该类的对象可以由包内任何其他类来创建,但在包外则不行;
- 如果默认无访问权限修饰符修饰的类的某个static成员是public的话,则客户端程序员仍然可以调用该static成员,尽管他们并不能生成该类的对象(类加载)。
注意:
访问权限控制,专注于类库创建者和该类库的外部使用者之间的关系,这种关系也是一种通信方式。