1. 多态的前提
- 类与类的继承:extends
- 类与接口的实现:implements
- 接口与接口的继承:extends
2. 什么是多态
- 一个对象拥有多种形态,就是多态。
如:一个人的多级继承关系:小明->学生->人->生物;
此时,小明显然具有多种形态,既是学生,又是人,还是生物。
同理,接口的实现,接口的继承,也同样会产生多态性。 - 代码中的多态:父类引用指向子类对象。
格式:父类名 对象名=new 子类名(); 接口名 对象名=new 实现类名(); //显然,学生也是人,子类就是一个父类。
3. 访问成员变量
1.直接通过对象名访问成员变量
看等号左边是谁,就是谁的成员变量,没有才向上找
编译看左边,运行也看左边
package linxu.day08;
public class fu {
int num=30;
}
package linxu.day08;
public class zi extends fu{
int num=50;
}
package linxu.day08;
public class maindemo {
public static void main(String[] args) {
fu obj=new zi();
System.out.println(obj.num);//30,obj这个对象,等号左边是fu类,所以是fu类的num
}
}
- 间接通过成员方法访问成员变量
看该方法属于谁,优先用谁,没有则向上找
编译看左边,运行看右边
本质上是对成员方法的访问
即看该对象new的是谁
package linxu.day08;
public class fu {
int num=30;
public void showNum(){
System.out.println(num);
}
}
package linxu.day08;
public class zi extends fu{
int num=50;
public void showNum(){
System.out.println(num);
}
}
package linxu.day08;
public class maindemo {
public static void main(String[] args) {
fu obj=new zi();
obj.showNum();//obj是zi类new的对象,因此showNum优先查找zi类方法
}
}
4. 访问成员方法
在多态的代码中,成员方法的访问:
new的是谁,优先用谁,没有则向上找
编译看左边,运行看右边
package linxu.day08;
public class fu {
public void mathodfu(){System.out.println("父类方法");}
public void mathod(){System.out.println("公共方法——父类");}
}
package linxu.day08;
public class zi extends fu{
public void mathodzi(){System.out.println("子类方法");}
public void mathod(){System.out.println("公共方法——子类");}
}
package linxu.day08;
public class maindemo {
public static void main(String[] args) {
fu obj=new zi();
obj.mothod();//公共方法——子类,编译时看左边,是fu obj,fu类有mothod;运行看右边是new zi(),zi类也有mothod方法,优先使用子类方法
obj.mothodfu();//父类方法,编译时看左边,是fu obj,fu类有mothodfu;运行看右边是new zi(),zi类没有mothodfu方法,向上找
obj.mothodzi();//错误写法;编译时看左边,是fu obj,而fu类没有mothodzi,所以报错
}
}
5.为什么要用多态
通过上图,我们可以知道,既然左边的类型统一了,那么,我们能做的就很多了,
- 比如,只需要一个ArrayList的数组,其中可以同时存放不同子类的对象。
- 可以通过重写父类抽象方法实现每一个子类不同的方法。
- 甚至在传参时,只需要使用父类作为参数类型,所有子类对象都可以传入,不需要写多个重载的函数
示例如下:
package linxu.day08;
public abstract class Employee {
String name;
public abstract void show();
}
package linxu.day08;
public class teacher extends Employee{
String name;
public teacher(String name) {
this.name = name;
}
@Override
public void show() {
System.out.println(name);
}
}
package linxu.day08;
public class assistant extends Employee{
String name;
int age;
public assistant(String name, int num) {
this.name = name;
this.age = num;
}
@Override
public void show() {
System.out.println(name+" "+age);
}
}
package linxu.day08;
import java.util.ArrayList;
public class maindemo {
public static void main(String[] args) {
ArrayList<Employee> list =new ArrayList<Employee>();
Employee one=new teacher("李老师");
list.add(one);
Employee two=new assistant("王助教",19);
list.add(two);
//俩个teacher、assistant类的对象,通过多态巧妙的放入了一个数组。
list.get(0).show();// one.show();//李老师
list.get(1).show();//two.show();//王助教 19
System.out.println("-------------------");
showHHH(list,0);// one.show();//李老师
showHHH(list,1);// one.show();//王助教 19
}
public static void showHHH(ArrayList<Employee> list,int i){
list.get(i).show();
}
//只一个函数,便同时实现了传入不同类型数据的功能,既可以传入Employee类,
//也可以是teacher、assistant类
}
这个示例同时介绍了抽象方法、ArrayList、多态、继承等概念的综合使用。
- 不管Employee还会派生多少子类,绝大部分方法都不需要有任何改动。为软件的升级和维护提供了极大的便利。
- 接口和父类作用相同,都可以作为参数,这里的Employee也可以写成接口,不一定非得是父类
6. 对象的向上转型
其实就是多态的写法。
格式:父类名 对象名 = new 子类名();
含义:右侧创建一个子类对象,把它当作父类对象来看待
向上转型一定是安全的(小范围->大范围)。
弊端:一旦向上转型,就没办法调用子类特有方法。
解决方案:向下转型
7. 对象的向下转型
对象的向下转型,就是一个还原的过程。
格式:子类名 对象名 = (子类名)父类对象;
含义:将父类对象,还原为原本的子类对象。
此时就可以调用子类特有方法。
向下转型不一定安全,绝对不能向下转型成为其他的子类,否则在运行时会发生类转化异常(ClassCastException)
8. instanceof关键字
- 怎么知道一个父类引用的对象,本来是什么子类呢?
当需要使用子类特有方法,就需要向下转型,但如何判断转型是否正确?
使用instanceof关键字。 - 格式:
对象名 instanceof 类名
这将得到一个bollean值,判断前面对象是否可以当作后面的类来使用。 - 例如:
if(one instanceof teacher) { teacher tea1 = (teacher) one; System.out.println("one的子类是teacher"); tea1.show(); } if(two instanceof teacher) { teacher tea2 = (teacher) two; System.out.println("two的子类是teacher"); tea2.show(); } /*打印结果: one的子类是teacher 李老师 */