问题描述
Java中为什么静态方法不能被重写?为什么静态方法不能隐藏实例方法?诸如此类。
前期准备
Person student= new Student(); student.work();
例子
package com.learn.pra06; class Demo01{ public void method1(){ System.out.println("This is father non-static"); } public static void method2(){ System.out.println("This is father static"); } } public class Demo02 extends Demo01{
public void method1(){ System.out.println("This is son non-static"); } public static void method2(){ System.out.println("This is son static"); } public static void main(String[] args){ Demo01 d1= new Demo01(); Demo02 d2= new Demo02(); Demo01 d3= new Demo02(); //父类引用指向子类对象 d1.method1(); d1.method2(); d2.method1(); d2.method2(); d3.method1(); d3.method2(); } }
分析
首先看看上面main 方法的字节码:
// access flags 0x9 public static main([Ljava/lang/String;)V L0 LINENUMBER 19 L0 NEW com/learn/pra06/Demo01 DUP INVOKESPECIAL com/learn/pra06/Demo01.
()V ASTORE
1 L1 LINENUMBER
20 L1 NEW com/learn/pra06/Demo02 DUP INVOKESPECIAL com/learn/pra06/Demo02.
()V ASTORE
2 L2 LINENUMBER
21 L2 NEW com/learn/pra06/Demo02 DUP INVOKESPECIAL com/learn/pra06/Demo02.
()V ASTORE
3 L3 LINENUMBER
22 L3 ALOAD
1 INVOKEVIRTUAL com/learn/pra06/Demo01.method1 ()V L4 LINENUMBER
23 L4 INVOKESTATIC com/learn/pra06/Demo01.method2 ()V L5 LINENUMBER
24 L5 ALOAD
2 INVOKEVIRTUAL com/learn/pra06/Demo02.method1 ()V L6 LINENUMBER
25 L6 INVOKESTATIC com/learn/pra06/Demo02.method2 ()V L7 LINENUMBER
26 L7 ALOAD
3 INVOKEVIRTUAL com/learn/pra06/Demo01.method1 ()V L8 LINENUMBER
27 L8 INVOKESTATIC com/learn/pra06/Demo01.method2 ()V L9 LINENUMBER
28 L9 RETURN L10
编译时把对象的静态类型(声明类型)作为该方法的接受者。运行时则根据指令集再进行更改。
INVOKEVIRTUAL指令流程
package com.learn.pra06; public class ClassReference {
static class Person { @Override public String toString(){ return "I'm a person."; } public void eat(){ System.out.println("Person eat"); } public void speak(){ System.out.println("Person speak"); } } static class Boy extends Person{ @Override public String toString(){ return "I'm a boy"; } @Override public void speak(){ System.out.println("Boy speak"); } public void fight(){ System.out.println("Boy fight"); } } static class Girl extends Person{ @Override public String toString(){ return "I'm a girl"; } @Override public void speak(){ System.out.println("Girl speak"); } public void sing(){ System.out.println("Girl sing"); } } public static void main(String[] args) { Person boy = new Boy(); Person girl = new Girl(); System.out.println(boy); boy.eat(); boy.speak(); System.out.println(girl); girl.eat(); girl.speak(); } }
public static main([Ljava/lang/String;)V L0 LINENUMBER 47 L0 NEW com/learn/pra06/ClassReference$Boy DUP INVOKESPECIAL com/learn/pra06/ClassReference$Boy.
()V ASTORE
1 L1 LINENUMBER
48 L1 NEW com/learn/pra06/ClassReference$Girl DUP INVOKESPECIAL com/learn/pra06/ClassReference$Girl.
()V ASTORE
2 L2 LINENUMBER
49 L2 GETSTATIC java/lang/System.out : Ljava/io/PrintStream; ALOAD
1 INVOKEVIRTUAL java/io/PrintStream.println (Ljava/lang/Object;)V L3 LINENUMBER
50 L3 ALOAD
1 INVOKEVIRTUAL com/learn/pra06/ClassReference$Person.eat ()V L4 LINENUMBER
51 L4 ALOAD
1 INVOKEVIRTUAL com/learn/pra06/ClassReference$Person.speak ()V L5 LINENUMBER
53 L5 GETSTATIC java/lang/System.out : Ljava/io/PrintStream; ALOAD
2 INVOKEVIRTUAL java/io/PrintStream.println (Ljava/lang/Object;)V L6 LINENUMBER
54 L6 ALOAD
2 INVOKEVIRTUAL com/learn/pra06/ClassReference$Person.eat ()V L7 LINENUMBER
55 L7 ALOAD
2 INVOKEVIRTUAL com/learn/pra06/ClassReference$Person.speak ()V L8 LINENUMBER
57 L8 RETURN L9
为什么静态方法不能隐藏实例方法?
静态方法的调用的是通过在编译器静态绑定的,而实例方法的调用是在运行时动态绑定的,2者的调用的方式不同,所以二者只能存在其一,否则会存在歧义!
为什么静态方法能隐藏静态方法?
因为调用方式一致,不会像上面造成歧义,虽然父类和子类都定义了同样的函数,但是编译器会根据对象的静态类型激活对应的静态方法的引用,造成了重写的假象,实则不是重写!
总结
总体流程就是:编译器将类编译成class文件,其中方法会根据静态类型从而将对应的方法引用写入class中,运行时,JVM会根据INVOKEVIRTUAL 所指向的方法引用在常量池找到该方法的偏移量,再根据this找到引用类型真实指向的对象,访问这个对象类型的方法表,根据偏移量找出存放目标方法引用的位置,取出这个引用,调用这个引用实际指向的方法,完成多态!
发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/210722.html原文链接:https://javaforall.net
