类加载机制与反射

类加载机制与反射

当程序主动使用某个类时,若是该类还未被加载到内存中,则系统会经过加载、链接、初始化三个步骤来对该类初始化。java

类的加载

类加载指的是将类的class文件读入内存,并为之建立一个java.lang.Class对象。类的加载由类加载器完成,加载器由JVM提供。数组

类的链接

当类被加载以后,系统会生成一个对应的Class对象,接着就会进入链接阶段,链接阶段负责把类的二进制数据合并到JRE中。类的链接步骤:学习

  • 验证:验证阶段用于检验被加载的类是否有正确的内部结构,并和其余类协调一致。
  • 准备:类的准备阶段负责为类的类变量分配内存。并设置默认初始值。
  • 解析:将类的二进制数据中的符号引用替换成直接引用。

类的初始化

JVM负责对类进行初始化,主要就是对类变量进行初始化。在类中对类变量指定初始化值有两种方式:测试

1,声明类变量时指定初始化值;code

2,使用静态初始化块为类变量指定初始值。orm

JVM初始化一个类包含如下几个步骤:对象

1,假如这个类尚未被加载和链接,则程序先加载并链接该类。继承

2,假如该类的直接父类尚未被初始化,则先初始化其直接父类。内存

3,假如类中有初始化语句,则系统依次执行这些初始化语句。文档

经过反射查看类信息

java程序中许多对象在运行时都会出现两种类型:A a=new B();这种代码将会生成一个a变量,其中A为编译时类型,B为运行时类型。必须时B继承A

为了解决这些问题,程序须要在运行时发现对象和和类的真实信息,有以下两种作法:

1,假设在编译时和运行时发现对象和类的信息,咱们可使用instanceof运算符赖判断,再利用强制类型转化成其运行时类型。

2,没法知道该对象和类的信息,这里就要使用反射了。

得到Class对象

每一个类被加载都会生成一个Class对象,经过该Class对象就能够访问到JVM中的这个类。能够经过一下三种方式得到程序中的Class对象。

1,使用Class类的forName(String clazzName)静态方法,clazzName是某个类的全限定名(完整包名)。

2,调用某个类的class属性来获取该类对应的Class对象,好比A.class将会返回A类的Class对象。

3,调用某个对象的getClass方法,该方法属于Object对象,因此全部的类均可以调用,返回当前对象所属类对应的Class对象。

public class InstanceofTest {
	public static void main(String[] args) throws Exception {
		InstanceofA p = new InstanceofB();
		System.out.println(p instanceof InstanceofA);
		if (p instanceof InstanceofB) {
			System.out.println("1:" + Class.forName("www.com.lining.test1.InstanceofA"));
			System.out.println("2:" + "InstanceofA.class");
			System.out.println("3:" + p.getClass());
		} else {
			System.out.println(p.getClass());
		}
	}
}

输出

1:class www.com.lining.test1.InstanceofA
2:InstanceofA.class
3:class www.com.lining.test1.InstanceofB

运行时和编译时类型不同可是有继承关系,使用getClass方法获取此时的Class对象都是运行时对象

获取class对象以后就能够获取class对象中的信息了,好比获取此类的全部public构造器、全部的public的方法等,能够参照帮助文档中Class类的方法来学习。

使用反射生成并操做

Class对象能够得到类的方法,构造器,成员变量(实例变量),这三个类都位于java.lang.reflect包下。

建立对象

1,使用Class对象的newInstance()方法来建立该Class对象对应类的实例,这种方式要求该Class对象的对应类有默认构造器,使用newInstance()来建立实例其实就是利用了默认的构造器。

2,先使用Class对象获取指定的Constructor对象,再调用Constructor对象的newInstance()方法来建立该Class对象对应的类的实例,这种方式能够选择使用指定的构造器来建立实例。(java9以后就废弃了直接使用newInstance()来建立实例,而是使用getDeclaredConstructor().newInstance()建立)

package www.com.lining.test1;

import java.io.FileInputStream;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.Properties;
import javax.swing.JFrame;
/*
 * 经过反射来建立未知的类的对象,利用流来读取配置文件而后解析为k-v模式,而后获取该value,利用value建立对象
 */
public class ObjectPoolFactory {
	// 定义一个对象池,前面是对象名,后面是实际对象
	private Map<String, Object> pool = new HashMap<>();

	// 定义一个建立对象的方法
	// 该方法只要传入一个字符串类名(权限定名)程序玖能够根据该类名生成java对象
	@SuppressWarnings("unused")
	private Object createObject(String clazzName) throws Exception {

		Class<?> clazz = Class.forName(clazzName);// 获取class对象
		return clazz.getDeclaredConstructor().newInstance();// 建立该class对应的对象的实例
	}
	/*
	@SuppressWarnings("unused")
	private Object createObject(String clazzName) throws Exception {
//使用带参数的构造器建立对象
		Class<?> clazz = Class.forName(clazzName);// 获取class对象
		Constructor ctor = clazz.getConstructor(String.class);
		return ctor.newInstance("20190607");// 建立该class对应的对象的实例
	
	}
*/

//该方法根据指定文件来初始化对象池,根据配置文件来建立对象
	public void initPool(String filename) {

		try (FileInputStream input = new FileInputStream(filename);) {
			Properties props = new Properties();
			props.load(input);// 从流中读取属性列表,解析为k-v的属性列表
			for (String name : props.stringPropertyNames()) {// 返回此属性列表的键集k-v
				pool.put(name, createObject(props.getProperty(name)));
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

	public Object getObject(String name) {

		return pool.get(name);// 获取map集合中name对应的值
	}
	public static void main(String[] args) {
		ObjectPoolFactory fact = new ObjectPoolFactory();
		fact.initPool("index");//这里的路径是放在工程下的文件,直接在工程下建立。
		Date date = new Date();
		System.out.println(date);
		System.out.println(fact.getObject("a"));
		JFrame jf = new JFrame();
		System.out.println(jf);
		System.out.println(fact.getObject("b"));
	}
}

输出

Fri Sep 27 15:51:34 CST 2019
Fri Sep 27 15:51:33 CST 2019
javax.swing.JFrame[frame0,0,0,0x0,invalid,hidden,layout=java.awt.BorderLayout,title=,resizable,normal,defaultCloseOperation=HIDE_ON_CLOSE,rootPane=javax.swing.JRootPane[,0,0,0x0,invalid,layout=javax.swing.JRootPane$RootLayout,alignmentX=0.0,alignmentY=0.0,border=,flags=16777673,maximumSize=,minimumSize=,preferredSize=],rootPaneCheckingEnabled=true]
javax.swing.JFrame[frame1,0,0,0x0,invalid,hidden,layout=java.awt.BorderLayout,title=,resizable,normal,defaultCloseOperation=HIDE_ON_CLOSE,rootPane=javax.swing.JRootPane[,0,0,0x0,invalid,layout=javax.swing.JRootPane$RootLayout,alignmentX=0.0,alignmentY=0.0,border=,flags=16777673,maximumSize=,minimumSize=,preferredSize=],rootPaneCheckingEnabled=true]

index文件:

a=java.util.Date
b=javax.swing.JFrame

注意:这里用带参数的构造器建立实例时,.newInstance(xx)x表明一个或多个参数,这里的参数个数类型顺序要和建立的类的构造方法参数相对应,否则会报异常java.lang.reflect.InvocationTargetException

调用方法

当得到某个类对应的Class对象后,就能够经过getMethods()方法返回数组或者getMethod()方法返回对象来获取所有或者指定的方法。java.lang.reflect.Method里包含一个invoke方法。

  • Object invoke(Object obj,args....):该方法调用指定的对象obj上的方法,args表明该方法的参数,个数的多少用于重载机制
  • void setAccessible(boolean flg):值为 true 则指示反射的对象在使用时应该取消 Java 语言访问检查。值为 false 则指示反射的对象应该实施 Java 语言访问检查。
package www.com.lining.test1;

import java.io.FileInputStream;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;
import java.util.Properties;

public class MethodPoolFactory {

	private Map<String, Object> pool = new HashMap<>();// 对象池,放置建立后的对象k-v
	private Properties pro = new Properties();// 建立一个持久的属性集对象

	// 从指定文件中初始化Properties对象
	public void init(String filename) {
		try (FileInputStream input = new FileInputStream(filename))// 建立读取配置文件的流
		{
			pro.load(input);// 读取流里的数据解析为k-v
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

//定义一个建立对象的方法
	public Object createObject(String name) throws Exception {
		Class<?> clazz = Class.forName(name);
		return clazz.getDeclaredConstructor().newInstance();
	}

	// 根据读取的文件来初始化对象池,建立对象
	public void initPool() throws Exception {
		for (String name : pro.stringPropertyNames()) {// 返回此属性列表的键值集
			if (!name.contains("%")) {
				pool.put(name, createObject(pro.getProperty(name)));
			}
		}
	}

	// 根据属性文件来调用指定对象的xx方法,xx能够是建立对象中的任何一个实例方法,好比setter
	public void initProperty() throws Exception {

		for (String name : pro.stringPropertyNames()) {
			if (name.contains("%")) {
				String[] objAndProp = name.split("%");// 将配置文件按照“%”来分割
				Object target = getObject(objAndProp[0]);// 获取建立的对象
				String mtdName = "set" + objAndProp[1].substring(0, 1).toUpperCase() + objAndProp[1].substring(1);
				Class<?> targetClazz = target.getClass();// 获取该对象的Class实例
				Method mtd = targetClazz.getMethod(mtdName, String.class);// 经过该实例获取该方法
				mtd.setAccessible(true);
				mtd.invoke(target, pro.getProperty(name));// 调用该方法

			}
		}
	}

	public Object getObject(String name) {
		return pool.get(name);
	}

	public static void main(String[] args) throws Exception {

		MethodPoolFactory method = new MethodPoolFactory();
		method.init("index1");
		method.initPool();
		method.initProperty();
		System.out.println(method.getObject("b"));

	}
}

输出

123
%12345%

8
9ddddddd
kjfjf
www.com.lining.test1.Test@1f32e575

index1文件

b=www.com.lining.test1.Test
b%title=123,%12345%,,8,9ddddddd,kjfjf

反射调用的类(能够本身建立也能够运用jdk中的)

package www.com.lining.test1;
public class Test {

	public void setTitle(String sb) {
		for (String s : sb.split(",")) {
			System.out.println(s);
		}
	}
}

注意:

1,调用的方法有可能在类中有重载,因此要注意参数的个数类型。

2,setAccessible(boolean flg)方法不属于Method,而是属于AccessibleObject类,因此根据继承关系能够知道File,Method,Constructor均可以调用这个方法。该方法能够实现对private等权限的调用。

访问成员变量

1,经过Class对象的getFields()或getField()方法能够获取该类所包括的所有成员变量或指定成员变量。File提供了以下两组方法来读取或设置成员变量值。

  • getXXX(Object obj):获取obj对象的该成员变量的值。当这里的obj是引用数据类型,则要取消get后面的XXX.
  • setXXX(Object obj,XXX val):将obj对象的该成员变量设置成val值。当这里的XXX是引用数据类型,则要取消get后面的XXX.
package www.com.lining.test1;

public class FilePoolFactory {

	private String name;
	private int age;

	public String toString() {

		return "FilePoolFactory[name:" + name + ",age:" + age + "]";
	}

}

测试类:

package www.com.lining.test1;

import java.lang.reflect.Field;

public class FilePoolTest {

	public static void main(String[] args)
			throws NoSuchFieldException, SecurityException, IllegalArgumentException, IllegalAccessException {

		FilePoolFactory f = new FilePoolFactory();// 建立一个FilePoolFactory的对象
		Class<FilePoolFactory> clazz = FilePoolFactory.class;// 获取对应的class对象
		Field nameFiled = clazz.getDeclaredField("name");// 经过class对象获取成员变量
		nameFiled.setAccessible(true);// 设置获取的权限
		nameFiled.set(f, "jessica");// 设置对象f的成员变量name的值。
		Field ageFiled = clazz.getDeclaredField("age");
		ageFiled.setAccessible(true);
		ageFiled.set(f, 19);
		System.out.println(f.toString());
	}

}

输出:

FilePoolFactory[name:jessica,age:19]

注意:这里必定要设置访问权限setAccessible(boolean flg),若是权限是字段是private权限,那么就须要使用该方法了,否则会报 java.lang.IllegalAccessException。

相关文章
相关标签/搜索