JavaSE自学笔记012

JavaSE自学笔记012,第1张

JavaSE自学笔记012 JavaSE自学笔记012_Real(多态polymorphism) (一)概述

多态形成的三个条件:有继承 有重写 有父类引用指向子类对象
多态形成:animal类中有Dog类和Cat类,我们在方法的参数列表中有animal类型,则在调用方法时,传入的参数可以时Dog或者Cat类对象

代码案例实现(养宠物):

//宠物类(Pet类)
public class Pet {
    public void coquetry(){
        System.out.println("宠物在撒娇");
    }
}
//狗类(Dog类)
public class Dog extends Pet{
    @Override
    public void coquetry() {
        System.out.println("狗在撒娇!");
    }
}
//猫类(Cat类)
public class Cat extends Pet{
    @Override
    public void coquetry() {
        System.out.println("猫在撒娇!");
    }
}
//Girl类
public class Girl {
    public void feed(Pet pet){
        pet.coquetry();
    }

    public static void main(String[] args) {
        Girl girl = new Girl();
        Dog dog = new Dog();
        Pet cat = new Cat();

        girl.feed(dog);
        girl.feed(cat);
        girl.feed(new Pet());
    }
}

输出结果:
狗在撒娇!
猫在撒娇!
宠物在撒娇
(三)多态的底层逻辑

1、字节码分析:
一段程序从写代码到代码运行会经历编译和运行两个阶段,编译是将.java文件转换为jvm识别的字节码文件,jvm会将字节码文件加载到内存,并执行。
下面一行代码:Animal animal = Math.random() > 5? new Dog() : new Cat();
当代码还没运行的额时候,我们不知道运行结果animal是Dog类型还是Cat类型,只有运行之后才会知道。

我们将Animal叫做animal的静态类型、编译类型、申明类型、外观类型,将右侧的Dog和Cat叫做动态类型、运行时类型、实际类型。

【常量池】是我们的资源仓库,里面存放了大量的【符号引用】(就是我们给类、方法、变量起的名字),这些符号引用有一部分是在类加载阶段或者第一次使用的时候就转化为【直接引用】,这种转化叫做【静态解析】,另一部分会在与逆行期间转化为【直接引用】,这一部分叫做动态解析。

2、字节码反编译的结果:
jvm生成的.class文件

PS D:NewSoftwaresJavasooftwarejavaprojectsJavaSEBasicProjectsoutp
roductioninheritancecomydlclass> javap -v .computer.class
Classfile /D:/NewSoftwares/Javasooftware/javaprojects/JavaSEBasicProjec
ts/out/production/inheritance/com/ydlclass/computer.class
  Last modified 2021年11月23日; size 678 bytes
  MD5 checksum c17cb3f2a3937047a0dc64b896a5befa
  Compiled from "Computer.java"
public class com.ydlclass.Computer
  minor version: 0
  major version: 55
  flags: (0x0021) ACC_PUBLIC, ACC_SUPER
  this_class: #2                          // com/ydlclass/Computer
  super_class: #7                         // java/lang/Object
  interfaces: 0, fields: 0, methods: 3, attributes: 1
Constant pool:
   #1 = Methodref          #7.#27         // java/lang/Object."":
()V
   #2 = Class              #28            // com/ydlclass/Computer
   #3 = Methodref          #2.#27         // com/ydlclass/Computer."":()V
   #4 = Fieldref           #29.#30        // java/lang/System.out:Ljava
/io/PrintStream;
   #5 = Methodref          #2.#31         // com/ydlclass/Computer.plus
:(II)I
   #6 = Methodref          #32.#33        // java/io/PrintStream.printl
n:(I)V
   #7 = Class              #34            // java/lang/Object
   #8 = Utf8               
   #9 = Utf8               ()V
  #10 = Utf8               Code
  #11 = Utf8               LineNumberTable
  #12 = Utf8               LocalVariableTable
  #13 = Utf8               this
  #14 = Utf8               Lcom/ydlclass/Computer;
  #15 = Utf8               plus
  #16 = Utf8               (II)I
  #17 = Utf8               i
  #18 = Utf8               I
  #19 = Utf8               j
  #20 = Utf8               main
  #21 = Utf8               ([Ljava/lang/String;)V
  #22 = Utf8               args
  #23 = Utf8               [Ljava/lang/String;
  #24 = Utf8               computer
  #25 = Utf8               SourceFile
  #26 = Utf8               Computer.java
  #27 = NameAndType        #8:#9          // "":()V
  #28 = Utf8               com/ydlclass/Computer
  #29 = Class              #35            // java/lang/System
  #30 = NameAndType        #36:#37        // out:Ljava/io/PrintStream;
  #31 = NameAndType        #15:#16        // plus:(II)I
  #32 = Class              #38            // java/io/PrintStream
  #33 = NameAndType        #39:#40        // println:(I)V
  #34 = Utf8               java/lang/Object
  #35 = Utf8               java/lang/System
  #36 = Utf8               out
  #37 = Utf8               Ljava/io/PrintStream;
  #38 = Utf8               java/io/PrintStream
  #39 = Utf8               println
  #40 = Utf8               (I)V
{
  public com.ydlclass.Computer();
    descriptor: ()V
    flags: (0x0001) ACC_PUBLIC
    Code:
      stack=1, locals=1, args_size=1
         0: aload_0
         1: invokespecial #1                  // Method java/lang/Objec
t."":()V
         4: return
      LineNumberTable:
        line 3: 0
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
            0       5     0  this   Lcom/ydlclass/Computer;

  public int plus(int, int);
    descriptor: (II)I
    flags: (0x0001) ACC_PUBLIC
    Code:
      stack=2, locals=3, args_size=3
         0: iload_1
         1: iload_2
         2: iadd
         3: ireturn
      LineNumberTable:
        line 5: 0
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
            0       4     0  this   Lcom/ydlclass/Computer;
            0       4     1     i   I
            0       4     2     j   I

  public static void main(java.lang.String[]);
    descriptor: ([Ljava/lang/String;)V
    flags: (0x0009) ACC_PUBLIC, ACC_STATIC
    Code:
      stack=4, locals=2, args_size=1
         0: new           #2                  // class com/ydlclass/Com
puter
         3: dup
         4: invokespecial #3                  // Method "":()V
         7: astore_1
         8: getstatic     #4                  // Field java/lang/System
.out:Ljava/io/PrintStream;
        11: aload_1
        12: iconst_2
        13: iconst_3
        14: invokevirtual #5                  // Method plus:(II)I
        17: invokevirtual #6                  // Method java/io/PrintSt
ream.println:(I)V
        20: return
      LineNumberTable:
        line 9: 0
        line 10: 8
        line 11: 20
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
            0      21     0  args   [Ljava/lang/String;
            8      13     1 computer   Lcom/ydlclass/Computer;
}
SourceFile: "Computer.java"

3、重载方法的静态分派过程

//Human类
public class Human {
}
//Man类
public class Man extends Human{
}
//Woman类
public class Woman extends Human{
}
//Party类
public class Party {
    public void play(Human human){
        System.out.println("人类的狂欢");
    }
    public void play(Man man){
        System.out.println("男人的狂欢");
    }
    public void play(Woman woman){
        System.out.println("女人的狂欢");
    }

    public static void main(String[] args) {
        Party party = new Party();
        Human human = new Human();
        party.play(human);
        Human man = new Man();
        party.play(man);
        Human woman = new Woman();
        party.play(woman);
    }
}
运行结果:
人类的狂欢
人类的狂欢
人类的狂欢

虚拟机在选择重载方法的时候,使用过【静态类型】决定的而不是【动态类型】,由于静态类型编译的时候就可以知道,事实上虚拟机在编译期就已经知道选择哪一个重载方法,并且把这个方法的符号i引用到invokevirtual的指令中。

所有依赖【静态类型】决定党法执行的分派动作称之为静态分派,JVM帮助我们选择一个合适的方法的时候,也是尽最大努力,选择它认为最适合的版本,因为确实存在诸如自动拆装箱,对象转型等问题。

3、重载方法和重写方法的综合使用
代码实现:

//Animal类
public class Animal {
    public void eat(){
        System.out.println("animal id eating");
    }
    public void eat(String food){
        System.out.println("animal is eating "+food);
    }
}
//Dog类
public class Dog extends Animal{
    @Override
    public void eat(){
        System.out.println("Dog is eating");
    }
    @Override
    public void eat(String food){
        System.out.println("Dog is eating "+food);
    }

    public static void main(String[] args) {
        Animal animal = new Dog();
        animal.eat("meat");
    }
}
运行结果:
Dog is eatingmeat

在JVM运行程序的时候,先进行静态解析,确定animal的类型为Animal,然后进行动态分派,确定动态类型为Dog()类型,然后调用Dog()中的eat(String food)方法.

注意:多态只和方法有关,和属性没有关系

(四)对象转型

向上转型:子类对象转为父类,向上转型不需要显式的转化 Father father = son;
向上转型会丢失子类独有的特性
向下转型:父类对象转化为子类,向下转型需要强制转化 Son son = (Son)father;

欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/zaji/5597658.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-12-15
下一篇 2022-12-15

发表评论

登录后才能评论

评论列表(0条)

保存