----------------------- android培训、java培训、java学习型技术博客、期待与您交流! ----------------------
反射
1. 什么是反射
程序在运行时期,对一个类的class文件,进行解剖
获取成员方法,成员变量,构造方法,来运行这些方法
当编译时根本无法预知该对象和类可能属于哪些类,程序只依靠运行时信息来发现该对象和类的真实信息,就必须要用到反射。
2. 反射的目的:增强程序的扩展性
获取class文件--字节码文件
Java中一切都是对象,class文件也是对象java.lang.Class
一个class文件中,包含有构造方法,成员变量,成员方法
* 构造方法也是一个对象,描述这个对象的类 java.lang.reflect.Constructor
* 成员变量也是一个对象,描述这个对象的类 java.lang.reflect.Field
* 成员方法也是一个对象,描述这个对象的类 java.lang.reflect.Method
四种获取class文件对象的方式:
a.通过对象获取,使用的是getClass()方法 类的对象.getClass()
b.通过类的静态属性.class获取 类名.class
c.通过Class类的一个静态方法 forName (String classname)类名,最常用
例如Class.forName("cn.itcast.reflect.Person");类名,是一个字符串的表现形式,可以采用参数传递的方式来获取。灵活性,更高
forName(String name, boolean initialize, ClassLoader loader);
通过指定的类加载器来加载指定字符串的全类名的Class对象
d.自定义类加载器从特殊的位置获取指定的Class对象
Class clazz =new MyClassLoader().loadClass("D:\\BlackHorse\\OuterClass");
4. 从字节码文件对象中获取构造方法,并运行
步骤:
A 获取字节码文件对象
B 通过字节码文件对象,获取构造方法
getConstructors()返回数组,所有的公共的
getDeclaredConstructors()返回数组,所有的,包含私有的
getConstructor()返回一个构造,公共的
getConstructor(可变参数)
getDeclaredConstructor()返回一个,私有的
C 通过newInstance()方法,建立对象
/*
获取构造方法并运行
*/
import java.lang.reflect.*;
class ReflectDemo1
{
public static void main(String[] args) throws Exception
{ //获取字节码文件对象
Class clazz = Class.forName("Person");
//Constructor<?>[] getConstructors() 获取所有公共构造方法
// 返回一个包含某些 Constructor 对象的数组,获取所有公共构造方法。
//Constructor类,描述构造方法对象的类
//Constructor<?>[] getDeclaredConstructors()获取全部的构造方法
/*Constructor[] cons = clazz.getDeclaredConstructors();
for(Constructor c : cons){
System.out.println(c);
}
*/
//获取到的构造方法,运行起来,建立对象
//获取空参数的一个构造方法,就可以了
//getConstructor获取一个构造方法
//Constructor<T> getConstructor(Class<?>... parameterTypes) 括号里可以加上带参数的格式,如带上int获取的就是int参数的person构造方法
Constructor con = clazz.getConstructor(int.class);//传入(int x)的构造函数
// T newInstance(Object... initargs)
Object obj = con.newInstance(99);//创建实例 不new对象 直接用
System.out.println(obj);
//获取私有的构造方法并运行 暴力访问
Constructor conprivate = clazz.getDeclaredConstructor(String.class,int.class);
System.out.println(conprivate);
// Constructor父类的方法 void setAccessible(boolean flag)
conprivate.setAccessible(true); //为true则取消访问检查,暴力访问
Object objprivate = conprivate.newInstance("zhangsan",18);
System.out.println(objprivate);
//不获取构造方法,直接创建对象
Class clazz2 = Class.forName("Person");
//T newInstance() Class类的方法,创建此 Class 对象所表示的类的一个新实例。
//适合有空参数的,权限是public的情况
Object obj2 = clazz2.newInstance();
System.out.println(obj2+ "没有获取构造,直接建立的对象");
}
}
5. 从字节码文件,获取成员属性,并改值
步骤
A 获取字节码文件对象
B 通过字节码文件对象,获取成员属性
Field[] getFields() 获取所有的成员变量,返回数组,公共的
Field getField(变量的名字)获取一个成员变量,公共的
Field[] getDecalredFields() 获取所有的成员变量,返回数组,全部的,包含私有
Field getDeclaredField(变量的名字)获取一个成员变量,包含私有
C 使用Field类中的方法 set(获取到的变量,值)修改成员变量的值
举例:
Class clazz = Class.forName("cn.itcast.reflect.Person");
Field field = clazz.getDeclaredField("name");
field.setAccessible(true);
Object obj = clazz.newInstance();//Class类中的方法 newInstanse()保证类有空参数的,权限是public的构造方法
field.set(obj, "abc");
System.out.println(obj);
6. 从字节码文件。获取成员方法,并运行. 重要部分********
A 获取字节码文件对象
B 通过字节码文件对象,获取成员方法
Method[] getMethods()获取所有的,返回数组,公共的,继承的
Method getMethod(方法名,方法中的参数)获取指定的方法
C 调用Method方法中的invoke运行方法
/*
获取成员方法并运行
*/
import java.lang.reflect.*;
class ReflectDemo2
{
public static void main(String[] args) throws Exception
{
Class clazz = Class.forName("Person");
//getMethods返回所有的方法,包括继承的,和公共的
/*Method[] method = clazz.getMethods();
for(Method m : method){
// System.out.println(m);
}
//getDeclaredMethods()获取方法,包含共有,私有,不包含继承的
Method[] method2 = clazz.getDeclaredMethods();
for(Method m2 : method2){
System.out.println(m2);
}*/
//获取person类中method方法,并运行 ----getMethod(方法名)
Method method = clazz.getMethod("method",String.class);
System.out.println(method);
//运行方法,调用Method类中的一个方法,invoke (Object obj, Object... args)
Object obj = clazz.newInstance();
method.invoke(obj,"你好");
//获取私有方法,并运行
Method methodprivate = clazz.getDeclaredMethod("function");
methodprivate.setAccessible(true);
methodprivate.invoke(obj);
}
}
程序通过配置文件运行,用户配置什么,运行什么. 重要部分********
如果后期需要进行修改的话,如果是服务器端程序修改源码,编译成class文件,在部署到服务器中。这样的写法,不利于修改,后期的扩展,硬编码
可不可以,不修改源码,实现程序的扩展性呢?
在不修改源码的情况下,采用配置文件的方法,由用户指定运行哪一个类,哪一个方法。
配置文件,其实就是一个记事本。这样的形式,以配置文件出现,不会修改源码,软编码
用到了:反射技术,配置文件技术 Properties, IO流
实现步骤:
A 准备配置文件 集合类 Properties做
B 使用IO流技术,关联集合类,读取配置文件
C 通过配置文件获取用户配置的类和方法
D 反射获取用户配置的类的字节码文件对象
E 获取类中的方法。并运行
//通过配置文件运行 重点内容
import java.io.FileInputStream;
import java.util.Properties;
import java.lang.reflect.Method;
class ReflectTest
{
public static void main(String[] args)throws Exception
{
//建立集合对象
Properties proc = new Properties();
//字节输入流,读取配置文件
FileInputStream fis = new FileInputStream("config.txt");
//集合IO关联
proc.load(fis);
//System.out.println(proc.getProperty("className"));
//System.out.println(proc.getProperty("methodName"));
System.out.println(proc.size());
for(int x = 1 ; x <= proc.size()/2; x++){
String className = proc.getProperty("className"+x);
String methodName = proc.getProperty("methodName"+x);
//获取字节码文件对象
Class clazz = Class.forName(className);
//通过字节码文件对象获取方法
Method method = clazz.getMethod(methodName);
//建立对象
Object obj = clazz.newInstance();
//运行方法
method.invoke(obj);
}
}
}
使用反射技术,绕过泛型的编译期间检查----泛型的擦除
import java.util.*;
import java.lang.reflect.*;
class ReflectDemo3
{
public static void main(String[] args) throws Exception
{
ArrayList<Integer> arr = new ArrayList<Integer>();
arr.add(123);
arr.add(456);
arr.add(789);
//arr.add("abc");
/*
使用反射技术,绕过泛型的编译期间检查
将不同的数据类型,存储到带有泛型的集合----泛型的擦除
*/
//获取ArrayList字节文件对象
Class clazz = arr.getClass();
//存储对象,调用的是集合中的add方法,反射获取add方法
Method method = clazz.getMethod("add",Object.class); //泛型 传object类型
//对象有了 arr,方法有了,运行add方法
method.invoke(arr,"abc");
method.invoke(arr,"QQQ");
method.invoke(arr,"www");
System.out.println(arr);
Iterator it = arr.iterator();
while(it.hasNext()){
System.out.println(it.next());
}
}
}
----------------------- android培训、java培训、java学习型技术博客、期待与您交流! ----------------------