文章目录
写在前面的话
访问控制的功能很明显:
通过访问(权限)控制符隐藏类中的具体实现。
不但将数据结构和对数据结构进行的操作进行封装;而且对封装后的成员通过访问控制符,该隐藏的隐藏,该暴露的暴露。达到访问的安全性、类的可维护性、类数据的完整性。
通过访问控制符可以实现良好的封装. 访问控制符与继承无关。
访问控制符:访问级别(public、protect、default(friendly)、 private)
| 同一类 | 同一包 | 不同包的同一子类 | 不同包的不同子类 | |
|---|---|---|---|---|
| public | ✔ | ✔ | ✔ | ✔ |
| protect | ✔ | ✔ | ✔ | |
| default | ✔ | ✔ | ||
| private | ✔ |
之前曾经介绍过类的封装
结合访问控制符, 大致可以通过下面的图示反映出一些特性:
下面的内容也与此密切相关
访问控制(或隐藏具体实现)概述
访问权限控制专注于类库的创建者(provider)和该类库的外部使用者之间的关系。
为类及类成员所遵守的界限。访问权限的控制常被称为是具体实现的隐藏。
安全访问性(consumer访问的角度):provider的类或类成员,
允许哪些consumer可以访问(对这些consumer而言是,类库或类成员的不可变动部分),
不允许哪些consumer访问 (对这些consumer隐藏了具体实现,对这些consumer而言是类库或类成员的可变动部分);
可维护性(从provider修改的角度):provider对类或类成员的修改,
不需要修改哪些consumer(对这些consumer隐藏了具体实现,对这些consumer而言是类库或类成员的可变动部分),
需要修改哪些consumer(对这些consumer而言是,类库或类成员的不可变动部分)。
访问控制采用的方式:
1) 访问控制符:为类、类成员设置不同级别的访问控制符
2) 库单元(类库):创建类库。
库单元(类库)是一个逻辑概念,即相关类的集合。提供了一个访问库类的界限。物理上,在java中是使用操作系统的子目录来实现库的,是相关×××.class文件的集合;在.net中是使用操作系统的动态连接库文件来实现库的,是相关类的集合。
两类开发人员:
1) 类库开发人员:提供第三方类库,不提供源程序。应该严格地遵循访问控制规则。
2) 应用程序开发人员:具有开发的源程序,该源程序的开发可以不必严格地遵循访问控制规则。
访问控制的三个原因
讨论
对于C语言来说,C语言函数定义首先进行声明(都是public),然后再进行函数实现,对于函数的实现,它是被封装起来的,因而变得可维护与安全.
但是对于Java语言来说,:
在类中可以出现访问控制符的三处单元
1) 类的声明
2) 方法的声明
3) 字段的声明
对于方法而言,方法定义 = 方法声明 + 方法实现 这个是和C语言很相似的,
但是Java却比C语言多了类的声明和字段的声明,所以具备有更好的封装的条件,这里很自然而然地引入了访问控制符
1, 访问安全:对于类库和类的使用者而言,不可以访问(偷窥和篡改)
访问权限控制符控制该类的类名、方法名、字段名可以被哪些类的方法所访问(即该类的类名、方法名、字段名可以出现在哪些类的方法中);或者说不可以被哪些类的方法所访问。
可以使用户不要碰触那些他们不该碰触地部分,这些部分对于包、类的内部操作是必要的,但是它并不属于客户端程序员所需接口的一部分。
所以应该将包、类内部设计为外部可访问部分和不可访问部分。类的公共接口(API方法)是用户真正能够访问到的,所以这一部分在分析和设计上是决定该类是否正确的最重要的部分。
从×××.java源程序的可读性,可理解上,
1) 库中外部可访问的类(public),放于源程序的最上部
顺序:public类、默认friendly类
2) 类中外部可访问的成员,放于类的前部。
顺序:field、method
顺序(例如method):public成员、protected成员、默认friendly成员、private成员
优点:类的使用者可以从头读起,首先阅读对他们而言最为重要的部分(即publec成员,因为可以从.class文件外部调用他们),等到预见作为内部实现的非public成员时停止阅读。
2, 类可维护性(如何维护类),也是最重要的原因。对于类库和类的设计者而言
类的重构(即重写代码)是不可避免的。可以让库设计者可以更改类的内部工作方式,而不必担心这样会对客户端程序员产生重大的影响,客户端代码可以不必重构或重新编译。
所以应该将包、类设计为不可变动部分和可变动部分,使接口和实现被明确地隔离和加以保护。
如果类的某单元对访问类是隐藏的,则该单元的修改,对该访问类而言具有可维护性。
3, 数据的完整性
尽可能将field访问控制符设置为private,通过公共的getName()、setName()方法来访问。setName()方法中可以放置完整性规则,同时也具有可维护性。
访问权限控制符
狭义上访问通常指类声明的访问,和方法声明的访问。
(由于域定义总是private,方法的实现总是被隐藏)
1.类的访问权限控制符
类的访问,通常是指类声明的访问。
1)类的访问权限控制符:public
2) 类的访问权限控制符:默认friendly
3)类构造器的访问权限控制符:private,
a.可以阻止类外部直接创建该类的实例
b.可能将阻碍对此类的继承。
4)内部类的访问权限控制符:public、protected、默认friendly、private
2. 类成员的访问权限控制符:public、protected、默认friendly、private
由于域通常总是private,而方法的实现是被隐藏的;所以类成员的访问,通常是指对方法声明的访问(接口)。
1)公共访问权限:public
2)子类访问权限:protected
与.NET的internal protected相同。
有两层含义:包访问权限或继承访问权
3)包访问权限:默认friendly
4)本类访问权限:private
3. 间接访问类成员
访问控制符使用原则
考虑到安全、可维护、完整性,尽可能使用低级别的访问控制符。
访问控制符从高到低的级别(范围):public、protected、默认friendly、private。
应尽可能地总是将字段指定为private,减少类间耦合;否则,将产生高耦合,数据结构的耦合不具有可维护性。
类中的方法实现与访问控制符无关,总是隐藏的,具有可维护性。
程序1:
包的可见性是public
package p1;
public class MyClass1 {
public int a = 5;
private int b = 10;
protected int c = 20;
int d = 30;
public void func1() {
System.out.println("func1");
}
private void func2() {
System.out.println("func2");
System.out.println(b);
}
protected void func3() {
System.out.println("func3");
}
void func4() {
System.out.println("func4");
}
}
程序2:
包的可见性是默认friendly
package p1;
class MyClass2 {
public void func1() {
System.out.println("func1 of MyClass2");
}
}
程序3
同包内访问
同包指MyClass1类、MyClass2类和Test类的包名相同,逻辑上在一个包内。
并不要求MyClass1类文件、MyClass2类文件和Test类文件一定在同一个子目录中。
package p1;
public class Test {
public static void main(String[] args) {
MyClass1 obj1 = new MyClass1();
//a是公共属性,任何地方都可以访问
System.out.println(obj1.a);
// Error,b为私有属性,类外无法访问
//System.out.println(obj1.b);
// c是受保护属性,同包的类可以访问
System.out.println(obj1.c);
// d是缺省属性,同包的类可以访问
System.out.println(obj1.d);
// func1()是公共方法,任何地方都可以访问
obj1.func1();
//Error,func2()为私有方法,类外无法访问
//obj1.func2();
// func3()是受保护方法,同一包中的类可以访问,其他包中的子类也可以访问
obj1.func3();
// func4()是缺省方法,同一包中的类可以访问
obj1.func4();
// 同一包中的缺省访问控制类可以访问
MyClass2 obj2 = new MyClass2();
}
}
程序4:
不同包间非继承访问
package p2;
import p1.MyClass1;
//Error,不能导入不同包中的缺省类
//import p1.MyClass2;
public class Test {
public static void main(String[] args) {
MyClass1 obj1 = new MyClass1();
// 公共属性,任何地方都可以访问
System.out.println(obj1.a);
// Error,b为私有属性,类外无法访问
//System.out.println(obj1.b);
// Error,c是受保护属性,不同包中的非子类无法访问
//System.out.println(obj1.c);
// Error,d是缺省属性,不同包中的类不能访问
//System.out.println(obj1.d);
// func1()是公共方法,任何地方都可以访问
obj1.func1();
// Error,func2()为私有方法,类外无法访问
//obj1.func2();
// Error,func3()是受保护方法,不同包中的非子类无法访问
//obj1.func3();
// Error,func4()是缺省方法,不同包中的类不能访问
//obj1.func4();
// Error,不可以访问不同包中的缺省类
//MyClass2 obj2 = new MyClass2();
}
}
程序5:
不同包间继承访问
package p3;
import p1.MyClass1;
//Error,不能导入不同包中的缺省类
//import p1.MyClass2;
public static void main(String[] args) {
public void func() {
// 公共属性,任何地方都可以访问
System.out.println(a);
// Error,b为私有属性,类外无法访问
//System.out.println(b);
// c是受保护属性,子类可以访问
System.out.println(c);
// Error,d是缺省属性,不同包中的类不能访问
//System.out.println(d);
// func1()是公共方法,任何地方都可以访问
func1();
// Error,func2()为私有方法,类外无法访问
//func2();
// func3()是受保护方法,子类可以访问
func3();
// Error,func4()是缺省方法,不同包中的类不能访问
//func4();
// Error,不可以访问不同包中的缺省类
//MyClass2 obj2 = new MyClass2();
}
}