面试时被问到这个问题,无法回答。希望您能够帮助我。
问题是:为什么 Java 人不添加对结构的支持?为什么无法将堆栈上分配的值类型添加到 Java 中?
我想以前的java版本应该有一些向后兼容性问题?但是,我无法提出任何建议。
面试时被问到这个问题,无法回答。希望您能够帮助我。
问题是:为什么 Java 人不添加对结构的支持?为什么无法将堆栈上分配的值类型添加到 Java 中?
我想以前的java版本应该有一些向后兼容性问题?但是,我无法提出任何建议。
虽然 Java 不允许堆栈分配,但它确实使用了称为 Escape Analysis 的东西。
找到了一个与此相关的链接:http: //www.ibm.com/developerworks/java/library/j-jtp09275/index.html
Java 语言没有提供任何方法来显式地在堆栈上分配对象,但是这一事实并不妨碍 JVM 在适当的情况下仍然使用堆栈分配。JVM 可以使用一种称为转义分析的技术,通过该技术,它们可以判断某些对象在其整个生命周期内都被限制在单个线程中,并且该生命周期受给定堆栈帧的生命周期的限制。这样的对象可以安全地分配在堆栈上而不是堆上。更好的是,对于小对象,JVM 可以完全优化分配,只需将对象的字段提升到寄存器中。
我认为class
带有公共变量的 a 非常接近 a struct
:
final class Person {
String name;
int age;
}
structs
因此,如果此类功能已被类覆盖,则无需添加类似内容。
如果问题的本质是“为什么无法将栈上分配的值类型添加到Java”,那么答案应该是Java中的数据位于堆中,并在栈中通过引用传递,除了泛型类型。
结构本质上是一组用胶带固定在一起的变量。复制结构或将其按值传递给方法相当于复制所有变量或将它们全部按值传递。如果一个人从不修改其构造函数之外的结构中的任何变量,除非将它们全部重写,那么结构的语义将反映类的语义;有些人认为一切都应该表现得像一个类,并且可以以任何其他方式使用的结构是“邪恶的”,但是对于人们想要的是一组用胶带固定在一起的变量的情况,暴露场结构是理想的。
例如,考虑 a 的设计Point3d
,这样一个类型的变量Point
封装了三个float
值,称为X
、Y
和Z
。理想情况下,希望每个变量、数组槽或其他类型的存储位置都Point
封装这三个值,而不是其他任何东西,这样当且仅当所有三个坐标都匹配时,两个存储位置才被认为是等效的。暴露字段结构将完美地封装该含义,因为字段声明Point3d foo, bar;
将为六个float
字段foo.X
、foo.Y
、foo.Z
、bar.X
、bar.Y
和分配空间bar.Z
。如果要说的话foo=bar; foo.X=5f; bar.Y=7f;
,那就相当于foo.X=bar.X; foo.Y=bar.Y; foo.Z=bar.Z; foo.X=5f; bar.Y=7f;
。因为foo.X
和Bar.X
是不同的变量,foo.Y
和一样bar.Y
,写入foo.X
不会影响Bar.X
,写入bar.Y
不会影响foo.Y
'.
如果Point3d
是不可变的类类型,就不可能说foo.X=5f;
; 相反,有必要做一些foo = new Point3d(5, foo.y, foo.z);
更笨重、更慢、更容易出错的事情。如果它是一个可变类类型,可以说foo.X = 5f;
,类型的每个存储位置Point3d
将不仅封装坐标,而且还包含与持有对同一实例的引用的每个其他存储位置的连接。保存对特定Point3d
实例的唯一现存引用的存储位置可用于仅封装坐标,但公开信息的唯一方法是将其复制到其他东西(可能是另一个Point3d
实例)。
值得注意的是,.NET 除了具有结构类型之外,还具有“byref”的概念。在 C# 中, ifPoint3d
是一个结构,并且定义了一个方法:
void MovePointRight(ref Point3d pt, float amount) { pt.X += amount; }
并称之为:
MovePointRight(ref foo, 5.3f);
然后当方法正在执行时,pt.X
将引用foo.X
, 并且同样使用.Y
and .Z
。在 Java 中,使变量可用于方法以便它可以在其中存储信息的唯一方法是创建包含这些变量的类对象的实例并传递对它的引用;无法控制该方法对该参考的作用。例如,它可以将引用传递给将在将来某个时间更改对象的其他线程。在 C# 中,将 byref 传递给变量的代码可以确定,当被调用的方法返回时,byref 及其所有副本都将消失。通过ref foo
将允许MovePointRight
做任何它想做的事情foo.X
, foo.Y
, 和foo.Z
,但是由于调用而可能发生的任何更改都将在它返回之前发生。
如果它们不必使用泛型,那么使用 JVM 向 Java 添加结构在某种程度上是可能的。只需让每个结构定义定义多个离散变量,并让结构数组定义定义多个数组。给定Point3d[] myPoints;
,创建一个四元素数组的请求应该创建三个四元素数组float
并将它们分配给myPoints.X[]
、myPoints.Y[]
和myPoints.Z[]
. 但是,结构类型并不是非常有用,因为缺少对泛型和 byref 的支持。如果每个具有由 ref 传递的任何变量的方法都声明一个类来保存这些变量,在进入时产生一个新实例,并在退出时丢弃它,那么可能有一些方法可以混合 byref 支持,但性能会相对较差。使结构类型运行良好需要对 JVM 进行大修,我怀疑这是否会发生。