黑马程序员_Java反射机制

------- android培训java培训、期待与您交流! ----------

类的加载:
    当程序要使用某个类时,如果该类还未被加载到内存中,则系统会通过加载,连接,初始化三步来实现对这个类的初始化。
     加载:
        就是指将class文件读入内存,并为之创建一个Class对象。
        任何类被使用时系统都会建立一个Class对象。
    连接:
        验证 是否有正确的内部结构,并和其他类协调一致
        准备 负责为类的静态成员分配内存,并设置默认初始化值
        解析 将类的二进制数据中的符号引用替换为直接引用
    初始化:
         当一个对象被创建之后,虚拟机会为其分配内存
        在为这些实例变量分配内存的同时,这些实例变量也会被赋予默认值。
类初始化时机
    创建类的实例
    访问类的静态变量,或者为静态变量赋值
    调用类的静态方法
    使用反射方式来强制创建某个类或接口对应的java.lang.Class对象
    初始化某个类的子类
    直接使用java.exe命令来运行某个主类
类加载器
    负责将.class文件加载到内在中,并为之生成对应的Class对象。
    虽然我们不需要关心类加载机制,但是了解这个机制我们就能更好的理解程序的运行。
类加载器的组成
    Bootstrap ClassLoader 根类加载器
    Extension ClassLoader 扩展类加载器   
    Sysetm ClassLoader 系统类加载器
Bootstrap ClassLoader 根类加载器
    也被称为引导类加载器,负责Java核心类的加载
        比如System,String等。在JDK中JRE的lib目录下rt.jar文件中
Extension ClassLoader 扩展类加载器
    负责JRE的扩展目录中jar包的加载。
        在JDK中JRE的lib目录下ext目录
Sysetm ClassLoader 系统类加载器
    负责在JVM启动时加载来自java命令的class文件,以及classpath环境变量所指定的jar包和类路径
反射
    JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性;这种动态获取的信息以及动态调用对象的方法的功能称为java语言的反射机制。要想解剖一个类,必须先要获取到该类的字节码文件对象。而解剖使用的就是Class类中的方法.所以先要获取到每一个字节码文件对应的Class类型的对象.

我们来看看获取字节码文件的方式。

反射:就是通过class文件对象,去使用该文件中的成员变量,构造方法,成员方法。
 Person p = new Person
 p.调用
 
 想要这样使用,首先你必须要得到class文件对象,其实也就是得到Class类的对象。
 Class类:
  成员变量:Field
  构造方法:Constructor
  成员方法:Method


先准备一个Person类以便后面的操作

private String name;
	int age;
	public String address;
	public Person(){}
	
	private Person(String name)
	{
		this.name = name;
	}
	
	Person(String name,int age)
	{
		this.name = name;
		this.age = age;
	}
	
	public Person(String name,int age,String address)
	{
		this.name = name;
		this.age = age;
		this.address = address;
	}
	
	public void show()
	{
		System.out.println("show");
	}
	
	public void method(String s)
	{
		System.out.println("method "+s);
	}
	
	public String getString(String s,int i)
	{
		return s+"---"+i;
	}
	
	private void function()
	{
		System.out.println("function");
	}

	@Override
	public String toString() {
		return "Person [name=" + name + ", age=" + age + ", address=" + address
				+ "]";
	}
下面我们开始操作

<span style="white-space:pre">		</span>//方式1
		Person p = new Person();
		Class c = p.getClass();//通过Object的getClass
		
		Person p2 = new Person();
		Class c2  = p.getClass();
		
		System.out.println(p == p2);//因为属于不同的对象所以是false
		System.out.println(c == c2);//他们对应的同一份字节码文件为true
		
		//方式2
		Class c3 = Person.class;//静态的通过类名.class能获得该字节码文件
		System.out.println(c == c3);
		
		//方式3
		Class c4 = Class.forName("com.reflect.Person");//找到该字符串对应的字节码文件。
		System.out.println(c == c4);
当我们拿到了一个字节码文件对象后,我们就可以对立面的内容进行操作了

首先来看看怎么样能拿到构造方法。

Construct:

//获取字节码文件
		Class c = Class.forName("com.reflect.Person");
		//获取构造方法。
		//public Constructor[] getConstructors()//返回公共的构造方法、获取到所有的构造方法存入一个数组里面
		//public Constructor[] getDeclaredConstructors()//返回所有的
		//Constructor[] cons = c.getDeclaredConstructors();
		//for(Constructor con:cons)
		//{
			//System.out.println(con);遍历整个数组拿到构造方法
		//}
		
		//获取单个构造方法。
		//public Constructor<T> getConstructor(Class<?>... parameterTypes)
		//参数表示的是:你要获取的构造方法的参数个数及参数类型的class字节码文件对象。
		Constructor con = c.getConstructor();//返回的是构造函数对象。
		
		//public T newInstance(Object...initargs)
		//使用此Constructor对象表示的构造方法来创建构造方法的声明的新实例,并用指定的初始化参数初始化该实例。
		Object obj = con.newInstance();//通过该构造函数对象创建一个实例
		System.out.println(obj);//创建一个实例对象。
我们发现上诉代码并未拿到私有的方法那我们来看看有参数的,并且私有的构造函数的操作。

//获取字节码文件对象。
		Class c = Class.forName("com.reflect.Person");
		//获取带参数的构造方法对象。
		Constructor con = c.getConstructor(String.class,int.class,String.class);
		//通过带参构造方法对象创建对象。
		Object obj = con.newInstance("张三",25,"北京");
		System.out.println(obj);

Class c = Class.forName("com.reflect.Person");
		Constructor con = c.getDeclaredConstructor(String.class);//获取到有一个String类型参数的构造函数。
		con.setAccessible(true);//当出现不能访问的时候就需要强制访问它的私有对象。
		Object obj = con.newInstance("李四");//我们发现通过getDeclaredConstructor()拿到了所有的构造方法但是却不能访问。
		System.out.println(obj);


Field:
在我们拿到了构造函数后就需要对各个成员变量进行进一步的访问,

Class c = Class.forName("com.reflect.Person");
//		Field[] fields = c.getFields();获取所有公共的成员变量。存入数组中
//		Field[] fields = c.getDeclaredFields();获取所有的成员变量。
//		for(Field field : fields)
//		{
//			System.out.println(field);//通过遍历获得成员变量
//		}
		
		Constructor con = c.getConstructor();//创建一个对象,以便对成员变量进行操作。
		Object obj = con.newInstance();
		
		//获取单个成员变量。
		//获取address,并对其赋值。
		Field addressField = c.getField("address");
		addressField.set(obj, "北京");//给obj对象的addressField字段赋值为"北京"
		System.out.println(obj);
		
		Field nameField = c.getDeclaredField("name");
		nameField.setAccessible(true);
		nameField.set(obj, "张三");
		System.out.println(obj);

剩下最后一个Method了,我们来看看它的各种方法。

Class c = Class.forName("com.reflect.Person");
		//获取所有的方法
//		Method[] methods = c.getMethods();获取自己的包括父类的公共方法。存入数组
//		Method[] methods = c.getDeclaredMethods();//获取自己的所有的方法
//		for(Method method:methods)
//		{
//			System.out.println(method);
//		}
		
		Constructor con = c.getConstructor();
		Object obj = con.newInstance();
		
		//	获取单个方法并使用。获取show();
		//public Method getMethod(String name, Class<?>... parameterTypes)
		//第一个参数表示方法名,第二个参数表示的是方法参数的clas类型。
		Method m1 = c.getMethod("show");
		//public Object invoke(Object obj, Object... args)
		//返回值是Object接收,第一个参数表示对象是谁,第二个参数表示调用该方法的实际参数。
		m1.invoke(obj);//调用obj对象的m1方法。	
		
		Method m2 = c.getMethod("method", String.class);
		m2.invoke(obj, "hello");
		
		Method m3 = c.getMethod("getString", String.class,int.class);
		Object obj1 = m3.invoke(obj, "张三",26);
//		String s = (String)m3.invoke(obj, "张三",26);
		System.out.println(obj1);
		
		Method m4 = c.getDeclaredMethod("function");
		m4.setAccessible(true);
		m4.invoke(obj);
		

反射中的三个对象已经了解的差不多了,他们的各类操作几乎都是一样的,我们来看看他们的实际应用。

看看反射对数组的操作:

需求是:我给你ArrayList<Integer>的一个对象,我想在这个集合中添加一个字符串数据,如何实现。

	//创建新集合
		ArrayList<Integer> array = new ArrayList<Integer>();
		Class c = array.getClass();//得到集合的class对象
		Method method = c.getMethod("add", Object.class);//获取到c的add方法,传入的是Object类型
		method.invoke(array, "hello");//调用array的add方法,传入hello
		method.invoke(array, "world");
		System.out.println(array);


我们知道反射操作的对象都是我们不知道的一些东西,我们就需要把那些不知道的数据以键值对的方式存入到文档中,以便于别人的查阅和传值,这个文档就是常说的配置文件。我们来看看他们的应用。

创建几个测试需要的学生,老师类。

public class Teacher {
	public void love()
	{
		System.out.println("讲课");
	}
}

package com.reflect.text;

public class Student {
	public void love()
	{
		System.out.println("学Java");
	}
}

这就是配置文件的形式


<span style="white-space:pre">		</span>Properties prop = new Properties();
		FileReader fr = new FileReader("class.txt");//读取出配置文件的信息
		prop.load(fr);//加载搭到prop中
		fr.close();
		
		//获取数据
		String className = prop.getProperty("className");//根据键获取到值
		String methodName = prop.getProperty("methodName");
		
		//反射
		Class c = Class.forName(className);
		Constructor con = c.getConstructor();
		Object obj = con.newInstance();//创建出对象
		//调用方法
		Method m1 = c.getDeclaredMethod(methodName);//获取到方法
		m1.setAccessible(true);
		m1.invoke(obj);//调用方法。

最后我们来看看用强大的反射,去设置一个私有的变量的值:

public void setProperty(Object obj,String properName,Object value) throws Exception
	{
		//根据对象获取字节码文件
		Class c = obj.getClass();
		//拿到该对象的properName成员变量
		Field field = c.getDeclaredField(properName);
		//强制访问
		field.setAccessible(true);
		//将obj的field对象赋值为value
		field.set(obj, value);
	}

我们开始操作Tool工具类

public class ToolDemo {
	public static void main(String[] args) throws Exception {
		Person p = new Person();
		Tool t = new Tool();
		t.setProperty(p, "name", "张三");//用反射机制我们不可思议的操作了一个私有成员,这可见反射的强大之处。
		t.setProperty(p, "age", 20);
		System.out.println(p);
	}
}

class Person
{
	private String name;
	public int age;
	
	@Override
	public String toString() {
		// TODO Auto-generated method stub
		return name + "---" + age; 
	}
}



------- android培训java培训、期待与您交流! ----------

详细请查看:www.itheima.com

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值