反射技术==>动态加载一个指定的类.获得该类的字节码文件对象-->获得该类中所有的内容.-->对类进行解剖
1.类的初始化:
类的初始化:就是类的加载,将类从硬盘放到内存中.
时机:什么时候使用到这个类,加载器就会将该类加载到内存中.
1. 创建类的实例
2. 类的静态变量,或者为静态变量赋值
3. 类的静态方法
4. 使用反射方式来强制创建某个类或接口对应的java.lang.Class对象
5. 初始化某个类的子类
6. 直接使用java.exe命令来运行某个主类
2.类的加载器: -->(原理:就是按照一定的格式将硬盘上的文件读入到内存中)
负责将.class文件加载到内在中,并为之生成对应的Class对象。
虽然我们不需要关心类加载机制,但是了解这个机制我们就能更好的理解程序的运行
Bootstrap ClassLoader 根类加载器.==>也叫引导类加载器
加载核心类库...System,String...主要是\lib\rt.jar
Extension ClassLoader 扩展类加载器==>
扩展类加载器,目前只要加载src\lib\ext文件夹.
System ClassLoader 系统类加载器
主要是加载用户自定义的,或者用户导入的第三方jar包.
3.如何获得Class文件对象,字节码文件对象.
1.通过该类的对象,直接获得:
Class clazz = obj.getClass(); ==>必须创建该类的对象.
2.通过类中的静态方法或者静态的成员变量.静态的属性class.
3.使用Class类中的方法.
Class clazz = Class.forName(classname);
注意:第三种和前两种的区别前两种你必须明确Person类型.后面是指定这种类型的字符串就行.这种扩展更强.我不需要知道你的类.我只提供字符串,按照配置文件加载就可以了
4. 反射的使用步骤:
1.获得指定类的Class文件对象,(获得字节码文件对象)
2.实例化对象,获得类的属性,方法或构造方法.
3.访问属性,方法,调用构造方法创建对象.
5.反射的用法:
1.获得Class文件的对象.
★★Class clazz = Class.forName(classname); 用于类加载.
classname需要全名..eg:com.mysql.jdbc.Driver -->不可以带class后缀.
Class clazz = Person.class 类名获取.
Class clazz = p.getClass() 类对象直接获取.
快速创建对象:
1.类需要有空参构造
2.该空参构造权限需要是public
Object obj =clazz.newInstance();
直接通过clazz文件对象创建对象,底层就是使用类的空参获得的实例.
2.反射类的构造方法:
Constructor con =clazz.getConstructor(Class<?>... params );//只能获得public修饰的.
获得任意一个公有的指定参数列表的构造,如果不指定参数,获得的就是空参构造.
Constructor con =clazz.getDeclareConstructor(Class<?>... params );//可以获得私有的构造
获得任意一个(包括私有的)指定参数列表的构造,如果不指定参数,获得的就是空参构造.
Constructor[] cons= clazz.getDeclareConstructors(Class<?>... params );//可以获得私有的构造
获得该类中的所有的(包括私有的)构造方法.
通过对象的构造器,获得该类的实例对象:
Object obj =con.newInstance(Object ... params); //参数传递,是根据使用的构造方法的参数列表来确定.
3.反射类的属性.
Field field = clazz.getField(String name);
//获得指定名称的属性字段对象.只能是public修饰的
Field[] fields = clazz.getDeclareFields();//无需名称.
获得该类中以及父类或者接口中继承的所有字段,包括私有的.
field.setAccessable(true);//取消访问权限.
field.set(Object obj,Object value); //设置obj对象的该field字段的值为value.
4.反射类的方法.
Method[] methods = clazz.getDeclareMethods();
//返获得该类中所有的方法,包括私有的.
返回 Method 对象的一个数组,这些对象反映此Class 对象表示的类或接口声明的所有的方法,
包括公共、保护、默认(包)访问和私有方法,但不包括继承的方法.
Method method = clazz.getDeclareMethod(Stringmethodname,Class<?>...params);
获得一个指定名称的方法.参数为该方法的参数类型的class文件.
method.setAccessable(true);
method.invoke(Object obj,params);
-->调用obj对象中的method方法.传入的参数为params.(需要和前面获得的方法的参数列表相同.)
反射的应用:
1.泛型擦除.
定义泛型限制的集合,向其中存入非泛型限定的数据类型.
* 分析:
* 1.要想在一个指定泛型(Integer类型)的list集合中存入一个String类型的对象,就需要使用到反射技术.
* 2.考虑到list添加元素的方法是add.因此,准备使用add方法进行反射添加.
ArrayList<Integer> list= new ArrayList<Integer>();
//第一步,获得该对象的字节码文件.
Class c = list.getClass();
//第一个参数,是方法名.第二个参数是参数类型的字节码文件.
Method method = c.getDeclaredMethod("add" ,Object.class );//获得该类的add方法
method.setAccessible( true); //取消访问限制
method.invoke(list, "我是字符串,就要加进来,气死你!");
使用代码进行测试:
/**
* 自定义一个测试类
* @author myth
*
*/
public class User {
//私有属性.
private String name;
private String sex;
//一个方法
public void say(String exten) {
System.out.println("你好:"+name+"附加字符为 :"+exten);
}
//构造默认为无参的.
public User(){} //可以不声明的.
}
用于测试用的类
反射的代码:
/**
* 这个测试,是为了说明,有了反射,有了对应类的全路径.
* 我们可以获得该对象的所有的字段,方法.以及构造.(包括私有的.)
* 并且,我们可以暴力反射,即使该类没有指定对应的get/set方法,我们也可以去设置值.
* @author myth
*
*/
public class ProxyTest {
@Test
public void test() throws Exception {
//获得该类的字节码文件对象.
Class clazz = Class.forName("com.myth.proxy.User");
//通过该类的空参构造一个对象.没有空参,这个方法就会报错.
User user = (User) clazz.newInstance();
//获得所有的属性字段.包括私有的.
Field[] declaredFields = clazz.getDeclaredFields();
//遍历所有字段.
for (Field field : declaredFields) {
//因为想对name字段赋值.所以对字段进行判断.
if ("name".equals(field.getName())) {
//暴力反射,否则无法设置参数.因为该字段声明是私有的.
field.setAccessible(true);
field.set(user, "二哈"); //设置对应字段的值.
//该语句类似于 user.setName("二哈");但是我们并没有提供get/set方法的.
}
}
//获得对应方法名称的方法.(方法也是一个Method类的对象.)
//通过匹配方法名,得到一个唯一方法对象.
Method method = clazz.getDeclaredMethod("say",String.class);
//System.out.println(method);//public void com.myth.proxy.User.say(java.lang.String)
//System.out.println(method.getName()); //say
method.invoke(user, "哈哈");
}
}