有 Java 编程相关的问题?

你可以在下面搜索框中键入要查询的问题!

Java中向后兼容的包装类

关于维护Java的向后兼容性,这里有一个interesting article。在包装类部分,我不能真正理解包装类完成了什么。在以下来自MyApp的代码中,WrapNewClass.checkAvailable()可以替换为Class.forName("NewClass")

static {
    try {
        WrapNewClass.checkAvailable();
        mNewClassAvailable = true;
    } catch (Throwable ex) {
        mNewClassAvailable = false;
    }
}
<>考虑^ {< CD4>}不可用时。在使用包装器的代码中(见下文),我们所做的只是将不存在的类替换为存在但无法编译的类,因为它使用的是不存在的类

public void diddle() {
    if (mNewClassAvailable) {
        WrapNewClass.setGlobalDiv(4);
        WrapNewClass wnc = new WrapNewClass(40);
        System.out.println("newer API is available - " + wnc.doStuff(10));
    }else {
        System.out.println("newer API not available");
    }
}

有人能解释为什么这会有不同吗?我认为这与Java如何编译代码有关——我对此不太了解


共 (3) 个答案

  1. # 1 楼答案

    这一点是让代码针对一些类编译,而这些类在运行时可能不可用。WrapNewClass必须存在于javac的类路径中,否则无法编译。但是,在运行时的类路径中可能会缺少它

    如果mNewClassAvailable为false,您引用的代码将避免引用WrapNewClass。因此,它将只打印“新API不可用”消息

    然而,我不能说我印象深刻。总的来说,我见过用java来安排这种事情。而不是试图捕获异常。顺便说一句,即使在编译时,类也看不到任何地方

  2. # 2 楼答案

    我在spring和richfaces中见过这种行为。例如,Spring执行以下操作

    • 对JSF有编译时依赖性
    • 声明一个private static内部类,它在其中引用JSF类
    • try/catchClass.forName(..)一个JSF类
    • 如果没有抛出异常,则引用内部类(通过faces上下文获得spring上下文)
    • 如果抛出异常,则从另一个源(servlet上下文)获取spring上下文

    请注意,内部类在被引用之前不会被加载,因此可以使用其中不满足的依赖项

    (spring类是org.springframework.web.context.request.RequestContextHolder

  3. # 3 楼答案

    从JSE中的1.1开始,我就一直需要支持每一个JVM,并使用这些包装技术兼容地支持可选API——也就是说,API使应用程序工作得更好,但对它来说不是必需的

    我使用的两种技术似乎(很差?)在您引用的文章中描述。与其对此作进一步评论,我将提供我如何做到这一点的真实例子

    最简单的静态包装方法

    需要:如果API可用,则调用它,否则什么也不做。这可以针对任何JVM版本进行编译

    首先,设置一个静态Method,它具有反射的方法,如下所示:

    static private final java.lang.reflect.Method SET_ACCELERATION_PRIORITY;
    static {
        java.lang.reflect.Method mth=null;
        try { mth=java.awt.Image.class.getMethod("setAccelerationPriority",new Class[]{Float.TYPE}); } catch(Throwable thr) { mth=null; }
        SET_ACCELERATION_PRIORITY=mth;
        }
    

    并包装反射的方法,而不是使用直接调用:

    static public void setImageAcceleration(Image img, int accpty) {
        if(accpty>0 && SET_ACCELERATION_PRIORITY!=null) {
            try { SET_ACCELERATION_PRIORITY.invoke(img,new Object[]{new Float(accpty)}); } 
            catch(Throwable thr) { throw new RuntimeException(thr); } // exception will never happen, but don't swallow - that's bad practice
            }
        }
    

    静态包装类

    需要:如果API可用,则调用该API,或者以其他方式调用较旧的API以获得等效但降级的功能。这必须针对较新的JVM版本进行编译

    首先设置一个静态包装器类;这可能是一个静态单例包装器,或者您可能需要包装每个实例创建。下面的示例使用静态单例:

    package xxx;
    
    import java.io.*;
    import java.util.*;
    
    /**
     * Masks direct use of select system methods to allow transparent use of facilities only
     * available in Java 5+ JVM.
     *
     * Threading Design : [ ] Single Threaded  [x] Threadsafe  [ ] Immutable  [ ] Isolated
     */
    public class SysUtil
    extends Object
    {
    
    /** Package protected to allow subclass SysUtil_J5 to invoke it. */
    SysUtil() {
        super();
        }
    
    /** Package protected to allow subclass SysUtil_J5 to override it. */
    int availableProcessors() {
        return 1;
        }
    
    /** Package protected to allow subclass SysUtil_J5 to override it. */
    long milliTick() {
        return System.currentTimeMillis();
        }
    
    /** Package protected to allow subclass SysUtil_J5 to override it. */
    long nanoTick() {
        return (System.currentTimeMillis()*1000000L);
        }
    
    // *****************************************************************************
    // STATIC PROPERTIES
    // *****************************************************************************
    
    static private final SysUtil            INSTANCE;
    static {
        SysUtil                             instance=null;
    
        try                  { instance=(SysUtil)Class.forName("xxx.SysUtil_J5").newInstance(); } // can't use new SysUtil_J5() - compiler reports "class file has wrong version 49.0, should be 47.0"
        catch(Throwable thr) { instance=new SysUtil();                                          }
        INSTANCE=instance;
        }
    
    // *****************************************************************************
    // STATIC METHODS
    // *****************************************************************************
    
    /**
     * Returns the number of processors available to the Java virtual machine.
     * <p>
     * This value may change during a particular invocation of the virtual machine. Applications that are sensitive to the
     * number of available processors should therefore occasionally poll this property and adjust their resource usage
     * appropriately.
     */
    static public int getAvailableProcessors() {
        return INSTANCE.availableProcessors();
        }
    
    /**
     * Returns the current value of the most precise available system timer, in milliseconds.
     * <p>
     * This method can only be used to measure elapsed time and is not related to any other notion of system or wall-clock
     * time. The value returned represents milliseconds since some fixed but arbitrary time (perhaps in the future, so
     * values may be negative). This method provides millisecond precision, but not necessarily millisecond accuracy. No
     * guarantees are made about how frequently values change. Differences in successive calls that span greater than
     * approximately 292,000 years will not accurately compute elapsed time due to numerical overflow.
     * <p>
     * For example, to measure how long some code takes to execute:
     * <p><pre>
     *    long startTime = SysUtil.getNanoTick();
     *    // ... the code being measured ...
     *    long estimatedTime = SysUtil.getNanoTick() - startTime;
     * </pre>
     * <p>
     * @return          The current value of the system timer, in milliseconds.
     */
    static public long getMilliTick() {
        return INSTANCE.milliTick();
        }
    
    /**
     * Returns the current value of the most precise available system timer, in nanoseconds.
     * <p>
     * This method can only be used to measure elapsed time and is not related to any other notion of system or wall-clock
     * time. The value returned represents nanoseconds since some fixed but arbitrary time (perhaps in the future, so values
     * may be negative). This method provides nanosecond precision, but not necessarily nanosecond accuracy. No guarantees
     * are made about how frequently values change. Differences in successive calls that span greater than approximately 292
     * years will not accurately compute elapsed time due to numerical overflow.
     * <p>
     * For example, to measure how long some code takes to execute:
     * <p><pre>
     *    long startTime = SysUtil.getNanoTick();
     *    // ... the code being measured ...
     *    long estimatedTime = SysUtil.getNanoTick() - startTime;
     * </pre>
     * <p>
     * @return          The current value of the system timer, in nanoseconds.
     */
    static public long getNanoTick() {
        return INSTANCE.nanoTick();
        }
    
    } // END PUBLIC CLASS
    

    并创建一个子类,以便在可用时提供更新的功能:

    package xxx;
    
    import java.util.*;
    
    class SysUtil_J5
    extends SysUtil
    {
    
    private final Runtime                   runtime;
    
    SysUtil_J5() {
        super();
    
        runtime=Runtime.getRuntime();
        }
    
    int availableProcessors() {
        return runtime.availableProcessors();
        }
    
    long milliTick() {
        return (System.nanoTime()/1000000);
        }
    
    long nanoTick() {
        return System.nanoTime();
        }
    } // END PUBLIC CLASS