为什么使用封装进行数据隐藏?

时间:2019-01-09 01:03:02

标签: java

这是一个非常简单的代码示例:

public class Name {

    public string Name = "John"

}

如果您在另一个类(Name n = Name();)中创建了Name类的实例,则可以通过执行n.Name = "Chris";来更改实例的Name字符串。但是,这只会更改Name类实例的名称,而不是类本身。如果您创建了Name类(Name na = Name();)的新实例,并调用了Name变量,它将仍然是“ John”而不是“ Chris”。

我知道使用封装的原因之一是用户无法修改类中的变量。在上面显示的示例中,用户没有修改类本身的变量。

3 个答案:

答案 0 :(得分:1)

比方说,您从服务器或本地数据库加载数据并将其解析为自己的对象。例如,用户的个人资料。

class User {

    public static final String TAG = User.class.getSimpleName();
    private String name; 
    private String userToken; 
}

User类没有任何默认值或静态值,因为数据将由服务器提供。

从服务器或数据库获取的数据将类似于

name: John
userToken: 0x23fa8.... 

如果您的User类中的字段是公共的,则用户(或任何其他方)将访问数据并对其进行操作。考虑一下用户更改其AuthToken,设备ID或备份关键字短语等的情况,如果提交了这种可操纵的数据,将会造成很多麻烦,并且显然不是您想要的。

就像@StalematOfTuning所说的那样,封装是关于保护对象实例的字段的,而不是保护预定义的类变量。如果您只想创建具有预定义类属性(静态值)的类,则实际上没有必要构造一个类,因为您创建的类无法在任何其他情况下重用。

例如,如果您已经知道Name类将只有一个值“ John”,那么即使构造一个类也是浪费的。创建类或对象的全部目的是将其重用于任何可能的输入或数据。

答案 1 :(得分:0)

好的,所以我认为您对这里的一些事情感到困惑。

对于初学者来说,让我们稍微整理一下这个示例,以便更容易理解。我要做一个Person课:

public class Person {
    public String name;
}

没有比这更简单的了。因此,现在我已经定义了类Person,但需要注意的是实际上还没有Person对象存在。 class Person可以看作是制作Person对象的蓝图。因此,让我们做一个人:

public static void main(String[] args) {
    Person john = new Person();
    john.name = "John";
}

在这里,我通过调用默认构造函数(请注意单词Person)来初始化new,并将其name属性设置为“ John”。

现在,如果其他人正在使用此代码,他们可能会说,嘿,让我们更改他的名字,因为为什么不这样做?

john.name = "Bob";

但是等等...在我们的程序环境中,更改人名可能没有意义。也许我们不希望人们能够随时随地更改其姓名,因为这可能会在以后给我们带来麻烦。更糟糕的是,有人可能会尝试做一些奇怪的事情,例如将其名字变成数字或任何其他完全破坏程序的疯狂垃圾。我们可以通过封装name属性来解决此问题。为此,我们将name设置为private字段,并定义了一个构造函数以及查看该字段的方法。

public class Person {
    private String name;

    public Person(String name) {
        this.name = name;
    }

    public String getName() { 
        return name;
    }
}

现在,我们必须在创建对象时定义希望name成为的对象。让我们再次成为约翰:

Person john = new Person("John");

但最重要的是,无法再更改John的名字。由于我们还没有提供public的方式,所以这是不可能的。

如果我们现在尝试更改john,我们的程序将无法编译:

john.name = "Bob"; //NOT ALLOWED

要查看我们的name,我们将不得不使用我们的getter方法:

System.out.println(john.getName()); //prints "John"

这是封装的本质:保护对象的属性免遭状态的意外更改。我们不仅隐藏数据,还隐藏它的实际状态,只允许通过受控通道对其进行操作。如果我们确实希望更改名称,那么也许可以接受,但前提是新名称符合特定条件。假设:新名称必须至少包含3个字符(抱歉,Al)。我们可以像这样提供设置器:

public void setName(String newName) {
    if(newName.length() < 3) {
        System.out.println("name is too short!");
    } else {
        name = newName;
    }
}

通过这种方式,我们可以更好地控制数据的使用方式。我希望这可以帮助您消除困惑。

还要注意,如果您创建一个新的Person,则将再次需要指定另一个String来在构造函数中设置其name。每个对象都必须进行唯一初始化。

答案 2 :(得分:0)

  

为什么要使用封装来隐藏数据?

模块化代码开发的一个主要优点是,模块可以彼此解耦。未耦合的模块彼此之间没有依赖性。这意味着它们可以独立和并行开发。此外,只要支配模块行为的合同不变,那么可以随时修改或重写未耦合的模块,而不会破坏其运行的系统(只要模块的行为继续保持一致)与其他模块(即其API)的合同。

请考虑以下内容:

public class A {
    public String name = "Hello";
        :
        :
}

public class B {
    private A myA = new A();
    myA.name = "Hello World!"
        :
        :
}

我们写B依赖于实现A的方式:即A具有一个名为name的实例字段。现在模块A和B彼此耦合,因为B依赖于具有此实例字段的A。如果我们决定改变A的实施方式,则可能会遇到麻烦,因为对A进行大修可能需要对B进行大修。

数据隐藏很有价值,因为它避免了此问题。我们想以这样一种方式编写B,即B仅取决于A的行为(即它是API),而不完全取决于该行为的实现方式。 A具有实例字段name是实现细节,我们希望将这些细节与软件系统的其余部分分开。通常,构成对象状态的任何可变字段都应该对系统的其他部分隐藏。它们是实现细节,不应将其作为模块导出到系统其余部分的API的一部分。

TL; DR:一般而言,未耦合的模块越多,它们的开发和维护就越容易且便宜。