美文网首页
Java 中读取文件内容的 n 中方式

Java 中读取文件内容的 n 中方式

作者: 万古Meth | 来源:发表于2020-04-19 17:47 被阅读0次

    前言

    刚接触 java 的时候很困惑一个事情 File相对路径,以哪个目录为参照物。

    随着 io 模型的发展,java 1.7 的 nio,使用 PathPathsFiles 等来方便 io 的操作。

    ClassLoader 用于获取class 文件 的 io,我们也可以用于获取文件的 io,以便于我们读取文件内容。

    本文设计内容

    • File ,ZipFile,JarFile 读取相对路径和绝对路径文件内容。
    • System.getProperty("user.dir”) 是怎么来的。
    • Paths、Path、Files 读取文件内容。
    • 类加载器获取文件内容,Class.getResourceAsStream 和 ClassLoader.getResourceAsStream。
    • 介绍类加载器的双亲委派模型,及在代码中找到对应的加载逻辑。

    <font color=red>代码基于 Mac 10.15.4,JDK 1.8。</font>

    基于 File 获取文件内容

    绝对路径的内容获取比较简单,直接获取文件 io ,然后利用工具类读取文件内容。

    获取绝对路径文件内容

    final File file = new File("/Users/zhangpanqin/github/fly-java/demo.txt");
    final byte[] bytes = cn.hutool.core.io.FileUtil.readBytes(file);
    System.out.println(new String(bytes, StandardCharsets.UTF_8));
    

    获取 JarFile 中的内容

    JarFile 继承 ZipFile 用于获取 jar 包中的内容。比如我想获取 jar 中的某个文件的的内容。

    
    final File file = new File("/Users/zhangpanqin/github/fly-java/src/main/resources/fastjson-1.2.68.jar");
    final JarFile jarFile = new JarFile(file);
    final JarEntry jarEntry = jarFile.getJarEntry("META-INF/LICENSE.txt");
    final InputStream inputStream = jarFile.getInputStream(jarEntry);
    // 工具类是 hutool
    System.out.println(IoUtil.read(inputStream, StandardCharsets.UTF_8));
    IoUtil.close(inputStream);
    

    获取相对路径的内容

    File.getAbsolutePath 查看源码可以发现,相对路径其实就是在前面拼接了 System.getProperty("user.dir")

    class UnixFileSystem extends FileSystem {
        public String resolve(File f) {
            if (isAbsolute(f)) return f.getPath();
            return resolve(System.getProperty("user.dir"), f.getPath());
        }
    }
    

    只要我们弄清楚 System.getProperty("user.dir"),问题就迎刃而解。Java System Properties 中介绍 user.dir 是用户的工作目录。

    image-20200418232813977

    <font color=red>什么是用户工作目录呢?就是执行 java 命令的目录。</font>在那个目录下执行命令,usr.dir 就会被 java 虚拟机赋值为执行命令的路径。我在 /Users/zhangpanqin/github/fly-java/test 目录下运行编译的 class 文件。-cp 指定 classpath 路径。

    image-20200418233359327

    nio 读取文件内容

    Path 可以类比 File 理解使用。然后工具类 Paths 可以获得 PathFiles 更是提供了丰富的 api 用于crud 操作文件 Path

    获取绝对路径内容

    @Test
    public void run33() throws IOException {
       final Path path = Paths.get("/Users/zhangpanqin/github/fly-java/demo.txt");
       final byte[] bytes = Files.readAllBytes(path);
       System.out.println(new String(bytes,StandardCharsets.UTF_8));
    }
    

    获取相对路径内容

    Paths 获取相对路径时,路径不以 / 开头。也可以理解成相对于 System.getProperty("user.dir") 路径。

    public static void main(String[] args) {
        System.out.println(System.getProperty("user.dir"));
        System.out.println(Paths.get("").toAbsolutePath());
    }
    
    image-20200419001203097

    基于 ClassLoader 获取文件内容

    ClassLoader.getResourceAsStream

    // ClassLoader.getResourceAsStream java 1.8 源码
    public InputStream getResourceAsStream(String name) {
        URL url = getResource(name);
        try {
            return url != null ? url.openStream() : null;
        } catch (IOException e) {
            return null;
        }
    }
    

    从代码可以看到主要逻辑还是集中在 getResource

    public URL getResource(String name) {
        URL url;
        if (parent != null) {
            url = parent.getResource(name);
        } else {
            url = getBootstrapResource(name);
        }
        if (url == null) {
            url = findResource(name);
        }
        return url;
    }
    

    以上代码的逻辑也即是我们常听到的 双亲委派机制。先让 父类加载 去加载资源,找不到再有自己找。类加载器单独讲

    类加载器读取读取资源,先从自己负责的路径查找。比如应用类加载器 sun.misc.Launcher.AppClassLoader#AppClassLoader 负责 classpath 查找资源。类加载器读取资源相对于 FilePath 优势在哪里呢?比如当我想获取一个 jar 中的资源,你用路径就比较麻烦了,ClassLoader 可以从负责的路径下寻找,还可以去 jar 包中寻找。

    final URL resource = Test2.class.getClassLoader().getResource("com/alibaba/fastjson/JSONArray.class");
    System.out.println(resource);
    

    上述打印结果

    jar:file:/Users/zhangpanqin/.m2/repository/com/alibaba/fastjson/1.2.62/fastjson-1.2.62.jar!/com/alibaba/fastjson/JSONArray.class
    

    我们还可以获取一个路径的 inputstream

    @Test
    public void run222(){
        final InputStream resourceAsStream = Test2.class.getClassLoader().getResourceAsStream("META-INF/maven/com.alibaba/fastjson/pom.properties");
        System.out.println(IoUtil.read(resourceAsStream, StandardCharsets.UTF_8));
    }
    

    上述结果为:

    #Generated by Maven
    #Mon Oct 07 22:09:36 CST 2019
    version=1.2.62
    groupId=com.alibaba
    artifactId=fastjson
    

    Class.getResourceAsStream

    Class.getResourceAsStream 实际也调用的 ClassLoader 加载资源,但是它会将路径补充到相对于当前类所在包的路径。

    比如

    // com.fly.study.java.classloader.Test2
    @Test
    public void run555(){
        System.out.println(Test2.class.getResource("name"));
    }
    

    实际查找的资源为 com.fly.study.java.classloader.name。相对于当前类所在包的资源。

    类加载器

    我们经常会听到类加载器的 双亲委派模型

    img

    去哪里可以看到这些类加载呢。

    启动类加载器 不是 java 代码实现的我们看不到源码。

    sun.misc.Launcher 类中有我们知道的 扩展类加载器 sun.misc.Launcher.ExtClassLoader应用类加载器 sun.misc.Launcher.AppClassLoader

    java.lang.ClassLoader#getSystemClassLoader 代码看的话,实际返回的应用类加载器。

    public static ClassLoader getSystemClassLoader() {
        initSystemClassLoader();
        if (scl == null) {
            return null;
        }
        SecurityManager sm = System.getSecurityManager();
        if (sm != null) {
            checkClassLoaderPermission(scl, Reflection.getCallerClass());
        }
        return scl;
    }
    private static synchronized void initSystemClassLoader() {
            if (!sclSet) {
                sun.misc.Launcher l = sun.misc.Launcher.getLauncher();
                if (l != null) {
                    Throwable oops = null;
                    scl = l.getClassLoader();
                }
            }
    }
    

    运行代码测试,返回的是应用类加载器。

    // sun.misc.Launcher$AppClassLoader@18b4aac2
    @Test
    public void run66(){
        System.out.println(ClassLoader.getSystemClassLoader());
    }
    

    这三个类加载器负责不同路径下的类加载。

    启动类加载器介绍

    启动类加载器 BootClassLoader 负责System.getProperty("sun.boot.class.path") 的类加载。也即是以下类。

    ${JAVA_HOME}/jre/lib/*.jar${JAVA_HOME}/jre/classes 类的加载。

    /Library/Java/JavaVirtualMachines/jdk1.8.0_231.jdk/Contents/Home/jre/lib/resources.jar
    /Library/Java/JavaVirtualMachines/jdk1.8.0_231.jdk/Contents/Home/jre/lib/rt.jar
    /Library/Java/JavaVirtualMachines/jdk1.8.0_231.jdk/Contents/Home/jre/lib/sunrsasign.jar
    /Library/Java/JavaVirtualMachines/jdk1.8.0_231.jdk/Contents/Home/jre/lib/jsse.jar
    /Library/Java/JavaVirtualMachines/jdk1.8.0_231.jdk/Contents/Home/jre/lib/jce.jar
    /Library/Java/JavaVirtualMachines/jdk1.8.0_231.jdk/Contents/Home/jre/lib/charsets.jar
    /Library/Java/JavaVirtualMachines/jdk1.8.0_231.jdk/Contents/Home/jre/lib/jfr.jar
    /Library/Java/JavaVirtualMachines/jdk1.8.0_231.jdk/Contents/Home/jre/classes
    
    @Test
    public void run77() {
        final URLClassPath bootstrapClassPath = Launcher.getBootstrapClassPath();
        final URL[] urLs = bootstrapClassPath.getURLs();
        Stream.of(urLs).forEach(item->{
            System.out.println(item.getFile());
        });
    }
    

    扩展类加载器

    扩展类加载器 sun.misc.Launcher.ExtClassLoader 为加载 System.getProperty("java.ext.dirs") 中的类。

    @Test
    public void run99() {
        final String property = System.getProperty("java.ext.dirs");
        final String[] split = property.split(":");
        Stream.of(split).forEach(item -> {
            System.out.println(item);
        });
    }
    
    /Users/zhangpanqin/Library/Java/Extensions
    /Library/Java/JavaVirtualMachines/jdk1.8.0_231.jdk/Contents/Home/jre/lib/ext
    /Library/Java/Extensions
    /Network/Library/Java/Extensions
    /System/Library/Java/Extensions
    /usr/lib/java
    

    应用类加载器

    应用类加载器 sun.misc.Launcher.AppClassLoader 加载 System.getProperty("java.class.path");

    # -cp 指定 classpath 路径,多个路径可以使用 : 分开(linux 下为 :,window 下为 ;),
    java -cp /Users/zhangpanqin/github/fly-java/target/classes com.fly.study.java.classloader.Test2 
    
    

    本文由 张攀钦的博客 http://www.mflyyou.cn/ 创作。 可自由转载、引用,但需署名作者且注明文章出处。

    如转载至微信公众号,请在文末添加作者公众号二维码。微信公众号名称:Mflyyou

    img

    相关文章

      网友评论

          本文标题:Java 中读取文件内容的 n 中方式

          本文链接:https://www.haomeiwen.com/subject/qxavbhtx.html