本文共 3928 字,大约阅读时间需要 13 分钟。
java习题和解答:关于构造方法、成员变量、继承、变量的赋值
发信人: ludongxing (ludongxing), 信区: Java
标 题: 大家猜猜这个java的运行结果是什么?发信站: 水木社区 (Sat Feb 8 22:46:21 2020), 站内 [累计积分奖励: 100/0] 文件名是 Test.java class A { int x=5; public A(){ pk(); } public void pk() { System.out.println(x); } } class B extends A{ int x=6; public void pk() { System.out.println(x); } } public class Test { public static void main(String args[]) { B mb = new B(); } } / 运行结果为啥不是 6 ? A的pk()不是被B的pk()覆盖了吗?结果不应该是 6 吗? -- ※ 来源:·水木社区 ·[FROM: 110.255.89.*]
发信人: elizabethxxy (此处留白), 信区: Java
标 题: Re: 大家猜猜这个java的运行结果是什么?发信站: 水木社区 (Sun Feb 9 12:51:02 2020), 站内 [累计积分奖励: 100/0] 1. 关于类的构造方法、类继承中构造方法的访问: 如果类没有给出构造方法,系统将给出一个默认的无参构造方法供这个类使用。 子类构造方法执行前,都会先执行父类的无参构造方法。因为子类会继承父类的成员方法,父类必须先初始化好了,子类才能继承。在子类的构造方法中,默认第一行有一条语句super(),即必须在构造方法的第一行。如果子类没有写super(),系统会默认加上一条。如果我们写了构造方法,系统就不会给我们提供构造方法了。 在楼主的代码中,类B没有明确写自己的构造方法,那么它的构造方法就是默认构造方法。因为类B是类A的子类,所以在执行类B的构造方法之前,会先执行父类A的无参构造方法。 2. 关于类继承中成员方法的覆盖: java继承中成员方法的访问特点是就近原则。子类中方法和父类中方法的声明相同时(这叫方法的覆盖override),调用的是子类中的方法。 在楼主的代码中,类A有成员方法pk(),类B也有成员方法pk(),由于类B继承了类A,所以类B是类A的子类。调用类B的构造方法时,会先调用父类A的构造方法。在父类A的构造方法中调用成员方法pk()时,由于成员方法pk()被子类B的成员方法pk()覆盖,这时调用的就是子类B中的成员方法pk()。 3. 关于变量的默认值、初始化和赋值: 类的成员变量有默认值,可以不用初始化就可以使用。给类的成员变量赋值的方法通常有两种:A.setXxx() B.构造方法。还有一种是直接赋值的方法,如int x = 5;但是这种方法不建议采用。局部变量没有默认值,必须先定义(即初始化)、赋值,最后使用。 在楼主的代码中,类A的成员变量x和类B的成员变量x都没有采用常用的赋值的方法来赋值,所以一开始采用的都是默认值。int数据类型的默认值是0,所以x的值都是0。后期待代码调用到第三种直接赋值的方法时,才给x赋值。 4. 关于成员变量和局部变量: 成员变量的位置在类中方法外。局部变量在方法内或者方法声明上(形式参数)。如果局部变量名和成员变量名称一致,在方法中使用的时候采用的是就近原则,所以用this来区分成员变量和局部变量。方法被哪个对象调用,this就代表谁。 成员变量名称相同时,在子类方法中访问变量,(1)在方法的局部范围找,如果有就使用。(2)在子类的成员范围找,如果有就使用。(3)在父类的成员范围找,如果有就使用。(4)如果还找不到,就报错。 在楼主代码中的x都是类的成员变量,pk都是类的成员方法。类A的成员方法pk内调用变量x,先在成员方法pk内找局部变量x,由于方法pk内没有局部变量x,按照就近原则,就在成员方法pk所属的类A的成员变量中找有没有名字为x的成员变量,找到了,就采用成员变量x。B的pk方法也是类似的思路,采用B的成员变量x。 综上所述,调用的大致路线是:类B的默认构造方法-->类A的构造方法(初始化A的成员变量x=0。在类A的代码之内方法之外,从上到下执行赋值语句,将A的成员变量x赋值为5。由于A的构造方法里调用了pk()方法,先用B的成员方法pk()把A的成员方法pk()覆盖,覆盖后的pk()代码按照就近原则寻找,因为现在的pk()是B的成员方法,所以它就近找到B的成员变量x。B的成员变量x还没有初始化,所以系统采用x的默认值为0。将B的成员变量x现在的值0打印出来。到此,A的构造方法执行完毕。)-->返回到B的构造方法里,先初始化B的成员变量x=0。在类 B的代码之内方法之外,从上到下执行赋值语句,将B的成员变量x赋值为6。再执行B的构造方法里下面的代码(无)。到此,B的构造方法执行完毕。 ~~~~~~~~ 以上是对楼主代码的分析,下面是一些建议: 1. 不要在父类的构造方法中调用可能被子类覆盖的方法。因为子类初始化会调用父类的构造方法,当父类构造方法调用被子类覆盖的方法时,往往由于子类初始化未完成导致异常。 2. 构造方法最好只用于给对象的数据进行初始化。 3. 构造方法至少写两个:空参构造方法、全参构造方法。 4. 给普通成员变量赋值时,最好采用这两种方法: A.setXxx() B.构造方法,在这两个方法内给成员变量赋值,不要在类A的代码之内方法之外给成员变量赋值。 ~~~~~~~~ 全部代码见我的第二个re贴。 over ※ 修改:·elizabethxxy 于 Feb 9 14:32:41 2020 修改本文·[FROM: 42.122.25.*] ※ 来源:·水木社区 ·[FROM: 42.122.25.*]
发信人: elizabethxxy (此处留白), 信区: Java
标 题: Re: 大家猜猜这个java的运行结果是什么?发信站: 水木社区 (Sun Feb 9 12:53:53 2020), 站内 [累计积分奖励: 100/0] 我的全部代码如下,同时以附件的形式上传了。 //file name: A.java public class A { int x=5; public A(){ System.out.println("调用pk()方法之前,A的成员变量x的值为:" + x); pk(); System.out.println("调用pk()方法之后,A的成员变量x的值为:" + x); } public void pk(){ System.out.println("从类A里调用pk()方法,开始"); System.out.println(x); System.out.println("从类A里调用pk()方法,结束"); } } //file name: B.java public class B extends A { int x=6; public void pk() { System.out.println("从类B里调用pk()方法,开始"); System.out.println(x); System.out.println("从类B里调用pk()方法,结束"); } } //file name: Test.java public class Test { public static void main(String args[]) { B mb = new B(); } } 输出结果: 调用pk()方法之前,A的成员变量x的值为:5 从类B里调用pk()方法,开始 0 从类B里调用pk()方法,结束 调用pk()方法之后,A的成员变量x的值为:5 over 【 在 ludongxing 的大作中提到: 】 : 文件名是 Test.java : class A { : int x=5; : ...................附件(481B) 附件(271B) 附件(128B) -- [upload=1][/upload][upload=2][/upload][upload=3][/upload] ※ 修改:·elizabethxxy 于 Feb 9 14:22:45 2020 修改本文·[FROM: 42.122.25.*] ※ 来源:·水木社区 ·[FROM: 42.122.25.*]
发信人: elizabethxxy (此处留白), 信区: Java
标 题: Re: 大家猜猜这个java的运行结果是什么?发信站: 水木社区 (Sun Feb 9 18:15:02 2020), 站内 狂汗 回答网友提出的问题的过程 也是 我自己学习提高的过程。采用以终为始的学习策略,以能够完整地把知识点讲出来的方式 来 检验 自己学习到的知识、查漏补缺,算是挤兑自己、强迫自己老老实实学习、不许偷懒的方式吧。 大家共同学习,共同提高! 【 在 galaxy123 的大作中提到: 】 : : 厉害! 条理清晰,完美回答了所有疑惑。 : : 【 在 elizabethxxy 的大作中提到: 】 : : 1. 关于类的构造方法、类继承中构造方法的访问: -- ※ 来源:·水木社区 ·[FROM: 42.122.25.*]
转载地址:http://qjymf.baihongyu.com/