java中实现多态有几种方式(Java多中包括态理解)
java中实现多态有几种方式(Java多中包括态理解)<pre class="prettyprint hljs scala" style="padding: 0.5em; font-family: Menlo Monaco Consolas "Courier New" monospace; color: rgb(68 68 68); border-radius: 4px; display: block; margin: 0px 0px 1.5em; font-size: 14px; line-height: 1.5em; word-break: break-all; overflow-wrap: break-word; white-space: pre; background-color: rgb(246 246 246); border: none; overflow-x: au
文章目录- (1)重写(override)(2)向上转型和向下转型B:向下转型(基本不用)
- 三:静态绑定与动态绑定
- 四:不要在构造方法中调用重写的方法
比如买票行为,学生和普通人都属于人,但是学生买票可以半价,而普通人是全票
二:Java多态实现Java多态实现:和C 类似,在Java中要实现多态,必须满足以下条件- 继承是多态的前提, 没有继承就没有多态
- 子类要对父类的方法完成 重写
- 需要通过 父类的引用 调用重写的方法
- 子类重写父类时 与父类方法原型保持一致 ,也即 修饰符 返回值类型 方法名(参数列表) 需要完全一致(注意:JDK7以后,被重写的方法返回值类型可以不同,但是必须是 具有父子关系的 ,这称之为 协变 )
- 访问权限不能比父类中被重写的方法的访问权限更低 :例如,如果父类方法被 public 修饰,那么子类中重写该方法时不可以被声明为 protected
- 父类被 static 、 private 、 final 所修饰以及构造方法都不能被重写
- 重写的方法可以使用 @Override 注解来显式指定。 此注解可以帮助我们进行一些合法性检验
<pre class="prettyprint hljs java" style="padding: 0.5em; font-family: Menlo Monaco Consolas "Courier New" monospace; color: rgb(68 68 68); border-radius: 4px; display: block; margin: 0px 0px 1.5em; font-size: 14px; line-height: 1.5em; word-break: break-all; overflow-wrap: break-word; white-space: pre; background-color: rgb(246 246 246); border: none; overflow-x: auto; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-style: initial; text-decoration-color: initial;">class Animal{
String name;
int age;
public Animal(String name int age){
this.name = name;
this.age = age;
}
public void eating(){
System.out.println(name "吃饭");
}
}
class Dog extends Animal{
public Dog(String name int age){
super(name age);
}
@Override //重写父类
public void eating(){
System.out.println(name "吃鱼");
}
}
class Cat extends Animal{
public Cat(String name int age){
super(name age);
}
@Override//重写父类
public void eating(){
System.out.println(name "啃骨头");
}
}</pre>
B:重写和重载的区别
- 方法重载 :是类的多态性表现
- 方法重写 :是子类与父类的一种多态性表现
- 向上转型 是合理、安全的 ,因为 大范围可以囊括小范围
- 优点 :让代码实现更为简单、灵活
- 缺点 :不能调用子类特有的方法
<pre class="prettyprint hljs java" style="padding: 0.5em; font-family: Menlo Monaco Consolas "Courier New" monospace; color: rgb(68 68 68); border-radius: 4px; display: block; margin: 0px 0px 1.5em; font-size: 14px; line-height: 1.5em; word-break: break-all; overflow-wrap: break-word; white-space: pre; background-color: rgb(246 246 246); border: none; overflow-x: auto; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-style: initial; text-decoration-color: initial;">class Animal{
String name;
int age;
public Animal(String name int age){
this.name = name;
this.age = age;
}
public void eating(){
System.out.println(name "吃饭");
}
}
class Dog extends Animal{
public Dog(String name int age){
super(name age);
}
@Override //重写父类
public void eating(){
System.out.println(name "啃骨头");
}
}
class Cat extends Animal{
public Cat(String name int age){
super(name age);
}
@Override//重写父类
public void eating(){
System.out.println(name "吃小鱼");
}
}
public class TestDemo {
public static void main(String[] args) {
Animal animal = new Dog("旺财" 3);//向上转型
}
}</pre>
向上转型一般会有用于以下场景
- **直接赋值 **:上面代码已经说过
- **方法传参 **:形参为父类引用,可以 接受任意子类对象
- **方法返回 **:返回值为父类引用,此时可以 返回任意子类对象
<pre class="prettyprint hljs java" style="padding: 0.5em; font-family: Menlo Monaco Consolas "Courier New" monospace; color: rgb(68 68 68); border-radius: 4px; display: block; margin: 0px 0px 1.5em; font-size: 14px; line-height: 1.5em; word-break: break-all; overflow-wrap: break-word; white-space: pre; background-color: rgb(246 246 246); border: none; overflow-x: auto; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-style: initial; text-decoration-color: initial;">class Animal{
String name;
int age;
public Animal(String name int age){
this.name = name;
this.age = age;
}
public void eating(){
System.out.println(name "吃饭");
}
}
class Dog extends Animal{
public Dog(String name int age){
super(name age);
}
@Override //重写父类
public void eating(){
System.out.println(name "啃骨头");
}
}
class Cat extends Animal{
public Cat(String name int age){
super(name age);
}
@Override//重写父类
public void eating(){
System.out.println(name "吃小鱼");
}
}
public class TestDemo {
//形参为父类引用,可以接受任意子类对象
public static void eatFood(Animal animal){
animal.eating();
}
public static void main(String[] args) {
Cat cat = new Cat("喵喵" 3);
eatFood(cat);
}
}</pre>
<pre class="prettyprint hljs java" style="padding: 0.5em; font-family: Menlo Monaco Consolas "Courier New" monospace; color: rgb(68 68 68); border-radius: 4px; display: block; margin: 0px 0px 1.5em; font-size: 14px; line-height: 1.5em; word-break: break-all; overflow-wrap: break-word; white-space: pre; background-color: rgb(246 246 246); border: none; overflow-x: auto; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-style: initial; text-decoration-color: initial;">class Animal{
String name;
int age;
public Animal(String name int age){
this.name = name;
this.age = age;
}
public void eating(){
System.out.println(name "吃饭");
}
}
class Dog extends Animal{
public Dog(String name int age){
super(name age);
}
@Override //重写父类
public void eating(){
System.out.println(name "啃骨头");
}
}
class Cat extends Animal{
public Cat(String name int age){
super(name age);
}
@Override//重写父类
public void eating(){
System.out.println(name "吃小鱼");
}
}
public class TestDemo {
//方法返回值为父类引用,此时可以返回任意子类对象
public static Animal buyAnAnimal(String animal_name){
if(animal_name.equals("狗")) {
return new Dog("旺财" 2);
}
else if(animal_name.equals("猫")) {
return new Cat("喵喵" 3);
}
else{
return null;
}
}
public static void main(String[] args) {
System.out.println("进入宠物店:请问你需要什么动物");
Scanner sc = new Scanner(System.in);
String animal_name = sc.nextLine();
Animal animal = buyAnAnimal(animal_name);
System.out.println("你得到了:" animal.name);
sc.close();
}
}</pre>
如下是向上转型, Animal animal = new Dog("旺财" 2) ,所以 animal 是无法调用 Dog 的特有方法 dog_method() 的
<pre class="prettyprint hljs java" style="padding: 0.5em; font-family: Menlo Monaco Consolas "Courier New" monospace; color: rgb(68 68 68); border-radius: 4px; display: block; margin: 0px 0px 1.5em; font-size: 14px; line-height: 1.5em; word-break: break-all; overflow-wrap: break-word; white-space: pre; background-color: rgb(246 246 246); border: none; overflow-x: auto; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-style: initial; text-decoration-color: initial;">class Animal{
String name;
int age;
public Animal(String name int age){
this.name = name;
this.age = age;
}
public void eating(){
System.out.println(name "吃饭");
}
}
class Dog extends Animal{
public Dog(String name int age){
super(name age);
}
@Override //重写父类
public void eating(){
System.out.println(name "啃骨头");
}
public void dog_method(){
System.out.println(name "的特有方法");
}
}
class Cat extends Animal{
public Cat(String name int age){
super(name age);
}
@Override//重写父类
public void eating(){
System.out.println(name "吃小鱼");
}
}
public class TestDemo {
public static void function(Animal animal){
animal.eating();
}
public static void main(String[] args) {
Animal animal = new Dog("旺财" 2);
animal.dog_mehtod();
}
}</pre>
但是我们可以向下转型,也即 让一个子类的引用指向强转后的父类对象 ,此时便可以调用 Dog 的特有方法 dog_method() 了
<pre class="prettyprint hljs java" style="padding: 0.5em; font-family: Menlo Monaco Consolas "Courier New" monospace; color: rgb(68 68 68); border-radius: 4px; display: block; margin: 0px 0px 1.5em; font-size: 14px; line-height: 1.5em; word-break: break-all; overflow-wrap: break-word; white-space: pre; background-color: rgb(246 246 246); border: none; overflow-x: auto; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-style: initial; text-decoration-color: initial;">public class TestDemo {
public static void function(Animal animal){
animal.eating();
}
public static void main(String[] args) {
Animal animal = new Dog("旺财" 2);
Dog dog = (Dog)animal;//向下转型
dog.dog_method();
}
}</pre>
向下转型很不安全,而且有时会有严重的歧义。 Java中为 了提高向下转型的安全性,引入了 instanceof ,如果该表达式为 true 则可以安全转换
(3)实现多态讲到这里,其实已经完成了多态,所以日后我们就可以利用继承和转型来完成 传哪个对象就调用哪个对象的方法的目的
<pre class="prettyprint hljs java" style="padding: 0.5em; font-family: Menlo Monaco Consolas "Courier New" monospace; color: rgb(68 68 68); border-radius: 4px; display: block; margin: 0px 0px 1.5em; font-size: 14px; line-height: 1.5em; word-break: break-all; overflow-wrap: break-word; white-space: pre; background-color: rgb(246 246 246); border: none; overflow-x: auto; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-style: initial; text-decoration-color: initial;">class Animal{
String name;
int age;
public Animal(String name int age){
this.name = name;
this.age = age;
}
public void eating(){
System.out.println(name "吃饭");
}
}
class Dog extends Animal{
public Dog(String name int age){
super(name age);
}
@Override //重写父类
public void eating(){
System.out.println(name "啃骨头");
}
}
class Cat extends Animal{
public Cat(String name int age){
super(name age);
}
@Override//重写父类
public void eating(){
System.out.println(name "吃小鱼");
}
}
public class TestDemo {
public static void function(Animal animal){
animal.eating();
}
public static void main(String[] args) {
Dog dog = new Dog("旺财" 2);
Cat cat = new Cat("喵喵" 3);
function(dog);
function(cat);
}
}</pre>
- **静态绑定 **:也称为 早绑定 ,是指 在编译时根据用户所传递实参类型确定所调用的方法 ,最典型的例子就是 方法重载
- **动态绑定 **:也称为 晚绑定 ,是指 在编译时不能确定该调用的方法,需要在程序运行确定 ,最典型的例子就是 多态 ,在程序运行时才能拿到具体的类型
下面的代码中,B继承自A, func 方法被重写。在构造B对象时会调用A的构造方法,在A的构造方法中调用了 func 方法,但此时是B对象,所以会调用到B的 func ,而B对象而没有完成构造,所以 num 处于未被初始化状态,为0
<pre class="prettyprint hljs java" style="padding: 0.5em; font-family: Menlo Monaco Consolas "Courier New" monospace; color: rgb(68 68 68); border-radius: 4px; display: block; margin: 0px 0px 1.5em; font-size: 14px; line-height: 1.5em; word-break: break-all; overflow-wrap: break-word; white-space: pre; background-color: rgb(246 246 246); border: none; overflow-x: auto; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-style: initial; text-decoration-color: initial;">class A{
public A(){
func();
}
public void func(){
System.out.println("A的func()方法");
}
}
class B extends A{
private int num = 1;
@Override
public void func(){
System.out.println("B的func()方法 num=" num);
}
}
public class TestDemo{
public static void main(String[] args) {
B b = new B();
b.func();
}
}</pre>
如下一个类 Shape ,其中有一个方法 draw ,作用是向屏幕打印某个具体的图形(没有具体实现),图形有很多种,这里给出了 Rectangle 、 Triangle 和 Circle 三个类,分别用于描述矩形、三角形和圆形,它们各自内部也有一个方法 draw ,重写了父类的方法,形成多态
<pre class="prettyprint hljs scala" style="padding: 0.5em; font-family: Menlo Monaco Consolas "Courier New" monospace; color: rgb(68 68 68); border-radius: 4px; display: block; margin: 0px 0px 1.5em; font-size: 14px; line-height: 1.5em; word-break: break-all; overflow-wrap: break-word; white-space: pre; background-color: rgb(246 246 246); border: none; overflow-x: auto; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-style: initial; text-decoration-color: initial;">class Shape{
public void draw(){
System.out.println("画一个图形");
}
}
class Rectangle extends Shape{
@Override
public void draw(){
System.out.println("画一个矩形");
}
}
class Triangle extends Shape{
@Override
public void draw(){
System.out.println("画一个三角形");
}
}
class Circle extends Shape{
@Override
public void draw(){
System.out.println("画一个三角形");
}
}
public class TestDemo {
public static void drawing(Shape input){
input.draw();
}
public static void main(String[] args) {
Circle circle = new Circle();
drawing(circle);
}
}</pre>
对于父类的方法,它的实现是没有意义的, 因为“画一个图形”这样的话太过抽象 ,无法具体去描述。 所以,我们可以不用实现它,将其改造为抽象方法,需要在方法返回值前加入关键字 abstract ,当某个类拥有抽象方法后它就变成了抽象类,也需要在其前面加入关键字 abstract 进行修饰
- 注意
- 注意:抽象方法就是C 中的纯虚函数
<pre class="prettyprint hljs scala" style="padding: 0.5em; font-family: Menlo Monaco Consolas "Courier New" monospace; color: rgb(68 68 68); border-radius: 4px; display: block; margin: 0px 0px 1.5em; font-size: 14px; line-height: 1.5em; word-break: break-all; overflow-wrap: break-word; white-space: pre; background-color: rgb(246 246 246); border: none; overflow-x: auto; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-style: initial; text-decoration-color: initial;">abstract class Shape{
abstract public void draw();//抽象方法
}
class Rectangle extends Shape{
@Override
public void draw(){
System.out.println("画一个矩形");
}
}
class Triangle extends Shape{
@Override
public void draw(){
System.out.println("画一个三角形");
}
}
class Circle extends Shape{
@Override
public void draw(){
System.out.println("画一个三角形");
}
}
public class TestDemo {
public static void drawing(Shape input){
input.draw();
}
public static void main(String[] args) {
Circle circle = new Circle();
drawing(circle);
}
}</pre>
(2)抽象类概念抽象类:在面向对象方法学中,所有对象都是需要通过类来描绘的,但这不意味着所有的类都是用来描绘对象的,如果一个类中没有包含足够的信息来描绘一个具体的对象,这样的类就应该设计为抽象类
- 例如上例中 Shape 类中也存在 draw 方法,但是 Shape 它并代表某个具体图形,是没有办法来实现的
⑤:抽象方法 不能被 final 和 static 修饰