方法的分类:
之前是按照是否有static修饰,分为静态方法和非静态方法。
现在按照新的分类标准,是否可以被重写:
非虚方法:除了虚方法以外的方法都叫做非虚方法。
静态方法,私有方法,构造器对应的实例初始化方法,final的方法等等。
虚方法:可以被重写的方法
JVM是模拟真实的计算机而设计的一个运行环境,里面设计了很多指令,它不完全等价于CPU指令。
JVM的指令。
这些指令有很多,其中关于方法调用有这样的一些指令:
invokestatic:调用静态方法的指令
invokespecial:调用的是私有方法的指令
invokevirtual:调用的虚方法的指令,这里final的方法虽然也是用这个指令执行的,但是它归到非虚方法里面。因为final修饰的方法是不能被重写。
JVM的指令。
这些指令有很多,其中关于方法调用有这样的一些指令:
invokestatic:调用静态方法的指令
invokespecial:调用的是私有方法的指令
invokevirtual:调用的虚方法的指令,这里final的方法虽然也是用这个指令执行的,但是它归到非虚方法里面。因为final修饰的方法是不能被重写。
多态引用的形式下:
非虚方法的访问原则:只看编译时类型
虚方法的访问原则:编译时看父类,运行时看子类重写与否
非虚方法的访问原则:只看编译时类型
虚方法的访问原则:编译时看父类,运行时看子类重写与否
/*
面试题:虚方法调用的过程和运行结果(难)
分析步骤:
(1)method方法发生调用时,判断method方法是否是虚方法?
是虚方法。就按照下面的四步走。
如果不是虚方法,只看编译时类型。
(2)看调用method方法的变量的编译时类型是什么?
my的编译时类型是MyClass类型
(3)在编译时类型中寻找调用哪个method方法?
此时的依据是实参与形参的匹配问题。实参和形参的匹配原则:
A:找最匹配的
匹配是按照实参的编译时类型来匹配。
B:找能够兼容
(4)看调用method方法的变量的运行时类型是什么?
这里,my的编译时类型和运行时类型一致,
my的运行时类型是MyClass类型,就执行MyClass类型中找到的最匹配的method方法。
如果调用method方法的变量的运行时类型是子类,就看子类是否重写了该方法,
如果没有重写,仍然执行刚找到最匹配的方法,
如果有重写,就执行重写的代码。
*/
public class TestExam {
public static void main(String[] args) {
Father f = new Father();
Father s = new Son();
Father d = new Daughter();
MyClass my = new MyClass();
my.method(f);//father
/*
(1)在MyClass类型中找最匹配的method方法,看实参与形参的匹配程度。
实参的编译类型 与 哪个方法的形参类型最匹配
实参是f,它的编译时类型是Father,与public void method(Father f) 最匹配
(2)因为my变量的运行时类型仍然是MyClass,所有还是执行刚刚找到的method
*/
my.method(s);//father
/*
(1)在MyClass类型中找最匹配的method方法,看实参与形参的匹配程度。
实参的编译类型 与 哪个方法的形参类型最匹配
实参是s,它的编译时类型是Father,与public void method(Father f) 最匹配
(2)因为my变量的运行时类型仍然是MyClass,所有还是执行刚刚找到的method
*/
my.method(d);//father
/*
(1)在MyClass类型中找最匹配的method方法,看实参与形参的匹配程度。
实参的编译类型 与 哪个方法的形参类型最匹配
实参是d,它的编译时类型是Father,与public void method(Father f) 最匹配
(2)因为my变量的运行时类型仍然是MyClass,所有还是执行刚刚找到的method
*/
}
}
class MyClass{
//问题1:这三个方法是什么关系?
//重载
public void method(Father f) {
System.out.println("father");
}
public void method(Son s) {
System.out.println("son");
}
public void method(Daughter f) {
System.out.println("daughter");
}
}
class Father{
}
class Son extends Father{
}
class Daughter extends Father{
}
public class TestExam2 {
public static void main(String[] args) {
Father f = new Father();
Son s = new Son();
Daughter d = new Daughter();
MyClass my = new MyClass();
my.method(f);//father
/*
(3)在MyClass类中,找最匹配的method方法
实参的编译时类型与形参类型最匹配
实参f的编译时类型是Father类,与 public void method(Father f)最匹配
(4)my的运行时类型还是MyClass,所以执行刚找到的method方法
*/
my.method(s);//son
/*
(3)在MyClass类中,找最匹配的method方法
实参的编译时类型与形参类型最匹配
实参s的编译时类型是Son类,与 public void method(Son s)最匹配
(4)my的运行时类型还是MyClass,所以执行刚找到的method方法
*/
my.method(d);//father
/*
(3)在MyClass类中,找最匹配的method方法
实参的编译时类型与形参类型最匹配
实参d的编译时类型是Daughter类,与 public void method(Father f) 兼容
(4)my的运行时类型还是MyClass,所以执行刚找到的method方法
*/
}
}
class MyClass{
public void method(Father f) {
System.out.println("father");
}
public void method(Son s) {
System.out.println("son");
}
}
class Father{
}
class Son extends Father{
}
class Daughter extends Father{
}
public class TestExam3 {
public static void main(String[] args) {
Father f = new Father();
Son s = new Son();
Daughter d = new Daughter();
MyClass my = new MySub();
my.method(f);//father
/*
(3)在MyClass中找最匹配或兼容的方法
实参的编译类型与形参的类型匹配
实参f的编译时类型是Father,与public void method(Father f)最匹配
(4)my的运行时类型是MySub,就要看MySub中是否重写了method(Father f)方法
这里没有重写。还是执行刚刚在MyClass中找到的方法。
*/
my.method(s);//son
/*
(3)在MyClass中找最匹配或兼容的方法
实参的编译类型与形参的类型匹配
实参s的编译时类型是Son,与public void method(Son f) 最匹配
(4)my的运行时类型是MySub,就要看MySub中是否重写了 method(Son s)方法
这里没有重写。还是执行刚刚在MyClass中找到的方法。
*/
my.method(d);//father
/*
(3)在MyClass中找最匹配或兼容的方法
实参的编译类型与形参的类型匹配
实参d的编译时类型是Daughter,与public void method(Father f) 兼容
(4)my的运行时类型是MySub,就要看MySub中是否重写了 method(Father f)方法
这里没有重写。还是执行刚刚在MyClass中找到的方法。
*/
}
}
class MyClass{
public void method(Father f) {
System.out.println("father");
}
public void method(Son s) {
System.out.println("son");
}
}
class MySub extends MyClass{
public void method(Daughter d) {
System.out.println("daughter");
}
}
class Father{
}
class Son extends Father{
}
class Daughter extends Father{
}
public class TestExam4 {
public static void main(String[] args) {
MyClass my = new MySub();
Father f = new Father();
Son s = new Son();
Daughter d = new Daughter();
my.method(f);//daughter
/*
(3)在my的编译时类型MyClass中找匹配的方法
实参f的编译时类型与形参Father类型最匹配,找到public void method(Father f)
(4)看my的运行时类型,MySub,
MySub中是否重写了public void method(Father f)
是重写了,就执行重写的public void method(Father f) 方法
*/
my.method(s);//son
/*
(3)在my的编译时类型MyClass中找匹配的方法
实参s的编译时类型与形参Son类型最匹配,找到public void method(Son s)
(4)看my的运行时类型,MySub,
MySub中是否重写了public void method(Son s)
没有重写,还是执行MyClass中找到public void method(Son s)
*/
my.method(d);//daughter
/*
(3)在my的编译时类型MyClass中找匹配的方法
实参d的编译时类型Daughter与形参Father类型兼容,找到 public void method(Father f)
(4)看my的运行时类型,MySub,
MySub中是否重写了 public void method(Father f)
是重写,执行重写的 public void method(Father f)
*/
}
}
class MyClass{
public void method(Father f) {
System.out.println("father");
}
public void method(Son s) {
System.out.println("son");
}
}
class MySub extends MyClass{
public void method(Father f) {
System.out.println("daughter");
}
}
class Father{
}
class Son extends Father{
}
class Daughter extends Father{
}