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 类私有 ✅(属性一般为私有)

table

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;
  }
}

访问控制

getterssetters是两种方法的命名,分别用于访问属性和修改属性,来替代直接访问。

让某个变量只能通过公共方法来存取,这些公共存取和变值方法就叫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”恒定
  }
}
posted @ 2021-02-19 12:43  吃饭睡觉打代码  阅读(66)  评论(0)    收藏  举报