java中的class用途_Java中 Class|Class.forName()|classloader一般用途

一、java加载机制

每个Java程序执行前都必须经过编译、加载、连接、和初始化这几个阶段。

加载:

查找并加载类的二级制数据。

连接:

验证、确保类被加载的正确性

准备、为类的静态变量分配内存,并为其初始化默认值

解释、把类中的符号引用转换为直接引用

初始化:

为类的静态变量赋予正确的默认值

加载是指将编译后的java类文件(也就是.class文件)中的二进制数据读入内存,并将其放在运行时数据区的方法区内,然后再堆区创建一个Java.lang.Class对象,用来封装类在方法区的数据结构,即加载后最终得到的是Class对象,并且更加值得注意的是:该Java.lang.Class对象是单实例的,无论这个类创建了多少个对象,他的Class对象时唯一的!!!!。 而 加载并获取该Class对象可以通过三种途径。Class.forName(类的全路径)、实例对象.class(属性)、实例对象getClass()

二、Class.forName、实例对象.class(属性)、实例对象getClass()的区别

A、相同点:得到的都是Java.lang.Class对象

1 packageus.codecraft.clazz;2

3 public classA {4

5 public static void main(String[] args) throwsException {6 System.out.println(A.class);//class us.codecraft.clazz.A

7 A a = newA();8 System.out.println(a.getClass());//class us.codecraft.clazz.A

9 System.out.println(Class.forName("us.codecraft.clazz.A"));//class us.codecraft.clazz.A

10 System.out.println("=====================================");11 System.out.println(A.class.newInstance());12 System.out.println(a.getClass().newInstance());13 System.out.println(Class.forName("us.codecraft.clazz.A").newInstance());14 }15 }

B、不同点

1、Class cl=A.class; JVM将使用类A的类装载器,将类A装入内存(前提是:类A还没有装入内存),不对类A做类的初始化工作.返回类A的Class的对象

2、Class cl=对象引用a.getClass();返回引用a运行时真正所指的对象所属的类的Class的对象

3、Class.forName("类名"); JAVA人都知道.装入类A,并做类的初始化

可以看出,现在可以看出,newInstance: 弱类型。低效率。只能调用无参构造,new: 强类型。相对高效。能调用任何public构造。Class对象的newInstance()(这种用法和Java中的工厂模式有着异曲同工之妙)实际上是把new这个方式分解为两步,即首先调用Class加载方法加载某个类,然后实例化。 这样分步的好处是显而易见的。我们可以在调用class的静态加载方法forName时获得更好的灵活性,提供给了一种降耦的手段。

三、Class对象解释

Java运行时系统一直对所有的对象进行所谓的运行时类型标识。这项信息纪录了每个对象所属的类。虚拟机通常使用运行时类型信息选准正确方法去执行,用来保存这些类型信息的类是Class类,Class类封装一个对象和接口运行时的状态,当装载类时,Class类型的对象自动创建。

Class 没有公共构造方法。Class 对象是在加载类时由Java 虚拟机以及通过调用类加载器中的 defineClass 方法自动构造的,因此不能显式地声明一个Class对象,虚拟机为每种类型管理一个独一无二的Class对象。也就是说,每个类(型)都有一个Class对象。运行程序时,Java虚拟机(JVM)首先检查是否所要加载的类对应的Class对象是否已经加载。如果没有加载,JVM就会根据类名查找.class文件,并将其Class对象载入,基本的 Java 类型(boolean、byte、char、short、int、long、float 和 double)和关键字 void 也都对应一个 Class 对象。每个数组属于被映射为 Class 对象的一个类,所有具有相同元素类型和维数的数组都共享该 Class 对象。一般某个类的Class对象被载入内存,它就用来创建这个类的所有对象。

注意:Class对象实际上描述的只是类型,而这类型未必是类或者接口。例如上面的int.class是一个Class类型的对象。由于历史原因,数组类型的getName方法会返回奇怪的名字

3、1   Class类的常用方法

1、getName():

一个Class对象描述了一个特定类的属性,Class类中最常用的方法getName以 String 的形式返回此 Class 对象所表示的实体(类、接口、数组类、基本类型或 void)名称

2:newInstance()

Class还有一个有用的方法可以为类创建一个实例,这个方法叫做newInstance()

x.getClass.newInstance(),创建了一个同x一样类型的新实例。newInstance()方法调用默认构造器(无参数构造器)初始化新建对象

3、getClassLoader():

返回该类的类加载器

4、getComponentType():

返回表示数组组件类型的 Class

5、getSuperclass() :

返回表示此 Class 所表示的实体(类、接口、基本类型或 void)的超类的 Class

6、isArray()

判定此 Class 对象是否表示一个数组类

3、2   Class的一些使用方法

1、forName和newInstance结合起来使用,可以根据存储在字符串中的类名创建对象,列如:Object obj = Class.forName(s).newInstance();

2、虚拟机为每种类型管理一个独一无二的Class对象。因此可以使用==操作符来比较类对象,列如: if(a.getClass() == A.class)

3、3   在初始化一个类,生成一个实例的时候,newInstance()方法和new关键字除了一个是方法,一个是关键字外,最主要有什么区别

1、它们的区别在于创建对象的方式不一样,前者是使用类加载机制,后者是创建一个新类

2、这主要考虑到软件的可伸缩、可扩展和可重用等软件设计思想,Java中工厂模式经常使用newInstance()方法来创建对象,因此从为什么要使用工厂模式上可以找到具体答案

3、从JVM的角度看,我们使用关键字new创建一个类的时候,这个类可以没有被加载。  但是使用newInstance()方法的时候,就必须保证 :这个类已经加载,这个类已经连接了      而完成上面两个步骤的正是Class的静态方法forName()所完成的,这个静态方法调用了启动类加载器,即加载 java API的那个加载器。

3、4   应用场景

1、加载数据库驱动的时候,

1 Class.forName("com.microsoft.sqlserver.jdbc.SQLServerDriver");2 Connection con=DriverManager.getConnection("jdbc:sqlserver://localhost:1433;DatabaseName==JSP","jph","jph");

为什么在我们加载数据库驱动包的时候有的却没有调用newInstance( )方法呢?

即有的jdbc连接数据库的写法里是Class.forName(xxx.xx.xx);而有一些:Class.forName(xxx.xx.xx).newInstance(),为什么会有这两种写法呢?

刚才提到,Class.forName("");的作用是要求JVM查找并加载指定的类,如果在类中有静态初始化器的话,JVM必然会执行该类的静态代码段,而在JDBC规范中明确要求这个Driver类必须向DriverManager注册自己,即任何一个JDBCDriver的Driver类的代码都必须类似如下:

1 public class MyJDBCDriver implementsDriver {2

3 static{4 try{5 DriverManager.registerDriver(newMyJDBCDriver());6 } catch(SQLException e) {7 e.printStackTrace();8 }9 }10

11 }

既然在静态初始化器的中已经进行了注册,所以我们在使用JDBC时只需要Class.forName(XXX.XXX);就可以了

ClassLoader概念

ClassLoader是用来动态的加载class文件到虚拟机中,并转换成java.lang.class类的一个实例,每个这样的实例用来表示一个java类,我们可以根据Class的实例得到该类的信息,并通过实例的newInstance()方法创建出该类的一个对象,除此之外,ClassLoader还负责加载Java应用所需的资源,如图像文件和配置文件等

ClassLoader类是一个抽象类。如果给定类的二进制名称,那么类加载器会试图查找或生成构成类定义的数据。一般策略是将名称转换为某个文件名,然后从文件系统读取该名称的“类文件”。ClassLoader类使用委托模型来搜索类和资源。每个 ClassLoader实例都有一个相关的父类加载器。需要查找类或资源时,ClassLoader实例会在试图亲自查找类或资源之前,将搜索类或资源的任务委托给其父类加载器

一个应用程序总是由n多个类组成,Java程序启动时,并不是一次把所有的类全部加载后再运行,它总是先把保证程序运行的基础类一次性加载到jvm中,其它类等到jvm用到的时候再加载,这样的好处是节省了内存的开销,因为java最早就是为嵌入式系统而设计的,内存宝贵,这是一种可以理解的机制,而用到时再加载这也是java动态性的一种体现。

顾名思义,类加载器(class loader)用来加载 Java 类到 Java 虚拟机中。一般来说,Java 虚拟机使用 Java 类的方式如下:Java 源程序(.java 文件)在经过 Java 编译器编译之后就被转换成 Java 字节代码(.class 文件)。类加载器负责读取 Java 字节代码,并转换成 java.lang.Class类的一个实例。每个这样的实例用来表示一个 Java 类。通过此实例的 newInstance()方法就可以创建出该类的一个对象。实际的情况可能更加复杂,比如 Java 字节代码可能是通过工具动态生成的,也可能是通过网络下载的

在程序运行期间, 通过java.lang.ClassLoader的子类动态加载class文件, 体现java动态实时类装入特性. 加载顺序是:自底向上检查类是否已经装在,有则返回,否则自顶向下尝试加载类

9578243d48d4b635fc992f4b318acac9.png

类装入的方式

显式 类装入:发生在使用以下方法调用装入的类的时候: cl.loadClass()(cl 是 java.lang.ClassLoader 的实例) Class.forName()(启动的类装入器是当前类定义的类装入器)

隐式类装入:发生在由于引用、实例化或继承导致装入类的时候(不是通过显式方法调用)

与显式类装入一样,如果类已经装入了,那么只是返回一个引用;否则,装入器会通过委托模型装入类

JDK默认ClassLoader

1、Bootstrp loader

Bootstrp加载器是用C++语言写的,它是在Java虚拟机启动后初始化的,它主要负责加载%JAVA_HOME%/jre/lib,-Xbootclasspath参数指定的路径以及%JAVA_HOME%/jre/classes中的类。

2、ExtClassLoader

Bootstrp loader加载ExtClassLoader,并且将ExtClassLoader的父加载器设置为Bootstrp loader.ExtClassLoader是用Java写的,具体来说就是 sun.misc.Launcher$ExtClassLoader,ExtClassLoader主要加载%JAVA_HOME%/jre/lib/ext,此路径下的所有classes目录以及java.ext.dirs系统变量指定的路径中类库。

3、AppClassLoader

Bootstrp loader加载完ExtClassLoader后,就会加载AppClassLoader,并且将AppClassLoader的父加载器指定为 ExtClassLoader。AppClassLoader也是用Java写成的,它的实现类是 sun.misc.Launcher$AppClassLoader,另外我们知道ClassLoader中有个getSystemClassLoader方法,此方法返回的正是AppclassLoader.AppClassLoader主要负责加载classpath所指定的位置的类或者是jar文档,它也是Java程序默认的类加载器。

三者之间的关系可以通过下图形象的描述

87b7297975d268e9bd313adca3640277.png

为什么要有三个类加载器,一方面是分工,各自负责各自的区块,另一方面为了实现委托模型

前面说了,java中有三个类加载器,问题就来了,碰到一个类需要加载时,它们之间是如何协调工作的,即java是如何区分一个类该由哪个类加载器来完成呢。 在这里java采用了委托模型机制,这个机制简单来讲,就是“类装载器有载入类的需求时,会先请示其Parent使用其搜索路径帮忙载入,如果Parent 找不到,那么才由自己依照自己的搜索路径搜索类”

下面代码来说明类加载的相关循序,

public class MyDemoTest {

public static void main(String[] args) {

ClassLoader c = MyDemoTest.class.getClassLoader();

System.out.println(c);

ClassLoader c1 = c.getParent();

System.out.println(c1);

ClassLoader c2 = c1.getParent();

System.out.println(c2);

//打印结果如下

//sun.misc.Launcher$AppClassLoader@18b4aac2

//sun.misc.Launcher$ExtClassLoader@12a3a380

//null

}

}

可以看出MyDemoTest是由AppClassLoader加载器加载的,AppClassLoader的Parent 加载器是 ExtClassLoader,但是ExtClassLoader的Parent为 null 是怎么回事呵,朋友们留意的话,前面有提到Bootstrap Loader是用C++语言写的,依java的观点来看,逻辑上并不存在Bootstrap Loader的类实体,所以在java程序代码里试图打印出其内容时,我们就会看到输出为null

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值