目录标题
对象多态性
多态性在面向对象中是最重要的,在java中面向对象多态性主要有以下两种主要体现:
1,方法的重载与覆写;
2,对象的多态性;
面向对象三个特性:
封装,是为了保护类中的属性不被外部直接访问到;
继承,是为了扩展类的功能;
多态,方法的重载、覆写;对象的多态;
对象的多态性,主要分为向上转型和向下转型。
面向对象向上转型、向下转型
向上转型
向上转型:子类对象用父类接收;对于向上转型程序会自动完成;
父类 父类对象 = 子类实例;
向下转型:父类对象转为子类对象,对于向下转型必须明确指明要转型的子类类型。
子类 子类对象 = (子类)父类实例;
class A{ // 定义类A
public void fun1(){ // 定义fun1()方法
System.out.println("A --> public void fun1(){}") ;
}
public void fun2(){
this.fun1() ; // 调用fun1()方法
}
};
class B extends A{
public void fun1(){ // 此方法被子类覆写了
System.out.println("B --> public void fun1(){}") ;
}
public void fun3(){
System.out.println("B --> public void fun3(){}") ;
}
};
public class PolDemo01{
public static void main(String asrgs[]){
B b = new B() ; // 实例化子类对象
b.fun1() ; // 此方法被子类覆写过
}
};
fun1()被子类覆写过,所以子类对象调用的时候,肯定是调用了覆写过的方法。
class A{ // 定义类A
public void fun1(){ // 定义fun1()方法
System.out.println("A --> public void fun1(){}") ;
}
public void fun2(){
this.fun1() ; // 调用fun1()方法
}
};
class B extends A{
public void fun1(){ // 此方法被子类覆写了
System.out.println("B --> public void fun1(){}") ;
}
public void fun3(){
System.out.println("B --> public void fun3(){}") ;
}
};
public class PolDemo01{
public static void main(String asrgs[]){
B b = new B() ; // 实例化子类对象
A a = b ; // 向上转型关系
a.fun1() ; // 此方法被子类覆写过
}
};
进行向上转型,运行后可以发现,子类对象向上转型成父类对象后,调用的方法依然是子类覆写过的方法。
class A{ // 定义类A
public void fun1(){ // 定义fun1()方法
System.out.println("A --> public void fun1(){}") ;
}
public void fun2(){
this.fun1() ; // 调用fun1()方法
}
};
class B extends A{
public void fun1(){ // 此方法被子类覆写了
System.out.println("B --> public void fun1(){}") ;
}
public void fun3(){
System.out.println("B --> public void fun3(){}") ;
}
};
public class PolDemo01{
public static void main(String asrgs[]){
B b = new B() ; // 实例化子类对象
A a = b ; // 向上转型关系
a.fun1() ; // 此方法被子类覆写过
a.fun2() ;
}
};
调用父类的fun2()方法,fun2()中去调用的fun1()依然是子类覆写过的fun1()。
结论:
可以发现,通过子类进行父类对象的实例化操作,即对象发生向上转型之后,如果调用的方法被子类覆写过,则调用的肯定都是子类中覆写过的方法,但是当方法没有被覆写过,则调用的就是父类中继承过来的方法,也就是从父类中去找的方法。
注意点:
转型后,因为操作的是父类对象,所以是无法找到在子类中定义的新方法的。
class A{ // 定义类A
public void fun1(){ // 定义fun1()方法
System.out.println("A --> public void fun1(){}") ;
}
public void fun2(){
this.fun1() ; // 调用fun1()方法
}
};
class B extends A{
public void fun1(){ // 此方法被子类覆写了
System.out.println("B --> public void fun1(){}") ;
}
public void fun3(){
System.out.println("B --> public void fun3(){}") ;
}
};
public class PolDemo01{
public static void main(String asrgs[]){
B b = new B() ; // 实例化子类对象
A a = b ; // 向上转型关系
a.fun1() ; // 此方法被子类覆写过
a.fun3() ;
}
};
向上转型以后,找不到子类中定义的新方法。
向下转型
将父类对象变为子类对象,称为向下转型。向下转型需要采用强制的手段。
class A{ // 定义类A
public void fun1(){ // 定义fun1()方法
System.out.println("A --> public void fun1(){}") ;
}
public void fun2(){
this.fun1() ; // 调用fun1()方法
}
};
class B extends A{
public void fun1(){ // 此方法被子类覆写了
System.out.println("B --> public void fun1(){}") ;
}
public void fun3(){
System.out.println("B --> public void fun3(){}") ;
}
};
public class PolDemo02{
public static void main(String asrgs[]){
A a = new B() ; // 向上转型关系
B b = (B)a ; // 发生了向下转型关系
b.fun1() ;
b.fun2() ;
b.fun3() ;
}
};
类B中存在3个方法,所以都可以调用到。
在进行对象向下转型操作之前,有一股注意点:
class A{ // 定义类A
public void fun1(){ // 定义fun1()方法
System.out.println("A --> public void fun1(){}") ;
}
public void fun2(){
this.fun1() ; // 调用fun1()方法
}
};
class B extends A{
public void fun1(){ // 此方法被子类覆写了
System.out.println("B --> public void fun1(){}") ;
}
public void fun3(){
System.out.println("B --> public void fun3(){}") ;
}
};
public class PolDemo03{
public static void main(String asrgs[]){
A a = new A() ; // 实例化了一个父类对象
B b = (B)a ; // 发生了向下转型关系
b.fun1() ;
b.fun2() ;
b.fun3() ;
}
};
编译时候没有任何的语法问题,但是在执行的时候报错,一个既空指针异常之后的又一个非常经典的错误,类型转换异常:
此异常是在对象强转的时候会出现,如果两个没有关系的对象,发生转换关系则肯定出现此异常。
分析:
仅仅知道A类的情况下,并不知道谁是它的子类;
但是知道B类的情况下,就可以通过B extends A,知道B是A的子类,这样,就知道了A和B两个类是有关系的;
发生向下转型之前已经发生了向上转型,然后再进行了向下转型,才不会有任何问题;例如:A a = new B() ; B b = (B)a ;
当直接进行向下转型,肯定是会报类型转换异常的。比如:A a = new A() ; B b = (B)a ;
设计一个方法,可以接收A类的任意子类对象。
方案一:不使用对象多态性,使用重载实现:
class A{ // 定义类A
public void fun1(){ // 定义fun1()方法
System.out.println("A --> public void fun1(){}") ;
}
public void fun2(){
this.fun1() ; // 调用fun1()方法
}
};
class B extends A{
public void fun1(){ // 此方法被子类覆写了
System.out.println("B --> public void fun1(){}") ;
}
public void fun3(){
System.out.println("B --> public void fun3(){}") ;
}
};
class C extends A{
public void fun1(){ // 此方法被子类覆写了
System.out.println("C --> public void fun1(){}") ;
}
public void fun5(){
System.out.println("C --> public void fun5(){}") ;
}
};
public class PolDemo04{
public static void main(String asrgs[]){
fun(new B()) ; // 传递B的实例
fun(new C()) ; // 传递B的实例
}
public static void fun(B b){
b.fun1() ; // 调用覆写父类中的fun1()方法
}
public static void fun(C c){
c.fun1() ; // 调用覆写父类中的fun1()方法
}
};
重载思路方案也是有局限性,没增加一个子类,fun()方法就得重载一次,如果有成百上千的子类的话,就得重载成百上千个方法了。
为了解决这样的局限性,就得使用对象多态性完成。
方案二:对象多态性实现:
class A{ // 定义类A
public void fun1(){ // 定义fun1()方法
System.out.println("A --> public void fun1(){}") ;
}
public void fun2(){
this.fun1() ; // 调用fun1()方法
}
};
class B extends A{
public void fun1(){ // 此方法被子类覆写了
System.out.println("B --> public void fun1(){}") ;
}
public void fun3(){
System.out.println("B --> public void fun3(){}") ;
}
};
class C extends A{
public void fun1(){ // 此方法被子类覆写了
System.out.println("C --> public void fun1(){}") ;
}
public void fun5(){
System.out.println("C --> public void fun5(){}") ;
}
};
public class PolDemo05{
public static void main(String asrgs[]){
fun(new B()) ; // 传递B的实例
fun(new C()) ; // 传递B的实例
}
public static void fun(A a){
a.fun1() ; // 调用覆写父类中的fun1()方法
}
};
父类对象接收子类实例;这样,不管有多少个子类,都可以轻松完成,不用再重载方法。
面向对象转型的限制
向上转型:自动完成。
向下转型:强制手段进行,发生向下转型之前,必须先发生向上的转型关系。
对象多态性可以解决方法接收参数的问题。
instanceof
instanceof关键字的作用及使用时机
在java中可以使用instanceof关键字判断,一个对象是哪个类的实例。
对象 instanceof 类 ---> 返回一个boolean类型
class A{ // 定义类A
public void fun1(){
System.out.println("A --> public void fun1(){}") ;
}
public void fun2(){
this.fun1() ; // 调用fun1()方法
}
};
class B extends A{
public void fun1(){
System.out.println("B --> public void fun1(){}") ;
}
public void fun3(){
System.out.println("B --> public void fun3(){}") ;
}
};
public class InstanceofDemo01{
public static void main(String asrgs[]){
A a1 = new B() ;
System.out.println("A a1 = new B() " + (a1 instanceof A)) ;
System.out.println("A a1 = new B() " + (a1 instanceof B)) ;
A a2 = new A() ;
System.out.println("A a2 = new A() " + (a2 instanceof A)) ;
System.out.println("A a2 = new A() " + (a2 instanceof B)) ;
}
};
a1是A的实例,也是B的实例;
a2是A的实例,但是并不是B的实例,所以a2是无法发生向下转型的,因为a2和类B是没有关系的。
使用instanceof对对象的转型进行安全验证
例子:
A类是父类,B是A的子类,C也是A的子类;判断,如果是B类则调用B类覆写过的fun1()方法后调用B类中新定义的fun3()方法;如果是C类则掉员工C类覆写过的fun1()方法然后调用C类中新定义的fun5()方法。
(对象发生向上转型以后,就相当于是父类对象了,对象只能调用子类覆写过的方法,对于子类中新定义的方法是无法访问到的)
class A{ // 定义类A
public void fun1(){ // 定义fun1()方法
System.out.println("A --> public void fun1(){}") ;
}
public void fun2(){
this.fun1() ; // 调用fun1()方法
}
};
class B extends A{
public void fun1(){ // 此方法被子类覆写了
System.out.println("B --> public void fun1(){}") ;
}
public void fun3(){
System.out.println("B --> public void fun3(){}") ;
}
};
class C extends A{
public void fun1(){ // 此方法被子类覆写了
System.out.println("C --> public void fun1(){}") ;
}
public void fun5(){
System.out.println("C --> public void fun5(){}") ;
}
};
public class InstanceofDemo02{
public static void main(String asrgs[]){
fun(new B()) ;
fun(new C()) ;
}
public static void fun(A a){
a.fun1() ;
if(a instanceof B){
B b = (B) a ;
b.fun3() ;
}
if(a instanceof C){
C c = (C) a ;
c.fun5() ;
}
}
};
在开发中对于向下转型,最好增加验证,保证转型时不发生ClassCastException。
如果现在增加A的这类,则就需要修改fun()方法,这样一来,程序就失去了灵活性和可维护性。问题出在哪里?父类的设计不合理,需要把父类重新设计,否则开发中会非常难以维护。
开发设计原则:
一个类永远不要去继承一个已经实现好的类,而是去继承抽象类或者实现接口。
父类一般都设计成抽象类或者接口。