Java反射

Java反射

一、类的加载
1、当程序要使用某个类时,如果该类还未被加载到内存中,则系统会通过加载,连接,初始化三步来实现对这个类进行初始化。
(1)加载 
就是指将class文件读入内存,并为之创建一个Class对象(class文件的对象)。
任何类被使用时系统都会建立一个Class对象
(2)连接
验证 是否有正确的内部结构,并和其他类协调一致
准备 负责为类的静态成员分配内存,并设置默认初始化值
解析 将类的二进制数据中的符号引用替换为直接引用
(3)初始化 
2、类初始化时机
(1) 创建类的实例
(2)类的静态变量,或者为静态变量赋值
(3) 类的静态方法
(4)使用反射方式来强制创建某个类或接口对应的java.lang.Class对象
(5)初始化某个类的子类(最先执行的是object类的registerNative方法,将对象注册到本地的操作系统)
(6) 直接使用java.exe命令来运行某个主类
3、类加载器
a.负责将.class文件加载到内在中,并为之生成对应的Class对象。
b.虽然我们不需要关心类加载机制,但是了解这个机制我们就能更好的理解程序的运行
4、类加载器的组成
a.Bootstrap ClassLoader 根类加载器
也被称为引导类加载器,负责Java核心类的加载
比如System,String等。在JDK中JRE的lib目录下rt.jar文件中
b.Extension ClassLoader 扩展类加载器
负责JRE的扩展目录中jar包的加载。
在JDK中JRE的lib目录下ext目录。
c.System ClassLoader 系统类加载器
负责在JVM启动时加载来自java命令的class文件,以及classpath环境变量所指定的jar包和类路径。

二、反射
JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性;这种动态获取的信息以及动态调用对象的方法的功能称为java语言的反射机制。
要想解剖一个类,必须先要获取到该类的字节码文件对象。而解剖使用的就是Class类中的方法.所以先要获取到每一个字节码文件对应的Class类型的对象。
1、Class类(lang包下的)
阅读API的Class类得知,Class 没有公共构造方法。Class 对象是在加载类时由 Java 虚拟机以及通过调用类加载器中的 defineClass 方法自动构造的


获取Class对象的三种方式
方式一: 通过Object类中的getObject()方法
Person p = new Person();
Class c = p.getClass();


方式二: 通过 类名.class 获取到字节码文件对象(任意数据类型都具备一个class静态属性,看上去要比第一种方式简单)。
Class c2 = Person.class;


方式三: 通过Class类中的方法(将类名作为字符串传递给Class类中的静态方法forName即可)。
Class c3 = Class.forName("Person");
注意:第三种和前两种的区别
前两种你必须明确Person类型.
后面是指定这种类型的字符串就行.这种扩展更强.我不需要知道你的类.我只提供字符串,按照配置文件加载就可以了
示例:
package com.lm;

import com.bean.Person;

/*
 * 获取一个类的class文件对象的三种方式
 * 1、对象获取
 * 2、类名获取
 * 3、Class类的静态方法获取
 */
public class ReflectDemo {

	public static void main(String[] args) {
		//1、对象获取
		Person p=new Person();
		//调用Person类的父类的方法getClass()
		Class c=p.getClass();
		System.out.println(c);
		
		//2、类名获取
		//每个类型,包括基本和引用,都会赋予一个静态属性,属性名为class
		Class c1=Person.class;
		System.out.println(c1);
		System.out.println(c.equals(c1)); //true 
		System.out.println(c==c1); //true  只在类加载的时候产生一个class文件对象
		
		//3、Class类的静态方法获取forName(全包名+类名)
		try {
			Class c2=Class.forName("com.bean.Person");
			System.out.println(c2);
		} catch (ClassNotFoundException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		
	}
}
附Person类
public class Person {
	//成员变量
	public String name;
	public int age;
	private String address;

	//构造方法
	public Person() {
		System.out.println("空参数构造方法");
	}
	
	public Person(String name) {
		this.name = name;
		System.out.println("带有String的构造方法");
	}
	//私有的构造方法
	private Person(String name, int age){
		this.name = name;
		this.age = age;
		System.out.println("带有String,int的构造方法");
	}
	
	public Person(String name, int age, String address){
		this.name = name;
		this.age = age;
		this.address = address;
		System.out.println("带有String, int, String的构造方法");
	}
	
	//成员方法
	//没有返回值没有参数的方法
	public void method1(){
		System.out.println("没有返回值没有参数的方法");
	}
	//没有返回值,有参数的方法
	public void method2(String name){
		System.out.println("没有返回值,有参数的方法 name= "+ name);
	}
	//有返回值,没有参数
	public int method3(){
		System.out.println("有返回值,没有参数的方法");
		return 123;
	}
	//有返回值,有参数的方法
	public String method4(String name){
		System.out.println("有返回值,有参数的方法");
		return "哈哈" + name;
	}
	//私有方法
	private void method5(){
		System.out.println("私有方法");
	}

	@Override
	public String toString() {
		return "Person [name=" + name + ", age=" + age + ", address=" + address+ "]";
	}
}
2、通过反射获取构造方法并使用
在反射机制中,把类中的成员(构造方法、成员方法、成员变量)都封装成了对应的类进行表示。其中,构造方法使用类Constructor表示。可通过Class类中提供的方法获取构造方法:
返回一个构造方法
public Constructor<T> getConstructor(Class<?>... parameterTypes) 获取public修饰, 指定参数类型所对应的构造方法
public Constructor<T> getDeclaredConstructor(Class<?>... parameterTypes) 获取指定参数类型所对应的构造方法(包含私有的)
返回多个构造方法
public Constructor<?>[] getConstructors() 获取所有的public 修饰的构造方法
public Constructor<?>[] getDeclaredConstructors() 获取所有的构造方法(包含私有的)
3、通过反射获取成员变量并使用
在反射机制中,把类中的成员变量使用类Field表示。可通过Class类中提供的方法获取成员变量:
返回一个成员变量
public Field getField(String name) 获取指定的 public修饰的变量
public Field getDeclaredField(String name) 获取指定的任意变量
返回多个成员变量
public Field[] getFields() 获取所有public 修饰的变量
public Field[] getDeclaredFields() 获取所有的 变量 (包含私有)
4、通过反射获取成员方法并使用
在反射机制中,把类中的成员方法使用类Method表示。可通过Class类中提供的方法获取成员方法:
返回获取一个方法:
public Method getMethod(String name, Class<?>... parameterTypes)
  获取public 修饰的方法
public Method getDeclaredMethod(String name, Class<?>... parameterTypes)
  获取任意的方法,包含私有的
参数1: name 要查找的方法名称; 参数2: parameterTypes 该方法的参数类型
返回获取多个方法:
public Method[] getMethods() 获取本类与父类中所有public 修饰的方法
public Method[] getDeclaredMethods() 获取本类中所有的方法(包含私有的)
三、练习
1、泛型擦除
package com.lm;

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

/*
 * 反射泛型擦除
 * 定义集合类,泛型String
 * 要求向集合中添加Integer类型
 * 
 * 反射方式,获取出集合ArrayList类的class文件对象
 * 通过class文件对象,调用add方法
 */
public class ReflectTest {

	public static void main(String[] args) throws NoSuchMethodException, SecurityException, IllegalAccessException, IllegalArgumentException, InvocationTargetException {
		ArrayList<String> array=new ArrayList<String>();
		array.add("a");
		Class c=array.getClass();
		//获取ArrayList.class文件中的方法add(编译后的class文件里没有泛型)
		Method method=c.getMethod("add",Object.class);//注意这里用的是Object
		//使用invoke运行ArrayList方法add
		method.invoke(array, 2);
		method.invoke(array, 12);
		method.invoke(array, 13);
		method.invoke(array, 14);
		System.out.println(array.size());
		System.out.println(array);
		//不能用增强for循环遍历,因为类型不一样,可以考虑迭代器
	}
}
2、反射配置文件
(1)通过反射配置文件,运行配置文件中指定类的对应方法
读取config.properties文件中的数据,通过反射技术,来完成Person对象的创建
config.properties文件内容如下:
className=com.bean.Person
methodName=eat
(2)读取配置文件,调用指定类中的对应方法
package com.lm;

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

/*
 * 有可能调用Person的方法,有可能调用Student的方法,有可能调用worker的方法
 * 类不确定,方法也不不确定
 * 解决办法:
 *  通过配置文件实现此功能
 *    运行的类名和方法名,以键值对的形式写在文件里
 *    运行哪个类就读取配置文件即可
 * 实现步骤:
 *    1、准备配置文件,键值对
 *    2、IO读取配置文件,Reader
 *    3、文件中的键值对存储到集合的Properties
 *      集合保存的键值对,就是类名和方法名
 *    4、反射获取指定类的class文件
 *    5、class文件对象,获取指定的方法
 *    6、运行方法
 */

public class Test {

	public static void main(String[] args) throws Exception {
		//IO读取配置文件
		FileReader r=new FileReader("config.properties");
		//创建集合对象
		Properties pro=new Properties();
		pro.load(r);
		r.close();
		
		String className=pro.getProperty("className");
		String methodName=pro.getProperty("methodName");
		
		Class c=Class.forName(className);
		Object obj=c.newInstance();
		Method method=c.getMethod(methodName);
		method.invoke(obj);
	}
}
四、总结
(1)如何获取.Class文件对象
a, 通过Object类 getClass()方法获取 Class对象 
b, 通过类名.class 方式 获取 Class对象
c, 通过反射的方式, Class.forName(String classname) 获取Class对象
public static Class<?> forName(String className)throws ClassNotFoundException
返回与带有给定字符串名的类或接口相关联的 Class 对象


(2)通过反射, 获取类中的构造方法,并完成对象的创建
获取指定的构造方法
  public Constructor<T> getConstructor(Class<?>... parameterTypes)
获取指定的public修饰的构造方法 
  public Constructor<T> getDeclaredConstructor(Class<?>... parameterTypes)
获取指定的构造方法,包含私有的
  获取所有的构造方法
  public Constructor<?>[] getConstructors() 获取所有的public 修饰的构造方法
  public Constructor<?>[] getDeclaredConstructors() 获取所有的构造方法,包含私有的


(3)通过反射, 获取类中的构造方法,并完成对象的创建
  步骤:
  a,获取字节码文件对象
  b,通过字节码文件对象 ,获取到指定的构造方法
  getConstructor(参数);
  c,通过构造方法,创建对象
  public T newInstance(Object... initargs)


(4)私有构造方法,创建对象
a,获取字节码文件对象
  b,通过字节码文件对象 ,获取到指定的构造方法
  getDeclaredConstructor (参数);
  c,暴力访问 
  con.setAccessible(true);
  d,通过构造方法,创建对象
  public T newInstance(Object... initargs)


(5)通过反射,获取Class文件中的方法
  获取指定的方法
  public Method getMethod(String name, Class<?>... parameterTypes)
  获取指定的public方法
  public Method getDeclaredMethod(String name, Class<?>... parameterTypes)
  获取指定的任意方法,包含私有的
  获取所有的方法
  public Method[] getMethods() 获取本类与父类中所有public 修饰的方法
  ublic Method[] getDeclaredMethods()获取本类中所有的方法,包含私有的

(6)通过反射,调用方法
  步骤:
  a,获取Class对象
  b,获取构造方法,创建对象
        c,获取指定的public方法
  d,执行方法
  public Object invoke(Object obj, Object... args)


(7)私有方法的调用:
 a,获取Class对象
 b,获取构造方法,创建对象
 c,获取指定的private方法
 d,开启暴力访问
m5.setAccessible(true);
 e,执行方法
public Object invoke(Object obj, Object... args)


(8)通过反射,获取成员变量(字段)
  获取指定的成员变量
  public Field getField(String name) 获取public修饰的成员变量
  public Field getDeclaredField(String name) 获取任意的成员变量,包含私有
  获取所有的成员变量
  public Field[] getFields() 获取所有public修饰的成员变量
  public Field[] getDeclaredFields() 获取司所有的成员变量,包含私有


(9)通过反射,获取成员 变量,并赋值使用
  步骤:
  a,获取字节码文件对象
  b,获取构造方法,创建对象
  c,获取指定的成员变量
  d,对成员变量赋值\获取值操作
  public void set(Object obj,  Object value) 赋值
  public Object get(Object obj) 获取值


(10)私有成员变量的使用
  步骤:
  a,获取字节码文件对象
  b,获取构造方法,创建对象
  c,获取指定的成员变量
  d,开启暴力访问
  e,对成员变量赋值\获取值操作
  public void set(Object obj,  Object value) 赋值
  public Object get(Object obj) 获取值




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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值