诸暨市住房和建设局网站,包装设计网站欣赏,wordpress打包app,阳江房产网签数据前言
本篇以Java初学者视角写下#xff0c;难免有不足#xff0c;或者术语不严谨之处。如有错误#xff0c;欢迎评论区指正。本篇说明多态相关的知识。若本文无法解决您的问题#xff0c;可以去最下方的参考文献出#xff0c;找出想要的答案。 多态概念
多态#xff08…前言
本篇以Java初学者视角写下难免有不足或者术语不严谨之处。如有错误欢迎评论区指正。本篇说明多态相关的知识。若本文无法解决您的问题可以去最下方的参考文献出找出想要的答案。 多态概念
多态polymorphism多种形态具体是不同对象完成某个行为它们的状态不同。 比如同样是跑1000米有些人气喘吁吁我还有一些大佬跑完气都不带喘的。 我认为多态是一种思想。而要理解多态得熟悉向上转型和方法重写的知识。 有关笔者继承的博客可以浅读了解一下
向上转型
字面意思转型类型转换向上即子类向超类转换。 向上转型属于自动转换类型可以不用强制类型转换运算符。
public class Main {public static void main(String[] args) {Dog dog Dog.Init(旺财,2);//这里采用的是静态工厂方法不熟悉的可以看下面的参考文献阅读一下。Animal animal dog;//这里Dog类转换成了父类Animal}
}
不熟悉的可以先跳过这里只是为了代码的完整性放这儿/
//Dog.java
class Dog extends Animal{public static Dog Init(String name,int age){Dog dog new Dog(name,age);return dog;}public Dog(String name,int age){super(name,age);}public Dog(){}Overridepublic void eat() {System.out.println(this.name 正在吃狗粮.......);}
}
//Animal.java
class Animal {String name;int age;public Animal(String name,int age){this.namename;this.age age;}public Animal(){}public static void fun(Animal animal){animal.eat();}public void eat(){System.out.println(this.name 正在吃法。);}}
在Java中对象变量是多态的它既可以引用自身的类型还可以引用其子类的对象。
实现向上转型有三种方式 1.直接赋值 Animal animal new Dog();//直接创建一个子类对象然后赋值给超类的对象变量2.方法传参
//Main.java
public class Main {public static void main(String[] args) {Animal.fun1(new Dog());}
}//Animal.java类中有如下静态方法
public static void fun1(Animal animal){}
这种通过调用超类的方法传子类的实参来达到向上转型的效果。
3.返回值
//Animal.java中有如下静态方法。
//函数内部实例化子类对象然后由于返回值要与类型匹配自动地发生向上转型。
public static Animal fun2(){Dog dog new Dog();return dog;}动态绑定
这里说明动态绑定但涉及到方法重写先忽略这个问题。 以下是三个类两个子类一个超类重点关注每个类中的eat方法.
//Dog.java
class Dog extends Animal{public static Dog Init(String name,int age){Dog dog new Dog(name,age);return dog;}public Dog(String name,int age){super(name,age);}//关注eat方法Overridepublic void eat() {System.out.println(this.name 正在吃狗粮.......);}
}
//Animal.java
class Animal {String name;int age;public Animal(String name,int age){this.namename;this.age age;}public static void fun(Animal animal){animal.eat();}//关注eat方法public void eat(){System.out.println(this.name 正在吃饭。);}}
//Bird.java
class Bird extends Animal{public static Bird Init(String name,int age){Bird bird new Bird(name,age) ;return bird;}public Bird(String name,int age){super(name,age);}//关注eat方法Override//eren jegerpublic void eat() {System.out.println(this.name 正在吃虫子.......);}
}
接下来来分析Main类了
public class Main {public static void main(String[] args) {Animal animal Dog.Init(kunkun,1);animal.eat();//这里的eat调用的是哪个类里面eat方法}
} 调用的是子类Dog为什么呢这里不是向上转型成Animal(超类)了吗 嗯这里就得介绍本标题的主角动态绑定机制
动态绑定机制简单来说以父类为编译类型创建子类对象的时候绑定子类运行类型当我们再调用方法的时候能够有序的寻找方法实现方法的有序调用。解释1.动态绑定机制在运行时调用方法绑定子类运行。 2.程序在编译时确实调用的时父类的eat方法。当运行代码的时候通过父类的引用调用了父类和子类重写的方法结果实际调用了子类的方法此时称这种情况为动态绑定。 3.程序运行并且采用动态绑定调用方法时,JVM会调用父类所引用对象的实际类型的方法。 三种解释任选理解。下面来说明方法重写。
方法重写
在上面的例子中用到方法重写我们这里详细说说。
//Dog类中子类public void eat() {System.out.println(this.name 正在吃狗粮.......);}
//Bird类中子类public void eat() {System.out.println(this.name 正在吃虫子.......);}
//Animal类中超类public void eat(){System.out.println(this.name 正在吃饭。);}显而易见Animal执行eat打印的是某某正在吃饭而Dog打印的是某某吃狗粮。 超类的有些方法对子类并不适用这里就是不够具体。每种动物的食物不同这里就可以通过方法重写来针对特定的类写对应的eat方法来覆盖原先超类的方法。 没错这就是方法重写又称覆盖方法。 若子类和超类的方法满足以下 1.方法名相同 2.参数相同参数类型参数个数参数序列相同 3.返回类型相同 则构成方法重写。 方法重写的注意事项 1.若方法前面有private static final 修饰或者其本身是构造器(构造方法)编译器本身可以确定应该调用哪些方法。这些方法不能重写。 2.方法重写时子类的访问权限一定大于等于超类的访问权限。若超类是public则子类也必须是public类不能protected等等。 3.子类和超类的覆盖方法返回类型可以不同但必须构成父子关系。 //Animal类一部分public Animal eat(){System.out.println(this.name 正在吃饭。);return null;}//Dog类一部分public Dog eat() {System.out.println(this.name 正在吃狗粮.......);return null;}
}重写与重载区别
重写与重载都是一种多态。 方法重载是类之间的多态。方法重写是子类与超类的多态。 方法重载 方法之间同名但参数列表必须修改。 返回类型访问修饰符随意。 方法重写 1.方法名必须相同 2.参数相同参数类型参数个数参数序列相同 3.返回类型相同构成父子关系可以不同 4.访问修饰符必须满足子类权限大于等于超类。 下面是补充部分
补充
向下转型
向下转型有风险。向下转型是超类强制转换成子类。这里必须借助强制转换运算符。 系统来说将一个超类类型的引用变量强制转换成子类的过程叫做向下转型。 但不是所有对象都能向下转型只要当这个对象原本就是子类向上转型过来时才能向下转型。 啥意思呢 举例
public class Main {public static void main(String[] args) {Animal animal new Animal(kunkun,1);//直接new一个超类对象向下转型Dog dog (Dog)animal;dog.eat();//结果如何呢打印Dog的eat还是Animal的eat呢}
}
结果是都不打印直接报错。
//本来是狗还原成狗可以。
public class Main {public static void main(String[] args) {Animal animal Dog.Init(kunkun,2);Dog dog (Dog)animal;dog.eat();}
}java
//本来是狗还原成鸟不合理。你家狗会飞
public class Main {public static void main(String[] args) {Animal animal Dog.Init(kunkun,2);Bird bird (Bird)animal;bird.fly();}
} 总结向下转型唯一正确的使用用法1.先向上转型让父类引用子类对象。2.向下转型类型要与最初子类类型相同。别从一个子类转换成另一个子类了。报错警告## instanceof关键字
**instanceof 是 Java 的保留关键字。它的作用是测试它左边的对象是否是它右边的类的实例返回 boolean 的数据类型。**
[instanceof关键字详解](http://t.csdnimg.cn/IIZbD)
java
public class Main {public static void main(String[] args) {Animal animal Bird.Init(kunkun,2);Bird bird (Bird)animal;if( bird instanceof Bird){bird.fly();}else{System.out.println(false);}}
}静态绑定
其实前面提过若方法前面有private static final 修饰构造器(构造方法)编译器本身可以确定在那个类调用这些方法。 像上述在这种程序执行前就已经绑定了方法在那个类中调用的方法称为静态绑定。
避免在构造方法中使用重写
//Test.java
class B {
public B() {
// do nothingfunc();
}
public void func() {System.out.println(B.func());
}
}
class D extends B {private int num 1;public void func() {System.out.println(D.func() num);}
}
public class Test {public static void main(String[] args) {D d new D();
}
}看main方法首先实例化D类对象先进入D中构造方法构造方法为默认构造方法由于继承关系子类构造还有里面还要调用父类构造方法。父类构造方法里的调用func即子类和父类方法重写由于动态绑定会调用子类的func而此时子类的num还没初始化为1默认为0.所以最终打印结果为 D.func() 0
总之构造器中最后不要调用实例化的方法调用静态绑定的方法比如finalprivate修饰的方法。因为若实例方法被子类重写那么就动态绑定了会调用子类的方法。
参考文献
Instance关键字详解静态绑定与动态绑定重载与重写—强烈建议看此篇向上转型与向下转型类加载顺序 愿此行终抵群星。