反射技术:
动态的获取指定的类以及动态的调用类中的内容。
以前是先有类,再new对象。有了反射后:先new对象,也就是说把创建对象的动作先做完,至于new哪个类由用户通过配置文件传递。
好处:大大的提高了程序的扩展性。
反射的应用:当使用的类不确定的时候,可以通过配置文件告诉应用程序,只要应用程序中使用反射技术就可以了。
需要哪些对象呢?
Class 获取Class字节码文件
Constructor 提供关于类的单个构造方法的信息以及对它的访问权限。
Field 获取类或接口的字段信息
Method 提供关于类或接口上单独某个方法
动态创建对象的方法。newInstance();默认调用的是被反射类的空参数构造函数。
如果要通过指定的构造函数来初始化对象,需要先通过getConstructor()方法获取构造器对象。
然后,在通过构造器创建对象,并初始化。
如果要通过指定的构造函数初始化对象怎么办呢?
1,获取字节码文件对象。
2,再获取给定的构造函数。
3,通过构造函数初始化对象。
要想获取字节码文件中的成员,必须要先获取字节码文件对象。
获取字节码文件对象的方式:
1,通过Object类中的getClass方法。
虽然通用,但是前提必须有指定类,并对该类进行对象的创建,才可以调用getClass方法。
2,使用的任意数据类的一个静态成员class,所有的数据类型都具备的一个属性。
好处:不用new对象。但是,还需要使用具体的类。
3,使用Class类中的forName方法。通过给定类名来获取对应的字节码文件对象。
这种方式很爽,只要知道类的名字就可以了。获取对应的字节码文件直接由forName方法自动完成。
这就是反射技术使用的获取字节码文件对象的方式。
字节码文件已经获取,那么它有什么用呢?
1.创建对象
2.获取字段
3.调用方法
创建对象:
1,通过给定的类名称,加载对应的字节码文件,并封装成字节码文件对象Class.
通过newInstance()就可以创建字节码对象所表示的类的实例。
2,通过new创建给定的类的实例。
3,调用该类的构造函数。
通常被反射的类都会有提供空参数的构造函数。
没有对应的构造函数,会报InstantiationException
如果有提供,但是权限不够,会报IllegalAccessException
获取字段:
获取字段对象。
Field field = 字节码对象.getField(fieldName); //获取是公共的字段。
Field field = 字节码对象.getDeclaredField(fieldName);//通过字段的名字获取字段
field.setAccessible(true);//取消权限检查,暴力访问。一般不访问私有。
获取方法:
getDeclaredMethods() //获取单个方法(包括私有)
getDeclaredMethod(String name, Class<?>... parameterTypes) //获取所有方法
getMethod(String name, Class<?>... parameterTypes) //获取所有公共方法
getMethods() //获取单个公共方法
练习:
<span style="font-size:24px;">/*自定义一个类,
* 面有公共成员变量,私有成员变量
* 公共成员方法,私有成员方法
*
*
*/
public class Reflect {
private String name;
public int i=100;
private void show(){
System.out.println("show---run");
}
public void show1(String str,int j){
System.out.println(str+"show1---run"+j);
}
}
</span>
<span style="font-size:24px;">public class ReflectDemo {
public static void main(String[] args) {
//配置文件位置
File file=new File("src\\class.properties");
//读取流引用
FileReader fr=null;
try {
//创建读取流
fr=new FileReader(file);
} catch (FileNotFoundException e2) {
System.out.println("未找到配置文件");
return;
}
//可以从流中读取键值对的类
Properties prop=new Properties();
try {
//从输入流中读取属性列表
prop.load(fr);
} catch (IOException e2) {
e2.printStackTrace();
}
//通过建获取值,赋值给className
String className = prop.getProperty("class");
//字节码对象的引用
Class<?> clazz;
try {
//获取字节码文件对象
clazz = Class.forName(className);
} catch (ClassNotFoundException e) {
System.out.println("没有这个类");
return;
}
//创建对象的引用
Object obj;
try {
//创建对象
obj = clazz.newInstance();
} catch (InstantiationException | IllegalAccessException e) {
System.out.println("没有适合的构造方法或权限不够");
return;
}
//获取字节码中的所有成员变量
Field[] field = clazz.getDeclaredFields();
//对成员变量进行遍历
for (Field field2 : field) {
try {
//判断此对象有没有被赋值
if (field2.get(obj)== null) {
//如果没有赋值...
field2.set(obj, "Ok");
//打印成员变量的值
System.out.println(field2.get(obj));
} else {
//如果已经赋值则直接打印
System.out.println(field2.get(obj));
}
} catch (IllegalArgumentException e) {
System.out.println("参数传递错误");
} catch (IllegalAccessException e) {
/*
* 如果成员是私有的,会进入到此catch中处理
*/
//暴力访问
field2.setAccessible(true);
try {
if (field2.get(obj) == null) {
field2.set(obj, "Ok");
System.out.println(field2.get(obj));
} else {
System.out.println(field2.get(obj));
}
} catch (Exception e1) {
e1.printStackTrace();
}
}
}
//获取所有方法
Method[] method = clazz.getDeclaredMethods();
//对方法进行遍历
for (Method method2 : method) {
try {
//如果方法有参数
if ((method2.getParameterTypes()).length != 0) {
//调用传参方法
method2.invoke(obj, "show运行", 1234);
} else {
//否则直接调用
method2.invoke(obj, null);
}
} catch (IllegalAccessException e) {
/*
* 如果成员是私有的,会进入到此catch中处理
*/
//暴力访问
method2.setAccessible(true);
try {
if ((method2.getParameterTypes()).length != 0) {
method2.invoke(obj, "show运行", 4321);
} else {
method2.invoke(obj, null);
}
} catch (Exception e1) {
e1.printStackTrace();
}
} catch (IllegalArgumentException e) {
System.out.println(method2 + "参数传递错误");
break;
} catch (InvocationTargetException e) {
System.out.println("无法调用此类");
}
}
}
}
</span>