10

我想使用 Java 1.6 中的java.text.Normalizer类进行 Unicode 规范化,但我的代码必须能够在 Java 1.5 上运行。

我不介意在 1.5 上运行的代码是否不进行规范化,但我不希望它在运行时给出NoClassDefFoundErrors 或ClassNotFoundExceptions 。

实现这一目标的最佳方法是什么?

4

7 回答 7

13

执行此操作的常用方法是通过反射,即不直接引用相关类,而是以编程方式调用它。如果有问题的代码不存在,这使您可以优雅地捕获异常,然后忽略它,或者尝试其他方法。反射抛出ClassNotFoundException,这是一个很好的正常异常,而不是NoClassDefFoundError,这有点可怕。

在 的情况下java.text.Normalizer,这应该很容易,因为它只是几个静态方法,并且很容易通过反射调用。

于 2009-08-14T11:09:35.847 回答
9
public interface NfcNormalizer
{
  public String normalize(String str);
}

public class IdentityNfcNormalizer implements NfcNormalizer
{
  public String normalize(String str)
  {
    return str;
  }
}

public class JDK16NfcNormalizer implements NfcNormalizer
{
  public String normalize(String str)
  {
    return Normalizer.normalize(str, Normalizer.Form.NFC);
  }
}

在您的客户端代码中:

NfcNormalizer normalizer;
try
{
  normalizer = Class.forName("JDK16NfcNormalizer").newInstance();
}
catch(Exception e)
{
  normalizer = new IdentityNfcNormalizer();
}
于 2009-08-14T11:08:24.913 回答
3

我不介意在 1.5 上运行的代码是否不进行规范化,但我不希望它在运行时给出 NoClassDefFoundErrors 或 ClassNotFoundExceptions。

如果您想避免反射,您实际上可以捕获这些错误。

这样,您可以使用 Java6 编译器针对闪亮的新类进行编译,并且它仍然可以在 Java5 上工作(如“不做任何事情,但也不崩溃”)。

您还可以结合这两种方法,并使用反射检查该类是否存在,以及它是否继续以非反射方式调用它。这就是安德鲁的解决方案正在做的事情。

如果你还需要在Java5上编译,那么你需要一路去反射。

于 2009-08-15T00:23:26.280 回答
1

我也有同样的需求,因为我们的代码需要在 Java 1.2 的所有 Java 版本上运行,但是如果有更新的 API 可用,一些代码需要利用它们。

在使用反射获得 Method 对象并动态调用它们的各种排列之后,我通常选择包装样式方法作为最佳方法(尽管在某些情况下,将反射的 Method 存储为静态并调用它会更好 - 这取决于) .

以下是一个示例“系统实用程序”类,它在运行早期版本时为 Java 5 公开了某些较新的 API——与早期 JVM 中的 Java 6 相同的原则。此示例使用单例,但如果底层 API 需要,可以轻松实例化多个对象。

有两个类:

  • 系统实用程序
  • SysUtil_J5

如果运行时 JVM 是 Java 5 或更高版本,则使用后者。否则,从 SysUtil 中的默认实现中使用与合同兼容的回退方法,该实现仅使用 Java 4 或更早的 API。每个类都使用特定版本的编译器进行编译,因此不会在 Java 4 类中意外使用 Java 5+ API:

SysUtil(使用 Java 4 编译器编译)

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();
    }

// *****************************************************************************
// INSTANCE METHODS - SUBCLASS OVERRIDE REQUIRED
// *****************************************************************************

/** 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 milliTime() {
    return System.currentTimeMillis();
    }

/** Package protected to allow subclass SysUtil_J5 to override it. */
long nanoTime() {
    return (System.currentTimeMillis()*1000000L);
    }

// *****************************************************************************
// STATIC PROPERTIES
// *****************************************************************************

static private final SysUtil            INSTANCE;
static {
    SysUtil                             instance=null;

    try                  { instance=(SysUtil)Class.forName("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 time in milliseconds.
 * <p>
 * Note that while the unit of time of the return value is a millisecond, the granularity of the value depends on the
 * underlying operating system and may be larger. For example, many operating systems measure time in units of tens of
 * milliseconds.
 * <p>
 * See the description of the class Date for a discussion of slight discrepancies that may arise between "computer time"
 * and coordinated universal time (UTC).
 * <p>
 * @return         The difference, measured in milliseconds, between the current time and midnight, January 1, 1970 UTC.
 */
static public long getMilliTime() {
    return INSTANCE.milliTime();
    }

/**
 * 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 (263 nanoseconds) 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.getNanoTime();
 *    // ... the code being measured ...
 *    long estimatedTime = SysUtil.getNanoTime() - startTime;
 * </pre>
 * <p>
 * @return          The current value of the system timer, in nanoseconds.
 */
static public long getNanoTime() {
    return INSTANCE.nanoTime();
    }

} // END PUBLIC CLASS

SysUtil_J5(使用 Java 5 编译器编译)

import java.util.*;

class SysUtil_J5
extends SysUtil
{

private final Runtime                   runtime;

SysUtil_J5() {
    super();

    runtime=Runtime.getRuntime();
    }

// *****************************************************************************
// INSTANCE METHODS
// *****************************************************************************

int availableProcessors() {
    return runtime.availableProcessors();
    }

long milliTime() {
    return System.currentTimeMillis();
    }

long nanoTime() {
    return System.nanoTime();
    }

} // END PUBLIC CLASS
于 2009-08-15T06:49:27.737 回答
1

检查/使用/修改Phramer 项目 ( http://sourceforge.net/projects/phramer/ , www.phramer.org ) 中的类info.olteanu.utils.TextNormalizer - 代码是 BSD 许可的。

该代码可以在 Java 5 中编译并在 Java 5 或 Java 6(或未来的 Java 版本)中运行。此外,它可以在 Java 6 中编译并在 Java 5 中运行(如果它使用正确的“-target”编译,以实现字节码兼容性)或 Java 6 或任何其他未来版本。

恕我直言,这完全解决了您的问题 - 您可以在任何 Java 5+ 平台上自由编译,并且您可以在任何 Java 5+ 平台上获得所需的功能(规范化)(*)

(*) SUN Java 5 标准化解决方案很可能不会出现在所有 Java 5 实现中,因此在最坏的情况下,当您调用 getNormalizationStringFilter() 方法时,您最终会收到 ClassNotFoundException。

于 2009-08-15T19:33:06.360 回答
0
    String str = "éèà";
    try {
        Class c = Class.forName("java.text.Normalizer");
        Class f = Class.forName("java.text.Normalizer$Form");
        Field ff = f.getField("NFD");
        Method m = c.getDeclaredMethod("normalize", new Class[]{java.lang.CharSequence.class,f});
        temp = (String) m.invoke(null, new Object[]{str,ff.get(null)});
    } catch (Throwable e) {
        System.err.println("Unsupported Normalisation method (jvm <1.6)");
    }
    System.out.println(temp+" should produce [eea]");
于 2009-11-10T22:33:23.907 回答
0

这是一个老问题,但仍然是实际的。我发现了一些答案中没有提到的可能性。

通常建议使用反射,如此处的其他一些答案所示。但是,如果您不想在代码中添加混乱,您可以使用icu4j 库。它包含具有与 java.text.Normalizer/sun.text.Normalizer 执行相同工作的方法的com.ibm.icu.text.Normalizer类。normalize()Icu 库有(应该有)自己的 Normalizer 实现,因此您可以与库共享您的项目,并且应该是独立于 java 的。
缺点是icu库比较大。

如果您使用 Normalizer 类只是为了从字符串中删除重音/变音符号,还有另一种方法。您可以使用包含with 方法的Apache commons lang 库(版本 3)StringUtilsstripAccents()

String noAccentsString = org.apache.commons.lang3.StringUtils.stripAccents(s);

Lang3 库可能使用反射来根据 java 版本调用适当的 Normalizer。所以优点是你的代码中没有反射混乱。

于 2012-11-23T09:41:29.570 回答