6.多态

阅读: 评论:0

6.多态

6.多态

多态-final-抽象类-接口

多态

D:code黑马codeoop-up-app02srccompolymorphism

多态的概念

  • 多态是在继承或实现的情况下的一种现象,分为对象多态和行为多态

    • 对象多态

    • 行为多态:

      • 编译看左边,运行看右边
      • people p2 = new Student(); p2.say();
      • 编译时只要左边people中有say方法就不会报错,但是运行时会在右边Student类中找重写后的say方法
      //父类
      public class people {public String name = "people";public void say(){System.out.println("I am a people");}
      }
      
      //子类1
      public class Student extends people{public String name = "Student";@Overridepublic void say(){System.out.println("I am a Student");}public void study(){System.out.println("I am studying");}
      }
      
      //子类2
      public class Teacher extends people{public String name = "Teacher";@Overridepublic void say(){System.out.println("I am a Teacher");}
      }
      public class Test {public static void main(String[] args) {//对象多态people p1 = new people();people p2 = new Student();people p3 = new Teacher();//不存在属性多态System.out.println(p1.name);System.out.println(p2.name);System.out.println(p3.name);//行为多态p1.say();//编译看左边,运行看右边p2.say();//p2.study();//会报错,因为p2是people类型,people没有study方法,所以无法调用study方法p3.say();}
      }
      /*
      result:peoplepeoplepeopleI am a peopleI am a StudentI am a Teacher
      */
      

多态的前提

  • 有继承或实现关系
  • 存在父类引用子类对象
  • 存在方法重写

多态注意事项

  • 多态是指对象和行为的多态,而不谈属性的多态。
    • 调用属性时永远看左边,不看右边。即只能调用父类有的属性,且只能找到父类的属性,即使子类中有重名的属性,见上面对属性的调用
    • 调用方法时永远是编译看左边,运行看右边
    • 所能调用的方法只能是子类重写后的方法,子类特有的无法调用,见上面的study( )

多态的好处

  • 可以解耦合,使代码扩展性更强

  • 使用父类对象作为方法的形参,可以接收一切子类对象

    //子类与父类的代码见上文代码
    public class Test {public static void main(String[] args) {people p1 = new people();people p2 = new Student();people p3 = new Teacher();sayHello(p1);sayHello(p2);sayHello(p3);}public static void sayHello(people p){//可以接受父类和所有子类对象p.say();}
    }
    

多态的问题

  • 无法调用子类特有的方法,只能调用子类重写的方法,代码见上文多态的概念中的代码中的Test类

多态下的类型转换

用于解决多态无法调用子类特有的方法的问题

  • 自动类型转换:父类 变量名 = new 子类()
  • 强制类型转换:子类 变量名 = (子类)父类变量
注意事项
  • 存在继承或实现关系就可以在编译阶段进行强制类型转换,编译阶段不会报错

  • 运行时,如果发现对象的真实类型与强转后的类型不同,会报异常

        public static void main(String[] args) {people p = new Teacher();//Student s = (Student) p;// 会报ClassCastException,因为原来的p是Teacher类型,不能转换为Student类型}
    
  • 解决异常问题,在强转前使用instanceof关键字判断对象真实类型,再强转

    public class Test2 {public static void main(String[] args) {people p = new Teacher();hhh(p);}public static void hhh(people p){p.say();System.out.println(p instanceof Student);System.out.println(p instanceof Teacher);if (p instanceof Student) {Student s = (Student) p;s.study();} else if (p instanceof Teacher) {Teacher t = (Teacher) ach();}}
    }
    /*
    result:I am a TeacherfalsetrueI am teaching
    */
    

final

D:code黑马codeoop-up-app02srccomfinall

final关键字可以修饰类、方法和变量

  • 修饰类:该类被称为最终类,特点是不能被继承了。

    • 一般在工具类中使用
  • 修饰方法:该方法被称为最终方法,特点是不能被重写了。

  • 修饰变量:该变量只能被赋值一次。(必须在初始化时赋值)

    • final可以用来修饰局部变量和成员变量(实例成员变量和静态成员变量)

    • final修饰基本类型变量时,变量存储的数据不能被改变

    • final修饰引用类型的变量时,变量存储的地址不能变,但是地址指向的对象的内容可以改变

      //final修饰局部变量的两种情况
      public class Test {public static void main(String[] args) {final int a = 10;// a = 20; // 会报错,因为a是final类型,不能再次赋值}public static void hhh(final int a){// a = 20; // 会报错,因为a是final类型,不能再次赋值}
      }
      
      //final修饰成员变量
      public class Test1 {static final int a = 10;final int b = 20;public static void main(String[] args) {
      // 		  会报错,因为静态成员变量a是final类型,不能再次赋值
      //         a = 20; 
      //         会报错,因为实例成员变量b是final型,不能再次赋值   
      //         b = 30;     }
      }
      
      //final修饰基本类型和引用类型的区别
      public class Test2 {public static void main(String[] args) {final int a = 10;final int[] list = {1,2,3};//        a = 11;// 会报错,因为a是final修饰的基本数据类型的int变量,不能再次赋值list[0] = 10;//不会报错,因为引用类型可以修改地址指向的内容的值
      //        list = null//会报错,因为final修饰的引用类型的变量所存储的地址不能再次赋值}
      }
      

常量

  • 概念
    • 使用static final修饰的已经初始化的成员变量
  • 作用
    • 通常用于记录系统的配置信息
  • 命名规范
    • 使用大写英文单词,多个单词使用下划线连接起来
  • 使用常量记录系统配置信息的优势
    • 代码可读性更好
    • 可维护性更好
  • 执行原理
    • 程序编译后,常量会被“宏替换”:出现常量的地方全部会被替换成其记住的字面量,这样可以保证使用常量和直接用字面量的性能是一样的。也就是说不会影响性能,这些常量不会在运行时才找值,而是在编译后就将所有常量直接替换成需要的字面量

      //.java文件
      public class Test3 {public static final String MY_NAME = "LMH";public static void main(String[] args) {System.out.println(MY_NAME);System.out.println(MY_NAME);System.out.println(MY_NAME);System.out.println(MY_NAME);}
      }
      
      //编译后的.class文件
      package com.finall;public class Test3 {public static final String MY_NAME = "LMH";public Test3() {}public static void main(String[] args) {System.out.println("LMH");//对常量进行了自动赋值System.out.println("LMH");System.out.println("LMH");System.out.println("LMH");}
      }
      

抽象类abstract

D:code黑马codeoop-up-app02srccomabstract_

  • 抽象类

    • abstract修饰的类
  • 抽象方法

    • abstract修饰的方法
    • 抽象方法中不能有方法体,只能由方法签名
  • 抽象类注意事项

    • 抽象类中不一定有抽象方法,有抽象方法的类一定为抽象类

      //将方法注释掉不会报错,因为抽象类中不一定有抽象方法
      //如果去掉类中的abstract会报错,因为有抽象方法的类一定为抽象类
      public abstract class Test {public abstract void test();
      }
    • 类该有的成员(成员变量,方法,构造器)抽象类都可以有,也就是说抽象类中既可以有抽象方法,也可以有实例方法,只要类中有一个抽象方法则该类就是抽象类

      //如果去掉类中的abstract会报错,因为类中有一个抽象方法
      public abstract class Test {public String name;//抽象类中可以有成员变量public Test() {//抽象类中可以有构造方法System.out.println("抽象类中可以有构造方法");}public String getName() {//抽象类中可以有普通方法return name;}public void setName(String name) {//抽象类中可以有普通方法this.name = name;}public abstract void test();//抽象类中可以有抽象方法
      }
      
    • 一个类继承抽象类,必须重写该类所有的抽象方法,否则必须将当前类也定义为抽象类

      //抽象类
      public abstract class Test{public abstract  void a();public abstract void b();
      }
      
      //重写了所有方法
      public class test1 extends Test{@Overridepublic void a() {System.out.println("a");}@Overridepublic void b() {System.out.println("b");}
      }
      
      //由于只重写了一个方法会报错,除了重写所有方法会在类名前添加abstract
      public class test2 extends Test{@Overridepublic void a() {System.out.println("a");}
      }
      
  • 抽象类特点

    • 抽象类不允许创建对象,仅作为一种特殊的父类,让子类继承并实现
  • 抽象类的好处

    • 父类知道每个子类都要做某个行为,但每个子类要做的情况不一样,父类就定义成抽象方法,交给子类去重写实现,我们设计这样的抽象类是为了更好的支持多态。
  • 案例

public abstract class Animal {private String name;public abstract void action();public String getName() {return name;}public void setName(String name) {this.name = name;}
}
public class dog extends Animal{@Overridepublic void action() {System.out.println("狗汪汪汪的叫");}
}
public class cat extends Animal{@Overridepublic void action() {System.out.println("猫喵喵喵的叫");}
}
public class test {public static void main(String[] args) {Animal d = new dog();d.setName("小狗");d.action();Animal c = new cat();c.setName("小猫");c.action();}
}
/*
result:狗汪汪汪的叫猫喵喵喵的叫
*/
  • 抽象类常见应用场景

    • 模板方法设计模式
      • 解决方法中存在部分重复代码的问题
    • 写法
      • 定义一个抽象类
      • 在类中写两个方法
        • 一个为模板方法:将相同代码放入
          • 最好使用final来修饰模板方法
        • 一个为抽象方法:将具体实现交给子类

public abstract class Person {private String name;private int age;public Person() {}public Person(String name, int age) {this.name = name;this.age = age;}//模板方法,用final修饰,放入相同代码public final void commonintroduce(){System.out.println("我的名字是:"+name+" 年龄:"+age + selfintroduce());}//抽象方法public abstract String selfintroduce();
}
public class Teacher extends Person{private String Skill;public Teacher() {}public Teacher(String name, int age ,String skill) {super(name, age);this.Skill = skill;}//实现抽象方法@Overridepublic String selfintroduce() {return " 我的技能是:"+Skill+"。";}
}
public class Consult extends Person{private int num;public Consult() {}public Consult(String name, int age,int num) {super(name, age);this.num = num;}//实现抽象方法@Overridepublic String selfintroduce() {return " 我的咨询人数是:"+num+"人。";}
}
public class Test {public static void main(String[] args) {Person p1 = new Teacher("张三", 20, "java");Person p2 = new Consult("李四", 30, 100);p1monintroduce();p2monintroduce();}
}
/*
result:我的名字是:张三 年龄:20 我的技能是:java。我的名字是:李四 年龄:30 我的咨询人数是:100人。
*/

接口

D:code黑马codeoop-up-app02srccominterface_

初识接口

  • 使用interface定义接口

  • 接口不能创建对象

  • 接口中只能由属性和抽象方法

    • 属性默认为常量,默认使用public static final修饰
    • 方法默认是抽象方法,默认使用public abstract修饰
  • 接口需要被类使用implements实现,实现接口的类叫实现类

  • 一个实现类可以实现多个接口,但是必须要重写所有接口的抽象方法,否则要将实现类定义为抽象类

    //接口1
    public interface A {// 接口中的属性都是常量,默认使用public static final修饰String MY_NAME = "A";// 接口中的方法都是抽象方法,默认使用public abstract修饰void sayA();
    }
    
    //接口2
    public interface B {String MY_NAME = "B";void sayB();
    }
    
    //实现类
    public class C implements A,B{
    //必须同时实现所有的接口中的抽象方法@Overridepublic void sayA() {System.out.println("sayA");}@Overridepublic void sayB() {System.out.println("sayB");}
    }
    
    //测试类
    public class Test {public static void main(String[] args) {// 接口不能被实例化// A a = new A();// B b = new B();// 前两个为多态,后一个为普通对象A a = new C();B b = new C();C c = new C();// 接口中的属性都是常量,默认使用public static final修饰System.out.println(A.MY_NAME);System.out.println(B.MY_NAME);
    //        System.out.println(C.MY_NAME);//会报错,因为C中并没有常量MY_NAME// 接口中的方法都是抽象方法,默认使用public abstract修饰a.sayA();//a中只有sayA()方法,没有sayB()b.sayB();//b中只有sayB()方法,没有sayA()c.sayA();//c中既有sayA()方法,也有sayB()}
    }
    /*
    result:ABsayAsayBsayAsayB
    */
    

接口的好处

  • 弥补了类单继承的不足,一个类同时可以实现多个接口。

  • 让程序可以面向接口编程,这样程序员就可以灵活方便的切换各种业务实现。

    //抽象父类
    abstract class people{public String name;public int age;public people(String name, int age) {this.name = name;this.age = age;}public abstract void say();
    }
    
    //driver接口
    interface Driver{void drive();
    }
    
    //cooker接口
    interface Cooker{void cook();
    }
    
    //实现
    class Student extends people implements Driver, Cooker{public Student(String name, int age) {super(name, age);}@Overridepublic void say() {System.out.println("我叫" + super.name + ",今年" + super.age + "岁,学生");}@Overridepublic void drive() {System.out.println("我叫" + super.name + ",今年" + super.age + "岁,司机");}@Overridepublic void cook() {System.out.println("我叫" + super.name + ",今年" + super.age + "岁,厨师");}
    }
    //测试
    public class Test {public static void main(String[] args) {Student student = new Student("张三", 18);student.say();student.drive();k();//使用接口来接受对象,可以更有效的标识对象,当前对象就是个司机,无法调用学生和厨师的方法Driver driver = new Student("李四", 20);driver.drive();
    //        driver.say();//会报错,因为是Driver类型,没有say方法Cooker cooker = new Student("王五", 22);k();}
    }
    /*
    result:我叫张三,今年18岁,学生我叫张三,今年18岁,司机我叫张三,今年18岁,厨师我叫李四,今年20岁,司机我叫王五,今年22岁,厨师
    */
    

案例

//实体类
public class Student {private String name;private char sex;private double score;public Student() {}public Student(String name, char sex, double score) {this.name = name;this.sex = sex;this.score = score;}public String getName() {return name;}public void setName(String name) {this.name = name;}public int getSex() {return sex;}public void setSex(char sex) {this.sex = sex;}public double getScore() {return score;}public void setScore(double score) {this.score = score;}@Overridepublic String toString() {return "name='" + name + ''' +", sex=" + sex +", score=" + score;}
}
//接口
public interface Studentoper {void printInfo(ArrayList<Student> students);void printAverageScore(ArrayList<Student> students);
}
//实现一,完成方案一
public class StudentoperImpl1 implements Studentoper{@Overridepublic void printInfo(ArrayList<Student> students) {for (Student student : students) {System.out.println(student);}}@Overridepublic void printAverageScore(ArrayList<Student> students) {double sum = 0;for (Student student : students) {sum += Score();}System.out.println("平均分为:" + sum / students.size());}
}
//实现二,完成方案二
public class StudentoperImpl2 implements Studentoper{@Overridepublic void printInfo(ArrayList<Student> students) {int countMan = 0;int countWoman = 0;for (Student student : students) {if (Sex() == '男')countMan++;elsecountWoman++;System.out.println(student);}System.out.println("男生人数:" + countMan);System.out.println("女生人数:" + countWoman);}@Overridepublic void printAverageScore(ArrayList<Student> students) {double max = (0).getScore();double min = (0).getScore();double sum = 0;for (Student student : students) {if (Score() > max)max = Score();if (Score() < min)min = Score();sum += Score();}double sum1 = sum - max - min;double avg = sum1/ (students.size() - 2);System.out.println("平均分为:" + avg);}
}
//操作类
public class ClassOper {private final ArrayList<Student> students = new ArrayList<>();//使用多态完成方案一private final Studentoper studentoper1 = new StudentoperImpl1();//使用多态完成方案二private final Studentoper studentoper2 = new StudentoperImpl2();//添加学生public ClassOper() {students.add(new Student("张三",'男' , 100));students.add(new Student("李四", '女', 99));students.add(new Student("王五",'男' , 98));students.add(new Student("赵六", '女', 97));students.add(new Student("田七",'女' , 96));students.add(new Student("周八", '女', 95));}public void printInfo(boolean flag) {System.out.println("---------------"+flag+"----------------");if (flag)studentoper1.printInfo(students);elsestudentoper2.printInfo(students);}public void printAverageScore(boolean flag) {if (flag)studentoper1.printAverageScore(students);elsestudentoper2.printAverageScore(students);}
}
//测试类
public class Test {public static void main(String[] args) {ClassOper classOper = new ClassOper();boolean flag = true;//如果更改为false,则使用StudentoperImpl2的实现classOper.printInfo(flag);classOper.printAverageScore(flag);}
}
/*
result:---------------true----------------name='张三', sex=男, score=100.0name='李四', sex=女, score=99.0name='王五', sex=男, score=98.0name='赵六', sex=女, score=97.0name='田七', sex=女, score=96.0name='周八', sex=女, score=95.0平均分为:97.5、---------------false----------------name='张三', sex=男, score=100.0name='李四', sex=女, score=99.0name='王五', sex=男, score=98.0name='赵六', sex=女, score=97.0name='田七', sex=女, score=96.0name='周八', sex=女, score=95.0男生人数:2女生人数:4平均分为:97.5
*/

接口的其他细节

JDK8之后接口中新增的三种方法
  • 默认方法,使用default修饰,可以被重写,但没必要
  • 私有方法,使用private修饰,只能在接口内部调用
  • 静态方法:使用static修饰
//接口
public interface A {/** 接口中的默认方法(实例方法)* 1. 必须使用default关键字修饰,默认被public修饰* 2. 接口中的默认方法,可以通过接口的实现类对象,直接调用* 3. 接口中的默认方法,也可以被接口的实现类进行覆盖重写*/default void method1(){System.out.println("A接口的默认方法");method2();}/** 接口中的私有方法(实例方法)* 1. 必须使用private关键字修饰* 2. 只能在接口中被调用* 3. JDK9之后才有私有方法,可以被接口中的默认方法或者静态方法调用*/private void method2() {System.out.println("A接口的私有方法");}/** 接口中的静态方法(类方法)* 1. 必须使用static关键字修饰,默认被public修饰* 2. 接口中的静态方法,可以通过接口名直接调用*/static void method3(){System.out.println("A接口的静态方法");}
}
//实现类
public class B implements A{
}
//测试类
public class Test {public static void main(String[] args) {A a = new B();a.method1();A.method3();}
}
/*
result:A接口的默认方法A接口的私有方法A接口的静态方法
*/
新增的三种方法的好处
  • 增加了接口的能力,更便于项目的扩展和使用
    • 在实际开发中,如果有100个类实现了某一个接口,那么当该接口需要新增一个功能时,对应的100个实现类都需要重写该新增的方法,那么工作量会很大,而现在只要使用新增的三种方法则只需要操作接口即可,实现类会自动继承新增的默认方法,也可通过接口直接调用新增的静态方法
接口的多继承

概念

  • 一个接口可以继承多个接口

好处

  • 便于实现类去实现接口

    • 比如A接口继承了B和C接口,那么实现类只需要实现A接口,就可以实现A,B,C三个接口,此时实现类必须重写A,B,C三个接口中的全部方法

      interface B{void m1();
      }
      interface C{void m2();
      }
      interface A extends B,C{void m3();
      }
      //只有同时实现A,B,C三个接口中的方法,才不会报错
      public class Test implements A{@Overridepublic void m1() {}@Overridepublic void m2() {}@Overridepublic void m3() {}
      }
      
接口其他注意事项(了解)
  • 一个接口继承多个接口,如果多个接口中存在方法签名不一致,则此时不支持多继承。

    • 此时当前接口不知道应该继承哪个方法
  • 一个接口继承多个接口,如果多个接口中存在方法签名一致,则此时只需要重写一次即可,支持多继承

    //一个接口继承多个接口,如果多个接口中存在方法签名不一致,则此时不支持多继承。
    interface D{void test();
    }
    interface E{String test();
    }
    //会报错,无法继承
    interface F extends D,E{
    }
    
    //一个接口继承多个接口,如果多个接口中存在方法签名一致,则此时只需要重写一次即可,支持多继承
    public class Test1 implements C{@Overridepublic void test() {System.out.println("test");}}
    interface A{void test();
    }
    interface B{void test();
    }
    interface C extends A,B{}
    
  • 一个类实现多个接口,如果多个接口中存在方法签名冲突,则此时不支持多实现。

    //一个类实现多个接口,如果多个接口中存在方法签名冲突,则此时不支持多实现。
    public class Test2 implements A1,B1{}
    //会报错
    interface A1{void m1();
    }
    interface B1{void m1(String s);
    }
    
  • 一个类继承了父类,又同时实现了接口,父类中和接口中有同名的默认方法,实现类会优先用父类的。

    //一个类继承了父类,又同时实现了接口,父类中和接口中有同名的默认方法,实现类会优先用父类的。
    public class Test3 {public static void main(String[] args) {T3 t3 = new T3();t3.m1();//没有重写的情况下,优先使用父类的方法}
    }class T1 {public void m1() {System.out.println("T1.m1");}
    }
    interface T2 {default void m1() {System.out.println("T2.m1");}
    }
    class T3 extends T1 implements T2 {
    }
    /*
    result:T1.m1	
    */
    
  • 一个类实现了多个接口,多个接口中存在同名的默认方法,可以不冲突,这个类重写该方法即可。

    //一个类实现了多个接口,多个接口中存在同名的默认方法,可以不冲突,这个类重写该方法即可。
    public class Test4 {public static void main(String[] args) {C3 c3 = new C3();c3.hi();}
    }
    interface C1{void hi();
    }
    interface C2{void hi();
    }
    class C3 implements C1,C2{@Overridepublic void hi() {System.out.println("hi");}
    }
    

}
//会报错
interface A1{
void m1();
}
interface B1{
void m1(String s);
}


- 一个类继承了父类,又同时实现了接口,父类中和接口中有同名的默认方法,实现类会优先用父类的。```java
//一个类继承了父类,又同时实现了接口,父类中和接口中有同名的默认方法,实现类会优先用父类的。
public class Test3 {public static void main(String[] args) {T3 t3 = new T3();t3.m1();//没有重写的情况下,优先使用父类的方法}
}class T1 {public void m1() {System.out.println("T1.m1");}
}
interface T2 {default void m1() {System.out.println("T2.m1");}
}
class T3 extends T1 implements T2 {
}
/*
result:T1.m1	
*/
  • 一个类实现了多个接口,多个接口中存在同名的默认方法,可以不冲突,这个类重写该方法即可。

    //一个类实现了多个接口,多个接口中存在同名的默认方法,可以不冲突,这个类重写该方法即可。
    public class Test4 {public static void main(String[] args) {C3 c3 = new C3();c3.hi();}
    }
    interface C1{void hi();
    }
    interface C2{void hi();
    }
    class C3 implements C1,C2{@Overridepublic void hi() {System.out.println("hi");}
    }
    

本文发布于:2024-01-31 21:01:35,感谢您对本站的认可!

本文链接:https://www.4u4v.net/it/170670609731326.html

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。

标签:多态
留言与评论(共有 0 条评论)
   
验证码:

Copyright ©2019-2022 Comsenz Inc.Powered by ©

网站地图1 网站地图2 网站地图3 网站地图4 网站地图5 网站地图6 网站地图7 网站地图8 网站地图9 网站地图10 网站地图11 网站地图12 网站地图13 网站地图14 网站地图15 网站地图16 网站地图17 网站地图18 网站地图19 网站地图20 网站地图21 网站地图22/a> 网站地图23