`
hehaibo
  • 浏览: 417672 次
  • 性别: Icon_minigender_1
  • 来自: 长沙
社区版块
存档分类
最新评论

java类的生命周期

 
阅读更多

1 类的加载

   1  概念:类的加载就是把类的.class文件中的二进制数据读入到内存中。把它存放在java运行时数据区的方法区内,然后在堆区创建一个java.lang.Class对象,用来封装类在方法区内的数据结构。

   2 来源: JVM可以从多种来源加载类的class德二进制数据

        1 从本地文件系统加载类的class文件

        2 从网络上下载类的class的二进制文件.

        3 从zip文件或jar文件或其他归档文件中提取.class文件。

        4 从一个专有的数据库中提取class文件.

        5 把一个java源文件动态编译为class文件。 

     类加载的最终产品是位于堆区的java.lang.Class对象。Class对象封装了类在方法区的数据结构。并且向java程序提供了访问类在方法区的数据结构的接口。

类的加载是由加载器完成的,可分为两种:

  1 java虚拟机自带的加载器,包括启动类加载器,拓展类加载器和系统类加载器。

  2 用户自定义的类加载器,是java.lang.ClassLoader类的子类的实例。用户可以通过它来制定类的加载器。

     类的加载器并不需要等到某个类被首次主动使用时再加载它,java虚拟机规范允许类加载器在预料某个类将要被使用时就预先加载它。

 

2  类的验证

       当类别加载后,就进入连接阶段连接就是把已经读入到内存的类的二进制数据合并到虚拟机的运行环境去。连接的第一步是类的验证,保证被加载的类有正确的内部结构,并且与其他类协调一致。如果jvm检查到错误,那么就会抛出相应的Error对象。由java编译器生成的java类的二进制数据肯定是正确的,为什么还要进行类的验证,因为java虚拟机并不知道某个特定的.class文件到底是如何被创建的,这个.class文件有可能是由正常的java编译器生成的,也可能是由黑客特制的,黑客视图通过它来破坏jvm环境。类的验证能提高程序的健壮性,确保程序被安全的执行。

    类验证的内容:

      1 类文件的结构检查,确保类文件遵从java类文件的固定格式。

      2 语义检查确保本身符号java语言的语法规定,如验证final类型的类没有子类等

      3 字节码验证:确保字节码可以被jvm安全的执行,字节码代表java方法(包括静态和实例方法),它是由被称做操作码的单字节指令组成的序列,每一个操作码后都跟着一个或多个操作数。字节码验证步骤会检查每个操作码是否合法,即是否有合法的操作数。

      4 二进制兼容的验证,确保相互引用的之间协调一致,如Worker类的goWork方法会调用Car类的run方法,jvm在验证Worker类时,会检查方法区内是否存在car类的run方法,如不存在会(Worker 类和car类版本不兼容就会出现问题),就会抛出NoSuchMethodError错误。

 

3  类的准备:在准备阶段,jvm虚拟机为类的静态变量分配内存,并设置默认的初始值。如int初始化值是0占是2个字节等。

类的解析:在解析阶段,jvm会把类的二进制数据中的符号引用替换为直接引用。在Worker类的gotowork方法中会引用Car类run方法。

public void gotowork()
	{
		car.run();//这段代码在Worker类的二进制数据中表示为符号引用
	}

 在Worker类的二进制数据中,包含了一个对Car类的run方法的的符号引用,它有run方法的全名和相关描述符号组成,在解析阶段jvm会把这个符号引用替换为一个指针,该指针指向Car类的run方法在方法区德内存地址,这个指针就是直接引用。

5 类的初始化:

在初始化阶段,jvm执行类的初始化语句,为类的静态变量赋予初始值。在程序中静态变量的初始化有两种途径:(1) 在静态变量的声明处进行初始化,(2)在静态代码块进行初始化。jvm会按照他们的先后顺序进行初始化。

     jvm初始化一个类包含的步骤:

       1 如果累没有加载和连接,那就先进行加载和链接。

       2 假如类存在直接的父类,并且这个类的父类没有初始化,那就先初始化直接的父类。

       3 假如类中存在初始化语句,那就依次执行这些初始化语句。

       当初始化一个类的直接父类时,也需要重复以上步骤,这会确保程序主动使用一个类时,这个类及所有的父类和间接父类都已初始化。

package init;

public class Base {
	static int a = 1;
	static {
		System.out.println("init Base");
	}

	public Base() {
		System.out.println("Base()");
	}

}

public class Sub extends Base {
	static int b = 1;
	static {
		System.out.println("init Sub");
	}

	public Sub() {
		System.out.println("Sub()");
	}
}

public class InitTest {
	static {
		System.out.println("init InitTest");
	}

	public static void main(String[] args) throws Exception {
		// System.out.println("b="+Sub.b);
		// init InitTest
		// init Base
		// init Sub
		// b=1

		// Base b;//不会初始化Base类
		// b=new Base();
		// System.out.println("创建一个Base类的实例");
		// System.out.println(b.a);
		// System.out.println("b=" + Sub.b);//没有初始化Base类 仅仅初始化Sub类
		// System.out.println("创建一个Sub");
		// new Sub();

	}

}

 

 

6 类的初始化时机:

   1 创建类的实例,包括用new创建实例,或者通过反射,克隆和反序列号来创建实例。

   2 调用类的静态方法。

   3 访问某个类的或接口的静态变量或者对该静态变量赋值。

   4 调用java api中某些反射方法如Class.forName("Worker"),如果Worker类没有初始化,那么forname方法会初始化Worker类,然后返回这个Worker类的class实例。

   5 初始化类的一个子类,例如初始化一个Sub类,可以看做是对它父类Base类的主动使用,先初始化Base类。

   7 jvm启动时被表明为启动类的类,如含有main方法的类

 

   以下使用java类的方式被看做是对类的被动使用都不会导致类的初始化。

      1 对final类类型的静态变量如果在编译时期就能计算出变量的取值,那么这种变量被看做编译时常量。java程序对类的编译时常量的使用,不会导致类的初始化。

public class Tester{
	public final static int a=3;// 编译时常量
	public final static int b =(int)Math.random();// 非编译时常量
}

 

当另外一个类使用Tester.a时,不会在使用方法的字节码中保存一个表示Tester.a的符号的引用,也不会分配内存。而是直接在字节码中嵌入常量值。使用Test.b时,看做是对类的主动使用,会进行类的初始化。

2  在jvm虚拟机初始化一个类时,要求它的所有父类都已经被初始化,但是不适用于接口,在初始化一个类是,并不会先初始化他所实现的接口。

在初始化一个接口时,并不会先初始化他的父接口。一个父接口并不会因为它的子接口或实现类初始化而初始化,只要当程序首次使用特定接口的静态变量时,才会导致该接口的初始化。

只有当程序访问的静态变量和静态方法的确在当前类或接口中定义是,才可以看做是对类或接口的主动使用。

package initbase;

class Base {
 static int a = 32;
 static {
  System.out.println("初始化Base");
 }

 public static void methodA() {
  System.out.println("method of Base");
 }
}

class Sub extends Base {
 static {
  System.out.println("init Base");
 }
}

public class Sample {
 public static void main(String[] args) {
  System.out.println(Sub.a);// 仅仅初始化Base
  Sub.methodA();
 }
}

 

3  调用ClassLoader类的loadClass()方法加载一个类,并不是对类的主动使用,不会导致类的初始化。当程序调用Class类的静态方法forName("ClassA")时,才是对ClassA的主动使用,将导致classA被初始化,他的静态代码块被执行。

package loaderinit;

class ClassA {
	static {
		System.out.println("now init ClassA");
	}
}

public class Tester {
	public static void main(String[] args) throws Exception {
		ClassLoader loader = ClassLoader.getSystemClassLoader();// 获得系统的类加载器
		System.out.println("系统类加载器:" + loader);
		Class objClass = loader.loadClass("loaderinit.ClassA");
		System.out.println("after load ClassA");
		System.out.println("before init Class");
		objClass = Class.forName("loaderinit.ClassA");// 初始化ClassA
	}

}

  

 

类加载器:

 类加载器用来把类加载到jvm中,从jdk1.2开始类的加载过程采用父亲委托机制,能保证java平台的安全。jvm自带的根加载器以外,其余的类加载器有且只有一个父加载器。java虚拟机自带的几种加载器:

根(Bootstrap)类加载器:该类没有父加载器,负责加载虚拟机的核心类库,如java.lang.*等。根类加载器从系统属性sun.boot.class.path所指定的目录中加载类库,根加载器的实现依赖于底层操作系统,属于虚拟机的实现的一部分,没有继承java.lang.ClassLoader。

拓展(Extension)类加载器:它的父加载器为根加载器,它从java.ext.dirs系统属性所指定的目录中加载类库,或者从jdk的安装目录的jre\lib\ext子目录下加载类库。如用户把用户创建的jar文件放在这个目录下也会自动由拓展类加载器加载,拓展类加载器是纯java类,是java.lang.ClassLoader类的子类。

系统(System)类加载器:也称为应用类加载器,它的父类加载器为拓展类加载器。它从环境变量classpath或者系统属性java.class.path所指定的目录中加载类。它是用户自定义的类加载器的默认父加载器。也是纯java类 ,java.lang.ClassLoader的子类。

自定义类加载器:继承抽象类java.lang.ClassLoader.

package classloader;

public class Example {

	/**
	 * @param args
	 */
	public static void main(String[] args) {
		Class c = null;
		ClassLoader loader1, loader2;
		// 获得系统类加载器
		loader1 = ClassLoader.getSystemClassLoader();
		System.out.println("系统类加载器:" + loader1);

		while (loader1 != null) {
			loader2 = loader1;
			loader1 = loader1.getParent();
			System.out.println(loader2 + "的父亲加载器是:" + loader1);
		}
		try {
			c = Class.forName("java.lang.Object");
			loader1 = c.getClassLoader();
			System.out.println("java.lang.Object 的加载器:" + loader1);// 根(Bootstrap)加载器

			c = Class.forName("classloader.Example");
			loader1 = c.getClassLoader();
			System.out.println("classloader.Example 的加载器:" + loader1);// 是根(Bootstrap)加载器
			System.out.println(Thread.currentThread().getContextClassLoader());
		} catch (Exception e) {
			e.printStackTrace();
		}

	}
	/**
	 * 
	 * 系统类加载器:sun.misc.Launcher$AppClassLoader@19821f
	 * sun.misc.Launcher$AppClassLoader@19821f的父亲加载器是:sun.misc.Launcher$ExtClassLoader@addbf1
	 * //拓展类加载器 sun.misc.Launcher$ExtClassLoader@addbf1的父亲加载器是:null
	 * java.lang.Object 的加载器:null //根加载器 classloader.Example
	 * 的加载器:sun.misc.Launcher$AppClassLoader@19821f
	 * sun.misc.Launcher$AppClassLoader@19821f //系统类加载器
	 * 
	 */
}

 

分享到:
评论

相关推荐

    Java中类的生命周期

    ### Java中类的生命周期 #### 一、Java虚拟机与程序生命周期 在深入了解Java中类的生命周期之前,我们首先需要了解Java虚拟机(JVM)及其生命周期的概念。 **1. Java虚拟机启动** 当我们通过`java`命令运行一个...

    通过代码实例解析JAVA类生命周期

    Java 类生命周期是指从类加载到内存到卸载出内存的过程,包括加载、验证、准备、初始化和卸载五个阶段。本文将通过一个具体的代码实例来详细解析这些阶段。 首先,让我们分析给定的代码: ```java public class ...

    详解java类命周期.doc

    ### 详解Java类的生命周期 #### 一、Java类生命周期概览 Java类的生命周期是指一个`.class`文件从被加载到最终卸载的过程。这一过程主要包括五个阶段:加载(Loading)、连接(Linking)、初始化(Initialization...

    Java虚拟机和Java程序的生命周期?

    Java程序的生命周期从类的加载开始,到程序结束为止。主要包括以下几个阶段: 1. **启动阶段**:启动Java虚拟机,创建主类的对象并调用其`main()`方法。 2. **运行阶段**:程序开始执行,包括类的加载、初始化、...

    java线程生命周期详细讲解

    Java多线程生命周期的讲解,很详细,希望能帮到大家

    java servlet生命周期

    java servlet生命周期 java servlet生命周期

    Java中类的生命周期与java垃圾回收机制

    Java中类的生命周期与java垃圾回收机制

    详解java类的生命周期.doc

    ### 详解Java类的生命周期 #### 引言 在探讨Java类的生命周期之前,我们先简单回顾一下Java类从创建到销毁的过程。Java作为一种广泛使用的编程语言,其强大的功能背后离不开Java虚拟机(JVM)的支持。对于Java...

    java对象的 生命周期

    ### Java对象的生命周期详解 Java对象的生命周期是一个关键概念,涉及到对象从创建到销毁的整个过程。理解这一过程对于高效地编写和管理Java程序至关重要。 #### 创建对象的方式 对象的创建是生命周期的起点,...

    Java中对象的生命周期 ..doc

    ### Java中对象的生命周期 #### 一、对象的生命周期概览 在Java中,对象的生命周期是指从对象被创建到最终被垃圾回收器回收这段时间。理解对象的生命周期对于有效地管理和优化Java应用程序至关重要。 #### 二、...

    java变量的生命周期与作用域

    Java 变量的生命周期和作用域是密切相关的,变量的生命周期可以分为三个阶段:定义、初始化和销毁,而作用域可以分为四种:方法作用域、块作用域、类作用域和实例作用域。同时,函数的概念与作用是实现代码的重用性...

    详解java类的生命周期

    本文详细讲述了一个java类自调入内存至被卸载的整个声明周期,对理解对象的生命周期,jvm中的类加载等内容有所帮助。

    Java中类的生命周期.doc

    ### Java中类的生命周期 #### 一、概述 在Java编程语言中,类的生命周期是从类被加载到Java虚拟机(JVM)中开始,经过一系列处理直至类被卸载的过程。这一过程中涉及的关键步骤包括类的加载、连接、初始化等。理解...

    Java线程生命周期.zip

    Java线程生命周期是Java编程中的核心概念,它关乎程序的并发执行和性能优化。线程在Java中扮演着至关重要的角色,特别是在多任务处理和实时系统中。理解线程的生命周期有助于开发者更有效地管理和控制程序运行流程。...

    Java对象生命周期管理.pdf

    Java对象生命周期管理是Java开发中不可或缺的一个重要环节。在Java编程中,对象的创建、使用和销毁是由垃圾收集器自动管理的。理解这一过程对于优化应用程序性能至关重要,因为不恰当的对象管理可能导致内存泄漏,...

    深入java虚拟机(三)——类的生命周期(下)类的初始化1

    【深入Java虚拟机(三)——类的生命周期(下)类的初始化1】 类的生命周期在Java中是一个关键的概念,它涵盖了从加载到卸载的整个过程。在类的生命周期中,初始化阶段是非常重要的,因为它涉及到类的静态变量的赋值...

    spring bean的生命周期

    Spring Bean的生命周期是Spring框架中的核心概念,它涵盖了Bean从创建到销毁的全过程。了解这一过程对于优化应用程序的性能和管理资源至关重要。在Spring中,Bean的生命周期主要分为以下几个阶段: 1. **初始化阶段...

    深入java虚拟机(二)——类的生命周期(上)类的加载和连接1

    【深入Java虚拟机(二)——类的生命周期(上)类的加载和连接】 Java虚拟机(JVM)是Java程序的核心,它负责解释和执行Java字节码。类的生命周期在JVM中是一个关键的概念,它涵盖了从类的加载到卸载的整个过程。...

Global site tag (gtag.js) - Google Analytics