如何检查一个类是否属于Java JDK

7

我使用一个外部库,它返回一些List<?>。 我需要检查此列表中的每个对象是否是JDK的对象(String、int、Integer等)。 这是一个适当的解决方案吗?

List<?> list = externalLibrary.search(...);
for(clazz : list) {
    if (clazz.getPackage().getName().startsWith("java.lang"))
      // do something different
}

有更好的选择吗?

1
请注意,现在还有以 javaxsuncom.sun 或甚至 oraclecom.oracle 开头的包。 - Thomas
3
顺便问一下,你需要那个干什么? - Thomas
1
那你为什么不检查一下你得到的是特殊对象还是字符串呢?我也怀疑你最多只会得到基本类型、它们的包装类、StringDateBigIntBigDecimal等返回值(你几乎肯定只会从数据库中获得“数据类型”类,没有内置核心类、集合、Swing类等)。因此,你最可能只有一组最多20个需要检查匹配的类/类型。 - Thomas
这个相关问题似乎符合你的需求。谢谢。 - yves amsellem
使用 clazz.equals(String.class) 对6-10种Java类型进行测试比使用 clazz.getPackage().getName().startsWith("java.lang") 更好吗? - yves amsellem
显示剩余3条评论
6个回答

5

根据您对 "JDK对象" 的定义,这可能会变得非常模糊,但不,这并不能做到。 java.lang包只是JDK中包含的所有类的一小部分。

您可以检查每个对象是否由加载java.lang.String的相同ClassLoader加载 - 也就是说,

if (theObject.getClass().getClassLoader() == "".getClass().getClassLoader()) ...

一般情况下,系统类和应用程序类使用不同的 ClassLoader

1
它很聪明,但在我看来还不够。在单一类加载器系统中,所有类都由同一个类加载器加载。 - AlexR
在最简单的配置中,难道你没有一个单一的类加载器来加载所有对象吗?因此,如果你没有另一个类加载器,这将无法区分标准和第三方类。 - Thomas
@Thomas -- 取决于我们所谈论的“最简配置”是什么,我想。这适用于在Oracle的JVM下运行的命令行应用程序,并且当然也适用于任何Web应用程序。对于Android,也许不行--因为我们不再真正谈论一个JVM了。 - Ernest Friedman-Hill
@yvesamsellem -- 如果您向我们展示了您的测试,也许我们可以想出一些适合您的解决方案(或告诉您测试中有什么问题)。 - Ernest Friedman-Hill
1
是的,这取决于定义,但我指的是最简单的类加载器配置,只有系统类加载器,没有其他。但即使你有更多的类加载器,你也不能确定系统类加载器只会加载标准类,对吗?例如,在JBoss中,你有一个层次结构,在某些配置中,你可以确定哪些类是由你的应用程序加载的,但如果你沿着类加载器层次结构向上走,最终你会发现其他由系统类加载器加载的共享类(不仅仅是标准类)。 - Thomas

2

可能没问题,只需要检查以下的包:

java
javax
com.sun
sun

可能还有其他人...


1
我们使用以下类来检查类是否属于JDK。
public class JDKClass {

    private static Set<String> CS = new HashSet<String>();

    static {
        try {
            File file = new File(System.getProperty("java.home"),
                    "lib/classlist");
            BufferedReader r = new BufferedReader(new FileReader(file));
            String l;
            while (true) {
                l = r.readLine();
                if (l == null) {
                    break;
                } else {
                    CS.add(l.replace('/', '.'));
                }
            }
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    public static boolean contains(String o) {
        return CS.contains(o) || o.startsWith("java") || o.startsWith("com.sun")
                || o.startsWith("sun") || o.startsWith("oracle")
                || o.startsWith("org.xml") || o.startsWith("com.oracle");
    }

    private JDKClass() {
    }

}

1

个人而言,我喜欢基于类加载器的答案。但它也会在 StringBuilder 上返回 true。如果你想要更狭义的定义,即仅限于“内置”类型,你可以尝试评估这是否是原始类型(如 int)或包装类型(如 Integer)或 String。你可以编写类似这样的代码:

    import java.util.Map;
    import java.util.TreeMap;



    public class Utils {

        private static Map<String, Class<?>> SUBST_MAP = new TreeMap<String, Class<?>>();
        private static Map<String, Class<?>> SIMPLE_MAP = new TreeMap<String, Class<?>>();


        static {
            SUBST_MAP.put(Byte.class.getName(), Byte.TYPE);
            SUBST_MAP.put(Short.class.getName(), Short.TYPE);
            SUBST_MAP.put(Integer.class.getName(), Integer.TYPE);
            SUBST_MAP.put(Long.class.getName(), Long.TYPE);
            SUBST_MAP.put(Float.class.getName(), Float.TYPE);
            SUBST_MAP.put(Double.class.getName(), Double.TYPE);
            SUBST_MAP.put(Boolean.class.getName(), Boolean.TYPE);
            SUBST_MAP.put(Character.class.getName(), Character.TYPE);
            SIMPLE_MAP.put(String.class.getName(), Boolean.TRUE);

        }




    /**
     * Gets the the class type of the types of the argument.
     * 
     * if substPrimitiveWrapper is true,
     * then if there is argument, that represent primitive type wrapper (such as Integer),
     *      then it will be substituted to primitive type (such as int).
     * else no substitution will be done.
     *
     * @param arg object.
     * @param substPrimitiveWrapper - wheteher to do primitive type substitution.
     * @retrun class type.
     */

    public static Class<?> getClassType(Object arg, boolean substPrimitiveWrapper){
        Class<?> classType = null;
        String className = null;
        Class<?> substClass = null;

        if(arg != null ){
            //making default classType
            classType = arg.getClass();
            if(substPrimitiveWrapper){
                className = classType.getName();
                substClass = (Class<?>)SUBST_MAP.get(className);
                if(substClass != null){
                    classType = substClass;
                }

            }
        }
        return classType;
    }

    /**
     * This method consider JDK type any primitive type, wrapper class or String.
     * 
     * 
     * @param arg object
     * @return where arg is JDK type or now.
     */
    public static boolean isJDKClass(Object arg){
        Class<?> classType = getClassType(arg, true);
        boolean isJDKClass = false;
        if(classType!=null){
            //if(String.class.equals(classType)){
            //  isJDKClass = true; //this is String, note that String is final
            //}
            assert classType!=null;
            String className = classType.getName();
            Boolean isFound = (Boolean)SIMPLE_MAP.get(className);
            if(Boolean.TRUE.equals(isFound)){
                isJDKClass = true; //this is predefined class
            }


            boolean isPrimitiveType = classType.isPrimitive();
            if(isPrimitiveType){
                isJDKClass = true; //this is primitive type or wrapper class
            }
        }

        return isJDKClass;
     }

    } 

您还可以选择添加对java.math.BigDecimaljava.util.Datejava.sql.Timestamp等类的支持。但请注意,它们并不是最终版本,因此我假设如果有人即使以微不足道的方式扩展了它们,也不会被视为JDK类。

1

您可以使用 ClassLoader.getSystemResources,然后检查类是从哪个 jar 文件加载的(例如,如果它来自 rt.jar)。

您将获得以下 URL:

jar:file:/C:/Users/user/.m2/repository/org/slf4j/slf4j-log4j12/1.6.1/slf4j-log4j12-1.6.1.jar!/org/slf4j/impl/StaticLoggerBinder.class

从SLF4j中获取的示例代码:

  private static String STATIC_LOGGER_BINDER_PATH = 
     "org/slf4j/impl/StaticLoggerBinder.class";
  private static void singleImplementationSanityCheck() {
    try {
      ClassLoader loggerFactoryClassLoader = LoggerFactory.class
          .getClassLoader();
      Enumeration paths;
      if (loggerFactoryClassLoader == null) {
        paths = ClassLoader.getSystemResources(STATIC_LOGGER_BINDER_PATH);
      } else {
        paths = loggerFactoryClassLoader
            .getResources(STATIC_LOGGER_BINDER_PATH);
      }
  List implementationList = new ArrayList();
  while (paths.hasMoreElements()) {
    URL path = (URL) paths.nextElement();
    implementationList.add(path);
  }
    ....
  }

0
我认为一个更简单的解决方案是这样考虑问题: 编写一个方法来识别所有由您定义的类。在大多数情况下,所有用户定义的类都遵循 com.something.something 这样的模式。然后,如果它们不属于 com.something.something,则是 JDK 类。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接