Java获取外部资源文件的几种方式和区别

获取外部资源的几种方式

1.使用FileInputStream(不推荐)


使用FileInputStream要传入File对象,此处便于演示,将输出配置文件的位置。
代码
   InputStream is = new FileInputStream(new File("ReflectDemo/resource/pro.properties"));
   System.out.println(new File("ReflectDemo/resource/pro.properties").getPath());
结果
ReflectDemo\resource\pro.properties
注:使用此方法获取获取配置文件要指定文件的绝对路径/相对路径。并且获取到的是在项目根目录下盘符下的配置文件,对于出现在classpath(类路径)下的配置文件,FileInputStream是读不到的。

2.使用Class.getResourceAsStream()方法


此处便于演示,将调用Class.Resource().getPath()来输出文件位置
代码
System.out.println(ReflectDemo.class.getResource("/pro.properties").getPath());
结果
/D:/idea/object/out/production/ReflectDemo/pro.properties

此方法可以用于获取类路径的配置文件。

3.Class.getClassLoder().getResourceAsStream()方法


此处便于演示,将调用Class.getClassLoder().Resource().getPath()来输出文件位置
代码
System.out.println(ReflectDemo.class.getClassLoader().getResource("pro.properties").getPath());
结果
/D:/idea/object/out/production/ReflectDemo/pro.properties

以上两方法区别
Class.getResourceAsStream(String name) 其实调用的就是
Class.getClassLoder().getResourceAsStream(String name)。

     public InputStream getResourceAsStream(String name) {
        name = resolveName(name);
        ClassLoader cl = getClassLoader0();
        if (cl==null) {
            // A system class.
            return ClassLoader.getSystemResourceAsStream(name);
        }
        return cl.getResourceAsStream(name);
    }
调用之前多了一个用于解析name的操作,如果开头是“/”则跟直接调用Class.getClassLoder().getResourceAsStream(String path)一样
例:Class.getClassLoder().getResourceAsStream("");==Class.getResourceAsStream("/");
如果Class.getResourceAsStream(String name);开头不是"/",则Class.resolveName(name)方法将会返回此类文件的目录+name
也就是说此方法获取的Resource是在此类目录下的并非classpath根目录
他们最终都会调用ClassLoder.getResource(String name);
这个方法通过递归实现,双亲委派原理。
源代码

   /**
     * ...
     * @param  name
     *         The resource name
     *
     * @return  A URL object for reading the resource, or
     *          null if the resource could not be found or the invoker
     *          doesn't have adequate  privileges to get the resource.
     *
     * @since  1.1
    **/
    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;
    }

"该方法将首先在父类加载器中搜索资源; 如果父类加载器为null,则搜索虚拟机内置的类加载器的路径。 那个方法失败,将调用findResource(String)来查找资源。"

简单的说 就是第一次执行进去首先迭代进入最顶层的启动类加载器(Bootstrap ClassLoader)如果找不到资源,就会返回null ,并跳出去执行它子加载器(Extension ClassLoader)重写的findResource方法,如果返回null就继续跳出执行下一个子加载器(App ClassLoader)的findResoure方法,如果继续返回空的话(在没有自定义类加载器的情况下,就会直接返回 null。有的话,继续执行自定义类加载器的findResource方法)。这个方法会返回resource的URL对象 或者 nul。

对于喜欢"钻牛角尖"的小白,可能对于上面出现的 双亲委派Bootstrap ClassLoaderExtension ClassLoaderApp ClassLoader术语很困扰。请点击链接进行详细了解。

4.ClassLoader.getSystemResource()方法


相信眼睛尖的小伙伴已经发现了Class.getResourceAsStream()源代码中出现了这个方法。其实正如此方法所言,就是通过调用系统类加载器(App ClassLoader)的getResource()方法,和前两种方法没有什么本质上的区别。

总结

1.一般不推荐通过FileInputStream 来获取资源文件
1.现在的web项目,resources编译后就在classes目录下,通过classloader可以直接获取
2.对于变动的环境(现在都是部署在linux机器上)来说不太适用。
2.class.getResource("/") == class.getClassLoader().getResource("")
其实,Class.getResource和ClassLoader.getResource本质上是一样的,都是使用ClassLoader.getResource加载资源的。
3.Class.getResource真正调用ClassLoader.getResource方法之前,会先获取文件的路径(path不以'/'开头时,默认是从此类所在的包下取资源;path以'/'开头时,则是从项目的ClassPath根下获取资源)。
4.ClassLoader.getResource方法会通过双亲委派机制,先委派双亲去加载类,如果双亲没有加载到,则再由自己加载。

参考链接&进一步阅读

https://blog.csdn.net/zhangshk_/article/details/82704010
https://blog.csdn.net/eff666/article/details/52203406

物以类聚 人以群分
菅江晖

上一篇:Java对象的深克隆与浅克隆(对象复制)(下)


下一篇:用汽车拉苹果的例子来说HTTPS的安全性很容易理解 但HTTPS并不是绝对可靠的