私有变量比公共变量更安全的真实程度是多少?

时间:2014-12-13 00:30:02

标签: java encapsulation abstraction

对于私有变量,Stackoverflow是littered with articles。他们中的大多数说的是使用私有变量“更安全”。虽然,我从来没有找到一个真正具体和可满足的私有变量实际上使任何东西更安全的情况,除此之外,用户不能意外地从课外修改私有变量。

那么私有变量比公共变量更安全的实际程度是什么?私有变量是否只是保护代码免受未来的影响,以及同一项目的开发人员?它是否意味着保护它免受其他使用您的软件作为API的开发人员的影响? 私有变量提供的安全网是否会扩展到防止黑客和恶意用户更改代码中非常重要和敏感​​的部分?

4 个答案:

答案 0 :(得分:6)

封装是松耦合的:

封装的好处不是隐藏值,或者实际上是为了防止直接访问它们。

好处是隐藏实施,以便不会与公共界面紧密结合

这使安全更改了实施,因为没有客户端依赖于实施细节或副作用。

如果您公开使用ArrayList来存储项目,则客户端代码将与ArrayList实现相关联。您无法安全地将其更改为TreeSet,因为该实现是公共接口的一部分。如果公共接口是Collection,那么您可以更改实现,因为您只承诺提供Collection语义。

与存储将实例及时称为long相同:如果您想将其更改为java.util.DateCalendarJodaTime something,那么您可以' t,因为如果该实现是long,每个客户都期望public

答案 1 :(得分:2)

  

除此之外,用户不能意外地从类外部修改私有变量

嗯,开始时这已经是一个好处。

然而,私有变量最好的一点是,它们是私有的,有一个原因;就是封装。

您需要为您的班级提供对功能f1和f2的访问权限,并且在内部它依赖于私有的p1和p2,不应该访问以便您的班级可以安全地运行

这就是它的全部,真的。授予对“局外人”访问这些变量的权利会损害您班级的安全操作。

当然,有恶意用户会使用反射来干涉他们,但他们不是你班级的主要目标,是吗?

答案 2 :(得分:0)

如果"更安全"应该是指安全性,那么它并不是真的。有很多方法可以访问对象的私有变量。

如果"更安全"意味着引用健壮的代码,意味着代码不太可能遇到异常或者无法正常工作,那么是的,它更安全。好处是您的班级可以完全控制价值。

最简单的例子是空值。考虑这个课程:

public class Person {
    public String firstName;
    public String lastName;
    public List<Person> relatives;
}

如果我想编写一个接受Person实例的方法,我不能保证这些字段不为null。我必须用支票弄乱我的代码:

void populateFieldsWith(Person person) {
    firstNameField.setText(person.firstName != null ? person.firstName : "");
    lastNameField.setText(person.lastName != null ? person.lastName : "");

    if (person.relatives != null) {
        List<String> names = new ArrayList<>();
        for (Person relative : person.relatives) {
            if (relative != null
                && relative.firstName != null
                && relative.lastName != null) {

                names.add(relative.firstName + " " + relative.lastName);
            }
        }
        nameList.setData(names);
    } else {
        nameList.setData(Collections.emptyList());
    }
}

如果字段是私有的,那么Person类是唯一可以修改它们的类,这使Person类有权确保它们是非空的:

public class Person {
    private String firstName;
    private String lastName;
    private final List<Person> relatives = new ArrayList<>();

    /**
     * Creates a new Person instance.
     *
     * @param firstName person's first name; cannot be null
     * @param lastName person's last name; cannot be null
     *
     * @throws RuntimeException if any argument is null
     */
    public Person(String firstName,
                  String lastName) {
        setFirstName(firstName);
        setLastName(lastName);
    }

    /**
     * Returns this person's first name.  This never returns null.
     */
    public String getFirstName() {
        return firstName;
    }

    /**
     * Sets this person's first name.
     *
     * @param name new non-null first name
     *
     * @throws RuntimeException if any argument is null
     */
    public void setFirstName(String name) {
        Objects.requireNonNull(name, "Name cannot be null");
        this.firstName = name;
    }

    /**
     * Returns this person's last name.  This never returns null.
     */
    public String getLastName() {
        return lastName;
    }

    /**
     * Sets this person's last name.
     *
     * @param name new non-null last name
     *
     * @throws RuntimeException if any argument is null
     */
    public void setLastName(String name) {
        Objects.requireNonNull(name, "Name cannot be null");
        this.lastName = name;
    }

    /**
     * Returns a list of this person's relatives.  This never returns null
     * but it may return an empty list.
     */
    public List<Person> getRelatives() {
        return new ArrayList<Person>(relatives);
    }

    public void setRelatives(List<Person> relatives) {
        Objects.requireNonNull(relatives, "List cannot be null");
        for (Person person : relatives) {
            Objects.requireNonNull(person, "List cannot contain null");
        }
        this.relatives.clear();
        this.relatives.addAll(relatives);
    }

    public void addRelative(Person relative) {
        Objects.requireNonNull(relative, "Person cannot be null");
        this.relatives.add(relative);
    }
}

现在我们有一个牢不可破的类(除非有人使用反射来绕过私人访问,但在这种情况下,你会处理恶意代码并进入完全不同的主题运行时安全性。)

firstName无法为null,因为任何外部代码都可以设置它的唯一方法是通过setFirstName方法,并且该方法不允许该字段为设置为空值。

现在,让我们回到想要使用Person实例的代码:

void populateFieldsWith(Person person) {
    firstNameField.setText(person.getFirstName());
    lastNameField.setText(person.getLastName());

    List<String> names = new ArrayList<>();
    for (Person relative : person.getRelatives()) {
        names.add(relative.getFirstName() + " " + relative.getLastName());
    }
    nameList.setData(names);
}

该方法不再需要使用null检查混乱。程序员可以安全地假设该值不为null,因为javadoc保证它。保证是诚实的,因为没有代码路径可以导致方法返回null。

空值只是一个用途。相同的setFirstName方法也可以检查字符串是否为空,检查它是否超过最大长度,检查它是否只包含某些类型的字符,依此类推,所有getFirstName方法都是如此{ {1}}方法可以通过其javadoc向其他程序员提供保证,它将返回有效数据。

答案 3 :(得分:-1)

更安全是一个无意义的术语,一遍又一遍地使用。所有这些书的意思是,就大型项目的可扩展性而言,私有属性应被视为一个单独与一个类绑定的实体,因此其价值永远不会被其他对象修改,以免改变其存在的条件(它的功能)。它更像是设计问题,而不是实施问题。

考虑一个对象,只有当它们的属性仅通过其确定的方法进行修改时,哪些方法才会返回正确的结果:如果使用随机值修改属性,则无法保证其方法将返回预期结果

此外,如果您正在编写API库,则私有元素不会在您的二进制文件中公开,这使得从试图实例化不希望由用户实例化的对象的恶意用户进行破解变得更加困难。