day7-Java封装性
JAVA封装
C的反例:C中可以直接访问和设置变量,
如myString.isUnderlined = true; myString.isItalics = true;
但这很危险,因为我们并不知道对象如何使用它,因此直接改变它们可能并不安全。
我们需要让外部对象访问内部数据时,想通过可控的方式进行。
通过一些额外方法,即以存取器“Accessor”和变异器“Mutators”的方法来实现。
boolean isUnderlined = false;//需要实现取消下划线
//实际上应进行如下操作:
void setUnderlined(boolean underline) ... //set = 变异器,改变内部数据状态
boolean getUnderlinedStatus() ... //get = 存取器,获得内部状态数据检查是否成功改变
Public, Private 和 Protected
这些是访问级别修饰符,决定了其他类是否可以使用该特定属性域或调用特定方法,是用于防止意外破坏封装的工具。
default (无修饰符)也是一类修饰符,它代表的访问控制级别为——包内私有package-private。
| 修饰符\可见范围 | 同Class | 同Package | 包外Subclass | World |
|---|---|---|---|---|
| Public | ✅ | ✅ | ✅ | ✅ |
| Protected 包内~+包外子类私有 | ✅ | ✅ | ✅ | ❌ |
| Default 包私有 | ✅ | ✅(方法一般为默认) | ❌ | ❌ |
| Private 类私有 | ✅(属性一般为私有) | ❌ | ❌ | ❌ |

private:仅(同包的)同类内私有;
default:同包任意类私有(如: 无访问级别修饰符的方法,为“包内公共”方法,可在同包下任意位置调用);
protected:同包任意类+不同包的子类(如:class C extends A 即C是A的子类,那即使C在其他包内也可被访问);
public:任意包/任意类位置都可调用,除非它位于“无导出声明的模块”中
模块 vs 包:
- 模块:就是一个 .java 文件。
- 包机制:为便于管理大型软件系统中数目众多的类,解决类的命名冲突的问题(类文件重名),能够提供类的多重类命名空间。这样,原本两个同名的类文件XXX.class,就可放在不同的包 A、B下,使用时以A.XXX或者B.XXX来区别即可。(包是类似于文件夹、多重路径名一样的存在。)
- 拓展:
- package命令:在定义
Cat类的.java文件首行输入package animal.pet;,就是指明将类Cat文件放入路径 animal/pet 的文件夹内,编译后,将产生的Cat.class 文件拖入该文件夹中。- import命令:在要使用
Cat类的文件中,首行输入 import 命令引入package路径:import animal.pet.Cat,语意为导入包animal.pet下的Cat类文件。也可通过import animal.pet.*命令告诉编译器到该目录下寻找所有类文件。
属性
- Public:可从任意位置访问(一般不好)
- Private:只有其定义所在类的内部 可访问(最常用)
- Protected:类内部及其子类内部都可访问(即其family下任意位置)
方法
- Public:可从任意位置访问
- Private:只有其定义所在类的内部可访问
- Protected:类内部及其子类内部都可访问(该家族下任意位置)
Data Hiding
应使用private来例行隐藏尽可能多的属性和方法,将private作为变量的默认访问方式。
另一个好处:我们可以修改其执行(比如完全去除某个属性),而无需修改对象访问类的方式。因为类以外的地方无法直接访问该private变量,只能通过public的公共方法访问,所以无需改变(使用操作与类的)接口。
public class Polygon{ //类本身还是可以public的
private Position[] points; //private的是该类中不想被随意修改的变量,用setter & getter 来进行访问控制
//private int noSides; //这里将属性noSides删去了也只对本地类有影响。
Polygon(Position[] points){ //初始化--为传入的点数组
this.points = points;
}
Polygon(Position[] points){ //初始化--返回点数组。输入参数一致可能有副作用:改变原值
return points;
}
int getNoSides(){
return points.length; //由于所删noSides是私有的,在外用其公共方法存取,接口不涉及noSides变量,因此接口没有改变
}
void setPoints(Position[] points){
this.points = points;
}
}
访问控制
getters和setters是两种方法的命名,分别用于访问属性和修改属性,来替代直接访问。
让某个变量只能通过公共方法来存取,这些公共存取和变值方法就叫accessor(getter)或mutator(setter)。
比如,设学生类 - 姓名变量Name 为私有private,只能通过公共方法getName()和setName(n)存取。
这样,在外部就根本不知道name在哪里存储,甚至name变量本身都可以不存在,对外只需要知道"Name"这个名称,就能操作这个想象中的Name变量。
为何谨慎访问封装?
为了保护类的假设不变、保护一些不变量或恒等条件不变。
-
getter:用于传递属性,因为不改变值,所以无副作用。
-
setter:用于保证要求的不变式(量)左右恒定。使整体状态(类的假设)得以不变、维持。
class Polygon{
Position[] points;
int noSides;
Polygon(Position[] points){ //与类同名,无返回类型,是构造函数(初始化)。
this.points = points;
noSides = points.length;
}
Position[] getPoints(){ //getter method
return points; //getter一般只“传递”重要属性
void setPoints(Positionp[] points){ //setter method
this.points = points;
noSides = points.length;//每次set完points,要同时更新sides数量
//这样使得不变式“points.length == noSides”恒定
}
}
浙公网安备 33010602011771号