我经常遇到将实例作为参数传递给函数的情况。我突然想到,同样可以转发对象的参数,并在方法中进行初始化。
例:
class MyCanvas extends JComponent {
private static final long serialVersionUID = 1L;
private static ArrayList<String> textData;
private static ArrayList<Rectangle> rectData;
@Override
public void paintComponent(Graphics g) {
if(g instanceof Graphics2D){
//Draw the rectangles and text
}
}
public void addText(int x, int y, String text){
textData.add(text);
}
//Do this:
public void addRect(Rectangle rect){
rectData.add(rect);
}
//Or do this?
public void addRect(int x, int y, int z, int q){
rectData.add(new Rectangle(x, y, z, q);
}
}
在这种情况下,传递四个整数可以减少可变性。从理论上讲,应减少误差表面积以及潜在的漏洞。
JVM如何处理这两个示例?
一个真正不容易出错/漏洞的人吗?
一个在性能方面会比另一个更有效率吗?
注意:这不是关于设计偏好的问题。有多种方式包装或绑定参数,使任一示例都灵活高效。我想知道的是字节码级别的区别是什么,以及字节码级别是否明显更有效/更安全。
从 API 设计的角度来看,传递Rectangle
(或Shape
)更好。它支持编码到接口,而不是实现。从某种意义上说,指定拐角和尺寸是一种实现;不同的实现将指定相反的角。通过传递形状,可以使代码更具适应性。
这里的主要问题是由于 Rectangle
的可变性 .对此有效率论据,但我想知道如果今天设计Shape
是否会是可变的。对于许多应用程序,不可变数据类型提供了许多好处,更安全的数据共享就是其中之一。
由于您正在实现MyCanvas
,因此您可以确信Rectangle
实例没有被修改,因此是"安全的"。但是,如果其他人正在编写呼叫者,并且MyCanvas
是他们无法完全信任的黑匣子,则有两件事可以提供帮助。
首先,您可以记录接受形状的MyCanvas
方法,并指定不修改形状。在Java中,这个规范应该放在Javadoc关于方法和类的注释中。这是一种常见的做法;除非您正在编写一个插件系统,该系统可能会执行由不可信的作者编写的代理,否则程序员通常依赖于 API 中的此承诺或合约。
其次,如果调用方没有这种保证,他们可以将"他们的"Rectangle
实例复制到临时副本,并将副本传递给您。由于它们在方法返回后从不读取Rectangle
的状态,因此对它执行的操作无关紧要。由于Rectangle
是可变的,因此可以相当有效地完成此操作。
从字节码的角度来看,传递Rectangle
更快。对传递的每个参数执行单独的指令。更多参数,更多指令。此外,传递Rectangle
允许重用调用方的实例,而传递基元元素需要分配可能不必要的新Rectangle
。
我不知道你在说什么,当你说,"在这种情况下,传递四个整数,可以减少可变性。从理论上讲,应该减少误差表面积以及潜在的漏洞。
我确实知道,在现实的现实世界中,具有相同类型的多个参数的方法(如四个int
参数)非常容易出错。像q
和z
这样将它们命名为废话会使这个问题变得更糟。利用参数的强类型化,通过在编译时消除错误,使程序更安全。
警惕体系结构安全性和语言安全性之间的区别。 事实上,大多数计算机科学教授都没有意识到这一点。
如果要"安全地"传递对象,可以在键盘上提示输入加密密钥;收集密钥;加密对象;然后传递它。 然后,接收函数可以重新提示并反转加密! 这完全取决于您要完成的目标。
对象的生存期是最大的问题。 传递后,参数值将位于堆栈上;如果我们假设一个观察者可以查看属于 JVM 的内存位置,那么我们就完蛋了。 因此,一旦检索到该值,请用垃圾就地覆盖它。 我们称之为"对象重用"考虑,尽管我们在将对象释放回系统时通常会牢记这一点:我们不会隐含地假设某个人共享我们的地址空间并不断偷看这里和戳那里。
一开始你有 4 个整数,最后需要一个矩形。这一切都归结为谁负责验证输入并进行转换。
虽然这个特定的例子非常简单,但你可以做到通过添加更有趣:
public void addRect(String rect){
// e.g. rect = "4 2 3 7"
}
public void addRect(int[] rect){
// e.g. rect = [4 2 3 7]
}
// etc...
在这种情况下,可以说调用者不应该关心矩形是构造的,这是MyCanvas
类的业务