面向对象的第三特征:多态
1.什么是多态
作为java三大特征之一的多态,顾名思义,就是同一事物在不同条件下下表现的状态,那不同的条件是什么呢? 比如说: 狗是动物的一种,狗的实例对象:旺财,他是狗的一种,也是动物的一种。用专业术语来说就是:作为狗的对象旺财,既可以调用狗类的属性、方法,也可以调用动物类的某些属性、方法
2.多态存在的3个必要条件
①要存在继承关系(包括接口的实现)(前提条件)
②子类重写父类的方法(前提条件)
③父类的引用指向子类的对象
格式:父类类名 变量名 = new子类类();
特点一:();在使用多态后的对象调用父类的方法时,会调用子类重写过的方法
代码演示:
public class demo5 {//父类
public void eat(){
System.out.println("猫会吃a");
}
}
class demo5b extends demo5{
//子类b重写父类的方法
public void eat(){
System.out.println("狗会吃b");
}
}
class demo5c extends demo5{
//子类c重写父类的方法
public void eat(){
System.out.println("猫会吃c");
}
}
class Test{//测试类
public static void main(String[] args) {
demo5 d5 = new demo5b();
//父类的引用指向子类b的对象
d5.eat();
d5 = new demo5c();
d5.eat();
//方法的形式参数类型是父类类型,而传递的实际参数可以是任意子类的对象
}
}
那么输出结果显而易见,多态后的对象调用父类方法时会调用子类重写的方法
特点二: 多态的 静态方法、成员变量 满足 :编译运行都看左边;
多态的 成员方法 满足:编译看左边,运行看右边
代码演示(编译运行看左边)//FU text = new ZI();:
package TextDemo;
public class FU {
String name = "旺财";//成员变量
static public void m1(){//静态方法
System.out.println("父类静态方法m1");
}
}
class ZI extends FU{
String name1 = "小黑";//子类成员方法
static public void m1(){
System.out.println("子类静态方法m2");
}
}
class Text{//测试类
public static void main(String[] args) {
//多态
FU text = new ZI();//父类引用指向子类的对象
/**
* 调用成员变量、静态方法,
* 编译和运行都看左边
* 左边是父类,所以调用父类的成员变量与静态方法
*/
System.out.println(text.name);
text.m1();//静态方法也可以用对象名调用,但是不建议这样做。
/**
* 输出结果:
* 旺财
* 父类静态方法m1
*/
}
}
(编译看左边,运行看右边)//FU text = new ZI();
public class FU {
public void m3(){
System.out.println("父类成员方法m3");
}
}
class ZI extends FU{
public void m3(){
System.out.println("子类成员方法m3");
}
}
class Text{//测试类
public static void main(String[] args) {
//多态
FU text = new ZI();//父类引用指向子类的对象
/**
* 调用成员方法,
* 编译看左边,运行右边
* 看起来是父类的方法,但实际运行的却是子类的方法
*/
text.m3();
/**
* 输出结果:
* 子类成员方法m3
*/
}
}
特点三: 多态的转型和instanceof关键字
使用多态,提高了程序的扩展性,这是它的优点。那么有优点必然也会有缺点。多态最直接的缺点就是无法继承子类独有的方法因为编译是看左边,左边是父类的引用,用父类的引用来调子类独有的成员方法,编译器就是报错。那有没有解决的方法?
转型就是一个很好的解决方法。转型,顾名思义:从一个类型的变量转换到另一个类型的变量,这很好理解,就好比基本数据类型的转型:
int a =10;
short b = (short) a;
long c = a;
这也符合小转大,随便转;大转小,要强转的法则。将对子类的对父类的对象可以默认转换,但是将父类的对象装换位子类的对象要强制转换,所以,之前的多态,也就是父类的引用指向子类的对象,都是默认的向上转型。那什么是向下转型呢?
子类类型 变量名=(子类类型) 父类类型的变量;
在配合instanceof(判断对象是否是某个数据类型//返回值是布尔值) 就可以完美的实现多态,技能调用父类属性,也能调用子类独有属性。
最后以作业的形式来完成多态:
编写一个Person抽象类,要求含有姓名(name)年龄(age)两个私有属性以及吃饭(eat) 和睡觉(sleep)两个抽象方法,并写出带参构造方法,创建学生(student)和工人(worker) 两个类,继承Person类,学生类多出了私有属性学号和学习方法(输出我爱学习),工人类多出了私有属性工号和工作方法(输出我爱工作),在主函数中创建学生和工人类 的实例对象,使用构造方法赋值,并输出所有属性和方法
public class demo2 {
public static void main(String[] args) {
//编译看左边,运行看右边
//学生类:
Person student = new Students();
//设置、输出名字
student.setName("张三");
System.out.println("名字是"+student.getName());
//设置、输出年龄
student.setAge(19);
System.out.println(student.getAge()+"岁");
//设置、输出学号
//先向下转型,在调用子类的方法
Students students = (Students) student;
students.setNum("1110001010");
students.getNum();
//父类的两个方法
student.eat();
student.sleep();
//独立的方法
students.study();
System.out.println("——————————————");
//工人类:
Person work = new Worker();
//设置、输出名字
work.setName("李四");
System.out.println("名字是"+work.getName());
//设置、输出年龄
work.setAge(19);
System.out.println(work.getAge()+"岁");
//设置、输出学号
//先向下转型,在调用子类的方法
Worker worker = (Worker) work;
worker.setNum2("0001110011");
worker.getNum2();
//父类的两个方法
work.eat();
work.sleep();
//独立的方法
worker.work();
}
}
abstract class Person{
private String name;
private int age;
public Person(String number){
System.out.println("Person带参构造方法");
}
public Person(){//无参构造方法
}
public abstract void eat();
public abstract void sleep();
//get、set私有属性
public void setAge(int age){
this.age = age;
}
public int getAge() {
return age;
}
public void setName(String name){
this.name = name;
}
public String getName(){
return name;
}
}
class Students extends Person{//子类:学生类
//省略了super();
public void eat(){
System.out.println(getName()+"学生吃");
}
public void sleep(){
System.out.println(getName()+"学生睡觉");
}
//私有属性方法
private String num;
public void study(){
System.out.println("我爱学习");
}
//get、set私有属性
public void setNum(String num){
this.num = num;
}
public String getNum(){
return num;
}
}
class Worker extends Person{//子类:工人类
public void eat(){
System.out.println(getName()+"工人吃饭");
}
public void sleep(){
System.out.println(getName()+"工人睡觉");
}
//私有属性和工作方法
private String num2;
public void work(){
System.out.println("我爱工作");
}
//get、set私有属性
public void setNum2(String num2){
this.num2 = num2;
}
public String getNum2(){
return num2;
}
}
/**
* 输出:
* 名字是张三
* 19岁
* 张三学生吃
* 张三学生睡觉
* 我爱学习
* ——————————————
* 名字是李四
* 19岁
* 李四工人吃饭
* 李四工人睡觉
* 我爱工作
*/