java程序绑定:【静态绑定+动态绑定】
(一)首先:要知道java的编译 运行过程:
①:java编译过程:java源文件(.java文件)-->字节码文件(.class文件--->JVM可执行代码)【这个过程中java不与内存打交道,编译器解析语法,语法不正确会报错】
②:java运行过程:是JVM装载字节码文件--->解释器来解析执行。【这个过程才会创立内存布局,执行java程序】
③ :java执行字节码的方式有两种:【 .java--->.class--->机器码(不同操作系统JVM转成不同机器码) 这就是java程序一次编译到处运行的原理】
1.0 即时编译方式:解释器-->将字节码--->机器码-->执行机器码.
2.0 解释执行方式:解释器通过每次解释并执行一小段代码来完成java字节码程序的所有操作。
(二):绑定--->一个方法的调用+方法所在的类(主体)关联起来。
(三):什么是静态绑定?什么是动态绑定?
(1)①:静态绑定(前期绑定):在程序执行前,方法已经被绑定。【编译过程中就已经知道这个方法是哪个类】;注意:java当中的方法只有final,static,private和构造方法是前期绑定的。
②:final,static,private和构造方法是前期绑定的理解.
1.0 final方法:虽然可以被继承-->可以被子类调用,但是不能被重写(覆盖)--->子类调用该的都是父类写的final方法。
【好处:①防止方法被覆盖②有效关闭java中的动态绑定】
2.0 static方法:static方法可以被继承,但是不能被子类重写(覆盖)。但是可以被子类隐藏。(这里意思是说如果父类里有一个static方法,它的子类里如果没有对应的方法,那么当子类对象调用这个方法时就会使用父类中的方法。而如果子类中定义了相同的方法,则会调用子类的中定义的方法。唯一的不同就是,当子类对象上转型为父类对象时,不论子类中有没有定义这个静态方法,该对象都会使用父类中的静态方法。因此这里说静态方法可以被隐藏而不能被覆盖)【静态的随着类的定义被分配装载在内存中】
3.0 private方法:不能被继承 --->没办法通过子类调用(只能通过本身对象调用)。
4.0 构造方法:不能被继承(编译器编译的时候就知道构造方法属于哪个类)
总结:一个方法不能被继承,或者继承后不可以被重写(覆盖),--->那么这个方法就是静态绑定的。
(2):动态绑定(后期绑定):运行时,根据具体对象的类型进行绑定。【编译器此时不知道对象的类型,方法调用机制自己去调查-->找到方法正确的所属的类】,所以会在对象中安插某些特殊类型的信息。
过程:
1.0虚拟机提取对象的实际类型的方法表。
2.0虚拟机搜索方法签名。
3.0调用方法。
Eg:和构造方法是前期绑定外,其他的方法全部为动态绑定。
而动态绑定的典型发生在父类和子类的转换声明之下:
比如:Parent p = new Children();
其具体过程细节如下:
1:编译器检查对象的声明类型和方法名。
假设我们调用x.f(args)方法,并且x已经被声明为C类的对象,那么编译器会列举出C 类中所有的名称为f 的方法和从C 类的超类继承过来的f 方法。
2:接下来编译器检查方法调用中提供的参数类型。
如果在所有名称为f 的方法中有一个参数类型和调用提供的参数类型最为匹配,那么就调用这个方法,这个过程叫做“重载解析”。
3:当程序运行并且使用动态绑定调用方法时,虚拟机必须调用同x所指向的对象的实际类型相匹配的方法版本。
假设实际类型为D(C的子类),如果D类定义了f(String)那么该方法被调用,否则就在D的超类中搜寻方法f(String),依次类推。
JAVA 虚拟机调用一个类方法时(静态方法),它会基于对象引用的类型(通常在编译时可知)来选择所调用的方法。相反,当虚拟机调用一个实例方法时,它会基于对象实际的类型(只能在运行时得知)来选择所调用的方法,这就是动态绑定,是多态的一种。动态绑定为解决实际的业务问题提供了很大的灵活性,是一种非常优美的机制。
与方法不同,在处理java类中的成员变量(实例变量和类变量)时,并不是采用运行时绑定,而是一般意义上的静态绑定。所以在向上转型的情况下,对象的方法可以找到子类,而对象的属性(成员变量)还是父类的属性(子类对父类成员变量的隐藏)。
Java代码
public class Father {
protected String name = "父亲属性";
}
public class Son extends Father {
protected String name = "儿子属性";
public static void main(String[] args) {
Father sample = new Son();
System.out.println("调用的属性:" + sample.name);
}
}
结论,调用的成员为父亲的属性。
这个结果表明,子类的对象(由父类的引用handle)调用到的是父类的成员变量。所以必须明确,运行时(动态)绑定针对的范畴只是对象的方法。
现在试图调用子类的成员变量name,该怎么做?最简单的办法是将该成员变量封装成方法getter形式。
代码如下:
public class Father {
protected String name = "父亲属性";
public String getName() {
return name;
}
}
public class Son extends Father {
protected String name = "儿子属性";
public String getName() {
return name; }
public static void main(String[] args) {
Father sample = new Son();
System.out.println("调用的属性:" + sample.getName());
}
}
结果:调用的是儿子的属性
java因为什么对属性要采取静态的绑定方法。这是因为静态绑定是有很多的好处,它可以让我们在编译期就发现程序中的错误,而不是在运行期。这样就可以提高程序的运行效率!而对方法采取动态绑定是为了实现多态,多态是java的一大特色。多态也是面向对象的关键技术之一,所以java是以效率为代价来实现多态这是很值得的。