当前位置:首页 » 编程语言 » java类的加载过程

java类的加载过程

发布时间: 2023-05-22 12:30:48

‘壹’ java解释器如何加载类

类加载次序:1、静态代码块或者静态方法->2、main方法调用到的方法
对象加载次序:1、静态代码块或者静态方法->2、非静态代码块或者非静态方法->3、对象的构造方法。
但是有一段代码没有办法解释。代码忘了,过段时间丢上来
个人感觉应该好像不大对劲,我觉得应该是:
类装载时,1、静态代码块或者静态方法被调用
然后是程序的运行,main调用到的方法会被执行,如果是新建一个对象,则
2、非静态代码块或者非静态方法->3、对象的构造方法顺序执行。

===============================================

首先我们要分析类加载原理,java中默认有三种类加载器:引导类加载器,扩展类加载器,系统类加载器(也叫应用类加载器)引导类加载器负责加载jdk中的系铅尘肆统类,这种类加载器都是用c语言实现的,在java程序中没有办法获得这个类加载器,对于java程序是一个概念而已,基本上不用考虑它的存在,像String,Integer这样的类都是由引导类加载器加载器的.
扩展类加载器负责加载标准扩展类,一般使用java实现,这是一个真正的java类加载器,负责加载jre/lib/ext中的类,和普通的类加载器一样,其实这个类加载器对我们来说也不是很重要,我们可以通过java程序获得这个类加载器。
系统类加载器,加载第一个应用类的加载器(其实这个定义并不准确,下面你将会看到),也就是执行java MainClass 时加载MainClass的加载器,这个加载器使用java实现,使用的很广泛,负责加载classpath中指定的类。

类加载器之间有一定的关系(父子关系),我们可以认为扩展类加载器的父加载器是引导类加载器(当然不这样认为也是可以的,因为引导类加载器表现在java中就是一个null),不过系统类加载器的父加载器一定是扩展类加载器,类加载器在加载类的时候会先给父加载器一个机会,只有父加载器无法加载时才会自己去加载。

我们无法获得引导类加载器,因为它是使用c实现的,而且使用引导类加载器加载的类通过getClassLoader方法返回的是null.所以无兄戚法直接操作引导类加载器,但是我们可以根据Class.getClassLoader方法是否为null判断这个类是不是引导类加载器加载的,可以通过下面的方法获得引导类加载器加载的类路径(每个jar包或者文件夹对应了一个URL);
sun.misc.Launcher.getBootstrapClassPath().getURLs()
你可以直接在你的main函数中输出就可以了
System.out.println(java.util.Arrays.asList(sun.misc.Launcher.getBootstrapClassPath().getURLs()).toString());
得到的结果是:
[file:/槐轿C:/Program%20Files/Java/j2re1.4.2_10/lib/rt.jar,
file:/C:/Program%20Files/Java/j2re1.4.2_10/lib/i18n.jar,
file:/C:/Program%20Files/Java/j2re1.4.2_10/lib/sunrsasign.jar,
file:/C:/Program%20Files/Java/j2re1.4.2_10/lib/jsse.jar,
file:/C:/Program%20Files/Java/j2re1.4.2_10/lib/jce.jar,
file:/C:/Program%20Files/Java/j2re1.4.2_10/lib/charsets.jar,
file:/C:/Program%20Files/Java/j2re1.4.2_10/classes]

其实我们是可以指定引导类加载器的类路径的,java提供了一个-Xbootclasspath参数,不过这个参数不是标准参数。
java -Xbootclasspath: 运行时指定引导类加载器的加载路径(jar文件或者目录)
java -Xbootclasspath/p:和上面的相同,不过把这个路径放到原来的路径前面
java -Xbootclasspath/a:这个就是在原引导类路径后面添加类路径。
上面我们有提过加载第一个应用类未必就是系统加载器。
如果我把这个应用类的路径放到引导类路径中,它将会被引导类加载器加载,大致这样
java -Xbootclasspath/a:myjar.jar MainClass
如果MainClass在myjar.jar中,那么这个类将会被引导类加载器加载。
如果希望看详情,使用-verbose参数,为了看的更清楚,使用重定向,大致为(windows下):
java -verbose -Xbootclasspath/a:myjar.jar MainClass -> C:\out.txt
通过这个参数我们可以实现自己的系统类,比如替换掉java.lang.Object的实现,自己可以扩展
一些方法,不过这样做似乎没有好处,因为那就不是标准了。

我们最关心的还是系统类加载器,一般都认为系统类加载器是加载应用程序第一个类的加载器,
也就是java MainClass命令中加载MainClass的类加载器,这种说法虽然不是很严谨,但基本上还是可以这样认为的,因为我们很少会改变引导类加载器和扩展类加载器的默认行为。应该说系统类加载器负责加载classpath路径中的而且没有被扩展类加载器加载的类(当然也包括引导类加载器加载的)。如果classpath中有这个类,但是这个类也在扩展类加载器的类路径,那么系统类加载器将没有机会加载它。
我们很少改变扩展类加载器的行为,所以一般你自己定义的类都是系统类加载器加载器的。

获得系统类加载器非常简单,假设MyClass是你定义的一个类
MyClass.class.getClassLoader()返回的就是系统类加载器,当然这种方法无法保证绝对正确,我们可以使用更简单而且一定正确的方式:
ClassLoader.getSystemClassLoader()获得系统类加载器。我们知道ClassLoader是一个抽象类,所以系统类加载器肯定是ClassLoader的一个子类实现。我们来看看它是什么
ClassLoader.getSystemClassLoader().getClass();
结果是class sun.misc.Lancher$AppClassLoader
可以看出这是sun的一个实现,从名字可以看出是一个内部类,目前我也没有看到这个源代码,似乎还不是很清晰:
我们在看看它的父类是什么:
ClassLoader.getSystemClassLoader().getClass().getSuperclass();
结果是:class java.net.URLClassLoader
这个是j2se的标准类,它的父类是SecureClassLoader,而SecureClassLoader是继承ClassLoader的。
现在整个关系应该很清楚,我们会看到几乎所有的ClassLoader实现都是继承URLClassLoader的。
因为系统类加载器是非常重要的,而且是我们可以直接控制的,所以我们后面还会介绍,不过先来看一下扩展类
加载器以及它们之间的关系。

扩展类加载器似乎是一个不起眼的角色,它负责加载java的标准扩展(jre/lib/ext目录下的所有jar),它其实就是一个普通的加载器,看得见摸得着的。
首先的问题是怎么知道扩展类加载器在哪里?
的确没有直接途径获得扩展类加载器,但是我们知道它是系统类加载器的父加载器,我们已经很容易的获得系统类加载器了,所以我们可以间接的获得扩展类加载器:
ClassLoader.getSystemClassLoader().getParent().getClass();
其实是通过系统类加载器间接的获得了扩展类加载器,看看是什么东西:
结果是:class sun.misc.Launcher$ExtClassLoader
这个类和系统类加载器一样是一个内部类,而且定义在同一个类中。
同样看看它的父类是什么:
ClassLoader.getSystemClassLoader().getParent().getClass().getSuperclass();
可以看出结果也是class java.net.URLClassLoader
扩展类加载jre/lib/ext目录下的所有类,包括jar,目录下的所有类(目录名不一定要classes).
现在可以回答上面的问题了,你写一个HelloWorld,放到jre/lib/ext/下的某个目录
比如 jre/lib/ext/myclass/HelloWorld.class
然后在你classpath也设置一份到这个类的路径,结果执行java HelloWorld时,这个类是被扩展类加载器加载器的,可以这样证明
public static void main(String[] args){
System.out.println("loaded by"+HelloWorld.class.getClassLoader().getClass());
System.out.println("Hello World");
}
结果可以得到class sun.misc.Launcher$ExtClassLoader
当然如果你把jre/lib/ext下myclass这个目录删除,仍然可以运行,但是这样结果是
class sun.misc.Lancher$AppClassLoader
如果你不知道这个过程的话,假设在你扩展类路径下有一份classpath中的拷贝,或者是比较低的版本,当你使用新的版本时会发现没有起作用,知道这个过程你就不会觉得奇怪了。另外就是两个不同的类加载器是可以加载一个同名的类的,也就是说虽然扩展类加载器加载了某个类,系统类加载器是可以加载自己的版本的,
但是现有的实现都没有这样做,ClassLoader中的方法是会请求父类加载器先加载的,如果你自己定义类加载器完全可以修改这种默认行为,甚至可以让他没有父加载器。

这里给出一个方法如何获得扩展类加载器加载的路径:
String path=System.getProperty("java.ext.dirs");
File dir=new File(path);
if(!dir.exists()||!dir.isDirectory()){
return Collections.EMPTY_LIST;
}
File[] jars=dir.listFiles();
URL[] urls=new URL[jars.length];
for(int i=0;i<jars.length;i++){
urls[i]=sun.misc.URLClassPath.pathToURLs(jars[i].getAbsolutePath())[0];
}
return Arrays.asList(urls);

对于扩展类加载器我们基本上不会去关心,也很少把你自己的jar放到扩展路径,大部分情况下我们都感觉不到它的存在,当然如果你一定要放到这个目录下,一定要知道这个过程,它会优先于classpath中的类。

现在我们应该很清楚知道某个类是哪个加载器加载的,并且知道为什么是它加载的,如果要在运行时获得某个类的类加载器,直接使用Class的getClassLoader()方法就可以了。

用户定义的类一般都是系统类加载器加载的,我们很少直接使用类加载器加载类,我们甚至很少自己加载类。
因为类在使用时会被自动加载,我们用到某个类时该类会被自动加载,比如new A()会导致类A自动被加载,不过这种加载只发生一次。
我们也可以使用系统类加载器手动加载类,ClassLoader提供了这个接口
ClassLoader.getSystemClassLoader().loadClass("classFullName");
这就很明确的指定了使用系统类加载器加载指定的类,但是如果该类能够被扩展类加载器加载,系统类加载器还是不会有机会的。
我们最常用的还是使用Class.forName加载使用的类,这种方式没有指定某个特定的ClassLoader,会使用调用类的ClassLoader。
也就是说调用这个方法的类的类加载器将会用于加载这个类。比如在类A中使用Class.forName加载类B,那么加载类A的类加载器将会用于加载类B,这样两个类的类加载器是同一个。

最后讨论一下如何获得某个类加载器加载了哪些类,这个似乎有一定的使用价值,可以看出哪些类被加载了。其实这个也不是很难,因为ClassLoader中有一个classes成员变量就是用来保存类加载器加载的类列表,而且有一个方法
void addClass(Class c) { classes.addElement(c);}
这个方法被JVM调用。
我们只要利用反射获得classes这个值就可以了,不过classes声明为private的,我们需要修改它的访问权限(没有安全管理器时很容易做到)
classes = ClassLoader.class.getDeclaredField("classes");
classes.setAccessible(true);
List ret=(List) classes.get(cl); //classes是一个Vector
可惜的是对于引导类加载器没有办法获得加载的类,因为它是c实现的,在java中很难控制

‘贰’ java类加载问题。不要告诉我结果,我问的是为什么会是这样的结果,加载顺序是什么

加载类、 静态成员变量初始化、 静态代码块、非静态成员变量初始化、 构造方法;
首先 加载类的时候先加载类的静态成员变量. 因为静态变量依赖类,不依赖对象,先加载
public static int k = 0;
public static T t1 = new T("t1"); 加载到这一行的时候就会new一个对象出来,并执行public int j = print("j"); 因为j是成员变量,依赖对象.
所以在对象初始化的时候才加载. 然后加载非静态的构造块 构造方法. t2静态变量同理.
t1 t2加载的时候,i n 并未初始化.因为静态变量i , n 在静态变量t1,t2后,所以i, n 就为0开始.
public static int i = print("i"); 加载到静态变量i时,执行print方法.
当所有静态变量加载完成后,加载静态块.
类的加载和对象的初始化是不一样的.

‘叁’ java中反射实例类装载的步骤及简要阐述

java反射和类装载

反射机制:

Person p=new Person();
这是什么?当然是实例化一个对象了.可是这种实例化对象的方法存在一个问题,那就是必须要知道类名才可以实例化它的对象,这样我们在应用方面就会受到限制.那么有没有这样一种方式,让我们不知道这个类的类名就可以实例化它的对象呢?Thank Goodness!幸亏我们用的是java, java就提供了这样的机制.

1).java程序在运行时可以获得任何一个类的字节码信息,包括类的修饰符(public,static等),基类(超类,父类),实现的接口,字段和方法等信息.
2).java程序在运行时可以根据字节码信息来创建该类的实例对象,改变对象的字段内容和调用对象方法.
这样的机制就叫反射技术.可以想象光学中的反射,就像我们照镜子,镜子中又出现一个自己(比喻可能不太恰当,但是足以表达清楚意思了).反射技术提供了一种通用的动态连接程序组件的方法,不必要把程序所需要的目标类硬编码到源程序中,从而使得我们可以创建灵活的程序.

反射的实现步骤( 不问不需要答) ,

1、获取类的常用方式有三种: a) Class.forName("包名.类名"),最常用、推荐;b) 包名.类名.class 最简捷;c) 对象.getClass 的方式获得。

2、对象的实例化,上面已经获取了类,只需要调用类的实例化方法,类.newInstance()便可。

3、获取属性和构造等,可以参考 JavaApi 的调用,类. getDeclaredFields,类. getConstructor(..)等。

Java的反射机制是通过反射API来实现的,它允许程序在运行过程中取得任何一个已知名称的类的内部信息.反射API位于java.lang.reflect包中.主要包括以下几类:
1).Constructor类:用来描述一个类的构造方法
2).Field类:用来描述一个类的成员变量
3).Method类:用来描述一个类的方法.
4).Modifer类:用来描述类内各元素的修饰符
5).Array:用来对数组进行操作.
Constructor,Field,Method这三个类都是JVM(虚拟机)在程序运行时创建的,用来表示加载类中相应的成员.这三个类都实现了java.lang.reflect.Member接口,Member接口定义了获取类成员或构造方法等信息的方法.要使用这些反射API,必须先得到要操作的对象或类的Class类的实例.通过调用Class类的newInstance方法(只能调用类的默认构造方法)可以创建类的实例.这样有局限性,我们可以先冲类的Class实例获取类需要的构造方法,然后在利用反射来创建类的一个实例.

类加载机制:



类的加载机制可以分为加载-链接-初始化三个阶段,链接又可以分为验证、准备、解析三个过程。

加载:通过类的加载器查找并加载二进制字节流的过程,在堆内存中的方法区生成 一个代表这个类的 java.lang.Class 对象,作为这个类的数据请求入口。(这里可以把上面类加载器加载文件的过程描述一下(参考版本一,不作重复))。

验证:主要是对一些词法、语法进行规范性校验,避免对 JVM 本身安全造成危害; 比如对文件格式,字节码验证,无数据验证等。但验证阶段是非必须的,可以通过参数 设置来进行关闭,以提高加载的时效。

准备:对类变量分配内存,并且对类变量预初始化,初始化成数据类型的原始值, 比如 static int a=11,会被初始化成成 a=0;如果是 static double a =11,则会被初始化成 a=0.0; 而成员变量只会成实例化后的堆中初始化。

解析:把常量池中的符号引用转换为直接引用的过程。

初始化:对类的静态变量和静态块中的变量进行初始化。(上面的准备阶段可以作为 预初始化,初始到变量类型的原值,但如果被 final 修饰会进行真正初始化)

上面加载、链接、初始化的各个阶段并不是彼此独立,而是交叉进行,这点很重要 。

***class.forName和 classloader的区别

Class.forName 和 ClassLoader 都是用来装载类的,对于类的装载一般为分三个阶段加载、链接、编译,它们装载类的方式是有区别。

首先看一下 Class.forName(..),forName(..)方法有一个重载方法 forName(className,boolean,ClassLoader),它有三个参数,第一个参数是类的包路径,第二个参数是 boolean

类型,为 true 地表示 Loading 时会进行初始化,第三个就是指定一个加载器;当你调用class.forName(..)时,默认调用的是有三个参数的重载方法,第二个参数默认传入 true,第三个参数默认使用的是当前类加载时用的加载器。

ClassLoader.loadClass()也有一个重载方法,从源码中可以看出它默认调的是它的重载 方法 loadClass(name, false),当第二参数为 false 时,说明类加载时不会被链接。这也是两者之间最大区别,前者在加载的时候已经初始化,后者在加载的时候还没有链接。如果你需要在加载时初始化一些东西,就要用 Class.forName 了,比如我们常用的驱动加载, 实际上它的注册动作就是在加载时的一个静态块中完成的。所以它不能被 ClassLoader 加载代替。

‘肆’ JVM之class加载过程

java虚拟机把描述类的数据从class文件加载到内存,并对数据进行 校验/准备/解析和初始化,最终形成可以被虚拟机直接使用的Java类型,这个过程被称作虚拟机的类加载机制。

称作虚拟机的类加载机制。

loading -> linking (verification -> preparation -> resolution)-> resolution)->initializing

loading: 把class文件load到内存中,采用双亲委派,主要是为了安全性

verification: 校验class文件是否符合标准

preparation: 静态变量分配内存并设初始值的阶段(不包括实例变量)

resolution:把符号引用转换为直接引用

initializing:静态变量赋初始值

类加载的过程主要分为三个部分:加载、连接、初始化这三个阶段。

类的加载指的是将类的.class文件中的二进制数据读入到内存中,将其放在运行时数据区的方法区内,然后在堆区创建一个这个类的java.lang.Class对象,用来封装类在方法区类的对象。主要步骤可以分为下面的三件事情:

加载阶段完成后,虚拟机外部的 二进制字节流就按照虚拟机所需的格式存储在方法区之中,而且在Java堆中也创建一个java.lang.Class类的对象,这样便可以通过该对象访问方法区中的这些数据。

类的加载的最终产品是位于堆区中的Class对象。Class对象封装了类在方法区内的数据结构,并且向Java程序员提供了访问方法区内的数据结构的接口。加载类的方式有以下几种:

2.加载器

JVM的类加载是通过ClassLoader及其子类来完成的,类的层次关系和加载顺序可以由下图来描述:

1.BootstrapClassLoader(启动类加载器)

在连接里面又可以被分成3个小阶段,分别是:验证,准备,解析

1.验证(目的):

2.验证内容:

验证是连接阶段的第一步,这一阶段的目的是为了确保Class文件的字节流中包含的信息符合当前虚拟机的要求,并且不会危害虚拟机自身的安全。验证阶段大致会完成4个阶段的检验动作:

验证阶段是非常重要的,但不是必须的,它对程序运行期没有影响,如果所引用的类经过反复验证,那么可以考虑采用-Xverifynone参数来关闭大部分的类验证措施,以缩短虚拟机类加载的时间。

3.准备:

在准备阶段,为静态变量的初值为jvm默认的初值,而不是我们在程序中设定的初值。jvm默认为静态变量的初值是这样的

4.解析:

这一阶段的任务就是把常量池中的符号引用转换为直接引用 什么是符号引用,什么是直接引用。

1.工作内容:

JVM负责主要对类变量(类变量就是static修改的变量)进行初始化这里主要对类变量(类变量就是static修改的变量)进行初始化,初始化主要有两个方式:

2.初始化时机:

类初始化时机:只有当对类的主动使用的时候才会导致类的初始化,类的主动使用包括以下六种:

3.初始化顺序:

如果有父类,则顺序是:父类static方法/static变量赋值 –> 子类static方法/static变量赋值

三、结语:

上面介绍的就是类(class)的加载,包含它的加载、链接、初始化。
Android进阶知识点,我最近整理了许多,里面讲解的非常详细。取
https://docs.qq.com/doc/DUkNRVFFzTG96VHNiAndroid 技术进阶手册丶面试题纲丶核心笔记资料。

‘伍’ 关于java 类的加载过程,下面哪些描述是正确的

关于Java 类的加载过程,下面哪些描述是正确的(A C )
A、在Java 中,有四种类型的类加载器:BootStrapClassLoader、ExtClassLoader、AppClassLoader 以及用户自定义的 ClassLoader。
B、使用 new 关键字创建类实例时,其实就显示地包含了类的加载过程
C、在Java 中,类的实例化流程分为两个部分:类的加载和类的实例化。类的加载又分
为显式加载和隐式加载。
D、Class.forName 来加载类时,是通过ExtClassLoader进行加载的。

‘陆’ java静态资源(静态方法,静态属性)是程序一运行就加载到jvm中,还是当被调用的时候才进行加载呢

java静态资源(静态方法,静态属性)是程序一运行就加载到jvm中的。

1、类中的静态属性会被加入到类对象(也可以叫做类的模板,是类的描述) 的构造器中,静态方法也会被加入到类对象中。

2、当第一次使用类时,JVM会通过类加载器,加载类对象,从而初始化静态属性,并装入类的方法,包括静态方法和实例方法(方法不会被调用,只是加载,从这个意义上来说,静态方法和实例方法是类似的)。嫌乎歼

3、当顷孝创建类的实例对象时,JVM会调用类的构造器,从而初始化类的属性。

(6)java类的加载过程扩展阅读:

JVM 类加载机制

JVM类加载机制分为五个部分:加载,验证,准备,解析,初始化,下面我们就分别来看一下这五个过程。

1、加载

加载是类加载过程中的一个阶段,这个阶段会在内存中生成一个代表这个类的java.lang.Class对象,作为方法区这个类的各种数据的入口。注意这里不一定非得要从一个Class文件获取,这里既可以从ZIP包中读取(比如从jar包和war包中读取),也可以在运行时计算生成(动态代理),也可以由其它文件生成(比如将JSP文件转换成对应的Class类)。

2、验证

这一阶段的主要目的是为了确保Class文件的字节流中包含的信息是否符合当前虚拟机的要求,并且不会危害虚拟机自身的安全。

3、准备

准备阶段是正式为类变量分配内存并设置类变量的初始值阶段,即在方法区中分配这些变量芹冲所使用的内存空间。

4、解析

解析阶段是指虚拟机将常量池中的符号引用替换为直接引用的过程。符号引用就是class文件中的:

CONSTANT_Class_info

CONSTANT_Field_info

CONSTANT_Method_info

等类型的常量。

5、初始化

初始化阶段是类加载最后一个阶段,前面的类加载阶段之后,除了在加载阶段可以自定义类加载器以外,其它操作都由JVM主导。到了初始阶段,才开始真正执行类中定义的Java程序代码。初始化阶段是执行类构造器<client>方法的过程。

‘柒’ java运行的五个步骤

Java程序从源文件创建到程序运行要经过两大步骤:1、源文件由编译器编译成字节码(ByteCode)
2、字节码由java虚拟机解释运行。因为java程序既要编译同时也要经过JVM的解释运行,所以说Java被称为半解释语言( "semi-interpreted" language)。
下面通过以下这个java程序,来说明java程序从编译到最后运行的整个流程。代码如下:
//MainApp.java
public class MainApp {
public static void main(String[] args) {
Animal animal = new Animal("Puppy");
animal.printName();
}
}
//Animal.java
public class Animal {
public String name;
public Animal(String name) {
this.name = name;
}
public void printName() {
System.out.println("Animal ["+name+"]");
}
}
第一步(编译): 创建完源文件之后,程序会先被编译为.class文件。Java编译一个类时,如果这个类所依赖的类还没有被编译,编译器就会先编译这个被依赖的类,然后引用,否则直接引用,这个有点象make。如果java编译器在指定目录下找不到该类所其依赖的类的.class文件或者.java源文件的话,编译器话报“cant find symbol”的错误。

编译后的字节码文件格式主要分为两部分:常量池和方法字节码。常量池记录的是代码出现过的所有token(类名,成员变量名等等)以及符号引用(方法引用,成员变量引用等等);方法字节码放的是类中各个方法的字节码。下面是MainApp.class通过反汇编的结果,我们可以清楚看到.class文件的结构:
第二步(运行):java类运行的过程大概可分为两个过程:1、类的加载 2、类的执行。需要说明的是:JVM主要在程序第一次主动使用类的时候,才会去加载该类。也就是说,JVM并不是在一开始就把一个程序就所有的类都加载到内存中,而是到不得不用的时候才把它加载进来,而且只加载一次。
下面是程序运行的详细步骤:
在编译好java程序得到MainApp.class文件后,在命令行上敲java AppMain。系统就会启动一个jvm进程,jvm进程从classpath路径中找到一个名为AppMain.class的二进制文件,将MainApp的类信息加载到运行时数据区的方法区内,这个过程叫做MainApp类的加载。
然后JVM找到AppMain的主函数入口,开始执行main函数。
main函数的第一条命令是Animal animal = new Animal("Puppy");就是让JVM创建一个Animal对象,但是这时候方法区中没有Animal类的信息,所以JVM马上加载Animal类,把Animal类的类型信息放到方法区中。
加载完Animal类之后,Java虚拟机做的第一件事情就是在堆区中为一个新的Animal实例分配内存, 然后调用构造函数初始化Animal实例,这个Animal实例持有着指向方法区的Animal类的类型信息(其中包含有方法表,java动态绑定的底层实现)的引用。
当使用animal.printName()的时候,JVM根据animal引用找到Animal对象,然后根据Animal对象持有的引用定位到方法区中Animal类的类型信息的方法表,获得printName()函数的字节码的地址。
开始运行printName()函数。
特别说明:java类中所有public和protected的实例方法都采用动态绑定机制,所有私有方法、静态方法、构造器及初始化方法都是采用静态绑定机制。而使用动态绑定机制的时候会用到方法表,静态绑定时并不会用到。

‘捌’ java 中静态内部类字段什么时候初始化是在外部类加载的时候就初始化吗

首先分析下名字:静态内部类
静态是static。静态的方法或者参数是随着类的加载而产生的(感觉应竖世该是JVM启动时static属性就加载了。)。这就是为什么不用实例化类,就可以调用它的原因。
内部类:用内部类是因为内部类与所在外部类有一定的关系,兆乱往往只有该外部类调用此内部类。其他类不会调用。所以没必要另写一个类。外部类可以调用内部类。而内部类不能调用外部类的方法。
所以我族纤档觉得
静态内部类中的属性,方法,是在外部类加载的时候初始化的。
希望对您有帮助。如果有问题请自行
谷歌
度娘.

‘玖’ 请描述一下JVM加载class文件的原理机制

原理:Java中的所有类,都需要由类加载器装载到JVM中才能运行。类加载器本身也是一个类,而它的工作就是把class文件从硬盘读取到内存中。

在写程序的时候,我们几乎不需要关心类的加载,因为这些都是隐式装载的,除非我们有特殊的用法,像是反射,就需要显式的加载所需要的类。

类装载方式,有两种 :

1、隐式装载, 程序在运行过程中当碰到通过new 等方式生成对象时,隐式调用类装载器加载对应的类到jvm中。

2、显式装载, 通过class.forname()等方法,显式加载需要的类

Java类的加载是动态的,它并不会一次性将所有类全部加载后再运行,而是保证程序运行的基础类(像是基类)完全加载到jvm中,至于其他类,则在需要的时候才加载。这当然就是为了节省内存开销。

(9)java类的加载过程扩展阅读:

Java的类加载器有三个,对应Java的三种类:

Bootstrap Loader :启动类加载器,是虚拟机自身的一部分。负责将存放在lib目录中的类库加载到虚拟机中。其无法被Java程序直接引用。负责加载系统类 (指的是内置类,像是String,对应于C#中的System类和C/C++标准库中的类)。

ExtClassLoader :负责加载扩展类(就是继承类和实现类)。

AppClassLoader :负责加载用户类路径(ClassPath)上所指定的类库(程序员自定义的类)。

JVM中类的加载是由类加载器(ClassLoader)和它的子类来实现行察的,Java中的类加载器是一个重要的Java运行时系统组件,它负责在运行时查找和装入类文件中的类。

由于Java的跨没带扰平台性,经过编译的Java源程序并不是一个可执行程序,而是一个或多个类文件。当Java程序需要使用某个类时,JVM会确保这个类已经被加载、连接(验证、准备和解析)和枯旦初始化。

类的加载是指把类的.class文件中的数据读入到内存中,通常是创建一个字节数组读入.class文件,然后产生与所加载类对应的Class对象。加载完成后,Class对象还不完整,所以此时的类还不可用。

当类被加载后就进入连接阶段,这一阶段包括

验证:为了确保Class文件的字节流中包含的信息符合当前虚拟机的要求,并且不会危害虚拟机自身的安全。

‘拾’ 北大青鸟java培训:创建新对象的两种方式

随着互联网编程开发技术的发展,编程开发语言已经由面向程序发展成为了面向对象的编程。
今天,我们就从两个方面来了解一下,java编程语言中如何创建新对象的。
java在new一个对象的时候,会先查看对象所属的类有没有被加载到内存,如果没有的话,就会先通过类的全限定名来加载。
加载并初始化类完成后,再进行对象的创建工作。
我们先假设是一次使用该类,这样的话new一个对象就可以分为两个过程:加载并初始化类和创建对象。
一、类加载过程(一次使用该类)java是使用双亲委派模型来进行类的加载的,所以在描述类加载过程前,我们先看一下它的工作过程:双亲委托模型的工作过程是:如果一个类加载器(ClassLoader)收到了类加载的请求,它先不会自己去尝试加载这个类,而是把这个请求委托给父类加载器去完成,每一个层次的类加载器都是如此,因此所有的加载请求终都应该传送到顶层的启动类加载器中,只有当父类加搏源载器反馈自己无法完成这个加载请求(它的搜索范围中没有找到所需要加载的类)时,子加载器才会尝试自己去加载。
使用双亲委托机制的基袭态好处是:能够有效确保一个类的全局性,当程序中出现多个限定名相同的类时,类加载器在执行加载时,始终只会加载其中的某一个类。
1、加载由类加载器负责根据一个类的全限定名来读取此类的二进制字节流到JVM内部,并存储在运行时内存区的方法区,然后将其转换为一个与目标类型对应的java.lang.Class对象实例2、验证格式验证:验证是否符合class文件规范语义验证:检查一个被标记为final的类型是否包含子类;检查一个类中的final方法是否被子类进行重写;确保父类和子类之间没有不兼容的一些方法声明(比如方法签名相同,但方法的返回值不同)操作验证:在操作数栈中的数据必须进行正确的操作,对常量池中的各种符号引用执行验证(通常在解析阶段执行,检查是否可以通过符号引用中描述的全限定名定位到指定类型上,以及类成员信息的访问修饰符是否允许访问等)3、准备为类中的所有静态变量分配内存空间,并为其设置一个初始值(由于还没有产生对象,实例变量不在此操作范围内)被final修饰的static变量(常量),会直接赋值;4、解析将常量池中的符号引用转为直接引用(得到类或者字段、方法在禅厅内存中的指针或者偏移量,以便直接调用该方法),这个可以在初始化之后再执行。
解析需要静态绑定的内容。
//所有不会被重写的方法和域都会被静态绑定以上2、3、4三个阶段又合称为链接阶段,链接阶段要做的是将加载到JVM中的二进制字节流的类数据信息合并到JVM的运行时状态中。
5、初始化(先父后子)4.1为静态变量赋值4.2执行static代码块注意:static代码块只有jvm能够调用如果是多线程需要同时初始化一个类,仅仅只能允许其中一个线程对其执行初始化操作,其余线程必须等待,只有在活动线程执行完对类的初始化操作之后,才会通知正在等待的其他线程。
因为子类存在对父类的依赖,所以类的加载顺序是先加载父类后加载子类,初始化也一样。
不过,父类初始化时,子类静态变量的值也有有的,是默认值。
终,方法区会存储当前类类信息,包括类的静态变量、类初始化代码(定义静态变量时的赋值语句和静态初始化代码块)、实例变量定义、实例初始化代码(定义实例变量时的赋值语句实例代码块和构造方法)和实例方法,还有父类的类信息引用。
二、创建对象1、在堆区分配对象需要的内存分配的内存包括本类和父类的所有实例变量,但不包括任何静态变量2、对所有实例变量赋默认值将方法区内对实例变量的定义拷贝一份到堆区,然后赋默认值3、执行实例初始化代码初始化顺序是先初始化父类再初始化子类,初始化时先执行实例代码块然后是构造方法4、如果有类似于Childc=newChild()形式的c引用的话,在栈区定义Child类型引用变量c,然后将堆区对象的地址赋值给它需要注意的是,福建IT培训http://www.kmbdqn.cn/发现每个子类对象持有父类对象的引用,可在内部通过super关键字来调用父类对象,但在外部不可访问

热点内容
魔兽世界如何快速增加服务器 发布:2024-05-19 23:53:37 浏览:693
安卓手机如何转入苹果手机内 发布:2024-05-19 23:50:35 浏览:404
安卓哪个能安装血染小镇 发布:2024-05-19 23:45:57 浏览:900
tensorflowmac编译 发布:2024-05-19 23:28:59 浏览:701
sqlmaxvarchar 发布:2024-05-19 23:24:02 浏览:702
linux配置网卡命令 发布:2024-05-19 23:22:57 浏览:504
python查看进程 发布:2024-05-19 22:59:37 浏览:158
androidhtml颜色 发布:2024-05-19 22:58:34 浏览:847
米3系统存储和内存设备 发布:2024-05-19 22:50:50 浏览:214
途乐有哪些越野配置 发布:2024-05-19 22:49:53 浏览:673