父类:
class Person {
//私有属性
private String name;
private String sex;
private int age;
//封装
public String getName() {
return name;
}
public String getSex() {
return sex;
}
public int getAge() {
return age;
}
//父类构造函数
Person(String name,String sex,int age ){
this.name = name;
this.sex = sex;
this.age = age;
}
public void eat(){
System.out.println("吃饭饭!");
}
}
子类1:chinese
class Chinese extends Person {
//子类构造函数
Chinese(String name,String sex,int age){
super(name,sex,age);//调用父类构造函数
}
//子类重写父类函数(多态)
public void eat() {
System.out.println("姓名:"+getName()+",性别:"+getSex()+",年龄:"+getAge()+",我是中国人,我喜欢吃饭!");
}
//子类特有函数
public void shadowBoxing() {
System.out.println(getName()+"在练习太极拳!");
}
/********* end *********/
}
子类2:English
class English extends Person {
//子类构造函数
English(String name,String sex,int age){
super(name,sex,age);//调用父类构造函数
}
//子类重写父类函数(多态)
public void eat() {
System.out.println("姓名:"+getName()+",性别:"+getSex()+",年龄:"+getAge()+",我是英国人,我喜欢吃三明治!");
}
//子类特有函数
public void horseRiding() {
System.out.println(getName()+"在练习骑马!");
}
}
一,向上转型
1,父类类型 父类对象 = new 子类构造函数;(父类引用指向子类对象)
例如:**Person person= new Chinese("张三","男",23);
**
2,父类类型 父类对象 = 子类实列;
例如:Chinese c = new Chinese("张三","男",23);
Person person = (Chinese) c;
以上两种方法都是向上转型,属于多态的一种;
用person对象调用eat():person.eat();
结果是:
调用的是子类方法而不是父类的。
总结:(**如果对象发生了向上转型关系后)
1,只能调用子类继承父类的函数,子类的其它的方法都不能访问,包括父类中的私有成员方法。
2,若子类重写了继承的父类的方法,所调用的方法一定是被子类重写的方法。**
二,向下转型
1,子类类型 子类对象 = (子类) 父类对象;
注意:父类对象必须是由子类向上转型得到的,而且父类对象转换前的子类对象必须与左边的一样。
简单来说就是:要想向下转换,必须先向上转换,向下转换也必须是转会原来的子类类型。
错误举例:
1,Chinese c= new Person();
//直接报错
2,Chinese c=(Chinese) new Person();
//编译时不会报错,运行报错。
正确做法是:Person person= new Chinese("张三","男",23);
Chinese c= person;
这样看来,向下转型貌似没用,转来转去,玩呢。。。。
2,向下转型其实是服务与向上转型的,
因为向上转型后无法调用子类中的特有方法,你想调用就必须再向下转型。
这时你又有疑问了,那最初还不如不转呢,多省事啊。
向上转型的好处
减少重复代码,使代码变得简洁。
提高系统扩展性。
具体解释:原文链接
举个例子:比如我现在有很多种类的动物,要喂它们吃东西。如果不用向上转型,那我需要这样写:
public void eat(Cat c){
c.eat();
}
public void eat(Dog d){
d.eat();
}
//……
eat(new Cat());
eat(new Cat());
eat(new Dog());
//…..
一种动物写一个方法,如果我有一万种动物,我就要写一万个方法,写完大概猴年马月都过了好几个了吧。好吧,你很厉害,你耐着性子写完了,以为可以放松一会了,突然又来了一种新的动物,你是不是又要单独为它写一个eat方法?
那如果我使用向上转型呢?我只需要这样写:
public void eat(Animal a){
a.eat();
}
eat(new Cat());
eat(new Cat());
eat(new Dog());
//…..
恩,搞定了。代码是不是简洁了许多?而且这个时候,如果我又有一种新的动物加进来,我只需要实现它自己的类,让他继承Animal就可以了,而不需要为它单独写一个eat方法。是不是提高了扩展性?
三,判断对象是某个类的实例对象
可以用instanceof 判断一个类是否实现了某个接口,也可以用它来判断一个实例对象是否属于一个类。instanceof的语法格式为:
对象 instanceof 类(或接口)
它的返回值是布尔型的,或真(true)、或假(false);
当使用向上转型时调用子类各独有 的方法时:
public static void show(Person a) {
if(a instanceof Chinese) {
Chinese aa=(Chinese) a;
aa.shadowBoxing();
}
else if (a instanceof English) {
English p1=(English) a;
p.horseRiding();
}
}
四,super(参数1,参数2…)与this(参数1,参数2…)的区别
super()用处:继承使子类拥有父类所有的属性和方法,但是父类对象中的私有属性和方法,子类是无法访问到的,只是拥有,但不能使用。父类可以通过有参构造对私有属性赋值。但,子类不能继承父类的构造函数,只是显式(用super()函数 )或隐式调用(默认使用无参super()函数)。
区别
区别点 | this() | super() |
---|---|---|
属性访问 | 访问本类中的属性,如果本类中没有该属性,则从父类中查找。 | 直接访问父类中的属性。 |
方法 | 访问本类中的方法,如果本类中没有该方法,则从父类中查找。 | 直接访问父类中的方法。 |
调用构造 | 调用本类构造函数,必须放在构造方法首行。 | 调用父类构造函数,必须放在子类构造方法首行。 |
相同点
1,这两个方法的参数与对应的构造方法相匹配时,就相当于把参数传给相对应的构造方法。
2,因为两个方法都要放在构造函数中第一行所以只能使用两者其中之一,所以不能同时使用。
5,子类若构造方法中没有this()和super()。默认super().调用父类无参构造。
注意理解this中如果本类中没有该方法,则从父类中查找。
由上规则决定了下面的顺序
java继承链中方法调用优先级顺序:
this.show(object)>super.show(object)>this.show((super)object)>super.show((super))
例题:来自原文链接
class A {
public String show(D obj) {
return ("A and D");
}
public String show(A obj) {
return ("A and A");
}
}
class B extends A{
public String show(B obj){
return ("B and B");
}
public String show(A obj){
return ("B and A");
}
}
class C extends B{
}
class D extends B{
}
public class Demo {
public static void main(String[] args) {
A a1 = new A();
A a2 = new B();
B b = new B();
C c = new C();
D d = new D();
System.out.println("1--" + a1.show(b));
System.out.println("2--" + a1.show(c));
System.out.println("3--" + a1.show(d));
System.out.println("4--" + a2.show(b));
System.out.println("5--" + a2.show(c));
System.out.println("6--" + a2.show(d));
System.out.println("7--" + b.show(b));
System.out.println("8--" + b.show(c));
System.out.println("9--" + b.show(d));
}
}
//结果:
//1--A and A
//2--A and A
//3--A and D
//4--B and A
//5--B and A
//6--A and D
//7--B and B
//8--B and B
//9--A and D
//能看懂这个结果么?先自分析一下。
前三个,强行分析,还能看得懂。但是第四个,大概你就傻了吧。为什么不是b and b呢?
咱一块分析下第四个:
1,首先,a2是向上转型(父类引用指向子类对象),所以要明白a2对象是调用子类中的函数,还必须是继续或重写的函数。虽然子类中有show(B obj),但这是子类特有的,所以不能调用,这就是结果不能是b and b的原因了。
2,然后,子类B中就没有符合的函数了,父类也么有符合的,也就是:this.show(object),super.show(object),这两个等级没用了进入下一级:
this.show((super)object)
3,也就是:a2.show(b)->a2.show((A)b);(第二种向上转型)
因为父类有show(A obj) 所以就输出了B and A;
理解了这个,后面估计都能看懂了。