一,函数绑定
函数绑定分为动态绑定和静态绑定。(绑定指的是调用)
当写完代码后,可使用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时,说明子类不能访问该变量,但此时该变量仍会被子类继承
下来,只是不能访问它。对于被继承下来的方法和变量,可直接用 对象 . 方法 或 对象 . 变量 来掉用。