首先
javac 编译 .java文件 生成 .class字节码的文件
new 一个对象的时候 首先检查这个类有没有父类,有父类的话 ClassLoader 先将 父类的 .class文件读取到内存中然后创建一个java.lang.Class对象 然后子类进行同样的步骤。
然后初始化父类的静态属性
初始化父类的静态代码块
初始化子类的静态属性
初始化子类的静态代码块
初始化父类的属性
初始化父类的方法
执行父类的构造函数
初始化子类的属性
初始化子类的方法
初始化子类的构造函数
执行子类的构造方法
虚拟机加载类的途径
1、由 new 关键字创建一个类的实例
在由运行时刻用 new 方法载入
如:Dog dog = new Dog();
2、调用 Class.forName() 方法
通过反射加载类型,并创建对象实例
如:Class clazz = Class.forName(“Dog”);
Object dog =clazz.newInstance();
3、调用某个 ClassLoader 实例的 loadClass() 方法
通过该 ClassLoader 实例的 loadClass() 方法载入。应用程序可以通过继承 ClassLoader 实现自己的类装载器。
如:Class clazz = classLoader.loadClass(“Dog”);
Object dog =clazz.newInstance();
三者的区别:
1和2使用的类加载器是相同的,都是当前类加载器。(即:this.getClass.getClassLoader)。3由用户指定类加载器。如果需要在当前类路径以外寻找类,则只能采用第3种方式。第3种方式加载的类与当前类分属不同的命名空间。另外,1是静态加载,2、3是动态加载
两个异常(exception)
静态加载的时候如果在运行环境中找不到要初始化的类,抛出的是NoClassDefFoundError,它在JAVA的异常体系中是一个Error
动态态加载的时候如果在运行环境中找不到要初始化的类,抛出的是ClassNotFoundException,它在JAVA的异常体系中是一个checked异常
Class.forName与ClassLoader.loadClass区别
Class的装载包括3个步骤:加载(loading),连接(link),初始化(initialize).
Class.forName(className)实际上是调用Class.forName(className, true, this.getClass().getClassLoader())。第二个参数,是指Class被loading后是不是必须被初始化。
ClassLoader.loadClass(className)实际上调用的是ClassLoader.loadClass(name, false),第二个参数指Class是否被link。
Class.forName(className)装载的class已经被初始化,而ClassLoader.loadClass(className)装载的class还没有被link。一般情况下,这两个方法效果一样,都能装载Class。但如果程序依赖于Class是否被初始化,就必须用Class.forName(name)了。
例如,在JDBC编程中,常看到这样的用法,Class.forName(“com.mysql.jdbc.Driver”).
如果换成了getClass().getClassLoader().loadClass(“com.mysql.jdbc.Driver”),就不行。
com.mysql.jdbc.Driver的源代码如下:
// Register ourselves with the DriverManager
static {
try {
java.sql.DriverManager.registerDriver(new Driver());
} catch (SQLException E) {
throw new RuntimeException(“Can’t register driver!”);
}
}
原来,Driver在static块中会注册自己到java.sql.DriverManager。而static块就是在Class的初始化中被执行。
所以这个地方就只能用Class.forName(className)。
对于相同的类,JVM最多会载入一次。但如果同一个class文件被不同的ClassLoader载入,那么载入后的两个类是完全不同的。因为已被加载的类由该类的类加载器实例与该类的全路径名的组合标识。设有 packagename.A Class ,分别被类加载器 CL1 和 CL2 加载,所以系统中有两个不同的 java.lang.Class 实例:
classload过程
https://www.cnblogs.com/gdpuzxs/p/7044963.html
https://blog.csdn.net/javazejian/article/details/73413292#编写自己的类加载器
jar包导入项目,启动后classnotfound:
-jar 后面所跟的jar包的优先级别最高。如果指定了-jar选项,所有环境变量和命令行制定的搜索路径都将被忽略。JVM APPClassloader将只会以jar包为搜索范围
https://www.cnblogs.com/zpbolgs/p/7267384.html
https://blog.csdn.net/sayyy/article/details/81120749
3、MANIFEST文件
https://www.jb51.net/article/131101.htm
本文主要研究的是web项目下读取classpath路径下的文件的问题,具体如下。
首先分两大类按web容器分类
一种是普通的web项目,像用Tomcat容器,特点是压缩包随着容器的启动会解压缩成一个文件夹,项目访问的时候,实际是去访问文件夹,而不是jar或者war包。
这种的无论你是用获取路径的方法this.getClass().getResource("/")+fileName
获取流的方法this.getClass().getResourceAsStream(failName);
import org.springframework.util.ResourceUtils;
File file= ResourceUtils.getFile("classpath:test.txt");
或者
ClassPathResource classPathResource = new ClassPathResource("test.txt");
获取文件:classPathResource .getFile();
获取文件流:classPathResource .getInputStream();
第二种是内嵌web容器,其特点是只有一个jar文件,在容器启动后不会解压缩,项目实际访问时jar包或者war包
这种最容易遇坑,最大的坑就是,用第一种方式读取,在eclipse,本地调试,完美运行,到linux环境下,就不行.
首先用获取路径的方法this.getClass().getResource("/")+fileName,获取流的方法this.getClass().getResourceAsStream(failName);
在本地运行时,绝壁能找到,你打印出来路径,没错,是咱们eclipse的工作目录,项目目录,但是在target目录下。
现在给你分析为什么去到线上,就GG了,很简单,线上内嵌的工程,我们只会放一个jar文件上去,我理解是jar里面的路径是获取不到的,jar是封闭性东西吧,不像文件夹,总不能c:/home/xx.jar/file.txt
读取jar里面的文件,我们只能用流去读取,不能用file,文件肯定要牵扯路径,jar那个路径刚刚我已经拼出来了
jar里面文件读取方式:
ClassPathResource classPathResource = new ClassPathResource("test.txt");
获取文件流:classPathResource .getInputStream();
总结
以上就是本文关于浅谈web项目读取classpath路径下面的文件的全部内容,希望对大家有所帮助。感兴趣的朋友可以继续参阅本站其他相关专题,如有不足之处,欢迎留言指出。感谢朋友们对本站的支持!