详解java的多态性

目录

1、多态
1、什么是多态? 
	多态指的是方法或对象具有多种状态,多态的前提是建立在封装和继承之上的。多态可以从方法和对象上分析
	1、方法多态:
		方法重载
		方法重写
	2、对象多态:
  		1、第一层面
			1、父类引用可以指向子类的对象 如 student extends Person, Person student = new Student();
			2、这里student包含两种类型:编译类型(Person),运行类型(Student)
			3、编译类型:由编译器所能识别的类型
			4、运行类型:jvm在运行时在堆中能查找到的实际类型
			5、(重要) 当我们想通过student调用方法时,只能调用到编译类型所拥有的方法和属性,而不是运行类型所拥有的方法!
			
			
		2、第二层面
			1、一旦编译类型确定后就不能改变,如student的编译类型就永远是Person类型,但是它的运行类型可以进行任何变化(只要你继承了父类person类)
		
			
#代码测试一、

public class polyTest {
   public static void main(String[] args) {
   
		//这里 Person是person里包含的编译类型,Student是运行类型
        Person person = new Student();	
        //person引用指向Student对象
        
        person.eat();   //person只能调用编译类型Person的eat()方法,无法调用
        System.out.println(person.name);

        person = new Teacher();
        //虽然person引用指向了新的Teacher对象的运行类型,但是person的编译类型还是Person
        person.eat();   
        //所以person还是只能调用编译类型Person的eat()方法
        System.out.println(person.name);
    }
}

class Person{
    public String name="张三";
    
    public void eat(){
        System.out.println("Person is eating..");
    }
}

class Student extends Person{
    public String name = "李四";
    
    public void study(){
        System.out.println("student is studying...");
    }
}

class Teacher extends Person{
    public String name = "王五";

    public void teaching(){
        System.out.println("teacher is teaching ...");
    }
}
		
输出:
	Person is eating..
	张三
	Person is eating..
	张三

说明
	1、当使用对象多态中的向上转型时,开发者只能通过引用person调用编译类型所拥有的方法和属性
	2、编译类型一旦确定不会变化,但运行类型可以任意变化(前提需要继承)
	
#多态解决了什么问题?、

1、不用多态

 public static void main(String[] args) {
        //不使用多态实现主人公xxx在xxx类型公司做xxx职位的工作
        Master master = new Master("张三");
        Master master1 = new Master("何润东");

        //公司类型
        InternetCompany Internet = new InternetCompany("力扣");
        EntertainmentCompany enter = new EntertainmentCompany("华谊兄弟");

        //职位
        Programmer programmer = new Programmer("java开发");
        Performer performer = new Performer("风云1男主角");

        master.JobInfo(Internet,programmer);
        master1.JobInfo(enter,performer);

    }
}

//主人公类
class Master{
    private String name;
    public Master(String name){
        this.name = name;
    }

    public String getName(){
        return name;
    }

    public void setName(String name){
        this.name = name;
    }

    public void JobInfo(InternetCompany companyType, Programmer job){
        System.out.println("主人公" + name + " 在" + companyType.getCompanyType() + "公司," + "担任 " + job.getJob() + "工作");
    }
    
    //方法重载
    public void JobInfo(EntertainmentCompany companyType, Performer job){
        System.out.println("主人公" + name + " 在" + companyType.getCompanyType() + "公司," + "担任 " + job.getJob() + "工作");
    }
    //思考:如果还有更多的公司+职位的组合,那不是要写更多的方法重载吗?而且有些方法是很少用到的,属实浪费了!
    // 那有没有一种解决方案,可以只写一个方法就承载所有组合呢?显然用多态就可以解决!
}

//公司类型类
class Company_Type{
    private String companyType;

    public Company_Type(String companyType){

        this.companyType = companyType;
    }

    public String getCompanyType(){
        return companyType;
    }

    public void setCompanyType(String companyType){
        this.companyType = companyType;
    }
}

//互联网公司
class InternetCompany extends Company_Type{
    public InternetCompany(String companyType) {
        super(companyType);
    }
}

//娱乐公司
class EntertainmentCompany extends Company_Type{
    public EntertainmentCompany(String companyType){
        super(companyType);
    }
}


//工作类型类
class Worke{
    private String job;
    public Worke(String job){
        this.job = job;
    }

    public String getJob(){
        return job;
    }

    public void setJob(String job){
        this.job = job;
    }
}

//软件开发
class Programmer extends Worke{

    public Programmer(String job){
        super(job);
    }
}

//影视演员
class Performer extends Worke{
    public Performer(String job){
        super(job);
    }

输出:
	主人公 张三 在力扣公司,担任 java开发工作
	主人公 何润东 在华谊兄弟公司,担任 风云1男主角工作






2、使用多态解决
	很简单,只需要稍微修改主人公类中的JobInfo()方法,如下

   
public class polyTest1 {

    public static void main(String[] args) {
        //不使用多态实现主人公xxx在xxx类型公司做xxx职位的工作
        Master master = new Master("张三");
        Master master1 = new Master("何润东");

        //公司类型
        InternetCompany Internet = new InternetCompany("力扣");
        EntertainmentCompany enter = new EntertainmentCompany("华谊兄弟");

        //职位
        Programmer programmer = new Programmer("java开发");
        Performer performer = new Performer("风云1男主角");

        master.JobInfo(Internet,programmer);
        master1.JobInfo(enter,performer);
        
        master.JobInfo(enter,programmer);
        master1.JobInfo(Internet,performer);
        
    }
}

//主人公类
class Master{
    private String name;
    public Master(String name){
        this.name = name;
    }

    public String getName(){
        return name;
    }

    public void setName(String name){
        this.name = name;
    }

    public void JobInfo(Company_Type companyType, Worke job){
        System.out.println("主人公 " + name + " 在 " + companyType.getCompanyType() + " 公司," + "担任 " + job.getJob() + " 工作");
    }
}

//公司类型类
class Company_Type{
    private String companyType;

    public Company_Type(String companyType){

        this.companyType = companyType;
    }

    public String getCompanyType(){
        return companyType;
    }

    public void setCompanyType(String companyType){
        this.companyType = companyType;
    }
}
//互联网公司
class InternetCompany extends Company_Type{
    public InternetCompany(String companyType) {
        super(companyType);
    }
}

//娱乐公司
class EntertainmentCompany extends Company_Type{
    public EntertainmentCompany(String companyType){
        super(companyType);
    }
}


//工作类型类
class Worke{
    private String job;
    public Worke(String job){
        this.job = job;
    }

    public String getJob(){
        return job;
    }

    public void setJob(String job){
        this.job = job;
    }
}

//软件开发
class Programmer extends Worke{

    public Programmer(String job){
        super(job);
    }
}

//影视演员
class Performer extends Worke{
    public Performer(String job){
        super(job);
    }
}

输出:
	主人公 张三 在 力扣 公司,担任 java开发 工作
	主人公 何润东 在 华谊兄弟 公司,担任 风云1男主角 工作
	主人公 张三 在 华谊兄弟 公司,担任 java开发 工作
	主人公 何润东 在 力扣 公司,担任 风云1男主角 工作
	
	

小结:
	1、因为父类引用可以指向子类对象,所以用父类引用可以接受所有子类的对象参数!如父类引用companyType可以接收子类对象Internet 和 子类对象enter; 父类引用job可以接收子类对象programmer 和 子类对象performer
	
	2、当Internet传入到companyType时,运行到companyType.getCompanyType(),此时的companyType运行类型是Internet类型,会去找Internet里的getCompanyType()方法,如果没有就去找父类的getCompanyType()方法,然后获取CompanyType的值!
	
	
	
	
重要!!!
3、向上转型(动态绑定机制);
	前提:两个对象(类)存在继承关系
	本质:父类引用指向子类对象
	格式:父类类型 引用名=new 子类类型(); Person person = new Student();
	技巧;编译类型在左边,运行类型在右边
	
	
#代码测试:
public class polyTest2 {
    public static void main(String[] args) {
        AA a = new AA();
        System.out.println(a.name);
        a.add();
        a.insert();
        a.change();
        System.out.println();


        AA b = new BB();//向上转型:编译类型AA是运行类型BB的父类
        //b.remove(); 多态里无法调用 只存在子类(运行类型)中而不存在父类(编译类型)中的方法
        System.out.println(b.name);
        b.add();
        b.insert();
        b.change();//依然是先在子类BB中找change()方法,没有的话去父类中找
    }
}

输出:
	AA
	AA 的 add 方法
	AA 的 insert 方法
	AA 的 change 方法
	
	AA	
	BB 的 add 方法
	BB 的 insert 方法
	AA 的 change 方法
	

小结:
	*方法层面
		1、在多态的向上转型中,无法调用只有子类有而父类没有的方法
		2、在调用上,只能调用父类中存在的方法,不管子类有没有
		3、在运行结果上(向上转型),会先在子类中寻找该方法,如果有就执行,没有就向父类找,父类有就执行父类的该方法
	
	*属性层面
		1、获取哪个类的属性值只看编译类型, AA a = new AA()和 AA b = new BB()的编译类型都是父类AA,所以获取的是AA的类属性值
		



重要!!!
4、向下转型(动态绑定机制);
	前提:
		1、两个对象(类)存在继承关系
		2、父类的引用必须指向当前目标类型的对象(如 AA a=new BB() 的编译类型要强转为BB类型,操作 BB b = (BB) a ,即先要让AA和BB绑定,你才能从AA向下转型BB)
	
	格式:子类类型 引用名=(子类类型) 父类引用;
	技巧;编译类型在左边,运行类型在右边
	本质:新增了一条子类引用指向子类对象

#代码测试;
public class polyTest2 {
    public static void main(String[] args) {
        AA a = new AA();
        System.out.println(a.name);
        /*
        a.add();
        a.insert();
        a.change();
        System.out.println();
        */

        AA b = new BB();//向上转型:编译类型AA是运行类型BB的父类
        //此时b只能调用add、insert、change方法
        System.out.println(b.name);

        //b.remove(); 多态里无法调用 只存在子类(运行类型)中而不存在父类(编译类型)中的方法
        //如果我想要调用BB类中的remove方法呢,用向下转型
        BB c = (BB)b;//向下转型
        c.remove();
        System.out.println(c.name);
        /*
        System.out.println(b.name);
        b.add();
        b.insert();
        b.change();//依然是先在子类BB中找change()方法,没有的话去父类中找
         */
    }

}

//父类AA
class AA{
    protected String name = "AA";
    public void add(){
        System.out.println("AA 的 add 方法");
    }
    public void insert(){
        System.out.println("AA 的 insert 方法");
    }
    public void change(){
        System.out.println("AA 的 change 方法");
    }
}
//子类BB
class BB extends AA{
    protected String name="BB";
    public void remove(){
        System.out.println("BB 的 remove 方法");
    }
    public void add(){
        System.out.println("BB 的 add 方法");
    }
    public void insert(){
        System.out.println("BB 的 insert 方法");
    }
}

输出:
	AA
	AA
	BB 的 remove 方法
	BB
	
	
小结:
	
	*方法层面:
		
		1、向下转型作用于当你使用向上转型时,你想调用父类没有的而子类有的方法时,用向下转型可以调用到该方法
		2、向下转型的两个前提是: 1、两个类或对象具有继承关系 2、父类的引用必须指向我想向下转型时的目标类型的对象
		3、向下转型的注意点:只能强转父类的引用,不能强转父类的对象
		#说明: AA a = new BB();BB b = (BB)a,这里刚开始栈中的a引用指向堆中BB()对象,然后执行BB b = (BB)a向下转型时,会先检查a是否是指向BB()对象,是的话,再在栈中新增b引用也指向BB(),此时父类类型的引用a和子类类型的引用b都指向BB()这个对象。所以本质上强转的是指向BB的引用,而不会影响BB对象本身!结论:当执行向下转型时,本质上是新增了一条子类引用指向子类对象
	
	
	*属性层面:
		
		1、获取哪个类的属性值只看编译类型, AA a = new AA()和 AA b = new BB()的编译类型都是父类AA,而BB c = (BB)b的编译类型是BB,所以a.name=b.name="AA",c.name="BB"




7、多态的练习题
	
 1、下面哪些语句是正确的,那些是错误的?为什么?
	
	public static void main(String[] args) {
        double d = 13.4;
        long l = (long)d;//√
        System.out.println(l);//13

        int i = 5;
        //boolean b = (boolean)i;//错误
        Object obj = "hello";//向上转型
        String objstr = (String)obj;//向下转型
        System.out.println(objstr);//hello

        Object objP = new Integer(5);//向上转型
        String str = (String)objP;//异常,抛出ClassCaseEexcption异常,父类引用没有指向子类对象
        Integer str1 = (Integer)objP;//向下转型
    }
    
    
2、以下程序输出的内容是什么?
public class polyTest3 {
	public static void main(String[] args) {
        Sub s = new Sub();
        System.out.println(s.i);//20 -->Sub注释后 20
        s.sum();//20-->Sub注释后 10

        Base b = s;
        System.out.println(b.i);//10-->Sub注释后 10
        System.out.println(b==s);//true,因为b和s都指向Sub()对象这块地址
        b.sum();//20-->Sub注释后 10
    }
}

class Base{
    int i = 10;
    public void sum(){
        System.out.println(this.i);
    }
}

class Sub extends Base{
    int i = 20;
//    public void sum(){
//        System.out.println(this.i);
//    }
}



3、以下程序输出结果是什么?
public class polyTest3 {
    public static void main(String[] args) {
        A a = new B();
        System.out.println(a.sum());//30-->先找B类中的sum()方法,没有找A类sum()方法,再去B中找getl()
        System.out.println(a.sum1());//20-->直接找的A类的sum1()方法
    }
}

class A{
    public int i = 10;
    public int sum(){
        return getl()+10;
    }
    public int sum1(){
        return i+10;
    }

    public int getl(){
        return i;
    }
}

class B extends A{
    public int i = 20;
//    public void sum(){
//        return i+20;
//    }
    public int getl(){
        return i;
    }
//    public int sum1(){
//        return i+10;
//        }
}


4、(多态数组应用) 数组定义为父类类型,里面保存实际元素为子类类型,现继承关系如下:
Student类和Teacher类都继承于Person类,Person类属性分别为:String name,int age,Person方法有Info()。要求创建五个年龄不等的对象,分别为Person1个、Student2个、Teacher2个!



/*
(多态数组应用) 数组定义为父类类型,里面保存实际元素为子类类型,现继承关系如下:
Student类和Teacher类都继承于Person类,Student有study(),Teacher有teaching()方法
Person类属性分别为:String name,int age,Person方法有Info()。
要求创建五个年龄不等的对象,分别为Person1个、Student2个、Teacher2个!
 */
public class polyTest3 {
    public static void main(String[] args) {
    Person[] person = new Person[]{new Person("老王",30),new Student("小李",18,67),new Student("小张",20,88),
    new Teacher("赵老师",38,6000),new Teacher("夏老师",40,10000)};
    for(int i=0;i<person.length;i++){
        if (person[i] instanceof Teacher){ //判断person[i]对象的运行类型
            Teacher tescher = (Teacher) person[i];//向下转型
            tescher.teaching();

        }else if (person[i] instanceof Student){
            Student student = (Student) person[i];
            student.study();
        }else{
            Person person1 = person[i];
            person1.Info();
        }
    }

    }
}

//Person类
class Person{
    private String name;
    private int age;
    public Person(String name,int age){
        this.name = name;
        this.age = age;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public void Info(){
        System.out.println("信息:name="+name+"age="+age );
    }
}

//Student类
class Student extends Person{
    private double score;
    public Student(String name,int age,double score){
        super(name,age);
        this.score = score;
    }

    public double getScore() {
        return score;
    }

    public void setScore(double score) {
        this.score = score;
    }

    public void study(){
        System.out.println("学生"+getName()+"正在学习java...");
    }
}


//Teacher类
class Teacher extends Person{
    private double salary;
    public Teacher(String name,int age,double salary){
        super(name, age);
        this.salary = salary;
    }

    public double getSalary() {
        return salary;
    }

    public void setSalary(double salary) {
        this.salary = salary;
    }

    public void teaching(){
        System.out.println("老师"+getName()+"正在教java...");
    }
}

输出:
	信息:name=老王age=30
	学生小李正在学习java...
	学生小张正在学习java...
	老师赵老师正在教java...
	老师夏老师正在教java...
	
	

5、多态参数:
    定义员工类Employee,包含name、salary及计算工资的getAnnual方法
    普通员工和经理继承员工类,但经理多个奖金和管理manage方法
    普通员工有work方法
    普通员工和经理要求重写getAnnual方法
    测试类中添加一个showEmpAnnal(Employee e)方法,实现获取任何员工对象的工资,并在main方法中动态绑定该方法[e.getAnnal()]
    测试类中添加一个testWork方法,如果是普通员工,调用work方法,如果是经理,则调用manage方法;




public class polyTest4 {
    /*
    多态参数:
    定义员工类Employee,包含name、salary及计算工资的getAnnual方法
    普通员工和经理继承员工类,但经理多个奖金和管理manage方法
    普通员工有work方法
    普通员工和经理要求重写getAnnual方法
    测试类中添加一个showEmpAnnal(Employee e)方法,实现获取任何员工对象的工资,并在main方法中动态绑定该方法[e.getAnnal()]
    测试类中添加一个testWork方法,如果是普通员工,调用work方法,如果是经理,则调用manage方法;
     */

    public static void main(String[] args) {

        //定义一个多态数组存放动态对象
        Employee[] employee = new Employee[]{new Manager("老王",10000,10,12,150000),
                new Work("小王",5000,30,20,12),
                new Employee("小谢",8000,25,12)};

        Manager manager = new Manager("老张",10000,28,12,100000);
        Work work = new Work("小张",6000,35,20,12);

        polyTest4 polyTest4 = new polyTest4();
        polyTest4.getAnnual(manager);
        polyTest4.getAnnual(work);

        System.out.println();
        polyTest4.testWork(employee);
    }

    //计算所有对象的工资
    public void getAnnual(Employee e){
        System.out.println("员工"+e.getName()+"年薪"+e.getAnnual());
    }

    //判断该对象,根据对象调用对应的方法
    public void testWork(Employee[] employee){
        for (int i=0;i<employee.length;i++){
            //判断每个对象类型
            if (employee[i] instanceof Manager){
                Manager manager = (Manager)employee[i];
                manager.manage();
            }else if ( employee[i] instanceof Work){
                Work work = (Work) employee[i];
                work.work();
            }else {
                Employee employee1 = (Employee)employee[i];
                employee1.info();
            }
        }
    }
}

//员工类
class Employee{
    private int  Annual=12;//工作了几个月
    private String name;
    private double one_Overtime_Money;//加班每小时单价
    private double salary;


    public Employee(String name,double salary, double one_Overtime_Money,int annual ) {
        Annual = annual;
        this.name = name;
        this.one_Overtime_Money = one_Overtime_Money;
        this.salary = salary;
    }

    public String getName() {
        return name;
    }

    public void setAnnual(int annual) {
        Annual = annual;
    }

    public double getOne_Overtime_Money() {
        return one_Overtime_Money;
    }

    public void setOne_Overtime_Money(double one_Overtime_Money) {
        this.one_Overtime_Money = one_Overtime_Money;
    }

    public void setName(String name) {
        this.name = name;
    }

    public double getSalary() {
        return salary;
    }

    public void setSalary(double salary) {
        this.salary = salary;
    }

    public void info(){
        System.out.println("员工"+getName()+"月薪是"+getSalary());
    }

    //计算工资
    public double getAnnual(){
        return salary*Annual;
    }
}

//普通员工类
class Work extends Employee{
    private double overtime;

    public Work(String name, double salary, double one_Overtime_Money, int annual, double overtime) {
        super(name, salary, one_Overtime_Money, annual);
        this.overtime = overtime;
    }

    //重写父类getAnnual()
    @Override
    public double getAnnual() {
        return super.getAnnual() + overtime*getOne_Overtime_Money();
    }

    //work方法
   public void work(){
       System.out.println("员工"+getName()+"正在工作");
   }

}

//经理类
class Manager extends Employee{
    private double bonus;//奖金

    public Manager(String name, double salary, double one_Overtime_Money,int annual, double bonus) {
        super(name, salary, one_Overtime_Money, annual);
        this.bonus = bonus;
    }

    //计算工资
    @Override
    public double getAnnual() {
        return super.getAnnual()+bonus;
    }

    //manage
    public void manage(){
        System.out.println("经理"+getName()+"正在管理工作。。。");
    }
}
2、多态总结:
1、java多态大致分为两类:
	1、方法的多态
		
		*方法重写 
			特点: 
				1、必须有继承关系且方法名相同,
				2、方法的形参列表和返回值类型必须与父类该同名方法一致,
				3、作用域发生在子类,
				4、对象调用时执行子类的该同名方法)
		
		*方法的重载 
			特点:
				1、必须在同一类中
				2、一个类中的多个同名方法的形参列表和返回类型必须不同
				3、作用域发生在同一类中
				4、调用时根据参数列表选择对应的方法
				
	
	2、对象的多态
		
		*向上转型
			如: B extends A,A a = new B();//父类引用a指向子类对象 B()
			
			前提:
				1、子父类之间必须有继承关系,2、子类必须有重写父类方法	
			
			调用:
				1、可以把a当成父类A对象,所以a可以调用所有父类方法和属性,如果方法同名,执行的是子类方法,属性同名,执行的是父类属性
			
			作用:
				1、把所有对子类的的操作全部封装到对父类的操作,提高代码简洁
				2、可以在父类中定义一个方法,把要操作的子类的类名传入到该方法形参上,通过它们接收子类的对象,然后通过调用子类对象的成员来处理业务
			
			特点:
				1、好处是隐藏了子类的方法和属性,提高了代码的简洁
				2、坏处是不能调用子类的中独有方法(解决:可以在父类中写一个空的同名方法解决)
			
			
		*向下转型
			B extends A,A a = new B(); B b = (B)a;
			#1、向下转型时会先检查父类引用a是否指向子类B对象,如果不是报错类型转换错误   2、只能父类引用指向子类对象,不能子类引用不能指向父类对象
			
			调用:
				1、可以把b当作子类B的对象,所以b能调用子类和父类的所有方法和属性	
				2、调用同名方法还是先找子类的方法执行的,同名属性就是执行子类的该属性
				
			作用:
				1、可以使用父类和子类的所有方法和属性
			特点:
				好处:可以使用父类和子类的所有方法和属性
				坏处:容易报类型转换错误(解决:使用instanceof进行判断)
				
上一篇:来 BB 几句!


下一篇:Numpy模块学习笔记3:数组的重塑、转置与广播