
使用JAVA开发或运行基于JAVA编写的程序,在安装JDK之后,一般需要配置如下几条环境变量(以Windows为例):
那么,它们的作用是什么呢?
JAVA_HOME 该变量指明了JAVA运行环境的安装路径。它和JAVA之间,除了字面相似度高外,没有什么联系。之所以配置该变量大约有三点原因:
如果确定没有应用依赖该变量,甚至可以不配置。但基于前两个优点,一般不建议这么做。
CLASSPATH 该变量指明JAVA运行环境JRE搜索 class 文件的路径。
PATH 该变量指明 *** 作系统查找可执行程序的路径。该变量由 *** 作系统使用,配置 %JAVA_HOME%\bin 以便快捷访问该目录下如 java 、 javac 等命令行和其他JDK工具。
明白了这三个变量的用途,可知对于JAVA程序最重要的是 ClassPath 环境变量,因为它指明JAVA程序搜索第三方和用户自定义类的路径,如果不正确配置,JAVA程序将不能正常运行。
那么如何正确配置 ClassPath 呢?有如下四种方式:
其中第三种方式和第四种方式可独自配置单个JAVA应用而不影响其他JAVA应用,官方推荐使用第三种方式配置 ClassPath 。具体的配置方法如开篇所述,形式类似如下:
每个路径之间使用英文分号‘;’进行分隔(linux环境下则使用英文冒号‘:’分隔)。这个例子中的单个路径都是文件夹,除此之外,单个路径还可以是特定的jar包和zip包路径。假如一个类文件的路径如下
如果该类位于 comzdb 包下,那么正确设置的 ClassPath 为:
如果该类引用了如下的第三方jar包:
此时 ClassPath 为:
如果该类引用了多个第三方jar包:ajar bjar cjar,此时 ClassPath 为:
也可以使用通配符‘’表示为:
需要注意的是:
针对第二点,上一个例子在程序运行时,使用系统属性 javaclasspath 获得的类路径可能为(注意abc的顺序已改变):
这个顺序对于JVM加载类颇为重要。假设特殊情况下,bjar和cjar中都含有同包名且同类名的一个类,那么JVM当查找到cjar时发现该类后,将不会继续查找bjar。如果JAVA应用依赖于jar包的加载顺序,那么需要明确指定jar包顺序,而不能使用通配符。
针对第三点, manifest 文件配置类路径和前三种方式稍有不同,形式为:
注意到,使用空格分隔各个路径而不是分号‘;’或冒号‘:’。另外,当类路径过多而需要换行时,从第二行开始,必须以两个空格开头。
附:
JAVA类路径官网介绍
Manifest文件类路径说明
一个jar包加载顺序引发的BUG
一段程序的完整执行过程是先编译,然后加载到jvm,然后初始化,然后你才能使用(当然中间还有需要细节)。初始化是执行类构造器方法的过程,类构造器方法是由编译器自动收集类中的所有类变量的赋值动作和静态语句块中的语句合并产生的(不是类的构造方法)。所以,简而言之,一个类在初始化的时候,会先执行它的静态代码块,当然,如果这个类有父类的话,同样的道理,如果发现该父类没有初始化,则需要先对其父类进行初始化,再对该类进行初始化。
至于一个类在什么时候初始化,简单的说,当程序第一次用到这个类时候就会对其进行初始化(前提是其没有进行过初始化)。
你的Egg类没有用到啊 ,该类没有被加载的
当类加载器加载Egg类的时候肯定是下面两句代码之一被执行:
new Egg();
ClassforName("Egg");
或者使用了继承:class Chicken extends Egg
printInit方法是初始化int变量的同时,打印一个字符串。
而这个变量是static即使静态变量,那么在类加载的时候就执行了,因此这个打印字符串总是第一个执行。
main方法中先声明并实例化了一个子类对象,那么其父类对象是首先构建起来的。(在Beetle构造方法中隐藏了一句调用父类无参构造方法的语句。)
其它的顺序应该很好理解了。
java虚拟机把描述类的数据从class文件加载到内存,并对数据进行 校验/准备/解析和初始化,最终形成可以被虚拟机直接使用的Java类型,这个过程被称作虚拟机的类加载机制。
称作虚拟机的类加载机制。
loading -> linking (verification -> preparation -> resolution)-> resolution)->initializing
loading: 把class文件load到内存中,采用双亲委派,主要是为了安全性
verification: 校验class文件是否符合标准
preparation: 静态变量分配内存并设初始值的阶段(不包括实例变量)
resolution:把符号引用转换为直接引用
initializing:静态变量赋初始值
类加载的过程主要分为三个部分:加载、连接、初始化这三个阶段。
类的加载指的是将类的class文件中的二进制数据读入到内存中,将其放在运行时数据区的方法区内,然后在堆区创建一个这个类的javalangClass对象,用来封装类在方法区类的对象。主要步骤可以分为下面的三件事情:
加载阶段完成后,虚拟机外部的 二进制字节流就按照虚拟机所需的格式存储在方法区之中,而且在Java堆中也创建一个javalangClass类的对象,这样便可以通过该对象访问方法区中的这些数据。
类的加载的最终产品是位于堆区中的Class对象。Class对象封装了类在方法区内的数据结构,并且向Java程序员提供了访问方法区内的数据结构的接口。加载类的方式有以下几种:
2加载器
JVM的类加载是通过ClassLoader及其子类来完成的,类的层次关系和加载顺序可以由下图来描述:
1BootstrapClassLoader(启动类加载器)
在连接里面又可以被分成3个小阶段,分别是:验证,准备,解析
1验证(目的):
2验证内容:
验证是连接阶段的第一步,这一阶段的目的是为了确保Class文件的字节流中包含的信息符合当前虚拟机的要求,并且不会危害虚拟机自身的安全。验证阶段大致会完成4个阶段的检验动作:
验证阶段是非常重要的,但不是必须的,它对程序运行期没有影响,如果所引用的类经过反复验证,那么可以考虑采用-Xverifynone参数来关闭大部分的类验证措施,以缩短虚拟机类加载的时间。
3准备:
在准备阶段,为静态变量的初值为jvm默认的初值,而不是我们在程序中设定的初值。jvm默认为静态变量的初值是这样的
4解析:
这一阶段的任务就是把常量池中的符号引用转换为直接引用 什么是符号引用,什么是直接引用。
1工作内容:
JVM负责主要对类变量(类变量就是static修改的变量)进行初始化这里主要对类变量(类变量就是static修改的变量)进行初始化,初始化主要有两个方式:
2初始化时机:
类初始化时机:只有当对类的主动使用的时候才会导致类的初始化,类的主动使用包括以下六种:
3初始化顺序:
如果有父类,则顺序是:父类static方法/static变量赋值 –> 子类static方法/static变量赋值
三、结语:
上面介绍的就是类(class)的加载,包含它的加载、链接、初始化。
Android进阶知识点,我最近整理了许多,里面讲解的非常详细。取
>
严格来说,你那个不叫单例,单例一个比较明显的特点就是构造函数私有化。这种机制说起来话长,你把public static int b = 0;理解成public static int b; public int b = 0;就好了,静态变量在类加载时已经为其分配了内存空间,但是并没有立即为其赋值,只有执行到赋值语句时才赋值的,你的构造函数先执行的执行完之后b = 1但是最后又执行b = 0的 *** 作,于是结果便是0了,a就不一样了,你并没有赋值,所以它相当于就被改变了一次于是就是1了
以上就是关于ClassPath详解全部的内容,包括:ClassPath详解、Java:类成员的初始化顺序 类加载 疑问(如图)、关于java的加载顺序问题等相关内容解答,如果想了解更多相关内容,可以关注我们,你们的支持是我们更新的动力!
欢迎分享,转载请注明来源:内存溢出
微信扫一扫
支付宝扫一扫
评论列表(0条)