当前位置: 首页 > 知识库问答 >
问题:

Java中getter的访问安全

杜焕
2023-03-14

因此,我们创建了一个包含一些私有类成员的简单类,并自动为其生成getter。但getter实际上返回了对该成员的引用,从而获得了对私有成员的完全访问权。这样可以吗?下面是一个类的代码:

public class User {

    private ArrayList<String> strings = new ArrayList(){ {
            add("String1");
            add("String2");
        } };

    public User() {
    }

    public ArrayList<String> getStrings() {
        return strings;
    }

    public void setStrings(ArrayList<String> strings) {
        this.strings = strings;
    }
}

主要方法代码:

    public class Main {
    public static void main(String[] args){
        User user = new User();

        System.out.println(user.getStrings());
        user.getStrings().add("String3");
        System.out.println(user.getStrings());
    }
}

和输出:

[字符串1,字符串2]

[字符串1、字符串2、字符串3]

我已经将getter更改为这个:

public ArrayList<String> getStrings() {
    return (ArrayList<String>)strings.clone();
}

但问题仍然是,如果不是为了安全,吸气剂的作用是什么?什么是正确的书写方式?

共有2个答案

龙默
2023-03-14

在我看来,getter通常应该有两个目的:

  • 首先,他们应该保护实施细节

如果您的示例违反了这些原则,则取决于上下文:

  1. 如果您的类应该拥有这些字符串,那么可能每个人都应该与容器对象交互以修改列表,而不是列表本身。要公开集合(例如,在需要集合的方法中进行处理),可以使用集合。unmodifiableList()。另一方面,如果类只拥有字符串列表,那么拥有列表不是实现细节
  2. 使用getter而不是直接访问字段,可以轻松添加数据对话、跟踪工具和其他内容,而无需更改字段的所有使用位置
冯嘉珍
2023-03-14

不,这不好,因为它破坏了封装,因此类无法维护自己的不变量。与施工人员相同。

但问题不在于getter/setter,而是自动生成它们的代码。

长话短说:不要盲目使用自动生成的访问器,如果它们处理的是可变结构,那么就制作防御性拷贝(或不可变的等价物)。

顺便说一句,我不会使用带有返回类型的数组列表的getter,即使它只是一个副本。您返回什么样的列表通常与客户无关,因此我的getter如下所示:

public List<String> getStrings() {
    return new ArrayList<>(strings);
}

或使用不可变视图:

public List<String> getStrings() {
    return Collections.unmodifiableList(strings);
}

或者使用Guava的ImMutableList类:

public List<String> getStrings() {
    return ImmutableList.copyOf(strings);
}

这三种解决方案之间存在细微的差异,因此哪一种最好可能会有所不同。一般来说,我更喜欢返回不可变的结构,因为这清楚地表明,对结构所做的更改不会被反映出来,即,<代码>用户。getStrings()。添加(“X”) 将失败并出现异常。

您向我们展示的代码的另一个微妙问题是双大括号的初始化。想象这样一个类:

public class Foo {
   private List<String> strings = new ArrayList() {{ add("bar");}};
   private Object veryLargeField; //the object stored here consumes a lot of memory

   public List<String> getStrings() {
     return strings;
   }
}

现在想象一下我们正在这样做:

private class Bar {
   private List<String> fooStrings;

   public Bar() {
     this.fooStrings = new Foo().getStrings();
   }
}

Bar会消耗多少内存(或者使用准确的术语:保留)?好吧,事实证明这相当多,因为您使用双括号初始化所做的是创建一个匿名内部类,它将包含对其外部类(Foo)的引用,因此当返回的列表可访问时,Foo的所有其他字段都将不符合垃圾回收机制的条件。

 类似资料:
  • 编译器为自动为所有的public的状态变量创建访问函数。下面的合约例子中,编译器会生成一个名叫data的无参,返回值是uint的类型的值data。状态变量的初始化可以在定义时完成。 pragma solidity ^0.4.0; contract C{ uint public c = 10; } contract D{ C c = new C(); functi

  • 考虑java中的此类(数据不是私有的或公共的): 我将这个类转换为kotlin 反编译后的代码是这样的: 在kotlin类中,dataPart2调用数据getter(检查反编译代码中的第2行),但我需要访问数据的实际值而不是getter,在kotlin中是否可以访问字段而不调用getter?我不想更改getter或方法名称。

  • 我有一个应用程序,它有一个ConcurrentHashMap本地存储一个存储在外部服务器上的数据副本。地图每隔几秒钟就会更新一次数据的新副本。 我有一个循环,每隔几秒钟运行一次,它可以访问HashMap并按照值的顺序将元素添加到数组中(实际上它做的事情还多一些,但这并不相关)。我的问题是,如果数据在创建数组的过程中发生了变化,您可能会在不同的地方有重复的键,或者完全省略一些键。 示例: 如您所见,

  • 我正在用Java RMI编写一个客户机-服务器程序,但遇到了一个错误: java.security.访问控制异常:拒绝访问(java.net.SocketPersion127.0.0.1:1099连接,解析) 我的代码如下: 怎么了?

  • 我的Quarkus申请一直面临一个问题。该应用程序在本地开发模式下运行时可以正常工作,但是当它作为本地映像构建时,我面临一些奇怪的问题。 遇到的错误: 访问私有intjava.util.ArrayList.size的偏移量时,不首先将字段注册为不安全访问。 org.hibernate.type.serializationexception不能反序列化 java.io.InvalidClassExc

  • 我已经创建了谷歌应用程序引擎项目,使用预测Api 1.5v。当我在本地均值localhost:8888使用谷歌o2Auth身份验证与客户端ID和客户端运行时,它对我来说工作正常secret.but当我实时运行它时,它会给出一个错误 Java语言安全AccessControlException:访问被拒绝(“java.io.FilePermission”“/base/data/home/apps/s