一、编译时类型(静态类型)
定义
- 变量在声明时所显式指定的类型,由编译器在编译阶段确定,在编译期就已知且不可变。
- 例如:
List<String> list = new ArrayList<>();
中,list
的编译时类型是List<String>
。
核心作用
类型检查与安全性
编译器通过编译时类型检查代码是否符合语法规则和类型约束,避免明显的类型错误。
示例:编译器会报错,因为List<String> list = new ArrayList<>(); list.add(123); // 编译错误:无法将int添加到List<String>
List<String>
要求只能存储String
类型元素,而123
是int
。确定可调用的方法
编译时类型决定了变量在编译阶段能调用哪些方法(即该类型的接口或父类中声明的方法)。
示例:虽然运行时Object obj = "Hello"; // 编译时类型是Object obj.length(); // 编译错误:Object类中没有length()方法
obj
是String
类型(包含length()
方法),但编译时类型为Object
,编译器不允许调用length()
。支持泛型与静态多态
泛型依赖编译时类型实现类型参数的静态检查,确保类型安全。
示例:泛型参数Map<String, Integer> map = new HashMap<>(); map.put("age", "18"); // 编译错误:类型不匹配
String
和Integer
在编译时确定,避免运行时类型错误。
二、动态运行时类型(动态类型)
定义
- 变量在运行时实际指向的对象类型,由 JVM 在运行阶段确定,可能与编译时类型不同(多态的体现)。
- 例如:
List<String> list = new ArrayList<>();
中,list
的运行时类型是ArrayList<String>
。核心作用
实现多态(动态绑定)
通过运行时类型确定具体调用哪个方法的实现(方法重写时),实现 “同一接口,不同实现”。
示例:编译时类型为interface Animal { void speak(); } class Dog implements Animal { public void speak() { System.out.println("汪"); } } class Cat implements Animal { public void speak() { System.out.println("喵"); } } Animal animal = new Dog(); // 编译时类型是Animal,运行时类型是Dog animal.speak(); // 运行时调用Dog的speak(),输出“汪” animal = new Cat(); // 运行时类型变为Cat animal.speak(); // 运行时调用Cat的speak(),输出“喵”
Animal
,但运行时根据实际对象类型动态决定调用哪个方法。处理不确定类型的场景
例如从集合中取出元素时(如Object obj = list.get(0)
),需通过运行时类型判断具体类型。支持类型转换与反射
- 反射:在运行时获取对象的实际类型,动态调用方法或属性。
Class<?> clazz = animal.getClass(); // 获取运行时类型Dog/Cat
- 向下转型:通过运行时类型判断是否可以安全转型(需结合
instanceof
)。Animal animal = new Dog(); if (animal instanceof Dog) { Dog dog = (Dog) animal; // 运行时类型为Dog,转型安全 dog.bark(); // 调用Dog特有的方法 }