最新消息:热烈庆祝IT小记上线!

java基础-------反射



------<a href="http://www.itheima.com" target="blank">Java培训、Android培训、iOS培训、.Net培训</a>、期待与您交流! -------

一、类的加载

当程序要使用某个类时,如果该类还未被加载到内存中,则系统会通过加载,连接,初始化三步来实现对这个类进行初始化。

1)加载

就是指将class文件读入内存,并为之创建一个Class对象。任何类被使用时系统都会建立一个Class对象。

2)连接

首先验证,看是否有正确的内部结构,并和其他类协调一致。其次准备, 负责为类的静态成员分配内存,并设置默认初始化值。最后解析, 将类的二进制数据中的符号引用替换为直接引用。

3)初始化

初始化时机:

创建类的实例

访问类的静态变量,或者为静态变量赋值

调用类的静态方法

使用反射方式来强制创建某个类或接口对应的java.lang.Class对象

初始化某个类的子类

直接使用java.exe命令来运行某个主类

二、类加载器及其作用

1)Bootstrap ClassLoader 根类加载器

      主要负责java核心类的加载,如System,String等。

2)Extension ClassLoader 扩展类加载器

      负责JRE的扩展目录中jar包的加载。

3)Sysetm ClassLoader 系统类加载器

      负责在JVM启动时加载来自java命令的class文件,以及classpath环境变量所指定的jar包和类路径。

三、反射

JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性;这种动态获取的信息以及动态调用对象的方法的功能称为java语言的反射机制。

要想解剖一个类,必须先要获取到该类的字节码文件对象。而解剖使用的就是Class类中的方法,所以先要获取到每一个字节码文件对应的Class类型的对象。

获取字节码文件对象有三种方式,假设有个Person类,创建Person类实例对象 Person p=new Person();获取Person类的字节码文件对象的方式如下:

Class c1=p.getClas();

Class c2=p.Class

Class c3=Class.forName(className);


1)获取构造方法对象,并通过构造方法获取对象

getConstructors()/获取所有公共构造方法。

getDeclaredConstructors()/获取所有构造方法,包括私有修饰的。

getConstructor()//获取单个公共构造方法。

getDeclaredConstructor()//获取私有构造方法

Constructor con = c.getConstructor();

Obj obj=con.newInstance();

getConstructor(参数类型.class,参数类型.class...)//获取带参构造方法

2)获取成员变量(以name为例)

Field nameField=c.getDeclaredField("name");//获取成员变量name

nameField.setAccessible(true);//取消访问检查

nameField.set(obj, "张三");//为对象obj的成员变量nameField赋值为“张三”。

3)获取成员方法

getDeclaredMethods()//获取本类所有成员方法

getMethods()//获取本类及父类所有成员方法

getMethod(方法名称,参数类型.class)获取指定成员方法

那么有了成员方法和成员变量及对象,如何使用呢?简单介绍如下:

Method m=c.getMethod(方法名,参数类型.class);

m.invoke(obj,实际参数x )/这步相当于对象obj调用m方法,传入实际参数x。

四、反射基本用法举例

1)获取带参构造方法

自定义学生类如下:

public class Student {
	private String name;
	private int age;

	public Student() {

	}

	private Student(String name, int age) {
		this.name = name;
		this.age = age;
	}

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

	public int getAge() {
		return age;
	}

	public void setAge(int age) {
		this.age = age;
	}
	public String toString()
	{
		return name+"---"+age;
	}

}

创建测试类如下:

import java.lang.reflect.Constructor;

public class InstanceDemo {
	public static void main(String[] args) throws Exception {
		// 获取字节码文件对象
		Class c = Class.forName("getinstance.Student");

		// 获取带参构造方法对象
		Constructor con = c.getDeclaredConstructor(String.class, int.class);

		// 获取实例对象
		con.setAccessible(true);
		Object obj = con.newInstance("zhangsan", 25);

		System.out.println(obj);
	}

}


2)利用反射获取成员变量

自定义学生类如下:

package bianliang;

public class Student {
	private String name;
	private int age;

	public Student() {}
	private Student(String name, int age) {
		this.name = name;
		this.age = age;
	}
	public void show() {
		System.out.println("无参成员方法");
	}
	private void fun(String name, int age) {
		System.out.println(name + "---" + age);
	}
	public String toString()
	{
		return name+"---"+age;
	}
}

测试类如下:

package bianliang;

import java.lang.reflect.Constructor;
import java.lang.reflect.Field;

//利用反射获取成员变量并使用
public class Test01 {
	public static void main(String[] args) throws Exception {
		//字节码文件对象
		Class c=Class.forName("bianliang.Student");
		
		//获取构造方法对象
		Constructor con= c.getDeclaredConstructor();
		con.setAccessible(true);
		Object obj=con.newInstance();
		
		//获取单个成员变量
		//获取姓名
		Field nameField=c.getDeclaredField("name");
		nameField.setAccessible(true);
		nameField.set(obj, "张三");
		
		//获取年龄
		Field ageField=c.getDeclaredField("age");
		ageField.setAccessible(true);
		ageField.set(obj, 27);
		System.out.println(obj);
		
	}

}

3)获取成员方法

同样以学生类为例,代码如下:

package fangfa;

public class Student {
	private String name;
	private int age;

	public Student() {

	}

	private Student(String name, int age) {
		this.name = name;
		this.age = age;
	}

	public void show() {
		System.out.println("无参成员方法");
	}

	private void fun(String name, int age) {
		System.out.println(name + "---" + age);
		
	}
	public String toString()
	{
		return name+"---"+age;
	}

}

测试类如下:

package fangfa;
//获取带参私有成员方法并使用
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;

public class Test2 {
public static void main(String[] args) throws Exception {
	//获取字节码文件对象
	Class c=Class.forName("fangfa.Student");
	
	//获取构造方法对象
	Constructor con= c.getDeclaredConstructor();
	con.setAccessible(true);
	Object obj=con.newInstance();
	
	//获取成员方法
	Method m1= c.getDeclaredMethod("fun", String.class,int.class);
	m1.setAccessible(true);
	Object ot=m1.invoke(obj, "王大五",22);

}
}


 五、反射的应用-----案例分析

案例一:配置文件配合反射运行类中的方法。用test.tex代替配置文件,已知txt文件中有两个键//className和methodName

自定义类代码如下:

package anli01;

public class Student {
	private String name;
	private int age;

	public Student() {
	}
	private Student(String name, int age) {
		this.name = name;
		this.age = age;
	}
	public void show() {
		System.out.println("无参成员方法");
	}
	private void fun(String name, int age) {
		System.out.println(name + "---" + age);
	}
	public String toString()
	{
		return name+"---"+age;
	}
}

测试类代码如下:

package anli01;

import java.io.FileReader;
import java.io.IOException;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.util.Properties;

//配置文件配合反射运行类中的方法。用test.tex代替配置文件,已知txt文件中有两个键
//className和methodName
public class PropertiesDemo {
	public static void main(String[] args) throws IOException, Exception {

		// 添加两个已知键进集合properties
		// 创建集合对象
		Properties p = new Properties();

		// 添加元素
		FileReader fr = new FileReader("test.txt");
		p.load(fr);
		fr.close();
		String className = p.getProperty("className");
		String methodName = p.getProperty("methodName");
		// 获取字节码文件对象
		Class c = Class.forName(className);
		// 获取构造方法对象
		Constructor con = c.getDeclaredConstructor();
		con.setAccessible(true);
		// 获取成员方法
		Method m = c.getDeclaredMethod(methodName);
		m.setAccessible(true);
		// 创建对象
		Object obj = con.newInstance();
		// 调用方法
		m.invoke(obj);
	}
}


案例二:反射越过泛型检查

代码如下:

package anli02;

import java.lang.reflect.Method;
import java.util.ArrayList;

//利用反射,向集合ArrayList<Integer>中添加字符串
//分析:因为集合加了泛型,所以不能传入字符串元素,但是集合的源码里是没有泛型的,可以添加字符串
//所以,用反射获取array的字节码文件,调用add底层方法添加字符串元素
public class ArrayListTest {
	public static void main(String[] args) throws Exception {
		ArrayList<Integer> array = new ArrayList<Integer>();

		Class c = array.getClass();// 获取集合array的字节码文件对象

		Method m = c.getMethod("add", Object.class);//add底层添加元素的类型为Object

		m.invoke(array, "我来了");
		System.out.println(array);
	}
}


 案例三:利用反射在外部类设置对象的私有属性

自定义类Student

package anli03;

public class Student {
	private String name;
	private int age;
	public String toString()
	{
		return name+"---"+age;
	}
}

工具类代码如下:

package anli03;

import java.lang.reflect.Field;

//设置对象属性的方法,把对象的A属性,赋值为B
public class SetTool {
	public void setProperty(Object obj, String A, Object B) throws Exception {
		Class c = obj.getClass();// 获取对象的字节码文件对象

		Field field = c.getDeclaredField(A);// 获取对象的成员变量A

		field.setAccessible(true);// 取消访问检查
		field.set(obj, B);
	}
}

测试类代码如下:

package anli03;

public class Test {
	public static void main(String[] args) throws Exception
	{
		//创建学生类对象
		Student s=new Student();
		//创建设置工具类对象
		SetTool st=new SetTool();
		//设置对象的name属性值为:张三
		st.setProperty(s,"name","张三");
		//设置对象的age值为27
		st.setProperty(s,"age",27);
		//打印输出
		System.out.println(s);
	}
}

 
 














猜您喜欢

备案号:苏ICP备12016861号-4