函数绑定

一,函数绑定

  函数绑定分为动态绑定和静态绑定。(绑定指的是调用)

  当写完代码后,可使用javap  -c  java文件.class,来查看java编译器为我们生成的字节码。(反汇编过程)

  具体操作:1,先在程序编辑界面,右击鼠标,然后选择 show in Explorer 就会将该代码生成的文件所在目录呈现出来。

       2,点击地址栏中的工程名,找到 out文件,打开并找工程名。

       3,找到该类的字节码文件,将地址栏中的地址删除,输入cmd回车,就会出现命令编辑器。

        4,输入   Javap -c 文件名.class   回车就会出现字节码文件。

              

 在这段字节码中,第6行invokespecia指令代表调用构造函数,第10行invokestatic指令就是在以静态绑定的方法调用函数,第14行 invokevirtual指令就是在以动态绑定的方法调用函数

  ♥ static方法都是静态绑定调用,实例方法都是动态绑定调用

  ♥ 静态绑定,又可叫早期绑定,指的是编译时期的绑定,如果被调用的目标方法在编译阶段就是确定好的,且运行期保持不变,这种情况下就将调用方法的符号引用转化为直接引用。(#7 = Class)

  ♥动态绑定,又可叫晚期绑定,指的是运行时期的绑定,如果被调用的方法在编译阶段无法确定下来,也就是说,只能在程序运行期将调用的方法符号引用转化为直接引用,由于这种转化过程具备动态性,因此也被称为动态链接。

 1 class A{
 2     protected int a;
 3     private String str;
 4     private Integer data;
 5 
 6     public A(int val){        //构造函数
 7         System.out.println("A()");
 8         this.a = val;
 9     }
10 
11     public static void show(){     //静态方法
12         System.out.println("static A.show");
13     }
14 
15     public void func(){        //实例方法 
16         System.out.println("instance A.func");
17     }
18     //此方法是对上面方法的重载
19     public void func(int data){    //因为方法名相同,返回类型相同,仅参数列表不同
20         System.out.println("instance A.func data");
21     }
22 }
23 
24 class B extends A{
25 
26     public B(int val) {
27         super(val);
28     }
29 
30     public static void show(){
31         System.out.println("static B.show");
32     }
33 
34     // 就构成重写(覆盖)的关系
35     public void func(){     //返回值类型,方法名,参数列表相同,仅作用域不同
36         System.out.println("instance B.func");
37     }
38 }
39 
40 
41 
42 public class 函数绑定 {
43     public static void main(String[] args) {
44         //B b = new B(20); // invokespecial 构造函数
45         //B.show(); // invokestatic 静态方法
46         //b.func(); // invokevirtual 实例方法
47         A a = new B(20);
48         A.show(); // 静态绑定A.show
49         a.func(); //  动态绑定A.func   instance B.func
50     }

 运行结果:

 

代码分析:

     第一行是因为在新new一个对象时,JVM会自动调用该对象的构造函数。

     第二行 A.show( )为静态绑定,在编译阶段,就确定好了调用那个方法,所以此处运行后,哪个类调用方法,就直接调用哪个类的静态方法show( )。

     第三行 a.func( )为动态绑定,在运行时才能确定调用哪个方法。A a = new B(20);此句是说新定义了一个A型变量a,然后又新new了一个B型对象,并将该对象的引用赋给变量a,而此处a.func( )运行后,机器识别出func()方法为动态方法,需要动态绑定,然后通过 a 找到在堆上的对象B,然后通过对象B的地址末尾的方法表地址又找到B类型的方法表,在里面查找,发现 在B类中继承的A类 中的func() 方法的地址已被重写。所以此时访问到的func( )方法就是被B类重写后的方法。所以打印出来就是B类中的。

 

注意

1.

———————图一———————————————————————————————

  

 ———————图二———————————————————————————————

—————————————————————————————————————— 

   图一说明:基类引用可以引用派生类对象

   图二说明:派生类引用不能引用基类对象

(形象理解:将人类比作基类,将教授比作派生类,A a = new B(20);说明此处需要一个人,所以将教授派去完全可以。 B b = new A(20);而这句报错是因为,此处需要一个教授,而你给的是一个人,这个人可能只是一个学生,并不能满足需求。)

2.把基类和派生类的继承结构,也经常称作从上到下的继承结构继承结构中的类型,只支持从下到上的转换,

   不支持从上到下的转换。(也就是说派生类[教授]可以转换为基类[人],但基类[人]不一定能转换成派生类[教授]。)

 扩充:

        1, final的应用场景有三个:

               1.final int data = 10; 可以用来定义常量

               2.final可以修饰类称作密封类,不能再被继承

       3.final可以修饰类的实例方法,称作密封方法,表示该方法不能再派生类中重写(覆盖)

   使用final关键字做标识有“最终的”含义  :

    ( final可以修饰类、方法、属性、变量 )

    final修饰类,则该类不允许被继承

    final修饰方法,则该方法不允许被覆盖(重写)

    final修饰属性,则该类的属性不会进行隐式的初始化(类的初始化属性必须有值),或者在构造方法中赋值(但只能选其一)

    final修饰变量,则该变量的值只能赋一次值,即常量

        2,继承结构中,基类和派生类的方法通常有两种关系:重载和重写

            重载:在一个类作用域中,函数名相同,参数列表不同

            重写:作用域不同,在基类和派生类中,返回值相同,函数名相同,参数列表也相同

                  ( 重写指的是派生类方法表中,派生类提供的重写方法,把基类相应的方法的地址给重写了(覆盖了))

        3,当父类中的成员变量的访问权限为private时,说明子类不能访问该变量,但此时该变量仍会被子类继承

       下来,只是不能访问它。对于被继承下来的方法和变量,可直接用     对象 . 方法  或 对象 . 变量  来掉用。

原文地址:https://www.cnblogs.com/ljl150/p/11694429.html