当前位置: 技术问答>java相关
轻松解答,高分相送!
来源: 互联网 发布时间:2015-03-23
本文导语: public class TestA1{ public TestA1(){ System.out.println("P "); this.init(); } void init(){ System.out.println("Q "); } public static void main(String argv[]){ TestB1 d= new TestB1(); } } class TestB1 extends TestA1{ int i =1; public...
public class TestA1{
public TestA1(){
System.out.println("P ");
this.init();
}
void init(){
System.out.println("Q ");
}
public static void main(String argv[]){
TestB1 d= new TestB1();
}
}
class TestB1 extends TestA1{
int i =1;
public TestB1(){
super();
System.out.println(i);
}
void init(){
System.out.println("C ");
i = 2;
System.out.println(i +" ");
}
}
输出
P
C
2
1
为什么?
public TestA1(){
System.out.println("P ");
this.init();
}
void init(){
System.out.println("Q ");
}
public static void main(String argv[]){
TestB1 d= new TestB1();
}
}
class TestB1 extends TestA1{
int i =1;
public TestB1(){
super();
System.out.println(i);
}
void init(){
System.out.println("C ");
i = 2;
System.out.println(i +" ");
}
}
输出
P
C
2
1
为什么?
|
public class TestA1{
public TestA1(){ // 4
System.out.println("P "); // 5 打印出P
this.init(); // 6,这里的this应该指的是TestB1 d,所以调用TestB1
} //的方法init()
void init(){
System.out.println("Q ");
}
public static void main(String argv[]){
TestB1 d= new TestB1();//程序从这里开始,1
}
}
class TestB1 extends TestA1{
int i =1;
public TestB1(){ // 2
super(); // 3,调用基类的构造方法
System.out.println(i); // 10 打印出 1
}
void init(){ // 7
System.out.println("C "); // 8 打印出C
i = 2;
System.out.println(i +" "); //9 打印出 2
}
}
public TestA1(){ // 4
System.out.println("P "); // 5 打印出P
this.init(); // 6,这里的this应该指的是TestB1 d,所以调用TestB1
} //的方法init()
void init(){
System.out.println("Q ");
}
public static void main(String argv[]){
TestB1 d= new TestB1();//程序从这里开始,1
}
}
class TestB1 extends TestA1{
int i =1;
public TestB1(){ // 2
super(); // 3,调用基类的构造方法
System.out.println(i); // 10 打印出 1
}
void init(){ // 7
System.out.println("C "); // 8 打印出C
i = 2;
System.out.println(i +" "); //9 打印出 2
}
}
|
这道题是一道关于多形性的题。
首先,TestB1继承TestA1,因此TestB1的void init()函数覆盖TestA1的void init()函数
其次,在主函数中TestB1 d= new TestB1();调用TestB1的构造函数,TestB1(),在TestB1中super()使得,TestB1调用他的父类TestA1的构造函数,TestA1(),因此
首先执行System.out.println("P ");输出P
再执行this.init();因此执行TestA1的init(),但是TestA1的init()已经被TestB1的init()重写,因此执行System.out.println("C ");
i = 2;
System.out.println(i +" ");
输出
C
2
最后再回到
public TestB1(){
super();
System.out.println(i);
}
中,执行System.out.println(i);输出1 //此时i的值仍为1
首先,TestB1继承TestA1,因此TestB1的void init()函数覆盖TestA1的void init()函数
其次,在主函数中TestB1 d= new TestB1();调用TestB1的构造函数,TestB1(),在TestB1中super()使得,TestB1调用他的父类TestA1的构造函数,TestA1(),因此
首先执行System.out.println("P ");输出P
再执行this.init();因此执行TestA1的init(),但是TestA1的init()已经被TestB1的init()重写,因此执行System.out.println("C ");
i = 2;
System.out.println(i +" ");
输出
C
2
最后再回到
public TestB1(){
super();
System.out.println(i);
}
中,执行System.out.println(i);输出1 //此时i的值仍为1
|
这里涉及两个问题:
1、构造器的调用顺序,构造器是按派生顺序从基类到子类的,
2、方法覆盖中,当将子类句柄变量传递给基类对象时,所引用的句柄对象的类型决定执行哪个版本的函数。
所在这个程序中;
1、当创建一个TestB1对象时,首先调用TestA1的构造器,所以执行System.out.println("P "); 输出P 。
2、然后执行TestA1的构造器中的this.init(),于是产生方法覆盖问题,由于引用的是TestB1对象,所以执行TestB1的init()函数,输出输出C和2。
3、最后执行TestB1的构造器而执行System.out.println(i),输出1。
4、在执行TestA1的构造器中this.init()时,还有一个造型问题,也就是将TestB1转化为TestA1,但实际上这时this是引用(或者指向)一个TestB1的句柄变量,所以执行TestB1的init()函数。
1、构造器的调用顺序,构造器是按派生顺序从基类到子类的,
2、方法覆盖中,当将子类句柄变量传递给基类对象时,所引用的句柄对象的类型决定执行哪个版本的函数。
所在这个程序中;
1、当创建一个TestB1对象时,首先调用TestA1的构造器,所以执行System.out.println("P "); 输出P 。
2、然后执行TestA1的构造器中的this.init(),于是产生方法覆盖问题,由于引用的是TestB1对象,所以执行TestB1的init()函数,输出输出C和2。
3、最后执行TestB1的构造器而执行System.out.println(i),输出1。
4、在执行TestA1的构造器中this.init()时,还有一个造型问题,也就是将TestB1转化为TestA1,但实际上这时this是引用(或者指向)一个TestB1的句柄变量,所以执行TestB1的init()函数。
|
要清楚的理解为什么输出结果,有以下两个关键点。
1 java 中所有的成员方法都是 “虚函数”, 对象只调用真实对象中的对应方法。在本例子中super构造器调用的init方法实际上是调用TestB1的init方法。
2 构造器中的执行顺序
调用super构造器
初始化成员变量
执行构造器的其他部分
1 java 中所有的成员方法都是 “虚函数”, 对象只调用真实对象中的对应方法。在本例子中super构造器调用的init方法实际上是调用TestB1的init方法。
2 构造器中的执行顺序
调用super构造器
初始化成员变量
执行构造器的其他部分
|
class T1 {
String s = "T1";
protected T1(String s) {
this.s = s + this.s; //this
this.msg(); //:如果这个this与上一行的this没什么不同,那么this就不是指向TestB1
}
void msg() { System.out.println(s); }
T1 getT1() { return this; }
}
class T2 extends T1 {
String s = super.s + "'s son";
T2(String s) {
super(s);
this.msg();
}
void msg() { System.out.println(s); }
T2 getT2() { return this; }
}
public class Test02 {
public static void main(String[] args) {
T2 t = new T2("hi ");
}
}
输出:
null
hi T1's son
似乎常见的this的作用就两个,如上例子,一是用来和方法那变量区分,一是 return this。
String s = "T1";
protected T1(String s) {
this.s = s + this.s; //this
this.msg(); //:如果这个this与上一行的this没什么不同,那么this就不是指向TestB1
}
void msg() { System.out.println(s); }
T1 getT1() { return this; }
}
class T2 extends T1 {
String s = super.s + "'s son";
T2(String s) {
super(s);
this.msg();
}
void msg() { System.out.println(s); }
T2 getT2() { return this; }
}
public class Test02 {
public static void main(String[] args) {
T2 t = new T2("hi ");
}
}
输出:
null
hi T1's son
似乎常见的this的作用就两个,如上例子,一是用来和方法那变量区分,一是 return this。
|
这是多态在构造函数中一个值得注意的地方。
在构造函数中,要做尽量简单的动作,以避免你的程序存在漏洞。
就上面的例子,当别人从你的类中继承来,他就可以让你的类执行到自己设计的代码,而不是你所愿意的。
如果在构造函数中调用的是private的方法,那就不会有这些情况出现了。
建议看看与此相关的内容.
在构造函数中,要做尽量简单的动作,以避免你的程序存在漏洞。
就上面的例子,当别人从你的类中继承来,他就可以让你的类执行到自己设计的代码,而不是你所愿意的。
如果在构造函数中调用的是private的方法,那就不会有这些情况出现了。
建议看看与此相关的内容.
|
楼上的,我的理解...
执行程序时候,
遇到TestB1 d = new TestB1();
构建器申请内存把class TestB1 加载进来,并把内存区域擦干净,这是i(及其有关的成员变量)就是默认值0。
接着,因为 'extends TestA1', 所以不再进行动作,先加载 TestA1,也一样TestA1此时设置默认值。
为什么不先 初始化i = 10, 是考虑到可能这个初始化动作会受到父类初始化值的影响,所以要在父类初始化后再初始化子类的成员变量。
执行程序时候,
遇到TestB1 d = new TestB1();
构建器申请内存把class TestB1 加载进来,并把内存区域擦干净,这是i(及其有关的成员变量)就是默认值0。
接着,因为 'extends TestA1', 所以不再进行动作,先加载 TestA1,也一样TestA1此时设置默认值。
为什么不先 初始化i = 10, 是考虑到可能这个初始化动作会受到父类初始化值的影响,所以要在父类初始化后再初始化子类的成员变量。
|
//xixi
class T1 {
String s = "T1";
protected T1(String s) {
this.s = s + this.s;
this.msg(); // call overrided msg(), print 'null'
this.msg("");// call native msg(""), print 'hi T1'
}
void msg() { System.out.println(s); }
private void msg(String trash) { System.out.println(s); }
}
class T2 extends T1 {
String s = super.s + "'s son";
T2(String s) {
super(s);
this.msg(); //print "hi T1's son"
this.msg(""); //print "hi T1's son"
}
void msg() { System.out.println(s); }
void msg(String trash) { System.out.println(s); }
}
public class Test02 {
public static void main(String[] args) {
T2 t = new T2("hi ");
}
}
结果:
null
hi T1
hi T1's son
hi T1's son
class T1 {
String s = "T1";
protected T1(String s) {
this.s = s + this.s;
this.msg(); // call overrided msg(), print 'null'
this.msg("");// call native msg(""), print 'hi T1'
}
void msg() { System.out.println(s); }
private void msg(String trash) { System.out.println(s); }
}
class T2 extends T1 {
String s = super.s + "'s son";
T2(String s) {
super(s);
this.msg(); //print "hi T1's son"
this.msg(""); //print "hi T1's son"
}
void msg() { System.out.println(s); }
void msg(String trash) { System.out.println(s); }
}
public class Test02 {
public static void main(String[] args) {
T2 t = new T2("hi ");
}
}
结果:
null
hi T1
hi T1's son
hi T1's son
|
to: alula(alula)
你这两段程序确实很好,他不仅说明了this的用法也说明了初始化顺序的问题。
在think in java中是这样说明初始化顺序的:
(1) 在采取其他任何操作之前,为对象分配的存储空间初始化成二进制零(或null)。
(2) 调用基础类构建器。此时,被覆盖的msg()方法会得到调用(的确是在T2构建器调用之前),此时会发现s的值为null,这是由于步骤(1)造成的。
(3) 按照原先声明的顺序调用成员初始化代码。
(4) 调用衍生类构建器的主体。
下面我再将你的第一个源程序运行顺序标示出来,互相比较,就可以看出其中的奥妙。
class T1 {
String s = "T1"; //4
protected T1(String s) { //3
this.s = s + this.s; //5
this.msg(); //6
} //8
void msg() { System.out.println(s); }
T1 getT1() { return this; }
}
class T2 extends T1 {
String s = super.s + "'s son"; //9
T2(String s) {
super(s); //2
this.msg(); //10
} //12
void msg() { System.out.println(s); } //7.....//11
T2 getT2() { return this; }
}
public class Test02 {
public static void main(String[] args) {
T2 t = new T2("hi "); //1......//13
} //14
}
至于this的含义,就只有一个指向当前类,
this.s、this.msg();和return this;是三种用法,但是他们的含义是相同的,都是指向当前类。而对于this.msg();和this.s中的this完全可以不要。
你这两段程序确实很好,他不仅说明了this的用法也说明了初始化顺序的问题。
在think in java中是这样说明初始化顺序的:
(1) 在采取其他任何操作之前,为对象分配的存储空间初始化成二进制零(或null)。
(2) 调用基础类构建器。此时,被覆盖的msg()方法会得到调用(的确是在T2构建器调用之前),此时会发现s的值为null,这是由于步骤(1)造成的。
(3) 按照原先声明的顺序调用成员初始化代码。
(4) 调用衍生类构建器的主体。
下面我再将你的第一个源程序运行顺序标示出来,互相比较,就可以看出其中的奥妙。
class T1 {
String s = "T1"; //4
protected T1(String s) { //3
this.s = s + this.s; //5
this.msg(); //6
} //8
void msg() { System.out.println(s); }
T1 getT1() { return this; }
}
class T2 extends T1 {
String s = super.s + "'s son"; //9
T2(String s) {
super(s); //2
this.msg(); //10
} //12
void msg() { System.out.println(s); } //7.....//11
T2 getT2() { return this; }
}
public class Test02 {
public static void main(String[] args) {
T2 t = new T2("hi "); //1......//13
} //14
}
至于this的含义,就只有一个指向当前类,
this.s、this.msg();和return this;是三种用法,但是他们的含义是相同的,都是指向当前类。而对于this.msg();和this.s中的this完全可以不要。
|
迷惑——
我对下面程序用debug作了一下追踪调试,附有执行顺序。
public class TestA1{
public TestA1(){//3
System.out.println("P ");//4
this.init();//5
}//10
void init(){
System.out.println("Q ");
}
public static void main(String argv[]){
TestB1 d= new TestB1();//1.......14
}//15
}
class TestB1 extends TestA1{
int i=10;//11
public TestB1(){//2
//super();
System.out.println(i);//12
}//13
void init(){
System.out.println("C ");//6
i++;//7
System.out.println(i +" ");//8
}//9
}
执行结果为
Q C 1 10
我想问,既然i++在 int i=10之前执行,为什么没有出现i未被初始化的错误。
由结果,我们也可以看出,在i++之前i是有值的为0,那么i是在什么时候被设定为整形且赋予初始值0的呢?关键是为什么!
我对下面程序用debug作了一下追踪调试,附有执行顺序。
public class TestA1{
public TestA1(){//3
System.out.println("P ");//4
this.init();//5
}//10
void init(){
System.out.println("Q ");
}
public static void main(String argv[]){
TestB1 d= new TestB1();//1.......14
}//15
}
class TestB1 extends TestA1{
int i=10;//11
public TestB1(){//2
//super();
System.out.println(i);//12
}//13
void init(){
System.out.println("C ");//6
i++;//7
System.out.println(i +" ");//8
}//9
}
执行结果为
Q C 1 10
我想问,既然i++在 int i=10之前执行,为什么没有出现i未被初始化的错误。
由结果,我们也可以看出,在i++之前i是有值的为0,那么i是在什么时候被设定为整形且赋予初始值0的呢?关键是为什么!
|
一句话,this 指向的是testbB1
|
我认为ChrisZhang说的有道理:
this是对当前对象引用的一个句柄变量(是不是可以认为是一个常量呢),这里只构造了TestB1的一个对象,所以this.init()应该是指TestB1的方法。
至于去掉this之后仍然输出原结果,我想应该是方法重载的效果。
this是对当前对象引用的一个句柄变量(是不是可以认为是一个常量呢),这里只构造了TestB1的一个对象,所以this.init()应该是指TestB1的方法。
至于去掉this之后仍然输出原结果,我想应该是方法重载的效果。