如何在Java中封装数组

我从Java开始,我正在学习setter,getters和encapsulation。 我有一个非常简单的程序,两个类:

  • Container有一个带有setter和getter的私有int数组( numArray )。

  • Main创建一个Container对象并在totalArray方法中使用它。


 public class Container { private int numArray[]= {0,0,0}; public int[] getNumArray() { return numArray; } public void setNumArray(int index, int value){ numArray[index] = value; } } public class Main { public static void main(String[] args) { Container conte = new Container(); System.out.println(totalArray(conte.getNumArray())); conte.getNumArray()[2]++; System.out.println(totalArray(conte.getNumArray())); } private static int totalArray (int v[]){ int total=0; for (int conta =0; conta<v.length;conta++){ total+=v[conta]; } return total; } } 

问题:我可以通过getter更改private int数组,我知道这是因为getNumArray返回对numArray的引用,而不是数组本身。 如果我对数组的单个元素感兴趣,我会使用索引值创建一个getter,但我希望整个数组用于totalArray方法。

如何防止numArray被修改出他的课程?

您可以做的就是阻止人们更改arrays,就是在getter中提供它的副本。

 public int[] getArray() { return Arrays.copyOf(numArray, numArray.length); } 

这样,其他方法可以更改自己的数组副本,但是当他们再次调用getter时,它们会获得原始版本,不变。 只有您提供的setNumArray()才能实际修改内部数组。

否则,如果要完全阻止容器,则必须删除数组并使用不可变对象。 某些库提供不可变列表,或使用Collections.unmodifiableList 。

如果要返回数组,可以克隆它:

  public int[] getArray() { return (int[]) numArray.clone(); } 

在公共API中,您应该确保向调用者清楚地记录(实际上,无论哪种方式,如果他们获得的数组将改变类的状态 – 他们需要知道)。

通常,您会查看您尝试向class级来电者提供的界面。

方法如:

 void addItem(Object item); Object getItem(int index); int getSize(); 

是你在容器类中提供的东西。 然后,他们代表呼叫者与私有arrays进行交互。

如果要在不允许更改的情况下返回整个数组,可以在getter中将数组复制到新数组中并返回副本。

或者,如果您使用Java集合类而不是原始数组,则它们提供了一个不可修改的Collections.unmodifiableList(myList) ()方法(例如Collections.unmodifiableList(myList) ),它为集合提供了一个只读包装器。

封装是隐藏实现的过程。 集合是否将数据存储在数组中是一个实现细节; 如果它是封装的,您可能希望能够将其更改为另一种存储类型。

事实上,您将状态(或派生状态)公开为getter和setter会破坏封装,并暗示您正在实现抽象数据类型而不是真正的面向对象类。 例如, ArrayList是一种数据类型,它不代表应用程序中任何行为的真正封装。 这是否是您想要的取决于类型的使用方式和位置。

如果它只是一个容器数据类型,我倾向于使Container实现Iterable用于外部交互,或者提供一个内部迭代器方法,如果它打算作为封装类传递访问者。 如果它是抽象数据类型,请强烈考虑使用内置的类型,例如int[]List

还有另一种方法,它不会复制数组,但还有其他缺点。 我会将它用于非常大的数组:

 private A[] items; public List getItems() { return Collections.unmodifiableList(Arrays.asList(items)); } 

如何在Java中封装数组

这个问题可能很清楚,但我想OOP的意思是将一个类设计为一个操作这个数组并提取它自己的api 的实体

如果你坚持下去,那么返回一个数组 / 防御副本克隆简单案例的方法

我想从我在这里看到的所有答案中提出一个不同的方法。 当您考虑封装时,它也很方便,也可以考虑规则“不要问对象的数据,让对象为您的数据操作”。

你没有给我一个numArray ,我打算假装你的目标是从math(而不是Java Vector)创建一个数字“Vector”,例如用途。

因此,您创建一个包含双精度数组的NumericVector类。 您的NumericVector将使用multiplyByScalar(double scalar)和addVector(NumericVector secondVector)等方法来添加元素。

你的内部数组是完全封装的 – 它永远不会逃脱。 对它进行的任何操作都是通过这些“业务方法”在NumericVector类中完成的。 操作后如何显示? 让NumericVector覆盖NumericVector.toString()以便正确打印出来,或者如果你有一个GUI,写一个“Controller”类来将数据从Model(NumbericVector)传输到你的视图(GUI)。 这可能需要一种从NumericVector流式传输元素的方法。

这也表明要避免的一些事情:不要自动创建setter和getter,它们会破坏你的封装。 你经常需要getter,但正如其他人所说,你应该让你的getter返回一个不可变版本的数组。 也尽量让你的类不可变。 我之前提到的那个方法numericVector.addVector(NumericVector secondVector)可能不应该修改numericVector而是返回一个带有解决方案的新NumericVector。

这个(和一般的OO)经常失败的情况是库 – 当你真的想为你的数组添加一些function但仍然把它作为通用数组。 在这种情况下,Java根本不会打扰封装,它只是添加了一个可以对集合/数组做事的辅助方法/类(查看“Arrays”对象以获得一些很好的例子)。

一种记忆效率高的方法是……

 package com.eric.stackoverflow.example; import java.util.List; import com.google.common.collect.ImmutableList; public class Container { private int numArray[] = {0, 0, 0}; public int[] getNumArray() { return ImmutableList.copyOf(numArray); } public void setNumArray(int index, int value) { numArray[index] = value; } } 

这允许ImmutableListList的后备数组中设置正确的项目数,并减少创建的中间对象的数量。