多态
1,定义
多态是同一个行为具有多个不同表现形式或形态的能力。
多态就是同一个接口,使用不同的实例而执行不同操作。
它是指在父类中定义的属性和方法被子类继承之后,可以具有不同的数据类型或表现出不同的行为,这使得同一个属性或方法在父类及其各个子类中具有不同的含义。
多态就是指程序中定义的引用变量所指向的具体类型和通过该引用变量发出的方法调用在编程时并不确定,而是在程序运行期间才确定,即一个引用变量倒底会指向哪个类的实例对象,该引用变量发出的方法调用到底是哪个类中实现的方法,必须在由程序运行期间才能决定。因为在程序运行时才确定具体的类,这样,不用修改源程序代码,就可以让引用变量绑定到各种不同的类实现上,从而导致该引用调用的具体方法随之改变,
即不修改程序代码就可以改变程序运行时所绑定的具体代码,让程序可以选择多个运行状态,这就是多态性。
2,Java多态的作用
- 消除类型直接的耦合关系,使代码更加清晰明了。
- 消除类型之间的耦合关系
- 可替换性
- 可扩充性
- 接口性
- 灵活性
- 简化性
3,Java多态的具体理解
简单的理解,就是用一个父类就能够引用不同的子类,这就是多态。 比如说我的一个Shape(父类),既可以是Triangle(子类),也可以是Square(子类),这样子的话,Shape呈现出了不同的形态,也是多态。
[(https://cdn.breakyizhan.com/wp-content/uploads/2019/05/JavaMind8_1-1.png)]
下面我们来看一个简单的例子:
Shape s = new Triangle();
s.draw();
上面的例子创建了一个指向子类的父类引用类型,调用的也是方法,会先调用父类Shape的方法,然后再是Triangle里面的方法。这是Java的一个“后期绑定”机制,只有在运行的时候,判断对象的类型,然后调用对应的方法。
创建一个指向子类的父类类型引用,该引用既可以处理父类Father对象,也可以处理子类Child对象,当相同的消息发送给子类或者父类对象时,该对象就会根据自己所属的引用而执行不同的行为,这就是多态
4.Java多态的实现条件
要想实现Java的多态,必须要有下面三种条件:
-
继承 (Java中的继承)
-
重写 (在Java中的重写)
-
向上转型 (Java的向上转型和向下转型)
- 向上转型:子类引用的对象转换为父类类型称为向上转型。通俗地说就是是将子类对象转为父类对象。此处父类对象可以是接口。
- 向下转型:父类引用的对象转换为子类类型称为向下转型。
通俗点来说,就是父类类型和子类类型的互相转换,但是子类是继承了父类的所有成员,而父类少了子类的一些成员(新建的一些成员),所以在向上转型的时候还要注意到这一点。
实例操作:
向上转型可能会发生的错误是父类用了子类里面有,而父类里面没有的方法
public class Animal {
public void eat(){
System.out.println("animal is eatting...");
}
}
class Fish extends Animal{
public void eat(){
System.out.println("Fish is eatting...");
}
public void swim(){
System.out.println("Fish is swimming...");
}
}
class Main{
public static void main(String[] args) {
Animal f=new Fish();
f.eat();
f.swim();
}
向下转型的话,如果父类引用的对象是父类本身,编译不会出错,但是运行时会出现java.lang.ClassCastException错误。它可以使用instanceof来避免出错此类错误。
public class Animal {
public void eat(){
System.out.println("animal is eatting...");
}
}
class Fish extends Animal{
@Overwrite
public void eat(){
System.out.println("Fish is eatting...");
}
public void swim(){
System.out.println("Fish is swimming...");
}
}
class main{
public static void main(String[] args) {
Animal f=new Fish();
f.eat();
Fish f2=(Fish)f;
f2.eat();
f2.swim();
Animal a = new Animal();
if(f3 instanceof Fish){
Fish f3=(Fish)a;
f3.eat();
f3.swim();
}
}
总结:
- 向上转型时,子类中新定义的成员方法会丢失。就像Animal访问不到swim的方法一样
- 子类引用不能指向父类对象。Fish c = (Fish)new Animal()这样是不行的。但是父类引用可以指向子类对象
- 向上转型的好处是减少重复代码,使代码变得简洁和提高系统扩展性,符合Java的面向对象的过程。
- 向下转型的前提是父类对象指向的是子类对象(也就是说,在向下转型之前,它得先向上转型)或者用instanceof,这样才会正确
- 向下转型只能转型为本类对象(鱼是不能变成鸟的)。
instanceof关键字可以判断对象是否是特定类的一个实例
instanceof是一个关键字,instanceof 运算符是用来在运行时指出对象是否是特定类的一个实例。instanceof通过返回一个布尔值来指出,这个对象是否是这个特定类或者是它的子类的一个实例。
以下是显示instanceof不同行为的Java程序。
5.Java多态的实现方式
多态的实现方式也有两种:基于继承实现和基于接口实现
1.基于继承实现的多态
基于继承的实现的多态主要表现在,父类可以对多个子类重写的方法,表现出来。我们来看一个例子:
public class Shape {
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Shape(){
}
public String draw(){
return "画的是 " + getName();
}
public String toString(){
return null;
}
}
public class Circle extends Shape{
public Circle(){
setName("Circle");
}
public String draw(){
return "画的是 " + getName();
}
public String toString(){
return "Shape : " + getName();
}
}
public class Triangle extends Shape{
public Triangle(){
setName("Triangle");
}
public String draw(){
return "画的是 " + getName();
}
public String toString(){
return "Shape : " + getName();
}
}
public class Test {
public static void main(String[] args) {
Shape[] Shapes = new Shape[2];
Circle Circle = new Circle();
Triangle Triangle = new Triangle();
Shapes[0] = Circle;
Shapes[1] = Triangle;
for(int i = 0 ; i < 2 ; i++){
System.out.println(Shapes[i].toString() + "--" + Shapes[i].draw());
}
System.out.println("-------------------------------");
}
}
2.基于接口实现的多态
基于接口实现的多态是通过实现接口并覆盖接口中同一方法的几不同的类体现的。我们可以来看一个例子:
interface Animal{
public void eat();
public void walk();
}
class cat implements Animal{
public void eat(){
System.out.println("猫在吃!!");
}
public void walk(){
System.out.println("猫在走!!");
}
}
class dog implements Animal{
public void eat(){
System.out.println("狗在吃!!");
}
public void walk(){
System.out.println("狗在走!!");
}
}
class Demo3
{
public static void main(String[] args)
{
Animal d = new dog();
d.eat();
d.walk();
}
}
Java的多态:重写
在 Java 中,当设计类时,被重写的方法的行为怎样影响多态性。
我们已经讨论了方法的重写,也就是子类能够重写父类的方法。
当子类对象调用重写的方法时,调用的是子类的方法,而不是父类中被重写的方法。
要想调用父类中被重写的方法,则必须使用关键字 super。
public class Employee {
private String name;
private String address;
private int number;
public Employee(String name, String address, int number) {
System.out.println("Employee 构造函数");
this.name = name;
this.address = address;
this.number = number;
}
public void mailCheck() {
System.out.println("邮寄支票给: " + this.name
+ " " + this.address);
}
public String toString() {
return name + " " + address + " " + number;
}
public String getName() {
return name;
}
public String getAddress() {
return address;
}
public void setAddress(String newAddress) {
address = newAddress;
}
public int getNumber() {
return number;
}
}
假设下面的类继承Employee类:
public class Salary extends Employee
{
private double salary;
public Salary(String name, String address, int number, double salary) {
super(name, address, number);
setSalary(salary);
}
public void mailCheck() {
System.out.println("Salary 类的 mailCheck 方法 ");
System.out.println("邮寄支票给:" + getName()
+ " ,工资为:" + salary);
}
public double getSalary() {
return salary;
}
public void setSalary(double newSalary) {
if(newSalary >= 0.0) {
salary = newSalary;
}
}
public double computePay() {
System.out.println("计算工资,付给:" + getName());
return salary/52;
}
}
现在我们仔细阅读下面的代码,尝试给出它的输出结果:
public class VirtualDemo {
public static void main(String [] args) {
Salary s = new Salary("员工 A", "北京", 3, 3600.00);
Employee e = new Salary("员工 B", "上海", 2, 2400.00);
System.out.println("使用 Salary 的引用调用 mailCheck -- ");
s.mailCheck();
System.out.println("\n使用 Employee 的引用调用 mailCheck--");
e.mailCheck();
}
}
以上实例编译运行结果如下:
Employee 构造函数
Employee 构造函数
使用 Salary 的引用调用 mailCheck --
Salary 类的 mailCheck 方法
邮寄支票给:员工 A ,工资为:3600.0
使用 Employee 的引用调用 mailCheck--
Salary 类的 mailCheck 方法
邮寄支票给:员工 B ,工资为:2400.0
例子解析
- 实例中,实例化了两个 Salary 对象:一个使用 Salary 引用 s,另一个使用 Employee 引用 e。
- 当调用 s.mailCheck() 时,编译器在编译时会在 Salary 类中找到 mailCheck(),执行过程 JVM 就调用 Salary 类的 mailCheck()。
- 因为 e 是 Employee 的引用,所以调用 e 的 mailCheck() 方法时,编译器会去 Employee 类查找 mailCheck() 方法 。
- 在编译的时候,编译器使用 Employee 类中的 mailCheck() 方法验证该语句, 但是在运行的时候,Java虚拟机(JVM)调用的是 Salary 类中的 mailCheck() 方法。
以上整个过程被称为虚拟方法调用,该方法被称为虚拟方法。
Java中所有的方法都能以这种方式表现,因此,重写的方法能在运行时调用,不管编译的时候源代码中引用变量是什么数据类型。
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)