面向对象编程有三大特性:封装、继承、多态。 封装隐藏了类的内部实现机制,可以在不影响使用的情况下改变类的内部结构,同时也保护了数据。对外界而已它的内部细节是隐藏的,暴露给外界的只是它的访问方法。 继承是为了重用父类代码。两个类若存在IS-A的关系就可以使用继承。 所谓多态就是指程序中定义的引用变量所指向的具体类型和通过该引用变量发出的方法调用在编程时并不确定,而是在程序运行期间才确定,即一个引用变量倒底会指向哪个类的实例对象,该引用变量发出的方法调用到底是哪个类中实现的方法,必须在由程序运行期间才能决定。因为在程序运行时才确定具体的类,这样,不用修改源程序代码,就可以让引用变量绑定到各种不同的类实现上,从而导致该引用调用的具体方法随之改变,即不修改程序代码就可以改变程序运行时所绑定的具体代码,让程序可以选择多个运行状态,这就是多态性。 但是向上转型存在一些缺憾,那就是它必定会导致一些方法和属性的丢失,而导致我们不能够获取它们。所以父类类型的引用可以调用父类中定义的所有属性和方法,对于只存在与子类中的方法和属性它就望尘莫及了。 下面通过一个简单的入门动物案例:package dao; /** * 描述: * 动物类-父类 * @author JAM * @create 2018-03-19 9:48 * **/ public class Animal { public void eat(){ System.out.println("吃东西"); } public void play(){ System.out.println("在玩"); } public void run(){ System.out.println("在跑步"); } }package dao; /** * 描述: * 猫 * @author JAM * @create 2018-03-19 9:50 * **/ public class Cat extends Animal { @Override public void eat(){ System.out.println("我吃鱼"); } @Override public void play(){ System.out.println("玩毛线球"); } @Override public void run(){ System.out.println("走猫步"); } }package dao; /** * 描述: * 狗 * @author JAM * @create 2018-03-19 9:55 * **/ public class Dog extends Animal { @Override public void eat() { System.out.println("我吃狗粮"); } @Override public void play() { System.out.println("我陪主人玩"); } @Override public void run() { System.out.println("我四只腿一起跑步"); } }package run; import dao.Animal; import dao.Cat; import dao.Dog; /** * 描述: * * @author JAM * @create 2018-03-19 9:52 * **/ public class RunAnimal { public static void main(String[] args) { //这就是向上转型,Animal animal = new Cat(); // 将子类对象Cat转化为父类对象Animal。这个时候animal这个引用调用的方法是子类方法。 Animal a1 = new Cat(); a1.eat(); a1.play(); a1.run(); System.out.println("=================================="); //向下转型--成功 Animal a2 = new Cat(); Cat c1 = (Cat) a2; c1.eat(); c1.play(); c1.run(); System.out.println("=================================="); //向下转型----失败 //Exception in thread "main" java.lang.ClassCastException: dao.Cat cannot be cast to dao.Dog Dog d1 = (Dog) a2; d1.eat(); //以上提示:狗怎么能变成猫 System.out.println("=================================="); //Exception in thread "main" java.lang.ClassCastException: dao.Animal cannot be cast to dao.Dog Animal animal1 = new Animal(); Dog d2 = (Dog) animal1; d2.eat(); System.out.println("=================================="); //以上提示:而animal1为Animal对象,它也不能被向下转型为任何子类对象。 // 比如你去考古,发现了一个新生物,知道它是一种动物,但是你不能直接说,啊,它是猫,或者说它是狗。 //instaceof:转型判断 RunAnimal r1 = new RunAnimal(); r1.eat(new Cat()); } public void eat(Animal animal){ if (animal instanceof Dog){ Dog dog = (Dog) animal; dog.eat(); dog.play(); dog.run(); return; } if (animal instanceof Cat){ Cat cat = (Cat) animal; cat.run(); cat.play(); cat.eat(); return; } animal.eat(); } }从该案例可以总结出以下几点:所以对于多态我们可以总结如下: 指向子类的父类引用由于向上转型了,它只能访问父类中拥有的方法和属性,而对于子类中存在而父类中不存在的方法,该引用是不能使用的,尽管是重载该方法。若子类重写了父类中的某些方法,在调用该些方法的时候,必定是使用子类中定义的这些方法(动态连接、动态调用)。 多态分为编译时多态和运行时多态。其中编辑时多态是静态的,主要是指方法的重载,它是根据参数列表的不同来区分不同的函数,通过编辑之后会变成两个不同的函数,在运行时谈不上多态。而运行时多态是动态的,它是通过动态绑定来实现的,也就是我们所说的多态性。 多态一般分为两种:重写式多态和重载式多态。重写和重载这两个知识点前面的文章已经详细将结果了,这里就不多说了。
- Java 中的instanceof 运算符是用来在运行时指出对象是否是特定类的一个实例。instanceof通过返回一个布尔值来指出,这个对象是否是这个特定类或者是它的子类的一个实例。用法: result = object instanceof class 参数: Result:布尔类型。 Object:必选项。任意对象表达式。 Class:必选项。任意已定义的对象类。 说明: 如果 object 是 class 的一个实例,则 instanceof 运算符返回 true。如果 object 不是指定类的一个实例,或者 object 是 null,则返回 false。但是instanceof在Java的编译状态和运行状态是有区别的:在编译状态中,class可以是object对象的父类,自身类,子类。在这三种情况下Java编译时不会报错。在运行转态中,class可以是object对象的父类,自身类,不能是子类。在前两种情况下result的结果为true,最后一种为false。但是class为子类时编译不会报错。运行结果为false。
- 向下转型的前提是父类对象指向的是子类对象(也就是说,在向下转型之前,它得先向上转型)
- 向下转型只能转型为本类对象(猫是不能变成狗的)
- 重载式多态,也叫编译时多态。也就是说这种多态再编译时已经确定好了。重载大家都知道,方法名相同而参数列表不同的一组方法就是重载。在调用这种重载的方法时,通过传入不同的参数最后得到不同的结果。
但是这里是有歧义的,有的人觉得不应该把重载也算作多态。因为很多人对多态的理解是:程序中定义的引用变量所指向的具体类型和通过该引用变量发出的方法调用在编程时并不确定,而是在程序运行期间才确定,这种情况叫做多态。 这个定义中描述的就是我们的第二种多态—重写式多态。并且,重载式多态并不是面向对象编程特有的,而多态却是面向对象三大特性之一(如果我说的不对,记得告诉我。。)。 我觉得大家也没有必要在定义上去深究这些,我的理解是:同一个行为具有多个不同表现形式或形态的能力就是多态,所以我认为重载也是一种多态,如果你不同意这种观点,我也接受。
- 重写式多态,也叫运行时多态。这种多态通过动态绑定(dynamic binding)技术来实现,是指在执行期间判断所引用对象的实际类型,根据其实际的类型调用其相应的方法。也就是说,只有程序运行起来,你才知道调用的是哪个子类的方法。
这种多态通过函数的重写以及向上转型来实现,我们上面代码中的例子就是一个完整的重写式多态。我们接下来讲的所有多态都是重写式多态,因为它才是面向对象编程中真正的多态。
动态绑定技术涉及到jvm,暂时不讲(因为我也不懂,哈哈哈哈),感兴趣的可以自己去研究一下,我暂时还没有时间去研究jvm。。
package run; /** * 描述: * 多态经典案例 * @author JAM * @create 2018-03-19 10:31 * **/ public class Demo1 { public static void main(String[] args) { A a1 = new A(); A a2 = new B(); B b = new B(); C c = new C(); D d = new D(); System.out.println("1--" + a1.show(b)); System.out.println("2--" + a1.show(c)); System.out.println("3--" + a1.show(d)); System.out.println("4--" + a2.show(b)); //分析5,a2.show(c),a2是A类型的引用变量,所以this就代表了A,a2.show(c),它在A类中找发现没有找到,于是到A的超类中找(super),由于A没有超类(Object除外),所以跳到第三级, // 也就是this.show((super)O),C的超类有B、A,所以(super)O为B、A,this同样是A,这里在A中找到了show(A obj),同时由于a2是B类的一个引用且B类重写了show(A obj) // 因此最终会调用子类B类的show(A obj)方法,结果也就是B and A。 System.out.println("5--" + a2.show(c)); System.out.println("6--" + a2.show(d)); System.out.println("7--" + b.show(b)); System.out.println("8--" + b.show(c)); System.out.println("9--" + b.show(d)); } } class A { public String show(D obj) { return ("A and D"); } public String show(A obj) { return ("A and A"); } } class B extends A{ public String show(B obj){ return ("B and B"); } @Override public String show(A obj){ return ("B and A"); } } class C extends B{ } class D extends B{ }多态的实现 Java实现多态有三个必要条件:继承、重写、向上转型。 继承:在多态中必须存在有继承关系的子类和父类。 重写:子类对父类中某些方法进行重新定义,在调用这些方法时就会调用子类的方法。 向上转型:在多态中需要将子类的引用赋给父类对象,只有这样该引用才能够具备技能调用父类的方法和子类的方法。 只有满足了上述三个条件,我们才能够在同一个继承结构中使用统一的逻辑实现代码处理不同的对象,从而达到执行不同的行为。 对于Java而言,它多态的实现机制遵循一个原则:当超类对象引用变量引用子类对象时,被引用对象的类型而不是引用变量的类型决定了调用谁的成员方法,但是这个被调用的方法必须是在超类中定义过的,也就是说被子类覆盖的方法。
查看原文:https://blog.gzczy.top/2018/03/19/java%e5%a4%9a%e6%80%81%e7%9f%a5%e8%af%86%e3%80%90%e6%89%a9%e5%b1%95%e4%b8%8e%e5%a4%8d%e4%b9%a0%e3%80%91/