我对封装概念有点困惑。我在同一个问题上经历了不少答案,但仍然感到困惑。据我所知,封装是将实例变量设为私有,以便外部无法直接访问它。将提供公共getter和setter方法来访问私有变量 假设我们有一个类如下:
class Address
{
int doorNumber;
public int getDoorNumber()
{
//some code
}
public void setDoorNumber(int doorNumber)
{
//some code
}
}
我们有另一个类,我们正在尝试访问Address类的变量。
class TestAddress
{
public static void main()
{
Address add=new Address();
add.doorNumber=10; //cannot be done
add.setDoorNumber(10);
}
}
虽然我们没有直接访问变量,但我们仍然使用setter方法修改doorNumber
以将其值设置为10。基本上,外部世界仍然可以访问私有领域并以其想要的方式对其进行修改。我不明白封装的重点是什么。能否请您提供一些了解封装的例子。还有未使用封装的情况以及由此引起的问题。
答案 0 :(得分:2)
请看下面的反例:
class Address
{
int floor, door;
public int getDoorNumber()
{
return floor*100+door;
}
public void setDoorNumber(int doorNumber)
{
int newFloor=doorNumber/100;
if(newFloor<0 || newFloor>6)
throw new IllegalArgumentException("no such door "+doorNumber);
floor=newFloor;
door=doorNumber-newFloor*100;
}
}
调用setDoorNumber(10)
的代码仍然有效,无需任何更改。属性“doorNumber”与对象内部表示的独立性,以及验证输入值的可能性,是封装的关键点。您不能使用public int doorNumber;
除此之外,还有开发人员认为像setDoorNumber(int)
这样的方法与封装相矛盾,或者至少是封装的弱形式。更强的封装模型可以在没有这种public
setter方法的情况下工作,但仅提供高级操作。这样的操作,比如预订酒店的房间,在内部将房间号码分配给地址之前,会执行更多相关操作,涉及其他对象,以及一致性检查......
答案 1 :(得分:1)
使用getter和setter比公开字段有一些好处:
您可以通过将int doorNumber;
替换为可能不同类型的一个或多个字段来重新设计班级的内部。在这种情况下,您可以保留getter和setter,这意味着不必修改其他类。如果您有首选私有字段,则此类中的任何更改都会强制执行使用它的任何其他类的更改。
您可以在setter或getter中添加验证和/或重新格式化逻辑,而无需更改字段的类型。否则,如果您使用公共字段,则必须在访问此字段的任何其他类中重复此逻辑。
您可以在Effective Java第14项
中阅读更多相关信息答案 2 :(得分:1)
考虑这个例子:
public class Address {
private int doorNumber;
public int getDoorNumber() {
return doorNumber;
}
public void setDoorNumber(int doorNumber) {
if (doorNumber <= 0) {
throw new IllegalArgumentException("door number must be > 0");
}
this.doorNumber = doorNumber;
}
}
如果doorNumber
字段被声明为public
,则某些代码可能会违反地址中门号应为正数的约束。
考虑这个例子:
public class Address {
private String doorNumber;
public int getDoorNumber() {
return Integer.parseInt(doorNumber);
}
public String getDoorNumber2() {
return doorNumber;
}
public void setDoorNumber(int doorNumber) {
this.doorNumber = doorNumber + "";
}
public void setDoorNumber(String doorNumber) {
this.doorNumber = doorNumber;
}
}
请注意,我们正在改进我们的API,以便我们可以代表地址"221B Baker St, London",但仍然允许使用我们旧的“门牌号是整数”API ...(某种程度上)。
但如果doorNumber
已公开,那么我们就会被迫更改已使用Address.doorNumber
的代码库中的每个位置。