11.多态

    1.对象的多态
1.向上转型
平行四边形是特殊的四边形,也就是说平行四边形是四边形的一种,那么就可以将平行四边形对象看作是一个四边形对象。鸡是家禽的一种,而家禽是动物中的一类,那么可以将鸡对象看作是一个动物对象。
class 四边形{
public static void draw(四边形 q){  //成员方法,这里的四边形可以是正方形,梯型类对象等一系列对象)
//something
}
}
public class 平行四边形 extends 四边形{
public static void main(String args[]){
平行四边形 p=new 平行四边形();
draw(p);//将四边形对象看作是四边形对象 
四边形 obj=new 平行四边形()
}}
就是把子类对象赋值给父类类型的变量,这种技术是”向上转型"此时obj对象既可以调用平行四边形从四边形中继承到的方法,也可以调用平行四边形复写四边形的方法,但是作为向上转的的代价丢失了和父类不一样的方法。而且调用的方法是子类复写的方法
package a.b;
public class A {
public void a1() {
       System.out.println("Superclass");
}
}
A的子类B:
package a.b;
public class B extends A {
public void a1() {
       System.out.println("Childrenclass"); //覆盖父类方法
}
       public void b1(){} //B类定义了自己的新方法
}
C类:
package a.b;
public class C {
public static void main(String[] args) {
       A a = new B(); //向上转型
       a.a1();
}
}
如果运行C,输出的是Superclass 还是Childrenclass?不是你原来预期的Superclass,而是Childrenclass。这是因为a实际上指向的是一个子类对象。当然,你不用担心,Java虚拟机会自动准确地识别出究竟该调用哪个具体的方法。不过,由于向上转型,a对象会遗失和父类不同的方法,例如b1()。有人可能会提出疑问:这不是多此一举吗?我们完全可以这样写:
B a = new B();
a.a1();
确实如此!但这样就丧失了面向抽象的编程特色,降低了可扩展性。其实,不仅仅如此,向上转型还可以减轻编程工作量。来看下面的显示器类Monitor:
package a.b;
public class Monitor{
public void displayText() {}
public void displayGraphics() {}
}
液晶显示器类LCDMonitor是Monitor的子类:
package a.b;
public class LCDMonitor extends Monitor {
public void displayText() {
       System.out.println("LCD display text");
}
public void displayGraphics() {
       System.out.println("LCD display graphics");
}
}
阴极射线管显示器类CRTMonitor自然也是Monitor的子类:
package a.b;
public class CRTMonitor extends Monitor {
public void displayText() {
       System.out.println("CRT display text");
}
public void displayGraphics() {
       System.out.println("CRT display graphics");
}
}
等离子显示器PlasmaMonitor也是Monitor的子类:
package a.b;
public class PlasmaMonitor extends Monitor {
public void displayText() {
       System.out.println("Plasma display text");
}
public void displayGraphics() {
       System.out.println("Plasma display graphics");
}
}
现在有一个MyMonitor类。假设没有向上转型,MyMonitor类代码如下:
package a.b;
public class MyMonitor {
public static void main(String[] args) {
       run(new LCDMonitor());
       run(new CRTMonitor());
       run(new PlasmaMonitor());
}
public static void run(LCDMonitor monitor) {
       monitor.displayText();
       monitor.displayGraphics();
}
public static void run(CRTMonitor monitor) {
       monitor.displayText();
       monitor.displayGraphics();
}
public static void run(PlasmaMonitor monitor) {
       monitor.displayText();
       monitor.displayGraphics();
}
}
可能你已经意识到上述代码有很多重复代码,而且也不易维护。有了向上转型,代码可以更为简洁:
package a.b;
public class MyMonitor {
public static void main(String[] args) {
       run(new LCDMonitor());                      //向上转型
       run(new CRTMonitor());                     //向上转型
       run(new PlasmaMonitor());            //向上转型
}
public static void run(Monitor monitor) { //父类实例作为参数
       monitor.displayText();
       monitor.displayGraphics();
}
}
 
 
 
2.向下转型
通过向上转型可以推理出向下转型是将抽象类转换为具体的类。这样的类通常会出现问题,不能说四边形是平行四边型的一种,所以的鸟都是鸽子。可以说子类对象总是父类的一个实例,但父类对象不一定是子类的实例
class 四边形{
public static void draw(四边形 q){
}
public class 平行四边形 extends  四边形{
draw(new 平行四边形);//向上转型
四边形 q=new 平行四边形();//向上转型
平行四边形 p=(平行四边形)q;//告诉编译器就是平行四边形
}}
 
 
class parent{
         void doit() {
System.out.println("父类.doit()");
}
void doit2() {
System.out.println("父类.doit2()");
}
 
}
 
class Sub extends parent{
public void  doit() {
System.out.println("子类.doit()");
}
    void doit2() {
System.out.println("子类.doit2()");
}
public void doit3() {
System.out.println("子类.doit3()");
}
}
public class test{
public static void main(String[] args) {
parent p=new Sub();//向上转型
p.doit();
p.doit2();
//由于向上转型,丢失了与父类不同的方法,doit3方法 p不能调用
 
 
}
}
 
if( p instanceof Sub) {
Sub q=(Sub)p;
q.doit();
q.doit2();
q.doit3();//得到与父类不同的方法
}else {
System.out.println("错误");
}
 
 
 
 
 
 
                      2.使用instanceof操作符判断对象类型
当在程序中执行向下转型操作时,如果父类对象不是子类对象的实例,所以在执行向下转型之前需要养成一个良好的习惯,就是判断父类对象是否为子类对象的实例
这个判断通常使用instanceof操作符来完成。可以使用instanceof操作符判断是否一个类实现了某个接        
myobject              instanceof         exampleclass
某类的对象的引用                                某个类
class 四边形{   
public static void  draw(四边型)}}
class 正方形 extends 四边形{ }
class Anything{ }
public class 平行四边形 extends 四边形{
public static void main(String args[]){
四边形 q=new 四边形();
if( q instanceof 平行四边形){  判断父类对象q是平行四边形的实例
平行四边形 p =(平行四边形)q;//q是父类对象,显式转换平行四边形
}
 
3.方法的多态
构造方法的名称已经由类名决定,所以构造方法只有一个名称,但如果希望以不同的方式来实例化对象,就需要使用多个构造方法来完成。由于这些构造方法都需要更根据类名进行命名,为了让方法名相同而形参不同的构造方法同时存在,必须用到"方法重载".虽然方法重载就是在同一个类中允许同时存在一个以上的同名方法,只要这些方法的参数个数或类型不同即可。
public class demo{
     public static int add(int a,int b) {
    return a+b;
     }
     public static double add(double a,double b) {
    return a+b;
     }
     public static int add(int a) {
    return a;
     }
     public static void main(String[] args) {
System.out.println(add(1,2));
System.out.println(add(1.2,1.2));
System.out.println(1);
}
 
这里定义了不同的方法,前两个方法的参数类型不同,并且方法的返回值类型也不同,所以这两个方法构成方法重载关系
1.方法名不同,构成重载
2.参数类型,构成重载
3.参数个数不同,构成重载
4.参数顺序不一样,构成重载
 
方法名,方法各参数类型,参数的个数,参数的顺序来确定类中方法是否唯一。
如果只有方法的返回类型不同那么不足以区分两个方法的重载。
 
在谈个参数个数可以确定两个方法具有重载关系时,会想到定义不定长参数
class demo{
int add(int... a){
int s=0;
for(int i=0;i<a.length;i++)
s+=a[i];
return s;
}
public static void main(String[] args) {
demo s=new demo();
    int a=s.add(1,567,89,89,878);
    System.out.println(a);
 
}
}
根据传入的参数个数来进行操作
  4.多态的实际运用
利用多态可以使程序具有良好的扩展性,并可以对所有类对象进行通用的处理。
public class 四边形{
public void draw( 四边形 q){
//something
}
}
public static void main(String[] args){
四边形 q= new 四边形();
q.draw(new 平行四边形());
}
}
class Square extends 四边形{
public Square(){
System.out.prntln("正方形");
}
}
public 平行四边形 extends 四边形{
public 平行四边形(){
System.out.prntln("平行四边形");
}
}
//以不同的类对象为参数调用draw()方法可以处理不同的问题。使用多态节省了开发和维护时间,因为程序员无须在所有的子类中定义执行相同功能的方法,避免了大量重复代码的开发,同时只要实例化一个继承父类的子类对象即可调用相应的方法,这里只要维护父类中的这个方法即可。
4.抽象类与接口类
实现接口或继承抽象类的子类必须实现接口的所有方法或抽象类的所有抽象方法
1.抽象类
在解决实际问题时,一般将父类定义为抽象类,需要使用这个父类进行继承与多态处理。继承树中越是在上方的类越抽象,在多态机制中,并不需要将父类初始化对象,我们需要的只是子类对象,所以在Java语言中设置抽象类不可以实例化对象。
public abstract class test{
abstract void testAbstract();//定义抽象方法
}
abstract是定义抽象类的关键字。
使用abstract关键字定义的类称为抽象类,而使用这个关键字定义的方法称为抽象方法。抽象方法没有方法体,这个方法本身没有任何意义,除非他被重写,而承载这个抽象方法的抽象类必须被继承,实际上抽象类除了被继承之外没有任何意义。只要类中有一个抽象方法,此类就被标记为抽象类。
抽象类被继承后需要实现其中所有的抽象方法。继承抽象类的所有子类需要将抽象类中的抽象方法进行覆盖。这样在多态机制中,就可以将父类修改成抽象类。将父类的方法draw()设置为抽象方法。然后每个子类都重写这个方法来处理。,但是同样的这样的父类局限性也很多大,也许某个不需要draw()方法的子类也不得不重写draw()方法。如果将draw()方法放在另外一个类中让需要该方法的类来继承,而不需要draw()方法的类继承图形类,但所有的子类都需要图形类,同时某些类还需要draw()方法,但是java中规定,类不能同时继承多个父类。
public abstract class 画图{
abstract void draw();
}
abstract  class 四边形{
//Something
}
//此时平行四边形需要draw方法
//正方形不要draw方法
//那么平行四边形需要继承画图类,并重写draw类
//但是正方型类和平行四边形类都需要继承四边形类
//此时既要实现画图类和四边形类就会违反java设计模式
 
//如果将draw的方法放在四边形类,同时被正方形类和平行四边形继承
//但是继承抽象类后,里面的抽象方法必须全部被实现,但是正方形并不需要draw方法
2.接口
接口是抽象类的延伸,可以将它看作是存粹的抽象类,接口中的所有方法都没有方法体接口的方法默认就是public abstract,变量默认就是public static final可以将draw()方法封装到一个接口中,使需要draw()方法 的类实现这个接口,同时也继承图形类,这就是接口存在的必要性。
接口使用interface关键字进行定义:
public interface drawTes{
void draw()//接口内的方法,省略abstract关键字
}
一个类实现一个接口可以使用Implements关键字在接口中定义的方法必须被定义为Public或abstract形式,其他修饰符不被Java编译器认可,即使不声明为public形式,他也是Public。
在接口中定义的任何字段都自动是static和final的。
interface drawTest{
public void draw();
}
class 平行四边形 extends 四边形 implements drawTest{
pubLi void draw(){     //由于该类实现了接口,所以需要覆盖draw()方法
System.out.println("平行四边形.draw()");
}
void doAnything(){
//dosomething
}
}
class 正方形 extends 四边形 implements drawTest{
pubLi void draw(){     //由于该类实现了接口,所以需要覆盖draw()方法
System.out.println("正方形.draw()");
}
void doAnyThing(){
//something
}
}
public class 四边形{ }
 
 
 
原文地址:https://www.cnblogs.com/cainame/p/10092008.html