java类的加载,反射机制

一、类的加载机制

java程序运行需要某个类时,如果该类还没有加载到内存中,系统会通过加载、连接、初始化三个步骤来对类进行初始化

1.类加载

Java本身一.class字节码的形式存在,他不是一个可执行文件,所以需要jvm将类加载到内存中

类的加载由类加载器完成,jvm本身包含一个类加载器,称为 根类加载器(Bootstrap ClassLoader) 和JVM一样,根类加载器使用本地代码实现的,它负责加载核心java类 (即有java.*开头的类)

另外,jvm还提供了两个类加载器,它们都是用java语言写的,有根类加载器加载,其中,
扩展类加载器(Extension ClassLoader) 负责加载扩展类,包括所有javax.*开头的类和存放在JRE的扩展目录下(JAVA_HOME/jre/lib/ext)中JAR的类包
系统类加载器(Application ClassLoader) 负责加载应用程序自身的类

此外,java API中还提供了了一个ClassLoader抽象类,开发者可以通过继承ClassLoader基类来创建自定义的类加载器。

重点
1.类的class文件读入内存后,就会创建一个java.lang.Class对象。也就是说,当java程序中使用任何类时,系统都会为之创建一个java.lang.Class对象。一旦某个类被载入jvm中,同一个类就不会再次被载入。

2.一个类加载后,对应的Class对象,可以通过该类的实例的getClass() 方法得到,Class对象有一个getClassLoader() 方法,可以的到该类所用到的类加载器

上代码

//演示得到一个类的加载器
public class GetClassLoaderDemo{
	public static void main(String[] args){
		GetClassLoaderDemo g=new GetClassLoaderDemo();
	    Class c=g.getClass();
	    ClassLoader loader=c.getClassLoader();
	
    	System.out.println(loder);
    	//得到这个类加载器的父类信息,也就是扩展类加载器
    	System.out.println(loader.getParent());
	}
}

2.连接

当类加载后,系统就为之创建了一个对应的Class对象,接着就会进入连接阶段。连接阶段会负责把类的二进制数据合并到JRE中。类连接又可以分为如下三个阶段
1.验证:验证被加载的类是否有正确的内部结构,并和其他类协调一致
2.准备:负责为类的静态属性分配内存,并设置默认初始值
3.解析:将类的二进制数据中的符号引用替换成直接引用

3.初始化

随后,进行类的初始化阶段,jvm负责对类进行初始化,也就是对静态属性进行初始化。在java类中,对静态属性指定初始值的方式有两种:(1)声明静态属性时指定初始值
(2)使用静态初始化块为静态属性指定初始换值

jvm初始化一个类,一般包含下面几个步骤

  1. 假如这个类还没有被加载和连接,程序先加载并连接该类
  2. 假如该类的直接父类还没有被初始化,则初始化其直接父类
  3. 假如该类中有初始化语句,则系统依次执行这些初始化语句

二、反射

1.反射概念

所谓反射,是指在java中,可以在运行期载入、探知和使用编译器完全未知的类
换句话说,java程序可以装载一个运行期才能得到名称的类,获取其完成结构,并创建对象、或者对类的成员变量设定值、或者调用其方法。这种“看透”类的能力,被称为反省、内省或自省

在java API中的java.lang包和java.lang.reflect包中,提供了Class类、Field类、Method类、Constructor类、Array类等用于实现反射机制

2. 使用反射查看类信息

前面我讲到了在类加载的时候,系统会为该类生成一个对应的java.lang.Class对象。通过Class对象,我们可以访问JVM中的这个类。在java程序中获得Class对象的方法有三种

第一种方式:在编译期不知道类名,但是在运行期可以获得该类名称的时候,使用Class类的forName()静态方法就可以获得Class对象。 例如:

Class c=Class.forName("全限定类名");

第二种方式:在编译期知道类名的情况,可以调用该类的class属性来获得该类对象的Class对象。 例如:

Class c=类名.class;

第三种方式:如果一个类的实例对象已经得到,则调用该对象的getClass()方法返回该对象的Class对象。getClass()方法是java.lang.Object类的方法之一,所有对象都可以调用该方法。 例如:

Class c=对象名.getClass();

在Class类中,通过getFields(),getMethods(),getConstructors()方法,可以通过获得Class对象所含的类的 public 的属性,方法,构造器
而通过getDeclaredFields(),getDeclaredMethods(),getDeclaredConstructors()方法,可以获得Class所包含类的所有属性,方法,构造器信息。分别返回Field,Method和Constructor类。

3.使用反射创建并操作对象

通过Class对象获取某个类的属性,方法和构造器后,程序就可以通过Constructor对象调用相应的构造器创建对象,通过Field对象访问对象并修改对象的属性。通过Method对象来执行相应的方法。此外,Class类还有一个newInstance()方法用于创建此Class对象所表示的类的一个新实例。

java中,创建对象有四种方式,分别是:new 、反射、对象克隆、对象反序列化。

这里说一下通过反射创建对象的两种方式

  1. 使用Class对象的newInstance()方法来创建该Class对象对应类的实例。这种方式要求Class对象对应的类有默认构造器,执行newInstance()方法时,实际上是调用默认构造器来创建实例。用这种方法创建对象比较常见。
  2. 先使用Class对象获取指定的Constructor对象,再调用Constructor对象的newInstance()方法来创建该Class对象对应的类的实例。通过这种方式可以选择使用某个类的指定构造器来创建对象。

4.使用反射调用方法

  1. 通过Class.forName()或者 类名.class 或者 对象名.getClass()任意一种方法,获得Class对象
  2. 通过Class对象的getMethod()方法获取要调用的方法的Method对象
  3. 调用获取的Method对象的invoke()方法,来调用对应类的方法

5.访问属性值

通过Class对象的getFields()方法或者getField()方法可以获取一个类的全部属性或指定属性。这两个方法的返回值是一个Field对象数组或一个对象。Filed对象提供了如下两种方法来访问属性:

  1. getXXX(Object obj):获取obj对象的Field的属性值。这里XXX对应八种基本数据类型,如果是引用类型,则取消get后面的XXX。
  2. setXXX(Object obj ,XXX val):将obj对象的Field的属性值设置为val。这里XXX对应八种基本数据类型,如果是引用类型,则取消set后面的XXX。

6.动态创建和访问数组

使用Array类的静态方法newInstance(),可以创建Class对象对应类的数组。
Array类的静态方法set(),设置元素
Array类的静态方法et(),得到元素

public class ArrayDemo{
	public static void main(String [] args) throws Exception{
		Class c=Class.forName("java.lang.String");
		
		//创建长度为5的字符串数字
		Object array=Array.newInstance(c,5);
		//设置索引位置为2的元素为 李四
		Array.set(array,2,"李四");
		//得到索引为2的元素
		String s=(String)Array.get(array,5);
		System.out.println(s);
	}
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值