编译时类型由声明时类型决定,运行时类型由new创建的对象决定;调用方法执行运行时类型的方法(动态绑定),访问属性则使用编译时类型的字段(静态绑定),二者不一致时产生多态。

编译时类型和运行时类型,关键看“声明”和“创建”两步——声明时写的是谁,编译时类型就是谁;new 出来的是谁,运行时类型就是谁。
编译时类型:看等号左边的声明
它在代码写完、编译前就固定了,不会变。由变量声明时的类型决定,也就是 = 号左边的类型。
- 比如
Animal a = new Dog();,a 的编译时类型是Animal - 再比如
List<string> list = new ArrayList();</string>,list 的编译时类型是List<string></string> - 这个类型决定了你能调用哪些方法(只能调用 Animal 或 List 中声明的方法),也决定了访问哪个版本的属性(属性不具有多态性,只认编译时类型)
运行时类型:看等号右边 new 出来的对象
它取决于实际创建的对象类型,也就是 = 号右边 new 后面的类名。运行时才能确定,且可变化。
-
Animal a = new Dog();→ 运行时类型是Dog -
a = new Cat();→ 同一个变量 a,运行时类型变成Cat - 可用
a.getClass()在运行时查出来,返回class Dog或class Cat
调方法 vs 调属性:规则很明确
Java 对这两者的处理完全不同:
- 调方法:看运行时类型 → 执行子类重写后的方法(动态绑定)
- 调属性:看编译时类型 → 访问声明类型中定义的那个字段(静态绑定,无重写)
- 例如父类
Base和子类Sub都有int count = 10;和int count = 20;,那么Base b = new Sub(); System.out.println(b.count);输出的是10(不是 20)
多态发生的前提
只有当编译时类型和运行时类型不一致时,才出现(运行时)多态。常见于向上转型:
- 必须有继承或实现关系(如
Animal ← Dog或List ← ArrayList) - 必须用父类/接口类型声明,子类/实现类实例化
- 子类要重写父类的非 private、非 static 方法
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











