引出问题
这是只驴:(父类,有一个public的成员变量a初始化值为0,还有一个public的打印方法打印a)
public class Donkey {
// protected int a = 0;
public int a = 0;
public void p(){
System.out.println(a);
}
}
这是只死驴:(子类1,继承父类,有一个与父类同名的成员变量a初始值为1,还有一个主方法用于创建对象)
public class DonkeyDead extends Donkey{
public int a = 1;
public static void main(String[] args) {
//用子类自己创建子类对象
DonkeyDead donkey = new DonkeyDead();
donkey.a = 2;
donkey.p();
//用父类创建子类对象
Donkey donkey1 = new DonkeyDead();
donkey1.a = 2;
donkey1.p();
}
}
输出结果为:0 2;
这也是只死驴:(子类2,继承父类,只有一个主方法用于创建对象)
public class DeadDonkey extends Donkey{
// public int a = 1;
public static void main(String[] args) {
DeadDonkey donkey = new DeadDonkey();
donkey.a = 2;
donkey.p();
Donkey donkey1 = new DeadDonkey();
donkey1.a = 2;
donkey1.p();
}
}
输出结果为:2 2;
那么问题来了:
1.为什么用父类创建子类对象和用子类自己创建子类对象输出结果不一样?
2.为什么在子类中定义与父类成员变量同名的变量,会影响子类自己创建的对象,而不影响父类创建的子类对象?
先不慌着来解答这个问题
先来理解一下继承中父类与子类的关系
(1)子类继承父类的所有成员变量,包括实例成员变量(非静态变量,没有用static修饰)和类成员变量(用static修饰的静态变量)
(2)子类继承父类的除构造方法以外的成员方法,包括实例成员方法(非静态方法,没有被static修饰)和类成员方法(被static修饰符修饰的成员方法)
(3)子类不能删除父类成员
(4)子类虽然继承父类的私有成员(被private修饰词修饰的成员,包括私成员有变量和私有成员方法),但是不能使用父类的私有权限成员
(5)子类可以增加自己的成员方法和成员变量
(6)子类可以重定义父类成员
现在再来回看上面提出的两个问题,且不急着回答,先看这个问题是否存在问题,就是提出问题的方向是否有误
1.为什么用父类创建子类对象和用子类自己创建子类对象输出结果不一样?
我们可以来看一下暴力输出法,对于每一次变动a的值都有何变化:(也可以用打断点的方式,看一下每一次a的值都是多少)
public class DeadDonkey extends Donkey{
public int a = 1;
public static void main(String[] args) {
System.out.println("用子类自己创建子类对象:");
DeadDonkey donkey = new DeadDonkey();
System.out.println("初始化后a的值:"+donkey.a);
donkey.a = 2;
System.out.println("给a赋值后的a的值:"+donkey.a);
System.out.print("调用p()方法输出的a的值:");
donkey.p();
System.out.println("用父类创建子类对象:");
Donkey donkey1 = new DeadDonkey();
System.out.println("初始化后a的值:"+donkey1.a);
donkey1.a = 2;
System.out.println("给a赋值后的a的值:"+donkey1.a);
System.out.print("调用p()方法输出的a的值:");
donkey1.p();
}
}
用子类自己创建子类对象:
初始化后a的值:1
给a赋值后的a的值:2
调用p()方法输出的a的值:0
用父类创建子类对象:
初始化后a的值:0
给a赋值后的a的值:2
调用p()方法输出的a的值:2
用父类创建子类:Donkey donkey1 = new DeadDonkey();
实际上是new了一个DeadDonkey的子类对象,然后把它转换成父类Donkey对象。初始化后donkey1相当于是一个父类对象,而它所拥有的的成员变量a的值为父类成员变量初始化的值,也就是0.
在通过打点赋值改变donkey1的变量值:donkey1.a = 2;(这也是为父类对象的成员变量赋值)
调用父类方法来输出成员变量值:donkey1.p();输出的也是父类方法的成员变量的值,也就是2;
驴和死驴两个类看着不是很明了,为了更形象简洁的解释这个父类子类问题,我们来再举个例子:
猴子是一种动物,但是动物很多很多包含猴子,把动物当做父类,把猴子当做子类,猴子具有动物的一些通性,但也有自己的特性,就比如说名字。
父类:动物,有一个public修饰的成员方法name,初始值为“Animal”,有一个public修饰的say()方法,说出自己的名字。
子类:猴子,继承动物大类,它重写父类成员变量name,重新赋值为自己的名字,叫“Monkey”
public class Animal {
public String name = "animal";
public void say(){
System.out.println("My name is "+name);
}
}
public class Monkey extends Animal{
public String name = "Monkey";
}
public class Demo {
public static void main(String[] args) {
Animal monkey = new Monkey();
System.out.println(monkey.name);
monkey.say();
}
}
//输出结果:
animal
My name is animal
//解释:创建的monkey对象仍是一个Animal对象,它的属性也是其父类Animal的属性值。
其实这是一个“向上转型”问题,会造成属性和方法的丢失。
可以参考一下这个博文:https://blog.youkuaiyun.com/newchitu/article/details/90380094
用子类自己创建子类对象:DeadDonkey donkey = new DeadDonkey();
这样创建的对象就是一个彻底的子类对象:初始化后的donkey对象,它所拥有的成员变量也是自己的a,值为自己初始化的值,也就是1;
再用:donkey.a = 2; 给donkey对象的变量重新赋值为2;(给子类对象的成员变量重新赋值)
调用父类方法来输出成员变量值:donkey.p();由于此时调用的是父类方法,输出的变量值仍为父类中的成员变量值,所以a的值为为0;
2.为什么在子类中定义与父类成员变量同名的变量,会影响子类自己创建的对象,而不影响父类创建的子类对象?
这个问题看上去有语病,其实还是讲子类父类同名变量问题,但Java中对象的属性是不具备多态性的。
可以参考一下这个博文:https://www.cnblogs.com/xiarongjin/p/8306599.html
如果我们在子类中重写一下父类的打印方法,结果就会发生变化
public class DonkeyDead extends Donkey{
public int a = 1;
@Override
public void p() {
System.out.println(a);
}
public static void main(String[] args) {
//用子类自己创建子类对象
DonkeyDead donkey = new DonkeyDead();
donkey.a = 2;
donkey.p();
//用父类创建子类对象
Donkey donkey1 = new DonkeyDead();
donkey1.a = 2;
donkey1.p();
}
}
//输出:2 1
这个输出结果与上一个输出结果完全不同
再一次使用暴力输出法,看看对于每一次变动a的值都有何变化:
public class DeadDonkey extends Donkey{
public int a = 1;
@Override
public void p() {
System.out.println(a);
}
public static void main(String[] args) {
System.out.println("用子类自己创建子类对象:");
DeadDonkey donkey = new DeadDonkey();
System.out.println("初始化后a的值:"+donkey.a);
donkey.a = 2;
System.out.println("给a赋值后的a的值:"+donkey.a);
System.out.print("调用p()方法输出的a的值:");
donkey.p();
System.out.println("用父类创建子类对象:");
Donkey donkey1 = new DeadDonkey();
System.out.println("初始化后a的值:"+donkey1.a);
donkey1.a = 2;
System.out.println("给a赋值后的a的值:"+donkey1.a);
System.out.print("调用p()方法输出的a的值:");
donkey1.p();
}
}
//输出结果:
用子类自己创建子类对象:
初始化后a的值:1
给a赋值后的a的值:2
调用p()方法输出的a的值:2
用父类创建子类对象:
初始化后a的值:0
给a赋值后的a的值:2
调用p()方法输出的a的值:1
子类创建的子类对象donkey,变量a初始化为1,赋值后变为2,在调用p()方法时就是调用的子类覆盖父类后的打印方法,输出的也是子类变量a的值,因此为2.
父类创建的子类对象,就是向上转型的对象donkey1,变量a初始化为0,赋值后变为2,但是在调用方法p()时,上边也说到,已经是变质的子类方法,输出的值为子类的变量a的值,因此输出的a的值为其在子类中初始化的,也就是1.
对于子类父类包含同样的变量名,所带来的调用变量来历不明,我们可以在父类中提供一些能够访问private变量的非private方法,也就是我们通常所用的get和set方法。
这本来好像也不算是个很难的问题,但是深入去看看里边到底是怎么转换变化的,有助于我们深入理解子类父类之间的继承关系。
这篇博客写得比较紊乱,但始终也是在阐述一个问题,也算是把简单的问题复杂化吧,复杂化的目的也是在解决问题的本身。