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+"]";
}

原文地址:https://www.cnblogs.com/liu-wang/p/8214287.html

时间: 2024-08-11 03:48:47

java-多态-object的相关文章

由一道题目引出的java多态

某次逛论坛时发现一个非常有意思的题目,如下: class A<B> { public String show(A obj) { return ("A and A"); } public String show(B obj) { return ("A and B"); } } class B extends A { public String show(B obj) { return ("B and B"); } public Stri

理解JAVA - 面向对象(object) - 属性,方法

理解JAVA - 面向对象(object) - 属性,方法 多态的体现:    向上造型,父类接收子类对象:向上造型:    从父类角度看不到子类独有的方法: 面向对象,人类认知世界的方式:生活中每天都在多态:    这个人是谁,这是个人,多态来看待他了:    他是老师  把它向下造型来看待了,他是老师,知道他能讲课了,老师是人下面的子类型: 我们每天都已多态来接收事物,然后向下造型:    这是人,这人是老师:    这是车,这车是奥迪 小时候,不知到是什么,就问这是什么东西,世间万物都能成

【转】java多态详解

1.        Java中除了static和final方法外,其他所有的方法都是运行时绑定的.private方法都被隐式指定为final的,因此final的方法不会在运行时绑定.当在派生类中重写基类中static.final.或private方法时,实质上是创建了一个新的方法. 2.在派生类中,对于基类中的private方法,最好采用不同的名字. 3.包含抽象方法的类叫做抽象类.注意定义里面包含这样的意思,只要类中包含一个抽象方法,该类就是抽象类.抽象类在派生中就是作为基类的角色,为不同的子

Java多态对象的类型转换和动态绑定

Java多态对象的类型转换这里所说的对象类型转换,是指存在继承关系的对象,不是任意类型的对象.当对不存在继承关系的对象进行强制类型转换时,java 运行时将抛出 java.lang.ClassCastException 异常. 在继承链中,我们将子类向父类转换称为"向上转型",将父类向子类转换称为"向下转型". 很多时候,我们会将变量定义为父类的类型,却引用子类的对象,这个过程就是向上转型.程序运行时通过动态绑定来实现对子类方法的调用,也就是多态性. 然而有些时候为

Java多态的底层原理

作为一门面向对象语言,Java 拥有封装.继承.多态三大特性.多态就是允许不同类的对象对同一消息做出响应.基于多态,可以消除一些类型耦合关系,实现可替换.可扩充.Java 中使用多态特性的方法主要有,实现一个接口,实现抽象类的一个方法,覆盖父类的一个方法. Java多态的底层原理 多态的底层实现是动态绑定,即在运行时才把方法调用与方法实现关联起来.动态绑定涉及很多 JVM 的细节,全部写清楚需要很大篇幅,因此本文仅简单描述,后续会有其他文章就其中的细节一一分享. 静态绑定与动态绑定 JVM 的方

Java多态之动态绑定

目录 Java多态之动态绑定 引用变量的类型 编译时类型 运行时类型 方法绑定 静态绑定 动态绑定 方法表 Java多态之动态绑定 上篇回顾:多态是面向对象程序设计非常重要的特性,它让程序拥有 更好的可读性和可扩展性. 发生在继承关系中. 需要子类重写父类的方法. 父类类型的引用指向子类类型的对象. 自始至终,多态都是对于方法而言,对于类中的成员变量,没有多态的说法. 上篇说到:一个基类的引用变量接收不同子类的对象将会调用子类对应的方法,这其实就是动态绑定的过程.在理解动态绑定之前,先补充一些概

Java总结篇系列:java.lang.Object

从本篇开始,将对Java中各知识点进行一次具体总结,以便对以往的Java知识进行一次回顾,同时在总结的过程中加深对Java的理解. Java作为一个庞大的知识体系,涉及到的知识点繁多,本文将从Java中最基本的类java.lang.Object开始谈起. Object类是Java中其他所有类的祖先,没有Object类Java面向对象无从谈起.作为其他所有类的基类,Object具有哪些属性和行为, 是Java语言设计背后的思维体现. Object类位于java.lang包中,java.lang包包

java.lang.Object 之 clone() 使用

Java的所有类都默认继承 java.lang.Object 类,在 java.lang.Object 类中有一个方法 clone().JDK API的说明文档解释这个方法将返回Object对象的一个拷贝.要说明的有两点:一是拷贝对象返回的是一个新对象,而不是一个引用.二是拷贝对象与用 new操作符返回的新对象的区别就是这个拷贝已经包含了一些原来对象的信息,而不是对象的初始信息.对于Java中使用 clone() 方法有几点需要注意 1:被clone的类必须实现 Cloneable 接口,Clo

转!!java中Object转String

Object转为String的几种形式 在java项目的实际开发和应用中,常常需要用到将对象转为String这一基本功能.本文将对常用的转换方法进行一个总结.常用的方法有Object.toString(),(String)要转换的对象,String.valueOf(Object)等.下面对这些方法一一进行分析. 方法1:采用 Object.toString()方法请看下面的例子:Object object = getObject();System.out.println(object.toStr

java.lang.Object

java.lang包在使用的时候无需显示导入,编译时由编译器自动导入. Object类是类层次结构的根,Java中所有的类从根本上都继承自这个类. Object类是Java中唯一没有父类的类. 其他所有的类,包括标准容器类,比如数组,都继承了Object类中的方法. Object类中的方法 构造方法:public Object() 文档中的类概览: Java中的每个类都具有定义在Object类中的这些方法. protected Object clone() Creates and returns