Java封装概念让我感到困惑

4

你好!

我正在阅读一本关于封装的Java书籍,它提到了getter和setter方法。

我已经了解到,为了隐藏属性,我必须将我的实例变量标记为"PRIVATE",并创建一个getter和setter"PUBLIC"方法来访问数据。因此,我尝试编写了以下类似但不是传统的代码:

public class AddressBookEntry {

    private String name;
    private String address;
    private String telNo;
    private String email;

    public void getAllInfo() {
        name = JOptionPane.showInputDialog("Enter Name: ");
        address = JOptionPane.showInputDialog("Enter Address: ");
        telNo = JOptionPane.showInputDialog("Enter Tel. No: ");
        email = JOptionPane.showInputDialog("Enter Email Address: ");
    }
}

我的代码是否因为直接赋值而暴露了我的变量?我该如何改进?如果我使用传统的getter和setter方法并在其他类中分配值,会更好吗?"隐藏数据"是什么意思?

谢谢。

6个回答

7
您可以使用设置器(setters)和获取器(getters)使变量可从类外部访问。在您的示例中,您将拥有:
public class AddressBookEntry {

    private String name;

    public void setName(String name) {
       this.name = name;
    }

    public String getName() {
       return name;
    }
}

你需要从UI类中访问name属性(在同一个类中混合UI和业务逻辑不是一个好的做法):
public class MyPane extends JFrame {

  public getAllData() {
     String name = JOptionPane.showInputDialog("Enter Name: ");
     AddressBookEntry entry = new AddressBookEntry();
     entry.setName(name);
     // You can't use entry.name = name
  }

}

5
是和不是。封装的重点在于防止其他类需要知道你的类在幕后做什么。如果你将“name”存储在“String”中(就像这里所做的那样),从文件中读取/写入它,或进行其他操作,封装的重点在于对于你的类的用户来说,这些都无关紧要,因为所有他们看到的是 “ String getName()” 和“void setName(String name)”。
由于数据的修改完全受到您的类的控制,在这里并不会破坏封装性。如果您将“name”存储到文件中,则可以在“getAllInfo”中这样做,而不会让该类的任何其他用户知道。由于类的外部可观察行为仍然隐藏了类内部的操作,因此它仍然是封装的。
话虽如此,这是一种非常不寻常的方法。正如我在第一段中描述的那样,使用访问器方法(getter和setter)是一种更惯用的方法,并且易于其他人理解您的代码。你可以按照自己的想法做,它不会破坏封装,但我不认为它是优雅或典型的。

3
私有类成员(属性、字段)的概念是只能在声明类内(不是实例!)直接访问它们。所有其他类都必须使用其他访问器,例如getter和setter方法。因此,AdressBookEntry 存储名称、地址、电话号码和电子邮件值的方式完全隐藏在类内部。
您不必使用getter或setter,尽管有时在类内部这样做也有很好的理由。如果在设置值之前执行一些日志记录或验证,则使用getter和setter有时是有意义的。

1

这并不违反封装,因为它不会将内部数据暴露给另一个类。你可能希望查看模型-视图-控制器(MVC)来帮助将界面与数据分离。

传统上,您需要根据需要为每个变量创建单个getter和setter,如下所示:

public String getName() {
  return name;
}

public void setName(String name) {
  this.name = name;
}

不要为那些在类外部不需要被访问的变量创建getter,也不要为那些在类外部不需要被设置的变量创建setter。
如果遵循MVC并像上面使用传统的getter和setter,您将在其他地方拥有视图代码,并调用类似以下内容的内容。
myAddressBookEntry.setName(JOptionPane.showInputDialog("Enter Name: "));

1

我同意所有提供的优秀答案。我想再补充一个使用封装的好处,那就是通过隐藏数据(就像上面描述的例子一样)可以保证如何设置和获取这些实例变量,使得你的类更易于维护和测试(例如,如果有许多客户端操作你的对象变量,你不需要逐个类编写新功能,而只需修改getter和setter)。

通过仅使用对象的setter和getter,其实现细节可以对外部世界隐藏起来。这是一个很好的优势,因此试图与您的类交互的其他对象将无法破坏其功能。这也增加了系统有效实施的可能性。


1
我发现上面的答案已经回答了你的问题。但是,我看到你的代码还有另一个问题,那就是安全和数据完整性问题。你直接从用户输入中填充你的类成员,没有对其进行任何验证。例如,你可能想检查电子邮件地址是否具有xyz@abc.com模式。恶意用户也可能在输入中提供非常长的字符串,导致意外错误。

使用正则表达式,可参见此处示例 如何使用正则表达式验证电子邮件地址 - Assimiz

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接