a=2;
s1的变量a与其他无关;并其他类初始化时对对象的内部变量无影响
s1的变量a与其他无关;并其他类初始化时对对象的内部变量无影响
解决方案 »
- 用java做游戏 如何实现图片背景的连续啊 据说是使用什么多重缓冲技术,求高手教教我啊
- 这段代码的输出很让我困惑,求高手解释下(关于StringBuffer)
- 请求帮助
- JTextField的问题
- 有关java发射机制问题,迷茫!~~
- 一个有难度的问题——反射机制与类的定义(500分相赠)
- 200分,200分,不是很难的jni问题啊!但我困惑中………………………
- proguard 混淆类到default-package下面
- 【Android开发】请问Android怎么打开Word、Excel文件?
- 请问,哪儿有Jrun studio 望指点!!谁提供的且成功下载的定给分!!!:)
- 菜鸟问题:Java3D中如何加入线段?
- jdk安装问题
注意其中的一句是:
SuperClass s1=new SubClass();
如果它是
SubClass s1=new SubClass();
那结果当然是2,但是把子类实例化后赋值给超类:发生了如下变化:
new SubClass()的确发生效用了,SubClass的构造也运行了,SubClass的a值为2,但是s1被声明为
SuperClass,当有了此声明后,只要是SuperClass的对象就有了实例变量a=8,SubClass的a和SubClass的构造无法对SuperClass的a造成影响,SuperClass没有被new,也就没有构造,所以a的值还是8
既然说superclass也调用了构造器,那为什么没有执行a=a+1?
因为声明的时候,s1是SuperClass,所以s1.a访问到的a是SuperClass中的a
你试试看加一句:System.out.println(((SubTest)t1).a); -- 结果就是2了
SuperClass的构造函数肯定是被调用了的,无需质疑,
但是调用的add(int)方法,在实际运行中,运行的是SubClass的方法,影响到的field也是
SubClass里面的,对SuperClass中的a没有影响--也就是说,SubClass其实没有覆盖SuperClass中的a。如果你不相信我说的,可以找个Optimizeit Code Coverage验证一下
但是SubClass中的a没有覆盖SuperClass中的a
而打印出来的,是SuperClass中的a还望高人指点
SubClass s2=new SubClass(); 取得a=2;
这两个a实际是不同的,不会互相改变,只不过是名字相同的不同变量罢了!
int a=8;
SuperClass(){
//System.out.println(a);
System.out.println("superclass!");
add(1); }
public void add(int i){
System.out.println("superclass!add");
a=a+i;
System.out.println("super:"+a);
}
}class SubClass extends SuperClass{
int a=0;
SubClass(){
System.out.println("subclass!");
add(1);
}
public void add(int i){
System.out.println("subclass!add");
a=a+2*i;
System.out.println("sub:"+a);
}
}public class Test{
public static void main(String[] args){
SuperClass s1=new SubClass();
//SubClass s1=new SubClass();
//SubClass s2=new SubClass();
System.out.println(s1.a);
}
}
---------- Run Java Program ----------
superclass!
subclass!add
sub:2
subclass!
subclass!add
sub:2
8Output completed (0 sec consumed) - Normal Termination
class SubClass extends SuperClass
{
int a; //定义a,此时a等于0 SubClass()
{
super();
a = 0; //赋值,虽然定义的地方写了int a=0,实际上a=0这个操作在这里发生
add(1);
} public void add(int i)
{
a = a + 2 * i;
}
}
public static void main(String[] args){
SuperClass s1=new SuperClass();
System.out.println(s1.a);
System.out.println();
SubClass s2=new SubClass();
System.out.println(s2.a);
System.out.println();
SuperClass s3=new SubClass();
System.out.println(s3.a);
}
}---------- Run Java Program ----------superclass!
superclass!add
super:9
9superclass!
subclass!add
sub:2
subclass!
subclass!add
sub:2
2superclass!
subclass!add
sub:2
subclass!
subclass!add
sub:2
8
SuperClass.a=8
begin SuperClass's add(1)
a=0,run add a = a + 2 * i
a=2,after run a = a + 2 * i
after SuperClass's add(1),SuperClass.a=8
run SuperClass() end.
run SubClass()
SubClass.a=0
begin SubClass's add(1)
a=0,run add a = a + 2 * i
a=2,after run a = a + 2 * i
after add,SubClass.a=2
run SubClass() end.
8
int i = 100;
public void print() {
System.out.println(i);
}
}
class derived extends base{
int i = 999;
public void print() {
System.out.println(i);
}
}class test{
public static void main(String[] args) {
base b = new base(); derived d = new derived();
b.print();
System.out.print(b.i);
d.print();
System.out.print(d.i);
base b2 = d;
b2.print();
System.out.print(b2.i);
}
}
很简单的,前面的调用:
b.print();
打印100
System.out.print(b.i);
打印100
d.print();
打印999
System.out.print(d.i);
打印999. b2.print();打印999这是因为java是在运行过程中采用了动态联编的方法,在运行时刻来决定该reference指向的什么类的对象,从而决定调用哪一个类的方法,而不是根据reference的类型来决定调用哪一个类的方法但是
System.out.print(b2.i);
打印什么呢?结果是100, 而不是999。这是因为,在动态联编过程中,只是函数参与了,而对于类中的变量,则还是以静态的方式编译的,就是说,编译器是凭借reference的类型来决定类变量的。b2是base类型的reference, 所以b2.i
是基类中的变量的值。
虚函数操作的数据成员都是其真实对象的,
因此SubClass的对象构造时自动调用SupperClass的构造函数,
SupperClass的构造函数再调用add函数时调用的是SubClass的add函数,操作的对象也是
SubClass的a,没有影响Supper.a,
所以最后打印出来是 8.我用C++测试,打印出来是9,换句话说C++的SupperClass构造器调用的是自己的成员。
可见JAVA的实现有缺陷。所以要避免这种容易出现问题的代码。
但由构造器调用add,就让人迷惑了。按理构造器当然初始化自己的成员,即使调用函数,
也是调用自己的函数,否则初始化就不能达到要求的结果。但JAVA却不是这样。由此可以想到,如果SubClass的数据成员覆盖了SupperClass的数据成员,必然导致SupperClass相应的数据成员不能正确的初始化。这应该是JAVA的缺陷吧。如果一个类继承层数较多,难免会出现派生类定义和基类相同的数据成员,这时影响基类的
工作,呵呵。。
子类发放重载了父类方法。
就象我们写的APPLET中PAINT方法。成员变量也重载了。多态的表现吧。学无止境啊!
我们可以把它看成
SuperClass s1;
SubClass s2=new SubClass();
s1=s2;
这是一个超类对子类的引用,在java的概念中有这样一段:使用超类引用来引用
自类对象并激活draw方法,那程序将会正确地动态选择子类的draw方法,
这种方法称为动态方法绑定(超类和子类都包含draw方法)。所以我不认为这是JAVA的缺陷
但是运行的结果是8。这跟java对象的结构有关。
在new SubClass()时会按照类的继承体系从Object一直构造到SubClass。当构造到SuperClass时会根据多态原则调用SubClass.add,
当构造到SubClass()时同样也会调用SubClass.add(),这样SubClass.add()就被调用了两次,这就是为什么a的值可能是4,但是绝对
不可能是4,这跟java编译器的编译原理有关。
class SubClass extends SuperClass{
int a=0;
SubClass(){
add(1);
}
public void add(int i){
a=a+2*i;
}
}
被编译后的伪代码为:
class SubClass extends SuperClass{
int a;
SubClass(){
a = 0;
add(1);
}
public void add(int i){
a=a+2*i;
}
}
即把对字段的赋值语句转移到构造器中的最上层,这样的话,当SuperClass类的构造器运行完后,因为调用SubClass.add()的缘故而使
SubClass.a=2,在SubClass的构造器中将SubClass.a先清为0,接着调用SubClass.add(),这样SubClass.a=2。此时s1中有关于a的
两个版本一个是a=8,另一个是a=2,当调用s1.a时取的是SuperClass.a,而不是SubClass.a,因为java只对Method多态,不会对Fied多态。
class SuperClass{
int a=8;
SuperClass(){
System.out.println("SuperClass()");
add(1);
}
public void add(int i){
System.out.println("SuperClass.add()");
a=a+i;
}
}class SubClass extends SuperClass{
int a=0;
SubClass(){
System.out.println("SubClass()");
add(1);
}
public void add(int i){
System.out.println("SubClass.add()");
a=a+2*i;
}
}public class Test{
public static void main(String[] args){
SuperClass s1=new SubClass();
System.out.println(s1.a);
}
}
改个值就知道了:
class SuperClass{
int a=8;
SuperClass(){
System.out.println("SuperCreate");
add(1);
}
public void add(int i){
System.out.println("Superadd:"+i);
a=a+i;
}
}class SubClass extends SuperClass{
int a=1;
SubClass(){
System.out.println("SubCreate");
add(1);
}
public void add(int i){
System.out.println("SubAdd:"+i);
a=a+2*i;
System.out.println("this.a="+a+",super.a="+super.a);
}
}public class Test{
public static void main(String[] args){
SuperClass s1=new SubClass();
System.out.println(s1.a);
}
}
仔细看看上塑造型,
SuperClass s1=new SubClass();
new SubClass()后a的值为2,因为调用了SubClass的构造方法
但是上塑造型以后,a使用的是SuperClass中的值,并未调用构造方法,所以值为8SubClass s2=new SubClass();s2.a的值为2,因为调用了构造方法SuperClass s3=new SuperClass();s3.a的值为9,同上
看来大家对重载和覆盖理解的还不够