Java之静态绑定和动态绑定

概念

  • 程序绑定:绑定指的是一个方法的调用与方法所在的类(方法主体)关联起来,Java中绑定分为绑定分为 静态绑定动态绑定
  • 动态绑定:在面向过程中又称为后期绑定,在程序运行期进行了绑定,根据实际情况有选择的进行绑定
  • 静态绑定:在面向过程中又称为前期绑定,在程序编译期进行了绑定,即在还没运行时,就已经加载到内存

    对比

  • 动态绑定
    • 又称为后期绑定
    • 发生在运行时期
    • 虚方法(可以被子类重写的方法)会根据运行时的对象进行动态绑定
    • 动态绑定使用对象信息来完成
    • 典型应用是方法的重写(Override)
  • 静态绑定
    • 又称为前期绑定
    • 发生在编译时期
    • 使用private或static或final修饰的变量或者方法(包括构造方法)
    • 静态绑定使用类信息来完成
    • 典型应用是方法重载(Overload)
  • 运行期
    Java的编译过程是将Java源文件编译成字节码(.class文件,JVM可执行代码)的过程,在这个过程中Java是不与内存打交道的,在这个过程中编译器会进行语法的分析,如果语法不正确就会报错
  • 编译期
    Java的运行过程是指JVM(Java虚拟机)装载字节码文件并解释执行,在这个过程才是真正的创建内存,执行Java程序

Java字节码的执行有两种方式:

  • 即时编译方式:解释器先将字节编译成机器码,然后再执行该机器码
  • 解释执行方式:解释器通过每次解释并执行一小段代码来完成java字节码程序的所有操作。

Java程序在执行过程中其实是进行了两次转换,先将源文件转成字节码再转换成机器码。这也正是Java能一次编译,到处运行的原因。在不同的平台上装上对应的Java虚拟机,就可以实现相同的字节码转换成不同平台上的机器码,从而在不同的平台上运行

验证

关于final、static、private和构造方法是前期绑定的理解:

  • private
    对于private的方法,首先它对外是不可见的,所以肯定不能被继承,那么就没办法通过子类的对象来调用,而只能通过类自身的对象来调用,因此就可以说private方法和定义这个方法的类绑定在了一起
  • final
    final方法虽然可以被继承,但不能被重写(覆盖),虽然子类对象可以调用,但是调用的都是父类中所定义的那个final方法,(由此我们可以知道将方法声明为final类型,一是为了防止方法被覆盖,二是为了有效地关闭java中的动态绑定)
  • static
    对于static方法,可以被子类继承,但是不能被子类重写(覆盖),但是可以被子类隐藏
    就是说如果父类里有一个static方法,它的子类里如果没有对应的方法,那么当子类对象调用这个方法时就会使用父类中的方法。而如果子类中定义了相同的方法,则会调用子类的中定义的方法。唯一的不同就是,当子类对象向上转型为父类对象时,不论子类中有没有定义这个静态方法,该对象都会使用父类中的静态方法。因此这里说静态方法可以被隐藏而不能被覆盖。这与子类隐藏父类中的成员变量是一样的。隐藏和覆盖的区别在于,子类对象转换成父类对象后,能够访问父类被隐藏的变量和方法,而不能访问父类被覆盖的方法
    由上面我们可以得出结论,如果一个方法不可被继承或者继承后不可被覆盖,那么这个方法就采用的静态绑定。
  • 构造
    构造方法也是不能被继承的,我们知道子类是通过super()来调用父类的无参构造方法,来完成对父类的初始化,因此编译时也可以知道这个构造方法到底是属于哪个类

示例代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
public class SuperClass {
protected String attribute = "from SuperClass";
public String getAttribute() {
return attribute;
}
public static void print(SuperClass superClass) {
System.out.println(" static method " + superClass.attribute);
}
}
public class SubClass extends SuperClass {
protected String attribute = "from SubClass";
public String getAttribute() {
return attribute;
}
public static void print(SuperClass superClass) {
System.out.println(" static method " + superClass.attribute);
}
public static void main(String[] args) {
SuperClass superClass = new SubClass();
SubClass subClass = new SubClass();
superClass.print(superClass);
subClass.print(subClass);
System.out.println(" attribute " + superClass.attribute);
System.out.println(" method " + superClass.getAttribute());
}
}

输出结果

1
2
3
4
static method from SuperClass
static method from SuperClass
attribute from SuperClass
method from SubClass

反编译

使用JDK自带的javap命令反编译看看:
>javap -c SubClass

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
Warning: Binary file SubClass contains com.gogh.bind.SubClass
Compiled from "SubClass.java"
public class com.gogh.bind.SubClass extends com.gogh.bind.SuperClass {
protected java.lang.String attribute;
public com.gogh.bind.SubClass();
Code:
0: aload_0
1: invokespecial #10 // Method com/gogh/bind/SuperClass."<init>":()V
4: aload_0
5: ldc #12 // String from SubClass
7: putfield #14 // Field attribute:Ljava/lang/String;
10: return
public java.lang.String getAttribute();
Code:
0: aload_0
1: getfield #14 // Field attribute:Ljava/lang/String;
4: areturn
public static void print(com.gogh.bind.SuperClass);
Code:
0: getstatic #24 // Field java/lang/System.out:Ljava/io/PrintStream;
3: new #30 // class java/lang/StringBuilder
6: dup
7: ldc #32 // String static method
9: invokespecial #34 // Method java/lang/StringBuilder."<init>":(Ljava/lang/String;)V
12: aload_0
13: getfield #37 // Field com/gogh/bind/SuperClass.attribute:Ljava/lang/String;
16: invokevirtual #38 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
19: invokevirtual #42 // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
22: invokevirtual #45 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
25: return
public static void main(java.lang.String[]);
Code:
0: new #1 // class com/gogh/bind/SubClass
3: dup
4: invokespecial #54 // Method "<init>":()V
7: astore_1
8: new #1 // class com/gogh/bind/SubClass
11: dup
12: invokespecial #54 // Method "<init>":()V
15: astore_2
16: aload_1
17: invokestatic #55 // Method com/gogh/bind/SuperClass.print:(Lcom/gogh/bind/SuperClass;)V
20: aload_2
21: invokestatic #57 // Method print:(Lcom/gogh/bind/SuperClass;)V
24: getstatic #24 // Field java/lang/System.out:Ljava/io/PrintStream;
27: new #30 // class java/lang/StringBuilder
30: dup
31: ldc #58 // String attribute
33: invokespecial #34 // Method java/lang/StringBuilder."<init>":(Ljava/lang/String;)V
36: aload_1
37: getfield #37 // Field com/gogh/bind/SuperClass.attribute:Ljava/lang/String;
40: invokevirtual #38 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
43: invokevirtual #42 // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
46: invokevirtual #45 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
49: getstatic #24 // Field java/lang/System.out:Ljava/io/PrintStream;
52: new #30 // class java/lang/StringBuilder
55: dup
56: ldc #60 // String method
58: invokespecial #34 // Method java/lang/StringBuilder."<init>":(Ljava/lang/String;)V
61: aload_1
62: invokevirtual #62 // Method com/gogh/bind/SuperClass.getAttribute:()Ljava/lang/String;
65: invokevirtual #38 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
68: invokevirtual #42 // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
71: invokevirtual #45 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
74: return
}

通过javap我们只能看到静态绑定的部分,就是两个print方法的调用和superClass.attribute,直接看main方法里面的内容:

  • 17: invokestatic #55 // Method com/gogh/bind/SuperClass.print:(Lcom/gogh/bind/SuperClass;)V
    调用的SuperClass.print方法
  • 21: invokestatic #57 // Method print:(Lcom/gogh/bind/SuperClass;)V
  • 37: getfield #37 // Field com/gogh/bind/SuperClass.attribute:Ljava/lang/String;
    这个也是调用的SuperClass.print方法

    总结

    Java中的static方法和final方法属于前期绑定,子类无法重写final方法,除了static方法和final方法之外的其他方法属于后期绑定,运行时能判断对象的类型进行绑定。

与方法不同,在处理Java类中的成员变量(静态和非静态)时,并不是采用运行时绑定,而是一般意义上的静态绑定。所以在向上转型的情况下,对象的方法可以找到子类,而对象的属性(成员变量)还是父类的属性(子类对父类成员变量的隐藏)。

Java因为什么对属性要采取静态的绑定方法?这是因为静态绑定是有很多的好处,它可以让我们在编译期就发现程序中的错误,而不是在运行期,这样就可以提高程序的运行效率!由于动态绑定需要在运行时确定执行哪个方法实现或者变量,比起静态绑定起来要耗时。对方法采取动态绑定是为了实现多态,多态是Java的一大特色,多态也是面向对象的关键技术之一,所以Java是以效率为代价来实现多态这是很值得的,所以在不影响整体设计的情况下,我们可以考虑将方法或者变量使用private,static或者final进行修饰。

内容来自互联网+个人见解,如果有哪里有问题,请联系我并指正,我会及时纠正处理。

谢谢大爷~

  • 微信打赏二维码

    微信

  • 支付宝打赏二维码

    支付宝



目录
  1. 1. 概念
  2. 2. 对比
  3. 3. 验证
  4. 4. 反编译
  5. 5. 总结
本站访问量   |   您是第 位.