------- android培训、java培训、期待与您交流! ----------
反射技术:
动态的获取指定的类以及动态的调用类中的内容。
应用环境:
当软件完成时,想要后期进行功能上的增添修改,该如何做呢?既然想要实现功能,只需要实现应用程序的接口。但是应用程序已写好,后期出现的接口子类无法直接应用程序new创建对象。既然子类不确定,可以通过对外提供配置文件的形式,将不确定的信息存储到配置文件即可。
该程序只要写好如何读取配置文件信息即可,如果存储了指定的子类名,就根据具体的名称找该类并进行加载和对象的创建,这些动作在前期定义软件时写好的,没有类之前就将创建对象的动作完成了,这就是动态的获取指定的类,并使用类的功能。这就是反射技术的应用场景。
优点:
反射技术的出现大大的提高了程序的扩展性。
字节码文件对应的类
class Class{//描述字节码的类
Field field;//将字段封装成对象类型Field
Constructor cons;//将构造函数也封装成了对象类型Constructor
Method method;//将类中的成员都封装成了对象。
}
那么怎么获取字节码文件对象呢?
要想获取字节码文件中的成员,必须要先获取字节码文件对象,获取字节码文件对象的方式:
1.通过Object类中的getClass方法。弊端虽然通用,但是前提必须有指定类,并对该类进行对象的创建,才可以调用getClass方法
<span style="font-family:SimSun;font-size:18px;">public static void getClass_1(){
Person p = new Person();
Class clazz = p.getClass();
System.out.println(clazz.getName());//获取类的名字
}</span>
2.使用任意数据类型的一个静态成员class,所有的数据类型都具备的一个属性。好处:不用new对象。缺点:还需要具体的类。
<span style="font-family:SimSun;font-size:18px;">public static void getClass_2(){
Class clazz = Person.class;
}</span>
3.使用Class类中的forName方法。通过给定雷鸣来获取对应的字节码文件对象
这种方式只要知道类的名字就可以,获取对应的字节码文件直接有forName来完成
这就是反射技术使用的获取字节码对象的方式。
<span style="font-family:SimSun;font-size:18px;">public static void getClass_3(){
String calssName = "cn.itcast.domain.Person";//注意类名要全名
Class clazz = Class.forName(className);
System.out.println(clazz);
}</span>
通过newInstance()就可以创建字节码对象所表示的类的实例
1)包括八种基本类型(byte、short、int、long、float、double、char、boolean)的字节码对象和一种返回值为void类型的void.class。
2)Integer.TYPE是Integer类的一个常量,它代表此包装类型包装的基本类型的字节码,所以和int.class是相等的。
基本数据类型的字节码都可以用与之对应的包装类中的TYPE常量表示
数组类型的Class实例对象,可以用Class.isArray()方法判断是否为数组类型的。
总结:只要是在源程序中出现的类型都有各自的Class实例对象,如int[].class、void.class等。
public static void fuction()throws Exception{
String str1 = "abc";
Class cls1 = str1.getClass();
Class cls2 = String.class;
Class cls3 = Class.forName("java.lang.String");
System.out.println(cls1 == cls2);//true
System.out.println(cls1 == cls3);//true
//判断是否为基本类型:isPrimitive()
System.out.println(cls1.isPrimitive());//false
System.out.println(int.class == Integer.class);//false
//Integer.TYPE代表包装类对应的基本数据类型的字节码
System.out.println(int.class == Integer.TYPE);//true
System.out.println(int[].class.isPrimitive());//false
//判断是否为数组类型的
System.out.println(int[].class.isArray());//true
}
Constructor类
1、概述:Constructor代表某个类的构造方法
2、获取构造方法:
1)如何得到某个类的所有构造方法:如得到String类的所有构造方法
Constructor[] cons =Class.forName(“java.lang.String”).getConstructors();
2)获取某一个构造方法:
Constructor con =String.class.getConstructor(StringBuffer.class);
3、创建实例对象:
1)通常方式:String str = new String(new StringBuffer (”abc”));
2)反射方式:String str = (String)con.newInstance(new StringBuffer(“abc”));
调用获得的方法时要用到上面相同类型的实例对象,即两个StringBuffer()要对应相等。
NewInstance():构造出一个实例对象,每调用一次就构造一个对象。
注意:上面的两个地方都要用到StringBuffer,这必须是一致的。
第一个是指定要带StringBuffer参数类型的构造方法,即所需使用的是含StringBuffer类型的构造方法。
第二个是用这个构造方法创建对象,要传入的参数类型是StringBuffer。
Constructor constructor1 =
String.class.getConstructor(StringBuffer.class);
String str2 =
(String)constructor1.newInstance(new StringBuffer("abc"));
System.out.println(str2);
Field类
Field类代表某一个类中的一个成员变量。
获取成员变量:
1)获取公有的成员变量:
getField(String name)
get(变量)
2)获取私有的成员变量:暴力反射
getDeclared(String name)
setAccessible(boolean b),将b设为true即可
get(变量)
ReflectPoint pt1 = new ReflectPoint(3,5);
//fieldX和fieldY并不是对象身上的变量,而是类上的
//要用它去取某个对象上的对应的值,传入什么对象,就取相应对象的值。
Field fieldY = pt1.getClass().getField("y");
System.out.println(fieldY.get(pt1));
//获取私有的成员变量
Field fieldX = pt1.getClass().getDeclaredField("x");
fieldX.setAccessible(true);
System.out.println(fieldX.get(pt1));
}
//替换字符
private static void changeStringValue(Object obj) throws Exception {
Field[] fields = obj.getClass().getFields();
for(Field field : fields){
//此处需要用==比较,因为是同一份字节码对象
if(field.getType() == String.class){
String oldValue = (String)field.get(obj);
String newValue = oldValue.replace('b','a');
field.set(obj, newValue);
}
}
}
Method类
1、概述:Method类代表某个类中的一个成员方法。
调用某个对象身上的方法,要先得到方法,再针对某个对象调用。
2、专家模式:谁调用这个数据,就是谁在调用它的专家。
如人关门:
调用者:是门调用管的动作,对象是门,因为门知道如何执行关的动作,通过门轴之类的细节实现。
指挥者:是人在指挥门做关的动作,只是给门发出了关的信号,让门执行。
总结:变量使用方法,是方法本身知道如何实现执行的过程,也就是“方法对象”调用方法,才执行了方法的每个细节的。
3、获取某个类中的某个方法:(如String str = ”abc”)
1)通常方式:str.charAt(1)
2)反射方式:
Method charAtMethod =
Class.forName(“java.lang.String”).getMethod(“charAt”,int.class);
charAtMethod.invoke(str,1);
说明:如果传递给Method对象的invoke()方法的第一个参数为null,说明Method对象对应的是一个静态方法
4、用反射方式执行某个main方法:
首先要明确为何要用反射:在写源程序时,并不知道使用者传入的类名是什么,但是虽然传入的类名不知道,而知道的是这个类中的方法有main这个方法,所以可以通过反射的方式,通过使用者传入的类名(可定义字符串型变量作为传入类名的入口,通过这个变量代表类名),内部通过传入的类名获取其main方法,然后执行相应的内容。
<span style="font-family:SimSun;">private static void methodTest(String [] args) throws Exception {
String str1 = "abc";
//一般方法:
System.out.println(str1.charAt(1));
//反射方法:
Method methodCharAt =
Class.forName("java.lang.String").getMethod("charAt",int.class);
System.out.println(methodCharAt.invoke(str1,1));
//用反射方式执行某个main方法
//一般方式:
Test.main(new String[]{"111","222","333"});
//反射方式:
String startingClassName = args[0];
Method methodMain =
Class.forName(startingClassName).getMethod("main",String[].class);
//方案一:强制转换为超类Object,不用拆包
methodMain.invoke(null,(Object)new String[]{"111","222","333"});
//方案二:将数组打包,编译器拆包后就是一个String[]类型的整体
methodMain.invoke(null,new Object[]{new String[]{"111","222","333"}});
}
//定义一个测试类
class Test{
public static void main(String [] args){
for(String arg : args){
System.out.println(arg);
}
}
}</span>
框架与反射
框架的意思就是预先给定一个程序,他可以调用你所给定的程序或任何东西。这样你就可以利用这个框架快速的做一些你想做的事。 反射也能建立框架,比如你可以新建一个配置文件,还有一个预先写好的程序,这样只要在配置文件中修改即可。 我们这里的程序给定了一个config.properties配置文件,这样用户只要在这个配置文件中修改集合的特定类型,即可使用。