java隐藏方式运行,Java 数据隐藏和封装

类由一些数据和方法组成。最重要的面向对象技术之一是,是把数据隐藏再类中,只能通过方法获取。这种技术叫作 封装(encapsulation),因为它可以把数据(和内部方法)安全的密封在这个“容器”中,只能由可信的用户(即这个类中的方法)访问。

为什么要这么做呢?最重要的原因是,隐藏类的内部实现细节。如果避免让程序员依赖这些细节,你就可以放心的修改实现,而无需担心会破坏使用这个类的现有代码。

你应该始终封装自己的代码。如果没有封装好,那么几乎无法推知并最终确

认代码是否正确,尤其是在多线程环境中(而基本上所有 Java 程序都运行在

多线程环境中)。

使用封装的另一个原因是保护类,避免有意或无意做了糊涂事。类中经常包含一些相互依

赖的字段,而且这些字段的状态必须始终如一。如果允许程序员(包括你自己)直接操作

这些字段,修改某个字段后可能不会修改重要的相关字段,那么类的状态就前后不一致

了。然而,如果必须调用方法才能修改字段,那么这个方法可以做一切所需的措施,确保

状态一致。类似地,如果类中定义的某些方法仅供内部使用,隐藏这些方法能避免这个类

的用户调用这些方法。

封装还可以这样理解:把类的数据都隐藏后,方法就是在这个类的对象上能执行的唯一一

种可能的操作。

只要小心测试和调试方法,就可以认为类能按预期的方式运行。然而,如果类的所有字段

都可以直接操作,那么要测试的可能性根本数不完。

隐藏类的字段和方法还有一些次要的原因。

如果内部字段和方法在外部可见,会弄乱类的 API。让可见的字段尽量少,可以保持类的整洁,从而更易于使用和理解。

如果方法对类的使用者可见,就必须为其编写文档。把方法隐藏起来,可以节省时间和

精力。

访问控制

Java 定义了一些访问控制规则,可以禁止类的成员在类外部使用。

这个 public 关键字,连同 protected

和 private(还有一个特殊的),是 访问控制修饰符,为字段或方法指定访问规则。

访问包

Java 语言不直接支持包的访问控制。访问控制一般在类和类的成员这些层级完成。

已经加载的包始终可以被同一个包中的代码访问。一个包在其他包中是否

能访问,取决于这个包在宿主系统中的部署方式。例如,如果组成包的类

文件存储在一个目录中,那么用户必须能访问这个目录和其中的文件才能

访问包。

访问类

默认情况下,顶层类在定义它的包中可以访问。不过,如果顶层类声明为 public,那么在

任何地方都能访问。

访问成员

类的成员在类的主体里始终可以访问。默认情况下,在定义这个类的包中也可以访问成员。这种默认的访问等级一般叫作包访问。

这只是四个可用的访问等级中的一个。其他

三个等级使用 public、protected 和 private 修饰符定义。下面是使用这三个修饰符的示例代码:

public class Laundromat { // 所有人都可以使用这个类

private Laundry[] dirty; // 不能使用这个内部字段

public void wash() { ... } // 但能使用这两个公开的方法

public void dry() { ... } // 处理内部字段

// 子类可能会想调整这个字段

protected int temperature;

}

下述访问规则适用于类的成员:

类中的所有字段和方法在类的主体里始终可以使用。

如果类的成员使用 public 修饰符声明,那么可以在能访问这个类的任何地方访问这个成员。这是限制最松的访问控制类型。

如果类的成员声明为 private,那么除了在类内部之外,其他地方都不能访问这个成员。

这是限制最严的访问控制类型。

如果类的成员声明为 protected,那么包里的所有类都能访问这个成员(等同于默认的

包访问规则),而且在这个类的任何子类的主体中也能访问这个成员,而不管子类在哪

个包中定义。

如果声明类的成员时没使用任何修饰符,那么使用默认的访问规则(有时叫包访问),

包中的所有类都能访问这个成员,但在包外部不能访问。

默认的访问规则比 protected 严格,因为默认规则不允许在包外部的子类中

访问成员。

使用 protected 修饰的成员时要格外小心。假设 A 类使用 protected 声明了一个字段 x,而

且在另一个包中定义的 B 类继承 A 类(重点是 B 类在另一包中定义)。因此,B 类继承了这

个 protected 声明的字段 x,那么,在 B 类的代码中可以访问当前实例的这个字段,而且

引用 B 类实例的代码也能访问这个字段。但是,这并不意味着在 B 类的代码中能读取任何

一个 A 类实例的受保护字段。

示例如下:

A类的定义如下:

package javanut6.ch03;

public class A {

protected final String name;

public A(String named) {

name = named;

}

public String getName() {

return name;

}

}

B类的定义如下:

package javanut6.ch03.different;

import javanut6.ch03.A;

public class B extends A {

public B(String named) {

super(named);

}

@Override

public String getName() {

return "B: " + name;

}

}

Java 的包不能“嵌套”,所以 javanut6.ch03.different 和 javanut6.ch03

是不同的包。javanut6.ch03.different 不以任何方式包含在 javanut6.

ch03 中,也和 javanut6.ch03 没有任何关系。

可是,如果我们试图把下面这个新方法添加到 B 类中,会导致编译出错,因为 B 类的实例无法访问任何一个 A 类的实例(只能访问字段):

public String examine(A a) {

return "B sees: " + a.name;

}

如果把这个方法改成:

public String examine(B b) {

return "B sees another B: " + b.name;

}

就能编译通过,因为同一类型的多个实例可以访问各自的 protected 字段。当然,如果 B

类和 A 类在同一包中,那么任何一个 B 类的实例都能访问任何一个 A 类实例的全部受保护字段,因为使用 protected 声明的字段对同一个包中的每个类都可见。

访问控制和继承

Java规范规定:

子类继承超类中所有可以访问的示例字段和示例方法;

如果子类和超类在同一个包中定义,那么子类继承所有没使用 private 声明的实例字段和方法。

如果子类在其包中定义,那么它继承所有使用 protected 和 public 声明的实例字段和方法。

使用 private 声明的字段和方法绝不会被继承;类字段和类方法也一样;

构造方法不会被继承(而是链在一起调用)

不过,有些程序员会对“子类不继承超类中不可访问的字段和方法”感到困惑。这似乎暗

示了,创建子类的实例时不会为超类中使用 private 声明的字段分配内存。然而,这不是上述规定想表述的。其实,子类的每个实例都包含一个完整的超类实例,其中包括所有不可访问

的字段和方法。

那么,关于成员访问性的正确表述应该是:“所有继承的成员和所有在类中定义的成员都

是可以访问的。”这句话还可以换种方式说:

类继承超类的所有实例字段和实例方法(但不继承构造方法);

在类的主体中始终可以访问这个类定义的所有字段和方法,而且还可以访问继承自超类的可访问的字段和方法。

成员访问规则总结

3e4e7937824b41a8d1d97e11a0dc4e07.png

下面是一些使用可见性修饰符的经验法则:

只使用 public 声明组成类的公开API的方法和常量。使用 public 声明的字段只能是常量和不能修改的对象,而且必须同时使用 final 声明。

使用 protected 声明大多数使用这个类的程序员不会用到的字段和方法,但在其他包中定义子类时可能会用到。

如果字段和方法供类的内部实现细节使用,但是同一个包中协作的类也要使用,那么就使用默认的包可见性。

使用 private 声明旨在类内部使用,在其他地方都要隐藏的字段和方法。

严格来说,使用 protected 声明的成员是类公开 API 的一部分,必须为其编

写文档,而且不能轻易修改,以防破坏依赖这些成员的代码。

如果不确定该使用 protected、包还是 private 可见性,那么先使用 private。如果太过严

格,可以稍微放松访问限制(如果是字段的话,还可以提供访问器方法)。

设计 API 时这么做尤其重要,因为提高访问限制是不向后兼容的改动,可能会破坏依赖成员访问性的代码。

数据访问器方法

Circle 类的这个版本使用 protected 声明 r 字段,还定义了访问器方法 getRadius() 和

setRadius(),用于读写这个字段的值,而且限制半径不能为负数。r 字段使用 protected

声明,所以可以在子类中直接(且高效地)访问。使用数据隐藏和封装技术定义的 Circle 类。

package shapes; // 为这个类指定一个包

public class Circle { // 这个类还使用public声明

// 这是通用的常量,所以要保证声明为public

public static final double PI = 3.14159;

protected double r; // 半径被隐藏了,但在子类中可见

// 限制半径取值的方法

// 这是子类可能感兴趣的实现细节

protected void checkRadius(double radius) {

if (radius < 0.0)

throw new IllegalArgumentException("radius may not be negative.");

}

// 非默认的构造方法

public Circle(double r) {

checkRadius(r);

this.r = r;

}

// 公开的数据访问器方法

public double getRadius() { return r; }

public void setRadius(double r) {

checkRadius(r);

this.r = r;

}

// 操作实例字段的方法

public double area() { return PI * r * r; }

public double circumference() { return 2 * PI * r; }

}

我们在一个名为 shapes 的包中定义 Circle 类。因为 r 字段使用 protected 声明,所以

shapes 包中的任何其他类都能直接访问这个字段,而且能把它设为任何值。这里假设

shapes 包中的所有类都由同一个作者或者协作的多个作者编写,而且包中的类相互信任,

不会滥用拥有的访问权限影响彼此的实现细节。

最后,限制半径不能使用负数的代码在一个使用 protected 声明的方法中,这个方法是

checkRadius()。虽然 Circle 类的用户无法调用这个方法,但这个类的子类可以调用,而

且如果想修改对半径的限制,还可以覆盖这个方法。

在 Java 中,数据访问器方法的命名有个通用约定,即以“get”和“set”开

头。但是,如果要访问的字段是 boolean 类型,那么读取字段的方法使用的

名称可能会以“is”开头。例如,名为 readable 的 boolean 类型字段对应的

访问器方法是 isReadable() 而不是 getReadable()。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值