我是java新手,还在学习。我抓住了内心和匿名课程。现在我有一个技术问题,关于java在内存中的表现,分配对象,定义类等等。
当我有一个在外部类和内部类中定义的对象的字段时,就像内存看起来一样。静态类看起来与非静态类不同吗?
我只需要一个视觉参考。
谢谢大家
答案 0 :(得分:4)
答案 1 :(得分:3)
细节在实施中(不是规范)。然而,实现通常遵循非常简单的模式。 Java中的大多数内存布局都非常简单明了。我的术语可能与Java术语不匹配,因为我没有做很多Java编程。
通常,对象以指向其vtable的指针开头,然后有一堆字段跟随。字段是基本类型(int / bool / float)或指向对象的指针。这就是对象。 (类也是对象。)空指针就像C,它们是无效的,不像Python,其中None是一个对象。
在内部类中,有一个额外的隐藏字段,指向外部类的实例。这是内部类访问外部类中的数据的方式。匿名类的工作方式相同。静态方法只是类的方法而不是实例上的方法。
vtable是所有魔法发生的地方。每个类都有自己的vtable在所有对象之间共享。 vtable具有关于类的信息,例如其实例的大小以及字段的布局方式。垃圾收集器使用此信息。 vtable还有一个指向该类实现的所有方法的指针。当您调用方法时,运行时首先从对象中获取vtable指针,然后将方法指针从vtable中取出,然后调用该方法并将该对象作为隐式参数传递给该方法。它与C ++类似,但实现起来要简单得多。如果方法或类是“最终的”,则可以跳过该过程。
我知道Java没有“指针”,它有“符号句柄”或者其他类似的东西,但是常见的实现只使用普通的旧指针。
答案 2 :(得分:3)
欢迎来到Java世界。与C语言不同,语言结构和内存表示几乎一对一地映射,Java稍微复杂一些。
首先,当人们谈论Java时,它可能意味着两件事:Java-the-language和Java-the-platform。在这里,我将Java称为Java编程语言。用Java编写的代码首先被编译为字节码,Java虚拟机的机器代码。如果您对Java语言的详细信息感兴趣,请访问The Java Language Specification。对于JVM,有The Java Virtual Machine Specification。
当我有一个字段是外部类和内部类中定义的对象时,内存是什么样的。
我将这解释为Java虚拟机中的内存布局是什么样的,因为物理布局取决于JVM的实现。为此,我浏览了Structure of the Java Virtual Machine。
与Java语言类似,Java虚拟机以两种类型运行:原始类型和引用类型。相应地,有两种值可以存储在变量中,作为参数传递,由方法返回,并在以下操作:原始值和参考值。
Java虚拟机期望几乎所有类型检查都在编译时完成,而不是由Java虚拟机本身完成。特别是,无需标记数据或以其他方式检查数据以确定类型。
...
对对象的引用被视为具有Java虚拟机类型
reference
。类型reference
的值可以被认为是指向对象的指针。
所以答案似乎是两个字段看起来完全相同:reference
。
答案 3 :(得分:1)
就像我记忆中的样子一样 有一个对象的字段 在外部类中定义与 内心阶级。静态类看起来 不同于非静态?
非静态内部(或匿名)类的实例将引用用于实例化它的外部类实例。这允许内部类中的方法引用封闭类中声明的实例级成员。通常,此引用作为构造函数中的隐藏额外参数传递给内部类。但是,如果使用反射来创建内部类实例,则必须显式提供该额外参数。
(请注意,当匿名类在实例化它的方法范围内使用locals /参数时,会使用不同的机制...)
如果您需要更多细节,可以使用javap来反汇编一些简单示例类的字节码。
我只需要一个视觉参考。
对不起,我不做漂亮的照片: - )
答案 4 :(得分:0)
静态(嵌套)类的工作方式与顶级类的工作方式完全相同。唯一的区别是它的名字有另一个以它为前缀的类名。 (如果查看已编译的.class文件,您实际上会看到,对于名为Nested的类嵌套在名为Outer的类中,您将获得类似“Outer $ Nested.class”的内容。)
内部类有一个隐藏字段,它是对其外部类的包含实例的引用。当你写:
class Outer {
final int x;
class Nested {
int y;
Nested(int y) {
this.y = y;
}
int bar() {
return x + y;
}
}
void foo() {
Nested n = new Nested(5);
}
}
就像你写的那样:
class Outer {
final int x;
static class Nested {
Outer outer;
int y;
Nested(Outer outer, int y) {
this.outer = outer;
this.y = y;
}
int bar() {
return outer.x + y;
}
}
void foo() {
Nested n = new Nested(this, 5);
}
}
隐藏了该隐藏字段的名称(我称之为“外部”)
对你而言,虽然你可以通过说Outer.this
内部来引用它
内部类(当然,Outer
是你的外部类的名称)。
同样,请注意当内部类中的方法引用时
在外部类中的某些东西,该引用实际上是通过隐藏的
引用外层。
关于访问控制(例如:private)如何与嵌套/内部类一起工作,还有一些其他的复杂问题,但这并不会真正影响您所询问的“内存”问题。
答案 5 :(得分:0)
public class I {
class inner {
public void ctor() {};
}
}
看起来有点像,你可以使用JAD
class I$inner {
// Field descriptor #6 LI;
final synthetic I this$0;
// Method descriptor #8 (LI;)V
// Stack: 2, Locals: 2
I$inner(I arg0);
0 aload_0 [this]
1 aload_1
2 putfield I$inner.this$0 : I [10]
5 aload_0 [this]
6 invokespecial java.lang.Object() [12]
9 return
Line numbers:
[pc: 0, line: 3]
Local variable table:
[pc: 0, pc: 10] local: this index: 0 type: I.inner
// Method descriptor #14 ()V
// Stack: 0, Locals: 1
public void ctor();
0 return
Line numbers:
[pc: 0, line: 4]
Local variable table:
[pc: 0, pc: 1] local: this index: 0 type: I.inner
}
作为hexdump,它将以0xcafebabe