十、反射
10.1 反射的概述
- 反射的应用场合
在编译时根本无法知道该对象或者类可能属于哪些类,程序只依靠运行时的信息来发现该对象和类的真实信息。 - 反射的作用
通过反射可以使程序代码访问装载到JVM中的类的内部信息:- 获取已装载类的属性信息;
- 获取已装载类的方法;
- 获取已装载累类的构造方法的信息。
- 反射的机制
在JDK中,主要由这些类来实现java反射机制,这些类都位于java.lang.reflect包中:- Class类:代表一个类;
- Field类:代表类的成员变量(属性);
- Method类:代表类的成员方法;
- Constructor类:代表类的构造方法;
- Array类:提供了动态创建数组,以及访问数组的元素的静态方法。
- Class类
- Class类是java反射机制的起源和入口
- 用于获取与类相关的各种信息;
- 提供了获取类信息的相关方法;
- Class类继承自Object类。
- Class类是所有类的共同的图纸
- 每个类都有自己的对象,好比图纸和实物的关系;
- 每个类也可以看做是一个对象,有共同的图纸Class,存放类的结构信息,能够通过相应方法取出相应信息(类的名字、属性、方法、构造方法、父类和接口)。
10.2 反射的优缺点
- 优点
- 反射提高了java程序的灵活性和扩展性,降低耦合性,提高自适应能力。它允许程创建和控制人格类的对象,无需提前硬编码目标类;
- 反射是其他一些常用语言。如C、C++、Fortran或者Pascal等都不具备的;
- java反射技术应用领域很广,如软件测试、EJB、JavaBean等;
- 许多流行的开源框架例如Struts、Hibernate、Spring在实现过程中都采用了该技术。
- 缺点
-
性能问题
使用反射基本上是一种解释操作,用于字段和方法接入时要远慢于直接代码。因此java反射机制主要应用在对灵活性和扩展性要求很高的系统框架上,普通程序不建议使用。 -
使用反射会模糊程序内部逻辑
程序人员希望在源代码中看到程序的逻辑,反射的等绕过了源代码的技术,因而会带来维护问题。反射代码比相应的直接代码更复杂。
10.3 反射的方法和使用
Class类的常用方法:
方法名 | 功能说明 |
---|---|
getFields() | 获得类的public类型的属性 |
getDeclaredFieldds() | 获得类的所有属性 |
getField(Strign name) | 获得类的指定属性 |
getMethods() | 获得类的public类型的方法 |
getMethod(String name,Class[] args) | 获得类的指定方法 |
getConstrutors() | 获得类的public类型的构造方法 |
getConstrutor(Class[] args) | 获得类的特定构造方法 |
newInstance() | 通过类的无参构造方法创建该类的一个对象 |
getName() | 获得类的完整名字 |
getPackage() | 获取此类所属的包 |
getSuperclass() | 获得此类的父类对应的Class对象 |
使用反射来实例化对象:
public class demo1 {
public static void main(String[] args) {
try {
//1.加载类(加载到JVM中)
Class clz = Class.forName("com.qhj.fanshe.Administrator");
//2.1 实例化对象1
Object objInstance1 = clz.newInstance();//相当于执行Administrator administrator = new Administrator();
System.out.println(objInstance1);
//2.2 实例化对象2(使用构造函数)
Constructor ct = clz.getDeclaredConstructor(null);
Object objInstance2 = ct.newInstance(null);
System.out.println(objInstance2);
} catch (Exception e) {
e.printStackTrace();
}
}
}
Administrator类:
public class Administrator {
public String sex;
String pox;
protected double money;
/**
* 管理员编号
*/
private String adminNumber;
/**
* 管理员密码
*/
private String adminPwd;
/**
* 管理员名字
*/
private String adminName;
public String getAdminNumber() {
return adminNumber;
}
public void setAdminNumber(String adminNumber) {
this.adminNumber = adminNumber;
}
public String getAdminPwd() {
return adminPwd;
}
public void setAdminPwd(String adminPwd) {
this.adminPwd = adminPwd;
}
public String getAdminName() {
return adminName;
}
public void setAdminName(String adminName) {
this.adminName = adminName;
}
public Administrator() {
}
public Administrator(String adminNumber, String adminPwd, String adminName) {
this.adminNumber = adminNumber;
this.adminPwd = adminPwd;
this.adminName = adminName;
}
public void Hello1(){
// System.out.println("Hello1");
}
public String Hello2(){
return "success";
}
private int Hello3(){
return 1;
}
public void Hello4(String name,int age){
System.out.println("大家好!我是:"+name+"今年:"+age+"岁了!");
}
public String Hello5(String name,int age){
return "success5";
}
}
-
属性(Field)
/** * 通过反射得到类里面的属性(字段) */ public void c1(){ try { //1.加载类(加载到jvm中) Class cls = Class.forName("com.qhj.fanshe.Administrator"); //2.得到类中所有的public属性 Field[] f = cls.getFields(); //3.得到类中的所有属性 Field[] f = cls.getDeclaredFields(); //4.获得类中指定的public类型属性 Field ff = cls.getField("sex"); System.out.println(ff.getName()+"\t访问类型:"+ff.getType()+"\t访问修饰符:"+ff.getModifiers()); for (Field field:f){ System.out.println(field.getName()+"\t访问类型:"+field.getType().getSimpleName()+"\t访问修饰符:"+ Modifier.toString(field.getModifiers())); } } catch (Exception e) { e.printStackTrace(); } }
-
访问类型
field.getType();//表示完整的输出(含包名) field.getType().getSimpleName();//表示只输出类型(不含包名)
-
属性名
field.getName();
-
访问修饰符
/** *default 0 *public 1 *private 2 */ field.getModifiers();//输出数字 Modifier.toString(field.getModifiers());//输出名字
-
给属性赋值和获取属性值
/** * 通过反射给类中的属性赋值 */ public void c2(){ try { //1.加载类(加载到jvm中) Class cls = Class.forName("com.qhj.fanshe.Administrator"); /** * 2.获得类中指定的属性 * 实例化对象,相当于 * Administrator administrator = new Administrator(); * administrator.setSex("男"); */ Field f1 = cls.getField("sex"); //实例化对象 Object objInstance = cls.newInstance(); //给sex属性赋值 f1.set(objInstance,"男"); //得到sex的属性值 System.out.println(f1.get(objInstance)); } catch (Exception e) { e.printStackTrace(); } }
-
方法(类中的方法)
/** * 通过反射访问类中的方法 */ public void c3() { try { //1.加载类(加载到jvm中) Class cls = Class.forName("com.qhj.fanshe.Administrator"); /** * 2.获取的是类的所有共有方法,这就包括自身的所有public方法,和从基类继承的、从接口实现的所有public方法 */ Method[] ms = cls.getMethods(); /** * 3.获取的是类自身声明的所有方法,包含public、protected和private方法 */ Method[] ms = cls.getDeclaredMethods(); /** * 4.获得类中指定的方法(只有public类型的方法可以指定) */ Method mm1 = cls.getMethod("Hello2");//无参 Method mmm1 = cls.getMethod("Hello5",String.class,int.class);//有参 Method mm2 = cls.getDeclaredMethod("Hello2");//无参 Method mmm2 = cls.getDeclaredMethod("Hello5", String.class, int.class);//有参 Object objInstance = cls.newInstance(); mm1.invoke(objInstance);//invoke()表示调用当前方法并返回执行当前方法的结果 System.out.println("方法名:"+mm1); mmm1.invoke(objInstance,"张三",23); mmm1.invoke(objInstance,new Object[]{"李四",12}); mm2.invoke(objInstance); System.out.println("方法名:"+mm2.getName()); System.out.println("返回值:"+mmm2.invoke(objInstance,"王五",52));//获得返回值 /** * 5.构造方法 */ //5.1获得类的public类型的无参构造方法 Constructor cz1 = cls.getConstructor(); System.out.println("方法名:"+cz1.getName()+"\t访问修饰符:"+Modifier.toString(cz1.getModifiers())); //5.2获得类的public类型的有参构造方法 Constructor cz2 = cls.getDeclaredConstructor(new Class[]{String.class,String.class,String.class}); Object objInstance2 = cz2.newInstance(new Object[]{"1001","103651","赵六"}); for (Method m:ms){ System.out.println("方法名:"+m.getName()+"\t访问修饰符:"+Modifier.toString(m.getModifiers())+"\t返回值类型:"+m.getReturnType().getSimpleName()); } } catch (Exception e) { e.printStackTrace(); } }
-
访问方法名
m.getName();
-
访问修饰符
m.getModifiers();//数字 Modifier.toString(m.getModifiers());//名字
-
返回值类型
m.getReturnType();//完整类型 m.getReturnType().getSimpleName();//只有返回值类型
-
类
/** * 通过反射访问类 */ public void c4(){ try { //1.加载类(加载到jvm中) Class cls = Class.forName("com.qhj.fanshe.Administrator"); //2.类的完整名字 System.out.println(cls.getName()); //3.类所在的包 System.out.println(cls.getPackage().getName()); //4.此类的父类 System.out.println(cls.getSuperclass()); } catch (Exception e) { e.printStackTrace(); } }
-
类完整名字
cls.getName();
-
类所在包
cls.getPackage();
-
此类的父类
cls.getSuperclass();
-
数组
-
一维数组
/** * 通过反射动态创建数组并存取元素 */ public void c5(){ try { //1.加载类(加载到jvm) Class cls = Class.forName("java.lang.Integer"); //2.创建数组对象 Object arr = Array.newInstance(cls,10); //3.给数组赋值 Array.set(arr,5,20); //4.取出元素 Object elem = Array.get(arr,5); System.out.println(elem); /** * 相当于 * int arr[]=new int[10]; * arr[5]=20; * System.out.println(arr[5]); */ } catch (Exception e) { e.printStackTrace(); } }
-
多维数组
/** * 通过反射动态创建数组并存取元素(多维) */ public void c6(){ // 创建一个含有10*15*18个元素的整型数组 int dims[] = { 10, 15, 18 }; Object arr = Array.newInstance(int.class, dims); // 给arr[5][8][10]赋值 Object arr5 = Array.get(arr, 5); Object arr58 = Array.get(arr5, 8); Array.set(arr58, 10, 30); // 取出arr[5][8][10]值并输出 Object elem = Array.get(arr58, 10); System.out.println(elem); /** * 相当于执行语句: * int arr[ ][ ][ ]=new int[10][15][18]; * arr[5][8][10]=20; System.out.println(arr[5][8][10]); */ }