Java中的子对象是如何构造的?
How is a child object constructed in Java?
在java中,子对象是如何构造的?
刚开始继承,有几点不是很清楚:
子对象是只依赖子class'的构造函数,还是也依赖父对象的构造函数?我需要关于那一点的一些细节。
此外,super() 是否总是在子构造函数中默认调用?
欢迎提供有关此主题的任何其他信息。
- 在继承中,一个childobject的构造依赖于至少一个parent构造函数。
- 调用super()方法不是必须的。默认情况下,Java 将调用不带参数的 parent 构造函数,除非您精确定义自定义构造函数。
- 举个例子
妈妈
public class Mother {
int a;
public Mother() {
System.out.println("Mother without argument");
a = 1;
}
public Mother(int a) {
System.out.println("Mother with argument");
this.a = a;
}
}
child
public class Child extends Mother {
public Child() {
System.out.println("Child without argument");
}
public Child(int a) {
super(a);
System.out.println("Child with argument");
}
}
如果你这样做:
Child c1 = new Child();
您将获得:
Mother without argument
Child without argument
如果你这样做:
Child c1 = new Child(a);
您将获得:
Mother with argument
Child with argument
但是,如果将第二个 child 构造函数更改为并删除 super(arg),则将调用不带参数的 parent 构造函数:
public Child(int a) {
// super(a);
System.out.println("Child with argument");
}
您将获得:
Mother without argument
Child with argument
- 也许这个初学者课程可以帮助你Coursera java inheritance
我认为“A child object”不是一个很好的思考方式。
您正在制作 object。和所有object一样,它是一些特定class的实例,(毕竟new SomeInterface()
不编译)和(几乎)所有object一样,它是因为一些代码某处(当然不一定是你的代码)运行某处java表达式new SomeSpecificClass(args);
。
我们可以说它是 'child object' 因为 SomeSpecificClass
是 child class 的 class.
但这没什么用。这意味着制作新 'non-child' object 的唯一方法是编写 new Object();
- 毕竟,除了 java.lang.Object
之外的所有 classes 都是 child class:如果你写 public class Foo {}
,java 的解释和你写 public class Foo extends java.lang.Object {}
完全一样。
因此,除非无用*无关紧要,所有 object 都是 child object,因此作为一个术语,'child object',我不会用那个。
也就是说ALLobject创作就是这样'okay and in what order and how do the constructors work'歌舞套路
它是如何工作的可能最容易通过脱糖来解释。如果您选择忽略它们,Javac(编译器)会注入一些东西,因为在 class 文件/JVM 级别,很多东西感觉是可选的(例如构造函数、超级调用或扩展子句),不是**。
Sugar #1 - 扩展子句
已涵盖:如果您的 class def 中没有 extends
子句,javac 会为您注入 extends java.lang.Object
。
Sugar #2 - 构造函数中没有超级调用
一个构造函数必须在它的第一行调用一些特定的超级构造函数,或,它必须在它的第一行调用同一个class中的一些其他构造函数第一行 (this(arg1, arg2);
)。如果你不这样做,java 会为你注入:
public MyClass(String arg) { this.arg = arg; }
// is treated as:
public MyClass(String arg) {
super();
this.arg = arg;
}
如果您的 parent class 没有可用的 zero-arg 构造函数,则特别包括编译器错误。
糖#3:没有构造函数
如果您编写的 class 没有构造函数,那么 java 会为您创建一个:
public YourClass() {}
它将是 public,它没有参数,也没有代码。然而,根据 sugar #2 规则,这会进一步扩展到:
public YourClass() {super();}
字段初始化和代码块被重写为一个块。
当您创建新的 object 时,构造函数并不是 运行 的唯一内容。想象一下这段代码:
public class Example {
private final long now = System.currentTimeMillis();
}
此代码有效;你可以编译它。您可以创建 Example
的新实例,并且 now
字段将保存您调用 new Example()
时的时间。那它是如何工作的呢?感觉 很多 像构造函数代码,不是吗?
好吧,它是这样工作的:从上到下浏览源文件,找到每个 non-static 初始化代码,你可以找到:
public class Example {
int x = foo(); // all non-constant initial values count
{
x = 10;
// this bizarre constructor is legal java, and also
// counts as an initializer.
}
}
然后将所有内容移至 classes 获得的唯一初始化程序,按照您看到它们的顺序。
正在订购
因此,通过 sugar 规则,我们减少了所有 classes 以遵守以下规则:
- 所有 classes 都有 parent class.
- 所有 classes 至少有 1 个构造函数。
- 所有构造函数调用另一个构造函数或来自 parent 的构造函数。
- 有一个'initializer'代码块。
现在唯一的问题是,执行的顺序是什么?
答案疯狂。抓住你的帽子。
这是顺序:
首先,将所有字段设置为整个 'construct' 的 0/false/null(构造涉及从 Child 一直到 Object 的每个字段,当然)。
从在 Child
上调用的实际构造函数开始。 运行直接,也就是说,从第一行开始,必然是this()
或super()
调用。
评估整行,特别是评估作为参数传递的所有表达式。即使这些本身就是对其他方法的调用。但是,javac 会做一些小的努力来阻止您访问您的字段(因为这些 all 未初始化!我还没有提到初始化器!!)。
是的,真的。这意味着:
public class Example {
private final long x = System.currentTimeMillis();
public Example() {
super(x); // x will be .... 0
// how's that for 'final'?
}
}
这将最终调用您的其他构造函数的第一行(它本身也是 this()
或 super()
调用)。要么我们永远无法离开这片森林,并且堆栈溢出错误中止了我们创建这个 object 的尝试(因为我们有一个无休止地相互调用的构造函数循环),或者在某个时候,我们 运行 进入 super()
调用,这意味着我们现在转到 parent class 并再次重复整个歌舞例程。
我们继续前进,一直到 java.lang.Object
,通过硬编码,它根本没有 this()
或 super()
调用,并且是唯一一个调用。
那么,我们先停下来。现在的工作是 运行 j.l.Object 的构造函数中的其余代码,但是 fist,我们 运行 Object 的初始化程序。
然后,object 的构造函数 运行 包含其中的所有其余代码。
那么,Parent的初始化器是运行。然后是使用的 parent 构造函数的其余部分。如果 parent 一直在横向移动(this()
在其构造函数中调用),那么这些都是 运行 在方法调用中正常的相反顺序。
我们终于在 Child 结束了;它的初始值设定项运行,然后是构造函数运行,最后我们完成了。
告诉我!
class Parent {
/* some utility methods so we can run this stuff */
static int print(String in) {
System.out.println("@" + in);
return 0;
// we use this to observe the flow.
// as this is a static method it has no bearing on constructor calls.
}
public static void main(String[] args) {
new Child(1, 2);
}
/* actual relevant code follows */
Parent(int arg) {
print("Parent-ctr");
print("the result of getNow: " + getNow());
}
int y = print("Parent-init");
long getNow() { return 10; }
}
class Child extends Parent {
Child(int a, int b) {
this(print("Child-ctr1-firstline"));
print("Child-ctr1-secondline");
}
int x = print("Child-init");
Child(int a) {
super(print("Child-ctr2-firstline"));
print("Child-ctr2-secondline");
}
final long now = System.currentTimeMillis();
@Override long getNow() { return now; }
}
现在是伟大的益智游戏。应用上述规则并尝试弄清楚这将打印什么。
@Child-ctr1-firstline
@Child-ctr2-firstline
@Parent-init
@Parent-ctr
@getNow 的结果:0
@Child-init
@Child-ctr2-二线
@Child-ctr1-secondline
- 构造函数的执行顺序是有效的:第一行在前,其余的在最后。
- 最后一个字段是 0,尽管看起来它永远不应该是 0。
- 你总是以 运行 宁你的 parent 的构造函数结束。
--
*) 您可以将它们用于锁或标记指针值。假设 'mostly useless'.
**) 你可以破解一个 class 文件,这样它描述的 class 没有 parent class(甚至 j.l.Object); java.lang.Object
的 class 文件就是这样工作的。但是你不能javac
做这个,你必须把它拼凑起来,这样的东西会很疯狂,没有真正有用的目的。
在java中,子对象是如何构造的? 刚开始继承,有几点不是很清楚:
子对象是只依赖子class'的构造函数,还是也依赖父对象的构造函数?我需要关于那一点的一些细节。
此外,super() 是否总是在子构造函数中默认调用?
欢迎提供有关此主题的任何其他信息。
- 在继承中,一个childobject的构造依赖于至少一个parent构造函数。
- 调用super()方法不是必须的。默认情况下,Java 将调用不带参数的 parent 构造函数,除非您精确定义自定义构造函数。
- 举个例子
妈妈
public class Mother {
int a;
public Mother() {
System.out.println("Mother without argument");
a = 1;
}
public Mother(int a) {
System.out.println("Mother with argument");
this.a = a;
}
}
child
public class Child extends Mother {
public Child() {
System.out.println("Child without argument");
}
public Child(int a) {
super(a);
System.out.println("Child with argument");
}
}
如果你这样做:
Child c1 = new Child();
您将获得:
Mother without argument
Child without argument
如果你这样做:
Child c1 = new Child(a);
您将获得:
Mother with argument
Child with argument
但是,如果将第二个 child 构造函数更改为并删除 super(arg),则将调用不带参数的 parent 构造函数:
public Child(int a) {
// super(a);
System.out.println("Child with argument");
}
您将获得:
Mother without argument
Child with argument
- 也许这个初学者课程可以帮助你Coursera java inheritance
我认为“A child object”不是一个很好的思考方式。
您正在制作 object。和所有object一样,它是一些特定class的实例,(毕竟new SomeInterface()
不编译)和(几乎)所有object一样,它是因为一些代码某处(当然不一定是你的代码)运行某处java表达式new SomeSpecificClass(args);
。
我们可以说它是 'child object' 因为 SomeSpecificClass
是 child class 的 class.
但这没什么用。这意味着制作新 'non-child' object 的唯一方法是编写 new Object();
- 毕竟,除了 java.lang.Object
之外的所有 classes 都是 child class:如果你写 public class Foo {}
,java 的解释和你写 public class Foo extends java.lang.Object {}
完全一样。
因此,除非无用*无关紧要,所有 object 都是 child object,因此作为一个术语,'child object',我不会用那个。
也就是说ALLobject创作就是这样'okay and in what order and how do the constructors work'歌舞套路
它是如何工作的可能最容易通过脱糖来解释。如果您选择忽略它们,Javac(编译器)会注入一些东西,因为在 class 文件/JVM 级别,很多东西感觉是可选的(例如构造函数、超级调用或扩展子句),不是**。
Sugar #1 - 扩展子句
已涵盖:如果您的 class def 中没有 extends
子句,javac 会为您注入 extends java.lang.Object
。
Sugar #2 - 构造函数中没有超级调用
一个构造函数必须在它的第一行调用一些特定的超级构造函数,或,它必须在它的第一行调用同一个class中的一些其他构造函数第一行 (this(arg1, arg2);
)。如果你不这样做,java 会为你注入:
public MyClass(String arg) { this.arg = arg; }
// is treated as:
public MyClass(String arg) {
super();
this.arg = arg;
}
如果您的 parent class 没有可用的 zero-arg 构造函数,则特别包括编译器错误。
糖#3:没有构造函数
如果您编写的 class 没有构造函数,那么 java 会为您创建一个:
public YourClass() {}
它将是 public,它没有参数,也没有代码。然而,根据 sugar #2 规则,这会进一步扩展到:
public YourClass() {super();}
字段初始化和代码块被重写为一个块。
当您创建新的 object 时,构造函数并不是 运行 的唯一内容。想象一下这段代码:
public class Example {
private final long now = System.currentTimeMillis();
}
此代码有效;你可以编译它。您可以创建 Example
的新实例,并且 now
字段将保存您调用 new Example()
时的时间。那它是如何工作的呢?感觉 很多 像构造函数代码,不是吗?
好吧,它是这样工作的:从上到下浏览源文件,找到每个 non-static 初始化代码,你可以找到:
public class Example {
int x = foo(); // all non-constant initial values count
{
x = 10;
// this bizarre constructor is legal java, and also
// counts as an initializer.
}
}
然后将所有内容移至 classes 获得的唯一初始化程序,按照您看到它们的顺序。
正在订购
因此,通过 sugar 规则,我们减少了所有 classes 以遵守以下规则:
- 所有 classes 都有 parent class.
- 所有 classes 至少有 1 个构造函数。
- 所有构造函数调用另一个构造函数或来自 parent 的构造函数。
- 有一个'initializer'代码块。
现在唯一的问题是,执行的顺序是什么?
答案疯狂。抓住你的帽子。
这是顺序:
首先,将所有字段设置为整个 'construct' 的 0/false/null(构造涉及从 Child 一直到 Object 的每个字段,当然)。
从在 Child
上调用的实际构造函数开始。 运行直接,也就是说,从第一行开始,必然是this()
或super()
调用。
评估整行,特别是评估作为参数传递的所有表达式。即使这些本身就是对其他方法的调用。但是,javac 会做一些小的努力来阻止您访问您的字段(因为这些 all 未初始化!我还没有提到初始化器!!)。
是的,真的。这意味着:
public class Example {
private final long x = System.currentTimeMillis();
public Example() {
super(x); // x will be .... 0
// how's that for 'final'?
}
}
这将最终调用您的其他构造函数的第一行(它本身也是 this()
或 super()
调用)。要么我们永远无法离开这片森林,并且堆栈溢出错误中止了我们创建这个 object 的尝试(因为我们有一个无休止地相互调用的构造函数循环),或者在某个时候,我们 运行 进入 super()
调用,这意味着我们现在转到 parent class 并再次重复整个歌舞例程。
我们继续前进,一直到 java.lang.Object
,通过硬编码,它根本没有 this()
或 super()
调用,并且是唯一一个调用。
那么,我们先停下来。现在的工作是 运行 j.l.Object 的构造函数中的其余代码,但是 fist,我们 运行 Object 的初始化程序。
然后,object 的构造函数 运行 包含其中的所有其余代码。
那么,Parent的初始化器是运行。然后是使用的 parent 构造函数的其余部分。如果 parent 一直在横向移动(this()
在其构造函数中调用),那么这些都是 运行 在方法调用中正常的相反顺序。
我们终于在 Child 结束了;它的初始值设定项运行,然后是构造函数运行,最后我们完成了。
告诉我!
class Parent {
/* some utility methods so we can run this stuff */
static int print(String in) {
System.out.println("@" + in);
return 0;
// we use this to observe the flow.
// as this is a static method it has no bearing on constructor calls.
}
public static void main(String[] args) {
new Child(1, 2);
}
/* actual relevant code follows */
Parent(int arg) {
print("Parent-ctr");
print("the result of getNow: " + getNow());
}
int y = print("Parent-init");
long getNow() { return 10; }
}
class Child extends Parent {
Child(int a, int b) {
this(print("Child-ctr1-firstline"));
print("Child-ctr1-secondline");
}
int x = print("Child-init");
Child(int a) {
super(print("Child-ctr2-firstline"));
print("Child-ctr2-secondline");
}
final long now = System.currentTimeMillis();
@Override long getNow() { return now; }
}
现在是伟大的益智游戏。应用上述规则并尝试弄清楚这将打印什么。
@Child-ctr1-firstline
@Child-ctr2-firstline
@Parent-init
@Parent-ctr
@getNow 的结果:0
@Child-init
@Child-ctr2-二线
@Child-ctr1-secondline
- 构造函数的执行顺序是有效的:第一行在前,其余的在最后。
- 最后一个字段是 0,尽管看起来它永远不应该是 0。
- 你总是以 运行 宁你的 parent 的构造函数结束。
--
*) 您可以将它们用于锁或标记指针值。假设 'mostly useless'.
**) 你可以破解一个 class 文件,这样它描述的 class 没有 parent class(甚至 j.l.Object); java.lang.Object
的 class 文件就是这样工作的。但是你不能javac
做这个,你必须把它拼凑起来,这样的东西会很疯狂,没有真正有用的目的。