我在理解Java中的封装方面遇到了一些麻烦。我所知道的是封装允许从程序中的任何其他地方隐藏信息(使用私有变量)。但是,有人能告诉我如何创建一个包含公共数据字段的类可能会导致问题?如果类提供了getter和setter方法,我不理解数据是如何被隐藏的。
public class Student{
private int id;
private String name;
private int grade;
public Student(){
}
public Student(int id; String name, int grade){
this.id = id;
this.name = name;
this.grade = grade;
}
public int getId(){
return id;
}
public void setId(int id){
this.id = id;
}
//more code
}
答案 0 :(得分:2)
“信息隐藏”一词并不一定要推断信息的安全性受到威胁。但要首先触及安全主题,在“完整代码”第2版中,Steve McConnell写道:
“公开成员数据违反了封装,并限制了对抽象的控制。”
这里的关键见解是,您无法控制使用您的代码的人与您希望代码的行为方式之间的交互。考虑到您的示例代码,请考虑这种情况:您的老板/客户/老师给出了一个要求/约束:对id字段进行一些错误检查,以确保它在一个范围内(可能只允许正数) 。你如何实现这个要求,以确保每当有人获得id后,它遵循该要求?
以下是该要求的示例实现:
public void setId(int id){
if(id < 0) {
this.id = 0;
} else {
this.id = id;
}
}
此实现允许您确定在给定要求的情况下适当地设置了id字段,这允许您告诉使用代码的人员id始终是正数。不幸的是,当其他人使用您的代码时,他们可以直接引用成员变量并将其设置为如果它是公共的那样:
Student student = new Student();
student.id = -1;
当有人坐在IDE(甚至可能是你自己继续执行代码)时,他们怎么会知道不使用上面设置id字段的方法?控制对该字段的访问的最佳方法是只允许一个入口点。
也许“信息隐藏”最重要的方面是,如果您通过访问器和更改器控制对成员变量的访问,您可以在不让用户变得更聪明的情况下进行实现更改。请考虑以下情况:您的老板/客户/教师现在要求您将id字段加密为字母数字字符串,而不会影响已使用软件版本1的代码的用户。如果您允许直接访问成员变量,那么您无法更改id字段的内部表示,而不会破坏人们可能已经在您之上编写的代码。
他们可能是做这样的事情:
Student student = new Student();
int currentId = student.id;
int idWithOffset = currentId + 100;
如果您突然需要/想要将id设为String或Id对象,会发生什么?如果您将id保持为私有,则可以根据需要进行这些更改,并在处理转换逻辑的setter / getter中编写代码,并且用户的代码永远不需要更改。
我最后(和个人最喜欢的理由)为什么'信息隐藏',例如让会员私密,有助于减少问题,是它删除了一些代码的细节,否则需要在UML图表,你的大脑,Javadocs中浮动等等......信息隐藏有助于抽象,这样你在工作这个课时就可以考虑课堂内的所有组件和玩家,而且在处理其他类时不必将它留在你的大脑中。
干杯。
另外,对于像Java语言这样的问题的一个很好的参考是Joshua Bloch(第2版)的“Effective Java”。
答案 1 :(得分:1)
这是一个公共字段可以使其类完全不可靠的示例。另一种私有版本可以保证其信息始终有效,并且不受欢迎的参与者无法接触。
公共字段类(不安全):
class VeryImportantCounterPublic {
public int currentCount;
public VeryImportantCounterPublic(int initial_count) {
currentCount = initial_count;
}
public void incrementCount() {
currentCount++;
}
public int getCurrentCount() {
return currentCount;
}
}
私人字段类(安全):
class VeryImportantCounterPrivate {
private int currentCount;
public VeryImportantCounterPrivate(int initial_count) {
currentCount = initial_count;
}
public void incrementCount() {
currentCount++;
}
public int getCurrentCount() {
return currentCount;
}
}
主:
public class CounterTest {
public static final void main(String[] ignored) {
VeryImportantCounterPublic counter = new VeryImportantCounterPublic(3);
counter.incrementCount();
counter.incrementCount();
counter.incrementCount();
//Should not be able to do this!!
//But it's a public field, so you can :(
counter.currentCount = -203847382;
System.out.println("[PUBLIC] Current count is " + counter.getCurrentCount());
VeryImportantCounterPrivate counter2 = new VeryImportantCounterPrivate(3);
counter2.incrementCount();
counter2.incrementCount();
counter2.incrementCount();
//Can't do this. Compiler error:
//counter2.currentCount = -203847382;
System.out.println("[PRIVATE] Current count is " + counter2.getCurrentCount());
}
}
输出:
[PUBLIC] Current count is -203847382
[PRIVATE] Current count is 6
完整来源:
public class CounterTest {
public static final void main(String[] ignored) {
VeryImportantCounterPublic counter = new VeryImportantCounterPublic(3);
counter.incrementCount();
counter.incrementCount();
counter.incrementCount();
counter.currentCount = -203847382;
System.out.println("[PUBLIC] Current count is " + counter.getCurrentCount());
VeryImportantCounterPrivate counter2 = new VeryImportantCounterPrivate(3);
counter2.incrementCount();
counter2.incrementCount();
counter2.incrementCount();
//Can't do this. Compiler error:
//counter2.currentCount = -203847382;
System.out.println("[PRIVATE] Current count is " + counter2.getCurrentCount());
}
}
class VeryImportantCounterPublic {
public int currentCount;
public VeryImportantCounterPublic(int initial_count) {
currentCount = initial_count;
}
public void incrementCount() {
currentCount++;
}
public int getCurrentCount() {
return currentCount;
}
}
class VeryImportantCounterPrivate {
private int currentCount;
public VeryImportantCounterPrivate(int initial_count) {
currentCount = initial_count;
}
public void incrementCount() {
currentCount++;
}
public int getCurrentCount() {
return currentCount;
}
}
答案 2 :(得分:1)
Getters和Setters创建looser coupling
。
假设您聘请某人为您携带钱包。
如果此钱包的位置为public
,则表明您的员工龋齿的位置。你可以直接抓住它,所以这个人必须随身携带它。
如果它的位置为private
,则不再允许您携带钱包。你需要要求它(使用getter
方法)。鉴于您的合同雇员仍然必须向您提供,他不必让您知道他保留的确切位置。所以他可以开始把它放在任何对他来说舒服的地方 - 在另一个口袋里或在他的包里 - 而不违反合同。因此,您仍然可以在需要时随身携带钱包,但您无需知道其携带的位置。