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() 拼字符串比较,性能差还容易写错包名。