加载TTF字体时触发java.awt.FontFormatException空指针异常的解决
Java字体重复加载偶发NullPointerException异常分析与解决
问题重现
多次加载同一字体文件时,偶发抛出包含NullPointerException的FontFormatException,异常堆栈如下:
Caused by: java.awt.FontFormatException: java.lang.NullPointerException at java.desktop/sun.font.TrueTypeFont.init(TrueTypeFont.java:614) at java.desktop/sun.font.TrueTypeFont.<init>(TrueTypeFont.java:219) at java.desktop/sun.font.TrueTypeFont.<init>(TrueTypeFont.java:199) at java.desktop/sun.font.SunFontManager.createFont2D(SunFontManager.java:2356) at java.desktop/java.awt.Font.<init>(Font.java:621) at java.desktop/java.awt.Font.createFont(Font.java:1225)
加载代码示例:
public static void main(String ...args) { File fontFile = new File("arial.ttf"); Font font = Font.createFont(Font.TRUETYPE_FONT, fontFile); // 此处触发异常 }
异常原因
- 底层字体初始化的线程安全问题:Sun/Oracle JDK的
SunFontManager和TrueTypeFont内部实现未完全处理并发加载场景,多线程重复加载同一字体时,内部缓存或资源引用会出现竞态条件,导致初始化阶段某个核心对象为null,进而触发NPE并被包装为FontFormatException。 - 文件资源竞争问题:重复调用
createFont(File)时,若文件句柄未被正确释放,或系统文件缓存存在竞争,可能导致读取到不完整的字体数据,间接引发初始化失败。
解决方案
1. 缓存已加载的字体实例(推荐)
第一次加载后将Font对象存入线程安全的缓存,后续直接复用,彻底避免重复加载操作:
import java.awt.Font; import java.io.File; import java.io.IOException; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; public class FontLoader { private static final Map<String, Font> FONT_CACHE = new ConcurrentHashMap<>(); public static Font getFont(String filePath) throws IOException, FontFormatException { return FONT_CACHE.computeIfAbsent(filePath, path -> { try { return Font.createFont(Font.TRUETYPE_FONT, new File(path)); } catch (IOException | FontFormatException e) { throw new RuntimeException("加载字体文件失败: " + path, e); } }); } }
2. 同步加载逻辑
如果必须重复加载,通过同步锁避免多线程并发加载同一文件:
import java.awt.Font; import java.io.File; import java.io.IOException; public class FontLoader { private static final Object LOAD_LOCK = new Object(); public static Font loadFont(String filePath) throws IOException, FontFormatException { synchronized (LOAD_LOCK) { return Font.createFont(Font.TRUETYPE_FONT, new File(filePath)); } } }
3. 使用输入流并确保资源释放
改用FileInputStream加载字体,手动控制流的关闭,减少文件资源泄漏风险:
import java.awt.Font; import java.io.File; import java.io.FileInputStream; import java.io.IOException; public class FontLoader { public static Font loadFont(String filePath) throws IOException, FontFormatException { try (FileInputStream fis = new FileInputStream(new File(filePath))) { return Font.createFont(Font.TRUETYPE_FONT, fis); } } }
内容的提问来源于stack exchange,提问作者John
相关产品推荐
相关产品推荐

