java-多态-object

概要图

一 多态

1.1 多态的产生

下面的 红色部分降低了代码的可扩展性

    Dog d = new Dog();
        method(d);

        Cat c = new Cat();
        method(c);
    }
    
    //接收Dog,让dog做事。
    public static void method(Dog d)
    {
        d.eat();
    }
    //接收Cat,让cat做事。
    public static void method(Cat c)
    {
        c.eat();
    }

 

对其改进 见下面红色部分

//多态技术的引出。解决什么问题?程序扩展性的问题。

//描述Dog
class Dog extends Animal 
{
    public void eat()
    {
        System.out.println("骨头");
    }
    public void lookHome()
    {
        System.out.println("看家");
    }
}

//描述猫
class Cat extends Animal 
{
    public void eat()
    {
        System.out.println("");
    }
    public void catchMouse()
    {
        System.out.println("抓老鼠");
    }
}
//进行抽取。将共性的功能抽取到父类Animal中。
abstract class Animal
{
    public abstract void eat();
}

class DuoTaiDemo
{
    public static void main(String[] args) 
    {
        Dog d = new Dog();
//        d.eat();
        method(d);

        Cat c = new Cat();
        method(c);
    }
    /*
    发现,每多一个动物,都需要为这个动物单独定义一个功能,
    让这个动物的对象去做事。
    这个程序扩展性就很差。
    如何提高这个扩展性呢?
    发现既然是让动作去eat,无论是dog,还是cat,
    eat是它们共性,干脆,将eat进行抽取。抽取到父类Animal中。

    Dog是Animal中的一种。
    Dog d = new Dog();
    Animal a = new Dog();
    Cat c = new Cat();
    Animal aa = new Cat();
    */
    //只要建立animal的引用就可以接收所有的dog cat对象进来。让它们去eat。
    //提高了程序的扩展性。
    public static void method(Animal a)
    {
        a.eat();
    }

    /*
    //接收Dog,让dog做事。
    public static void method(Dog d)
    {
        d.eat();
    }
    //接收Cat,让cat做事。
    public static void method(Cat c)
    {
        c.eat();
    }
    */

}

 

1.2 多态的一些问题

【体现】
父类的引用或者接口的引用指向了自己的子类对象。
Dog d = new Dog();//Dog对象的类型是Dog类型。
Animal a = new Dog();//Dog对象的类型右边是Dog类型,左边Animal类型。
【好处】
提高了程序的扩展性。
【弊端】
通过父类引用操作子类对象时,只能使用父类中已有的方法,不能操作子类特有的方法。

子类是父类的对象,父类不是子类的对象
【前提】
1,必须有关系:继承,实现。
2,通常都有重写操作。
【子类的特有方法如何调用呢?】
Animal a = new Dog();//Animal是父类型,new Dog()是子对象。
但是父类型引用指向子类对象时,这就是让子类对象进行了类型的提升(向上转型)。
向上转型好处:提高了扩展性,隐藏了子类型。弊端:不能使用子类型的特有方法。
如果要想使用子类的特有方法,只有子类型可以用。
可以向下转型,强制转换。
Animal a = new Dog();
a.eat();
Dog d = (Dog)a;//将a转型为Dog类型。向下转型。
d.lookHome();
向下转型什么时候用?当需要使用子类型的特有内容时。

注意:无论向上还是向下转型,最终都是子类对象做着类型的变化

【向下转型的注意事项】
Animal a = new Dog();
//Cat c = (Cat)a;向下转型因为不明确具体子类对象类型,所以容易引发ClassCastException异常。(代码下面红色)

public class Test {
 
    public static void main(String[] args) 
    {
        Dog d = new Dog();  
        method(d);
        Cat c = new Cat();
        method(c);
    }
    public static void method(Animal a)
    {
        
        a.eat();
        Dog d = (Dog)a;
        d.lookHome();
    }

}
class Dog extends Animal 
{
    public void eat()
    {
        System.out.println("骨头");
    }
    public void lookHome()
    {
        System.out.println("看家");
    }
}

//描述猫
class Cat extends Animal 
{
    public void eat()
    {
        System.out.println("");
    }
    public void catchMouse()
    {
        System.out.println("抓老鼠");
    }
}
//进行抽取。将共性的功能抽取到父类Animal中。
abstract class Animal
{
    public abstract void eat();
}

Exception in thread "main" 骨头
看家

java.lang.ClassCastException: test.Cat cannot be cast to test.Dog

 

 


所以为了避免这个问题,需要在向下转型前,做类型的判断。
判断类型用的是关键字 instanceof

if(a instanceof Cat)//a指向的对象的类型是Cat类型。
{
//将a转型Cat类型。
Cat c = (Cat)a;
c.catchMouse();
}
else if(a instanceof Dog)
{
Dog d = (Dog)a;
d.lookHome();
}

 

【转型总结】
1,什么时候使用向上转型呢?
提高程序的扩展性,不关系子类型(子类型被隐藏)。
需要用子类的特有方法吗?不需要,哦了。向上转型。
2,什么时候使用向下转型呢?
需要使用子类型的特有方法时。
但是一定要使用 instanceof 进行类型的判断。避免发生 ClassCastException

1.3 多态的例子

第一个例子

class 毕姥爷
{
    public void 讲课()
    {
        System.out.println("讲管理");
    }
    public void 钓鱼()
    {
        System.out.println("钓鱼");
    }
}

class 毕老师 extends 毕姥爷
{
    public void 讲课()
    {
        System.out.println("Java");
    }
    public void 看电影()
    {
        System.out.println("看电影");
    }
}


class DuoTaiTest 
{
    public static void main(String[] args) 
    {
        毕姥爷 x = new 毕老师();//多态,向上转型。
        x.讲课();
        x.钓鱼();
//        x.看电影();//不行。
        //想要使用毕老师的特有方法时,需要向下转型。
        if(x instanceof 毕老师)
        {
            毕老师 y = (毕老师)x;
            y.看电影();
        }
        //自始至终都是子类对象做着类型的变化。


    }
}

 

 注意:无论向上还是向下转型,最终都是子类对象做着类型的变化

第二个例子


/*
阶段一需求:笔记本电脑运行。
按照面向对象的思想,用代码体现。
名称提炼法
笔记本电脑。
行为:运行。

class NoteBook
{
//运行功能。
public void run()
{
System.out.println("notebook run");
}
}

 

 阶段二需求:想要在笔记本电脑上加上一个手握式鼠标。
多了个对象:鼠标。
行为:开启,关闭。

class Mouse
{
public void open()
{
System.out.println("mouse open");
}
public void close()
{
System.out.println("mouse close");
}
}

笔记本怎么用鼠标呢?
在笔记本中多一个使用鼠标的功能。
需要修改原来的笔记本类中的内容,添加一个功能。

这样的做法相当于把笔记本打开,把鼠标焊到里面

因为他直接在改原代码

class Mouse
{
public void open()
{
System.out.println("mouse open");
}
public void close()
{
System.out.println("mouse close");
}
}

 

public class Test {
    
    
    

    public static void main(String[] args) 
    {
        NoteBook n =new NoteBook();
        n.run();
        n.userMouse(null);
    
    }
    

}
class NoteBook {
    public void run (){
        System.out.println("notebook run");
    }
    public void userMouse(mouse m){
        if(m!=null){
            
            m.open();
            m.close();
        }
        
    }
}
class mouse {
    public void open (){
        System.out.println("mouse open");
    }
    public void close (){
        System.out.println("mouse close");
    }
    
}

结果 notebook run

 


//问题:如果想要加入一个键盘呢?
只要描述一个键盘类,并在电脑类中加入一个使用键盘的功能就哦了。
但是发现从鼠标开始这个问题就已经产生了,一旦需要添加新设备的时候,
都需要改变电脑的源码。这个扩展性是非常差的。

设计上该如何改进呢?
之前的问题在于外围设备的增加和笔记本电脑之间的耦合性过高。
如何降低外围设备和笔记本电脑的耦合性呢?
外围设备还不确定,我们不要面对外围具体设备。
为了让笔记本可以使用这些设备,可以事先定义好一些规则,
笔记本只要使用这些规则就可以了。
有了这些规则就可以进行笔记本的功能扩展。
后期这些外围设备只要符合这些规则就可以被笔记本使用了。

那么规则在java中该如何体现呢?接口

//1,描述接口。USB。

//2,描述笔记本电脑:运行功能,使用USB接口的功能。

*/
//USB接口定义。
interface USB
{
void open();
void close();
}

//描述笔记本电脑。 
class NoteBook
{
public void run()
{
System.out.println("notebook run");
}

//使用usb接口的功能。
public void useUSB(USB usb)//接口类型的变量。接口类型的变量指向自己的子类对象。
//USB usb = new Mouse();
{
if(usb!=null)
{
usb.open();
usb.close();
}
}
}
//需要鼠标 。想要被笔记本电脑使用,该鼠标必须符合规则。
//描述鼠标。
class Mouse implements USB
{
public void open()
{
System.out.println("mouse open");
}
public void close()
{
System.out.println("mouse close");
}
}

class KeyBoard implements USB
{
public void open()
{
System.out.println("KeyBoard open");
}
public void close()
{
System.out.println("KeyBoard close");
}
}

/*
发现,接口的出现,
1,扩展了笔记本电脑功能。
2,定义了规则。
3,降低了笔记本电脑和外围设备之间的耦合性。
*/

class DuoTaiTest2 
{
public static void main(String[] args) 
{
NoteBook book = new NoteBook();
book.run();
book.useUSB(null);
book.useUSB(new Mouse());
book.useUSB(new KeyBoard());
}
}

1.4 多态中成员调用的特点。

1,成员变量
当子父类中出现同名的成员变量时。
多态调用该变量时:
编译时期:参考的是引用型变量所属的类中是否有被调用的成员变量。没有,编译失败。
运行时期:也是调用引用型变量所属的类中的成员变量。
简单记:编译和运行都参考等号的左边。
编译运行看左边。

 

2,成员函数。
编译,参考左边,如果没有,编译失败。
运行,参考右边的对象所属的类。
编译看左边,运行看右边。

可以看作是方法的重写.当然要运行右边了
对于成员函数是动态绑定到对象上。(动态是指对象不确定)

3,静态函数。
编译和运行都参考左边。

也可以看作是重写.按理说应该是和上面的成员函数是一个结果的,但是静态函数是与对象无关的,

所以运行的是左边Fu 的method,只和调用者有关系

静态函数是静态的绑定到类上。(静态 是指在那个类中)

下面红色部分

package test;

public class Test {
    
    
    

    public static void main(String[] args) 
    {
        Fu f = new Zi();
        System.out.println(f.num);// 3
        f.show();  // zi show run..
        f.method(); // fu static method run
    
    }
    

}
class Fu
{
    int num = 3;
    
    void show()
    {
        System.out.println("fu show run");
    }
    static void method()
    {
        System.out.println("fu static method run");
    }
}
class Zi extends Fu
{
    int num = 5;

    void show()
    {
        System.out.println("zi show run..");
    }
    static void method()
    {
        System.out.println("zi static method run");
    }
}

 

【结论】
对于成员变量和静态函数,编译和运行都看左边。 
对于成员函数,编译看左边,运行看右边。

二 object

2.1 object 常用方法

API(应用程序接口(接口:指的是一些公共的东西))文 档

/*
Object类中的常用方法

Object类是所有类的根类,定义了所有对象都具备的功能。
API(应用程序接口)文档

*/

class Person extends Object
{
private int age;
Person(int age)
{
this.age = age;
}

//判断是否是同龄人。这个方法也是在比较两个person对象是否相等。
//注意:Person类中是否有比较两个Person对象相等的方法?有的!因为继承Object,它本身就具备着equals方法。
//既然有,还需要定义compare方法吗?不需要。
//但是,equals方法判断的是地址,不是我们所需要的内容。
//咋办?继续使用Object的equals方法,但是建立子类的自己的内容。传说中的重写。
【记住:以后判断对象是否相同,就需要覆盖equals方法。】

重写equals 

注意: 

1 参数是object obj

2 要向下转型

public boolean equals(Object obj)
{

//建立Person自己的判断相同的依据。判断年龄是否相同。
//    return this.age == obj.age;//obj所属类型Object,Object中没有定义age,所以编译失败。

//如果调用该方法的对象和传递进来的对象是同一个。就不要转型和判断,直接返回true。效率高一点。
if(this == obj)
return true;

//age是Person类型的属性。既然要用到子类型的内容,需要向下转型。
if(!(obj instanceof Person))
//    return false;
throw new ClassCastException("类型是不对的!请改正。");
Person p = (Person)obj;

return this.age == p.age;

}

 

 重写toString 

//覆盖toString方法,建立Person对象自己的字符串表现形式。
public String toString()
{
return "Person[age = "+age+"]";
}

 三 习题

1,描述图书:book  
    作者,书名,价格。
    行为:6个set get
    建立book比较相同的依据。只要书名相同就视为同一本书。  
    字符串判断相同:请查api文档。equals方法,String类重写Object类中的equals
    建立book对象的字符串表现形式。 Book[作者:+  书名 +  价格]


2,写出结果。如果编译失败,请注明原因。

class Super
{
    int i=0;
    public Super(String a)
    {
        System.out.println("A");
        i=1;    
    }
    public Super()
    {
        System.out.println("B");
        i+=2;
    }
}
class Demo extends Super
{
    public Demo(String a)
    {//super();
        System.out.println("C");
        i+=5;                
    }
    public static void main(String[] args)
    {
        int i=4;
        Super d=new Demo("A");
        System.out.println(d.i);
    }
}
//B C 7

3.
选择题,写出错误答案错误的原因,用单行注释的方式。
class Demo
{
     int show(int a,int b){return 0;}
}
下面那些函数可以存在于Demo的子类中。    
A.public int show(int a,int b){return 0;}//    可以的,重写了。权限大于父类方法权限。
B.private int show(int a,int b){return 0;}// 不可以的,要重写,但是权限不够。
C.private int show(int a,long b){return 0;}// 可以。相当于重载。
D.public short show(int a,int b){return 0;}// 不可以的。调用的不确定性。
E.static int show(int a,int b){return 0;}//    不可以的,static只能重写静态。


4,
写出程序结果,如果编译失败,请注明原因。

interface A{}
class B implements A
{
    public String test()
    {
        return "yes";
    }
}
class Demo
{
    public static A get()
    {
        return new B();
    }
    public static void main(String[] args)
    {


        A a=get();
        System.out.println(a.test());//编译失败,因为a所属的A接口中没有Test()方法。
//        A a = new B();相当于这句。
//        a.test(); 是多态调用不?是!test()是非静态成员函数不?是! 规律:编译看左边,有吗?没有,所以编译失败。

 

posted @ 2018-01-06 21:57  8亩田  阅读(366)  评论(0编辑  收藏  举报