多态就是对象的多种形态,即相同的消息使得不同的类做出不同的响应。
多态存在的三个必要条件:继承、重写、父类变量引用子类对象(向上转型)。
实现多态的技术称为动态绑定,是指在执行期间判断所引用对象的实际类型,根据其实际的类型调用其相应的方法。
Java中多态的实现方式:接口实现,继承父类进行方法重写,同一个类中进行方法重载
对象和对象引用
new关键字
创建一个Java对象需要三部:声明引用变量、实例化、初始化对象实例。
①声明引用变量
声明一个变量来指向一个对象,即引用
要声明一个变量,需要写:Animal animal;
这将告诉编译器你将使用animal引用一个Animal类型的对象。用一个原始变量,这个声明也保留了适当的内存量的变量。
②实例化
new运算符实例化一个类对象,通过给这个对象分配内存并返回一个指向该内存的引用。
“实例化一个类的对象”的意思就是“创建对象”。创建对象时,你正在创造一个类的“实例”,因而“实例化”一个类的对象。
③初始化对象实例
就是调用构造方法,对类的实例数据赋初值。构造函数的名称提供了需要实例化类的名称。
对象
对象是类的一个实例,创建对象的过程也叫做类的实例化。
如人类是一个类,那么我们每一个具体的人就是这个类的对象。
对象引用
我们知道new关键字是一个java运算符,它用来创建对象
Animal animal; //声明一个变量来指向一个对象,即引用
animal = new A animal();
如上代码,创建对象的语句是new Animal(),那么animal又是什么呢?animal只是一个引用,是指向一个对象的引用,这个对象指向Animal类。也就是说,Animal animal 这句话只是声明了一个Animal类的引用,它可以指向任何Animal类的实例。
一个引用可以指向多个对象,而一个对象也可以被多个引用所指。
引用多态
父类的引用可以指向本类的对象 Animal animal=new Animal();
父类的引用可以指向子类的对象 Animal animal=new Dog();
注意:父类的引用指向子类的对象时,子类中特有的方法和属性就不能使用了。
方法多态
创建本类对象时,调用的方法为本类方法
创建子类对象时,调用的方法为子类重写的方法或者继承的方法(不能为子类特有的)
类型转换
多态中的类型转换是基于存在继承关系的对象,有向上类型转换和向下类型转换。
向上转型:父类的引用可以指向子类的对象
如 Animal animal=new Dog(); //Animal类是Dog类的父类
Dog对象实例被向上转型成了animal,但Dog对象实例本质上还是Dog类型,只不过能力被暂时削弱了
向下转型:向上转型之后子类特有的方法和属性就不能被调用了,有时我们想用这些方法和属性,就需要将向上转型后的子类对象再转成子类,调用子类的方法
如 Dog dog=(Dog)animal;
animal的引用仍然是Animal类型,只不过功能增强了,然后交给dog引用
注:不能直接将父类对象强制转换为子类类型,只能将向上转型后的子类对象在此转为子类类型。
向上转型就好比把茶杯里的水往茶壶中倒,是安全的;向下转型就是把茶壶里的水往茶杯里倒,会存在风险,instanceof运算符就可以避免类型转换的安全问题了。
instanceof就是去判断向下转型的对象将要转换的类型是不是这个对象的类型
如:Animal animal = new Dog();//父类引用指向了子类的对象
Dog dog = (Dog)animal;//将子类对象的引用类型转成子类,用一个子类类型的引用来接收
Cat cat = (Cat)animal;//编译器不报错,但是animal是Dog类的对象,在运行时会报错,此时需要用instanceof去判断一下
instanceof运算符用法:左边的操作元是一个对象,右边是一个类。如果左边的对象是右边类的实例,该运算符运算的结果是true,否则是false
注意:一个类的实例包括本身的实例,以及所有直接或间接子类的实例
instanceof左边对象的类型(注意是它引用的对象的类型,不是变量的类型)与右边必须是同种类或是继承关系
class Animal{ }
class Dog extends Animal{ }
public class Test {
public static void main(String[] args) {
Animal animal=new Dog(); //向上转型
if(animal instanceof Dog){
Dog dog=(Dog)animal; //向下转型
}
else
System.out.println("不能转换");
}
}
一个经典的例子
public class A {
public String show(D obj) {
return ("A and D");
}
public String show(A obj) {
return ("A and A");
}
}
public class B extends A{
public String show(B obj){
return ("B and B");
}
public String show(A obj){
return ("B and A");
}
}
public class C extends B{ }
public class D extends B{ }
public class Test {
public static void main(String[] args) {
A a1 = new A();
A a2 = new B();
B b = new B();
C c = new C();
D d = new D();
System.out.println("1--" + a1.show(b));
System.out.println("2--" + a1.show(c));
System.out.println("3--" + a1.show(d));
System.out.println("4--" + a2.show(b));
System.out.println("5--" + a2.show(c));
System.out.println("6--" + a2.show(d));
System.out.println("7--" + b.show(b));
System.out.println("8--" + b.show(c));
System.out.println("9--" + b.show(d));
}
}
运行结果:
1--A and A
2--A and A
3--A and D
4--B and A
5--B and A
6--A and D
7--B and B
8--B and B
9--A and D
抽象类和接口
1.抽象类
当父类只知道子类的方法而不知道子类的方法如何实现时可以将子类定义为抽象类,若多个类都有一个共同的方法,则可以将它抽象成一个抽象类
抽象方法是一种特殊的方法,它只有声明,没有具体的实现(即方法体),以分号结束
如:abstract void fun();
如果一个类含有抽象方法,那这个类就是抽象类
抽象类中可以有普通方法和抽象方法
抽象类和抽象方法前面必须都有abstract修饰,因为抽象类中没有具体实现的方法,所以抽象类不能被实例化,实例化的工作交由子类去完成,它只需要有一个引用即可
抽象类就是为了继承实现的,如果一个类继承了抽象类,那么子类必须实现父类的抽象方法,如果子类没有继承父类的抽象方法,那么子类就必须也定义为抽象类
抽象方法必须为public或protected,如果没有访问控制符的话,默认为public
如果子类也是一个抽象类的话,可以不用实现父类的抽象方法,如果子类不是一个抽象类的话,就必须实现父类的抽象方法
2.接口
一个类只能extends一个父类,但可以implements多个接口,java通过接口的概念来实现c++中的多继承。一个接口可以同时extends多个接口,却不能implements任何接口,因而,Java中的接口是支持多继承的。
接口是由全局常量和公共的抽象方法所组成。
接口是要被继承实现的,所以不能用private和protected修饰,一般用public
接口中可以含有变量和方法,但接口中的变量会被隐式的指定为public static final(并且只能是public static final),因为接口中的属性是常量。方法会被隐式的指定为public abstract(并且只能是public abstract),因为接口中所有的方法不能有具体的实现,即这些方法必须都为抽象方法
允许一个类遵循多个接口
如果一个非抽象类遵循了某个接口,则这个类必须实现接口中的所有方法,如果一个抽象类遵循了某个接口。则可以不实现该接口中的方法
3.抽象类和接口的区别
抽象类中可以有普通的方法,接口中只能有抽象方法
抽象类中的成员变量可以使各种类型的,而接口中必须是public static final型的
接口中不能含有静态代码块以及静态方法,抽象类中可以有
一个类只能继承一个抽象类,而一个类可以遵循多个接口