【问题标题】:What is the real extent to which a private variable is safer than a public variable? [closed]私有变量比公共变量更安全的真正程度是多少? [关闭]
【发布时间】:2015-02-11 18:55:20
【问题描述】:

Stackoverflow 是 littered with articles 用于私有变量的目的。他们中的大多数人说使用私有变量“更安全”。虽然,我从来没有发现一个真正具体且令人满意的私有变量的案例,它确实使任何事情变得更安全,除了用户不能从类外意外修改私有变量这一事实之外。

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

【问题讨论】:

    标签: java encapsulation abstraction


    【解决方案1】:

    封装就是松耦合:

    封装的好处不是隐藏值,或者真正防止直接访问它们。

    这样做的好处是隐藏实现,这样它就不会与公共接口紧密耦合

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

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

    与将实例及时存储为long 之类的事情相同:如果您想将其更改为java.util.DateCalendarJodaTime something,那么您不能,因为每个客户都期待一个long 如果该实现是 public

    【讨论】:

    • 关键:这使得更改实现变得安全,因为没有客户端依赖于实现细节或副作用。 +1
    【解决方案2】:

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

    如果“更安全”是指健壮代码,即不太可能发生异常或无法正常工作的代码,那么是的,它更安全。好处是您的班级可以完全控制价值。

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

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

    如果我想编写一个接受 Person 实例的方法,我无法保证字段不为空。我必须用检查来弄乱我的代码:

    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 方法,并且该方法不允许将字段设置为 null价值。

    现在,让我们回到想要使用 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);
    }
    

    该方法不再需要检查是否为空。程序员可以放心地假定该值不为空,因为 javadoc 保证了这一点。保证是诚实的,因为没有可以导致方法返回 null 的代码路径。

    空值只是一种用途。同样的setFirstName 方法还可以检查字符串是否为非空,检查它是否不超过最大长度,检查它是否只包含某些类型的字符,等等,所有这些,所以getFirstName 方法可以通过其 javadoc 向其他程序员保证它将返回有效数据。

    【讨论】:

      【解决方案3】:

      更安全是一个反复使用的毫无意义的术语。所有这些书的意思是,就大型项目的可扩展性而言,私有属性应被视为与类明确关联的实体,因此它的值永远不会被其他对象修改,以免改变其存在的目的(它的功能)。与其说是实施问题,不如说是设计问题。

      想想一个对象,只有当它们的属性只被其非常确定的方法修改时,哪些方法才会返回正确的结果:如果用随机值修改属性,它不能保证它的方法会返回预期的结果。

      此外,如果您正在编写 API 库,私有元素将不会暴露在您的二进制文件中,这使得恶意用户更难以破解试图实例化不打算由用户实例化的对象。

      【讨论】:

      • safer 毫无意义是一个好的开始,其余的都是错误的,privateimmutability 无关,这是一个很好的目标,但无关与private 范围。您可以拥有immutable 数据,即publicpackage private。如果您认为private 使您的代码更难破解,您需要阅读reflection,因为您可以整天发现和修改private 成员,甚至如果他们是static final
      • 我不同意。我看到我们俩说的几乎一样。嗯……
      • 我对私有的定义和你的一样有效,不是不变性的定义,这与问题无关,我不明白你如何将它与我的答案联系起来。如果它们不是私有的,不同的对象可以在它们之间改变对不可变属性的引用。你在反思中抓住了我。
      【解决方案4】:

      除了用户不能从类外部意外修改私有变量这一事实之外

      嗯,这已经是一个好处。

      然而,私有变量最好的一点是它们是私有的。也就是封装。

      您需要为您的类提供对功能 f1 和 f2 的访问,并且在内部它依赖于 p1 和 p2,它们是私有的,不应该被访问以便您的类可以安全运行。。 p>

      这就是它的全部,真的。将这些变量的访问权限授予“外来者”会危及您班级的安全运行。

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

      【讨论】:

        猜你喜欢
        • 2011-05-25
        • 2011-12-03
        • 1970-01-01
        • 1970-01-01
        • 2012-01-25
        • 2013-01-02
        • 2012-09-12
        • 2011-11-13
        • 2020-08-15
        相关资源
        最近更新 更多