面向对象的三大特征:封装、继承、多态
在讲多态前先了解一些关于继承的Java基础知识:
*继承:就是一个类(该类被称为子类)可以继承另一个类(该类被称为父类)。
*继承“基本”作用是:代码复用。但是继承最“重要”的作用是:有了继承才有了以后“方法的覆盖”和“多态机制”。
*java语言当中的继承只支持单继承【一个类只允许继承一个父类】
*在一个子类被创建的时候,内存中首先会加载父类,然后在父类对象外部放上子类特有的属性,两者构成一个子类对象,子类虽然可以继承父类中所有的属性和方法【!!!包括private修饰的属性和方法】,但是子类也只是拥有父类中使用private修饰符修饰的属性和方法,
却不能直接使用它。但是父类往往会提供对外开放的接口,供子类访问这些private修饰的属性和方法(比如:getter和setter方法)。同时子类可以对继承的方法进行重写(@override),并且也可以拥有自己独有的方法。
一、向上转型:多态
如何理解多态?
->实现代码的通用性
->比如:Object类中定义的public void equals(Object obj){ }
往往自定义的类在使用 equals()方法时都需要重写,这里就体现了多态
public class Customer {
String name;
int id;
// 下面的equals(Object obj)方法是手动写的,不是系统自动生成的,可能会有不完善的地方,
//但是不影响理解多态
@Override
public boolean equals(Object obj) {
// TODO Auto-generated method stub
return super.equals(obj);
if (this == obj) {
return true;
}
if (obj instanceof Customer) {
Customer customer = (Customer) obj;
// 比较,两个对象的每个属性是否都相同
return this.id == customer.id && this.name.equals(customer.name);
}
return false;
}
}
//创建Customer对象
Customer cust1 = new Customer("wjk", 1002);
Customer cust2 = new Customer("wjk", 1002);
//使用equals()判断这两个对象是否相等
System.out.println(cust1.equals(cust2));
说明:上面的传入的cust2就使用了多态的思想,传给形参的是Object的子类(Customer)类型的对象
->JDBC:使用java程序操作(获取数据库连接、CRUD)数据库也使用到了向上转型知识
【数据库都有哪些?MySql、Oracle、DB2、SQL、Server】
->抽象类、接口的使用一定体现了多态性(因为抽象类、接口不能实例化】)
下面再举一个简单的上转型的例子:
class Animal {
public void move() {
System.out.println("动物会移动");
}
}
class Cat extends Animal {
public void move() {
System.out.println("小猫会捉老鼠");
}
public void craw(){
System.out.println("小猫会捉老鼠");
}
}
public static void main(String[] args){
Animal a1=new Cat();
a1.move();//小猫会捉老鼠
// a1.craw();//编译失败
}
说明:
* 1、Java程序永远都分为编译阶段和运行阶段
*
* 2、先分析编译阶段,再分析运行阶段,编译无法通过,根本是无法运行的
*
* 3、编译阶段编译器检查a1这个引用的数据类型为Animal.这个过程我们称为静态绑定【编译阶段绑定】
* 只有绑定成功之后才有后续的运行
*
* 4、在程序运行阶段,JVM堆内存当中真实创建的对象是Cat对象,那么以下程序在运行阶段一定会调用Cat对象的move()方法,
* 此时发生了程序的动态绑定,运行阶段绑定
*
* 5、无论是Cat类有没有重写move()方法,运行阶段一定调用的是Cat对象的move()方法,因为底层真实对象就是Cat对象
*
* 6、父类型引用指向子类型对象这种机制导致程序在编译阶段绑定和运行阶段绑定两种不同的形态/状态,
* 这种机制可以称为一种多态语法机制
*/
二、向下转型
1、为什么使用向下转型:有了对象的多态性以后,内存中实际上是加载了子类特有的属性和方法的,但是由于声明变量为父类类型,导致编译时,只能调用父类中声明的属性和方法。子类特有属性和方法不能调用。那么如何才能调用子类特有的属性和方法? -->使用向下转型
2、如何实现向下转型:
使用强制类型转换符()
3、使用时的注意事项:
->使用强转时可能出现java.lang.ClassCastException异常。
->怎么避免向下转型出现的ava.lang.ClassCastException呢?
* 使用instanceof运算符可以避免出现以上的异常
* instanceof运算符怎么用?a instacneof A:判断a是否是A类的实例
>如果返回true就进行向下转型。如果返回false,不进行向下转型
>要求a所属的类与类A必须时子类和父类的关系,否则编译阶段就会报错
class Animal{
public void move(){
}
}
class Cat extends Animal {
public void move(){
}
public void catchMouse() {
System.out.println("小猫会捉老鼠");
}
class Bird extends Animal {
public void move(){
}
public void fly() {
System.out.println("鸟儿会飞翔");
}
}
Animal a= new Bird();
if(a instanceof Cat) {
//下转型
Cat c = (Cat)a;
//调用子类中特有的方法,才需要进行强制类型转换
c.catchMouse();
}else if (a instanceof Bird) {
//调用子类中特有的方法,才需要进行强制类型转换
Bird b=(Bird)a;
b.fly();
}
4、Java规范中要求:在进行强制类型转换之前,建议采用instanceof运算符进行判断,避免出现ClassCastException异常。
软件开发很重要的一点:
降低程序的耦合度【低耦合】,提高程序的扩展力