首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> Java 程式設計 >> Java 相關知識

Java 獲取 Class 檔案絕對路徑工具類:支援 JAR、檔案系統與類別載入器完整解析

     編輯:Java 相關知識

為什麼需要這個工具

相對路徑總是相對於「當前工作目錄」,但在 Web 容器、模組化框架、外掛架構中,工作目錄不可預期。以 **自身 Class 檔案位置** 為基準,才能可靠定位同層級資源 (設定檔、腳本、原生庫等),實現真正的封裝與可移植性。

核心原理

  1. Class.getProtectionDomain().getCodeSource().getLocation() 取得程式碼來源 URL (目錄或 JAR 檔)。
  2. 若為 JAR 協定 (jar:file:/path/to.jar!/),剝離 !/ 之後路徑取得 JAR 檔案本身。
  3. 若為檔案協定且為目錄,拼接類別相對路徑 (package/Class.class)。
  4. 最後透過 File.getCanonicalPath() 正規化路徑 (解析符號連結、相對路徑)。
  5. 兜底策略:ClassLoader.getResource("package/Class.class")。

完整工具類原始碼

package mytools;

import java.io.*;
import java.net.*;
import java.security.*;

public class Path {

    /**
     * 取得指定類別的 .class 檔案絕對路徑 (若在 JAR 中則回傳 JAR 檔案路徑)。
     * @param cls 任意可被當前類別載入器載入的類別
     * @return 絕對路徑;找不到回傳 null
     */
    public static String getPathFromClass(Class cls) throws IOException {
        if (cls == null) throw new NullPointerException();
        URL url = getClassLocationURL(cls);
        if (url == null) return null;

        String path = url.getPath();
        if ("jar".equalsIgnoreCase(url.getProtocol())) {
            // jar:file:/path/to.jar!/pkg/Class.class → file:/path/to.jar
            try {
                path = new URL(path).getPath();
            } catch (MalformedURLException ignored) {}
            int idx = path.indexOf("!/");
            if (idx != -1) path = path.substring(0, idx);
        }
        return new File(path).getCanonicalPath();
    }

    /**
     * 以指定類別為基準,解析相對路徑為絕對路徑。
     * @param relatedPath 相對路徑 (如 "../../config/app.properties")
     * @param cls 基準類別
     * @return 解析後的絕對路徑
     */
    public static String getFullPathRelateClass(String relatedPath, Class cls) throws IOException {
        if (relatedPath == null) throw new NullPointerException();
        String clsPath = getPathFromClass(cls);
        File clsFile = new File(clsPath);
        // 若 clsPath 是 JAR 檔,getParent() 為 JAR 所在目錄
        String baseDir = clsFile.getParent();
        File target = new File(baseDir, relatedPath);
        return target.getCanonicalPath();
    }

    /** 內部核心:解析類別位置 URL */
    private static URL getClassLocationURL(final Class cls) {
        if (cls == null) throw new IllegalArgumentException("null input: cls");

        // 1. 透過 ProtectionDomain → CodeSource
        ProtectionDomain pd = cls.getProtectionDomain();
        if (pd != null) {
            CodeSource cs = pd.getCodeSource();
            if (cs != null) {
                URL loc = cs.getLocation();
                if (loc != null) {
                    // 處理 file: 協定:目錄 vs JAR
                    if ("file".equals(loc.getProtocol())) {
                        try {
                            String ext = loc.toExternalForm();
                            if (ext.endsWith(".jar") || ext.endsWith(".zip")) {
                                // JAR 檔案 → 轉為 jar: 協定指向類別項目
                                return new URL("jar:" + ext + "!/" + cls.getName().replace('.', '/') + ".class");
                            } else if (new File(loc.getFile()).isDirectory()) {
                                // 目錄 → 直接拼接類別相對路徑
                                return new URL(loc, cls.getName().replace('.', '/') + ".class");
                            }
                        } catch (MalformedURLException ignored) {}
                    } else {
                        return loc; // 非 file 協定 (如 jrt:, http:) 直接回傳
                    }
                }
            }
        }

        // 2. 兜底:ClassLoader.getResource()
        ClassLoader cl = cls.getClassLoader();
        return (cl != null) ? cl.getResource(cls.getName().replace('.', '/') + ".class")
                            : ClassLoader.getSystemResource(cls.getName().replace('.', '/') + ".class");
    }

    // 測試主程式
    public static void main(String[] args) {
        try {
            System.out.println("Path.class 位置: " + getPathFromClass(Path.class));
            System.out.println("相對路徑測試: " + getFullPathRelateClass("../test/abc/..", Path.class));
        } catch (Exception e) { e.printStackTrace(); }
    }
}

使用場景範例

場景程式碼結果
讀取同目錄下 propertiesPath.getFullPathRelateClass("config.properties", MyClass.class)/deploy/app/config.properties
載入 JNI 原生庫System.load(Path.getFullPathRelateClass("../lib/libmylib.so", MyClass.class))/deploy/lib/libmylib.so
定位 JAR 內資源 (需改用 getResourceAsStream)—本工具回傳 JAR 檔路徑,非 JAR 內部路徑

常見坑點提醒

  • JAR 中類別:回傳的是 **JAR 檔案路徑**,而非 JAR 內部條目路徑。若需讀取 JAR 內資源,請改用 Class.getResourceAsStream()。
  • 模組化系統 (JDK 9+):jrt:/ 協定不支援 File 操作,需改用 Module.getResourceAsStream()。
  • 自訂 ClassLoader:若類別由自訂載入器載入且未正確實作 getResource(),兜底策略可能失效。
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved