基本介绍
方法或者是对象具有多种形态。是面向对象的低三大特征,多太是建立在封装和继承的基础之上的。
多态的具体体现
(1)方法的多态
方法的重写和重载就体现了多态。
public class A {
//方法的重载
//都是sum方法,但是传入的参数不一样
public int sum(int n1, int n2) {
return n1+n2;
}
public int sum(int n1, int n2, int n3) {
return n1+n2+n3;
}
public void say(String a) {
System.out.println("调用A类方法say..."+a);
}
}
public class B extends A{
public void say(String a) {
System.out.println("调用B类方法say..."+a);
}
}
public class Test {
public static void main(String[] args) {
//通过传入不同的参数,从而调用不同的方法
A a = new A();
System.out.println(a.sum(20, 30));
System.out.println(a.sum(20, 30, 40));
B ob = new B();
//根据对象的不同,我们去调用不同的方法
//方法的重写
a.say("hello");
ob.say("ok");
}
}
50
90
调用A类方法say...hello
调用B类方法say...ok
(2)对象的多态
1.一个对象的编译类型和运行类型可以不一致。
2.编译类型在定义对象时,就确定了,不能改变。
3.运行类型是可以改变的。
4.编译类型看定义时 = 号的左边,运行类型看 = 号的右边。
//animal指向Animal这个对象
//编译对象是Animal,运行对象也是Animal
Animal animal = new Animal();
//如果Animal是Dog的父类,Dog是子类
//animal的编译类型是Animal,但是animal的运行类型是Dog
Animal animal = new Dog();
作用
public class Animal {
public void say() {
System.out.println("Animal say()方法...");
}
}
public class Cat extends Animal{
public void say() {
System.out.println("Cat say()方法...");
}
}
public class Dog extends Animal{
public void say() {
System.out.println("Dog say()方法...");
}
}
public class Test {
public static void main(String[] args) {
//animal的运行类型:Dog,编译类型是Animal
Animal animal = new Dog();
//输出Dog类当中的say方法
animal.say();
//animal的运行类型:Cat,编译类型是Animal
animal = new Cat();
//输出Cat类当中的say方法
animal.say();
//animal的运行类型:Animal,编译类型是Animal
animal = new Animal();
//输出Animal类中的say方法
animal.say();
}
}
Dog say()方法...
Cat say()方法...
Animal say()方法...
在上述代码我们可以看出,当我们创建的指向哪一个对象的时候,我们就会调用哪一个类当中的方法,也就是,我们真的调用方法的时候,是我们的运行对象当中的方法。
多态的向上转型
多态前提条件:两个对象(类)存在继承关系。
本质:父类的引用指向子类的对象
语法:父类类型 引用名 = new 子类类型();
特点:编译类型:看等号的左边,运行类型:看等号的右边
可以调用父类当中的所有成员(需要遵循访问权限的规则),不能调用子类当中特有的成员。
最中的运行效果还是需要看子类的具体实现。
public class Animal {
String name;
int age;
public void sleep() {
System.out.println("Animal sleep()方法...");
}
public void run() {
System.out.println("Animal run()方法...");
}
public void eat() {
System.out.println("Animal eat()方法...");
}
public void show() {
System.out.println("Animal show()方法...");
}
}
public class Cat extends Animal{
//方法的重写,子类重写父类
public void eat() {
System.out.println("Cat eat()方法...");
}
public void catchMouse() {
System.out.println("Animal catchMouse()方法...");
}
}
public class Test {
public static void main(String[] args) {
//向上转型
Animal animal = new Cat();
//这个是可行的,Animal的父类是Object,可以不是直接父类
//Object object = new Cat();
//调用规则:当我们进行方法的调用时,我们会首先来看我们的子类(运行类型),如果子类(运行类型)当中有这个方法,那么就进行调用
//如果子类(运行类型)没有这个方法,便会进入到父类当中(如果走到这里,运行类型会变成父类),如果父类有,那么便会调用,如果没有,就会接着向父类的父类查找
//知道object类,如果都没有便会报错
animal.eat();
animal.run();
animal.sleep();
animal.show();
//报错,因为catchMouse是子类Cat的特有方法
//在编译阶段,能调用的成员是由编译类型来决定的,Cat类时运行类型,所以不能调用
//animal.catchMouse();
}
}
Cat eat()方法...
Animal run()方法...
Animal sleep()方法...
Animal show()方法...
向下转型
语法:子类类型 引用名 = (子类类型)父类引用;
注意:只能强转父类的引用,不能强转父类的对象。
要求:父类的引用必须指向的是当前目标类型的对象。
作用:可以调用子类类型中所有的成员。
//向下转型
//cat的编译类型:Cat 运行类型:Cat
Cat cat = (Cat)animal;
//编译类型为cat,便可以查找到catchMouse方法
cat.catchMouse();
//如果说我们创建一个Dog类,其父类为Animal
//这里是运行不通过的,因为Dog与Animal没有向上转型,没有办法通过运行
//错误:
//Dog dog = (Dog)animal;
Cat catchMouse()方法...
多态注意事项
(1)属性
属性是没有重写之说的,属性的值要看编译类型。
class Base {
public int count = 10;
}
class Sub extends Base{
public int count = 20;
}
public class Test {
public static void main(String[] args) {
//属性的值要看编译类型
//base的编译类型为Base,运行类为Sub;
Base base = new Sub();
System.out.println("base.count="+base.count);
//sub编译类型为Sub,运行类型为Sub
Sub sub = new Sub();
System.out.println("sub.count="+sub.count);
//b1的编译类型为Base,运行类型为Base
Base b1 = new Base();
System.out.println("b1.count="+b1.count);
}
}
base.count=10
sub.count=20
b1.count=10
(2)instanceof
比较操作符:instanceof,用于判断对象的(运行类型)类型是否为某某类型或者是某某类型的子类型。
//instanceof
//BB的父类是AA
//bb的编译类型:BB,运行类型:BB
BB bb = new BB();
System.out.println(bb instanceof BB);
System.out.println(bb instanceof AA);
//aa的编译类型:AA,运行类型:BB
AA aa = new BB();
System.out.println(aa instanceof AA);
System.out.println(aa instanceof BB);
Object object = new Object();
System.out.println(object instanceof AA);
String str = "";
System.out.println(str instanceof Object);
true
true
true
true
false
false
true
Java的动态绑定机制
1.当调用对象方法的时候,该方法会和该对象的内存地址/运行类型绑定。
2.当调用属性的时候,属性没有动态绑定,哪里声明,哪里使用。
public class A {
public int n1 = 10;
public int sum() {
return getSum() + 10;
}
public int sum1() {
return n1 + 10;
}
public int getSum() {
return n1;
}
}
public class B extends A{
public int n1 = 20;
public int sum() {
return n1 + 20;
}
public int sum1() {
return n1 + 10;
}
public int getSum() {
return n1;
}
}
public class Test {
public static void main(String[] args) {
//a的编译类型:A,运行类型B
A a = new B();
//a.sum首先会调用运行类型的方法,在B子类当中查找sum方法,返回值为n1+20,20+20=40
System.out.println(a.sum());
//a.sum1首先调用运行类型的方法,即B子类,返回值为n1+10,20+10=30
System.out.println(a.sum1());
}
}
如果我们把B类当中的sum和sum1方法去掉
public class A {
public int n1 = 10;
public int sum() {
return getSum() + 10;
}
public int sum1() {
return n1 + 10;
}
public int getSum() {
return n1;
}
}
public class B extends A{
public int n1 = 20;
public int getSum() {
return n1;
}
}
public class Test {
public static void main(String[] args) {
//a的编译类型:A,运行类型B
A a = new B();
//首先a.sum在运行类型的B类查找,没有找到,然后到父类A查找,找到,返回值为getSum.()+10
//注意,这里的getSum方法要用B类当中的,这里getSum方法的查找也是一样,要从子类开始查找
//子类有那么就直接调用,如果子类没有在到父类查找。20+10=30
System.out.println(a.sum());
//这里的规则和上述是一样的,但是这里的返回值为n1+10,我们知道属性是没有动态绑定的,
//哪里声明,哪里使用,10+10 = 20;
System.out.println(a.sum1());
}
}
30
20
如果把B类的getSum方法去掉
public class A {
public int n1 = 10;
public int sum() {
return getSum() + 10;
}
public int sum1() {
return n1 + 10;
}
public int getSum() {
return n1;
}
}
public class B extends A{
public int n1 = 20;
}
public class Test {
public static void main(String[] args) {
//a的编译类型:A,运行类型B
A a = new B();
//注意,这里的getSum方法要用A类当中的,这里getSum方法的查找要从子类开始查找
//子类没有要到父类查找。
System.out.println(a.sum());
System.out.println(a.sum1());
}
}
20
20
如有错误,还望指正。