Java 怎么判断数据类型?
Java 是强类型语言,但用 Object、泛型、JSON 反序列化的时候,运行时类型常常不确定。判断类型的方法有好几种,用错了会得到意料之外的结果。
一、instanceof:判断是不是某个类型
Object obj = "hello";
if (obj instanceof String) {
String s = (String) obj;
}
特点:
- 能判断继承关系,子类实例对父类 instanceof 返回 true
- obj 为 null 时返回 false,不会抛异常
- 编译期会做类型检查,明显不兼容的类型直接编译报错
- Java 16 起支持模式匹配:if (obj instanceof String s) 直接完成转换
注意数组也算对象,"int[]" instanceof Object 是 true,但不是 instanceof int[],基本类型数组要用 int[].class。
二、getClass():判断精确类型
obj.getClass() == String.class
obj.getClass().getName() // java.lang.String
obj.getClass().getSimpleName() // String
和 instanceof 的关键区别:getClass 是精确匹配,不考虑继承。ArrayList 实例的 getClass() 是 ArrayList.class,和 List.class 不相等。
obj 为 null 时 getClass() 会抛 NullPointerException,用之前先判空。
三、基本类型的判断
基本类型不能为 null,也不需要 instanceof。常见的判断场景是判断包装类或者判断数字类型:
if (obj instanceof Integer) { ... }
if (obj instanceof Number) { ... } // Integer、Long、Double 都命中
Number 这个判断很实用,处理 JSON 里的数字时,不知道是 Integer 还是 Long,用 Number 兜住再统一转 BigDecimal。
四、Class.isAssignableFrom:判断能否赋值
Number.class.isAssignableFrom(Integer.class) // true
List.class.isAssignableFrom(ArrayList.class) // true
语义是"前者能否由后者赋值",和 instanceof 方向相反,容易记混。一般用在反射、框架的类型匹配里。
五、反射里的类型判断
Field f = clazz.getDeclaredField("name");
Class<?> t = f.getType();
if (t == String.class) { ... }
if (t.isArray()) { ... } // 是否数组
if (t.isPrimitive()) { ... } // 是否基本类型
if (t.isEnum()) { ... } // 是否枚举
if (Number.class.isAssignableFrom(t)) { ... } // 是否数字
这里要注意泛型。Field.getType() 只能拿到 List,拿不到 List<String>,泛型信息要用 getGenericType() 拿 ParameterizedType 再解析。运行时泛型是被擦除的。
六、判断"是不是数字/字符串"的字符串场景
如果拿到的本来就是 String,判断内容像不像数字:
// 正则,允许负号和小数
str.matches("-?\\d+(\\.\\d+)?");
// 或直接 try-catch
try {
new BigDecimal(str);
// 是数字
} catch (NumberFormatException e) {
// 不是数字
}
正则写法性能好但没有边界校验(比如 "1.2.3" 会被误判),BigDecimal 更严格但异常开销大。数据量大的场景用正则,要求严谨用 BigDecimal。
七、几个真实的坑
- 包装类和基本类型比较:Integer 用 == 比较,值在 -128~127 之间走缓存能相等,超出范围就不相等,必须用 equals。
- 泛型擦除:List<String> 和 List<Integer> 运行时都是 List,instanceof 判断不出来,只能靠元素逐个判断。
- null 判断顺序:if (obj instanceof String) 本身安全,但 if (obj.getClass() == String.class) 必须先判空。
- 动态代理:代理对象的 getClass() 返回的是代理类,不是原始类,判断原类型要用 AopUtils 或者检查接口。
- 数组的 instanceof:Object[] arr = new String[3],arr instanceof String[] 返回 true,但 arr instanceof Integer[] 返回 false,编译期不报错,运行时才看得出。
八、怎么选
业务代码里判断类型,优先用 instanceof,可读性好、空安全。框架或反射代码里需要精确匹配,用 getClass() 或 isAssignableFrom。别用 getClass().getName() 拼字符串比较,性能差还容易写错包名。