java 反射运用
一,获取私有的属性,方法,构造器(俗名:暴力反射)
现有一个类,属性,方法,构造器均为私有的,如何创建实例对象,更该属性值,调用方法?
public class Student { private String name; private int age; private Student() { System.out.println("调用了Student()无参私有构造器"); } private String info() { System.out.println("调用了info()无参私有方法"); return "Student [name=" + name + ", age=" + age + "]"; } private void look(String str) { System.out.println("调用了look(String str)参数为String类型的私有方法,参数为:"+str); } }
main方法:
首先要创建Student类的实例对象,即调用该类私有的构造方法
public static void main(String[] args) { try { //1.首先要创建Student类的实例对象 //1.1创建Student类的反射对象 Class<Student> clazz = Student.class; //1.2获取私有构造器 Constructor<Student> c0 = clazz.getDeclaredConstructor(); //1.3设置访问权限 c0.setAccessible(true); //1.4创建实例对象 Student student = c0.newInstance(); } catch (Exception e) { e.printStackTrace(); } }
运行,控制台输出结果:
调用了Student()无参私有构造器
证明调用了Student类的构造器,并创建了该类的一个实例对象
更改,获取该类的私有属性
public static void main(String[] args) { try { //1.首先要创建Student类的实例对象 //1.1创建Student类的反射对象 Class<Student> clazz = Student.class; //1.2获取私有构造器 Constructor<Student> c0 = clazz.getDeclaredConstructor(); //1.3设置访问权限 c0.setAccessible(true); //1.4创建实例对象 Student student = c0.newInstance(); //获取私有属性 name Field name = clazz.getDeclaredField("name"); //设置访问权限 name.setAccessible(true); //为该对象的该属性赋值 name.set(student, "张三"); Field age = clazz.getDeclaredField("age"); age.setAccessible(true); age.set(student, 18); //获取该属性的值:get(实例对象) Object nameVal = name.get(student); System.out.println("name:"+nameVal); Object ageVal = age.get(student); System.out.println("age:"+ageVal); } catch (Exception e) { e.printStackTrace(); } }
运行结果:
调用了Student()无参私有构造器
name:张三
age:18
调用私有方法
public static void main(String[] args) { try { //1.首先要创建Student类的实例对象 //1.1创建Student类的反射对象 Class<Student> clazz = Student.class; //1.2获取私有构造器 Constructor<Student> c0 = clazz.getDeclaredConstructor(); //1.3设置访问权限 c0.setAccessible(true); //1.4创建实例对象 Student student = c0.newInstance(); //获取私有属性 name Field name = clazz.getDeclaredField("name"); //设置访问权限 name.setAccessible(true); //为该对象的该属性赋值 name.set(student, "张三"); Field age = clazz.getDeclaredField("age"); age.setAccessible(true); age.set(student, 18); //获取该属性的值:get(实例对象) Object nameVal = name.get(student); System.out.println("name:"+nameVal); Object ageVal = age.get(student); System.out.println("age:"+ageVal); //获取info方法 Method infoMethod = clazz.getDeclaredMethod("info"); //设置访问权限 infoMethod.setAccessible(true); //执行该方法,获取返回值 Object infoRet = infoMethod.invoke(student); //打印返回值 System.out.println(infoRet); //获取look方法 Method lookMethod = clazz.getDeclaredMethod("look", String.class); //设置访问权限 lookMethod.setAccessible(true); //执行该方法,传参 lookMethod.invoke(student, "Hello World"); } catch (Exception e) { e.printStackTrace(); } }
运行结果:
调用了Student()无参私有构造器 name:张三 age:18 调用了info()无参私有方法 Student [name=张三, age=18] 调用了look(String str)参数为String类型的私有方法,参数为:Hello World
小结:通过java反射机制,可以调用任意类中的任意修饰符的属性,方法,构造器
java的反射机制跟封装冲突吗?
一个房子,没有门,没有窗户,但我为了进去,只能穿墙了...
二,通过反射机制灵活调用(多态)
先上一组简单代码
public interface Father { void look(); String see(String str); } public class AAA implements Father{ @Override public void look() { System.out.println("this is AAA look()"); } @Override public String see(String str) { System.out.println("this is AAA see()"); System.out.println("str is " + str); return str; } } public class BBB implements Father{ @Override public void look() { System.out.println("this is BBB look()"); } @Override public String see(String str) { System.out.println("this is BBB see()"); System.out.println("str is " + str); return str; } } public class Test01 { public static void main(String[] args) { Father a = new AAA(); a.look(); String seeA = a.see("Hello"); System.out.println(seeA); System.out.println("=================="); Father b = new BBB(); b.look(); String seeB = b.see("Hello"); System.out.println(seeB); } }
打印结果:
this is AAA look() this is AAA see() str is Hello Hello ================== this is BBB look() this is BBB see() str is Hello
当我们需要在程序运行时,有选择性的调用AAA类或BBB类
即:运行时编译,我们增加一个工厂类
public class Factory { public static Father getInstance(String className) { if("AAA".equals(className)) { return new AAA(); }else if("BBB".equals(className)) { return new BBB(); }else { return null; } } }
在main方法中调用:
public static void main(String[] args) { //可从外部获取(properties文件,数据库,或其他方法返回值) String className = "BBB"; Father a = Factory.getInstance(className); a.look(); String seeA = a.see("Hello"); System.out.println(seeA); }
这样,我们就可以在运行时选择性的创建对象了
这和反射有什么关系呢?
假设,需求变更,要增加一个CCC类,同样实现了Father接口,并也动态调用
需要修改的代码:
//增加一个CCC类,实现Father接口 public class CCC implements Father{ @Override public void look() { System.out.println("this is CCC look()"); } @Override public String see(String str) { System.out.println("this is CCC see()"); System.out.println("str is " + str); return str; } } //修改Factory工厂类,增加CCC类的创建方法 public class Factory { public static Father getInstance(String className) { if("AAA".equals(className)) { return new AAA(); }else if("BBB".equals(className)) { return new BBB(); }else if("CCC".equals(className)){ return new CCC(); }else { return null; } } }
注意:在实际项目中,修改一个已经运行成功没问题的方法,是存在风险的,改完万一出错了呢...(程序员都懂...)
所以,在这里如果使用反射机制,就更加灵活,以下代码举例说明
public class Factory { public static Father getInstance(String className) { Father f = null; // if("AAA".equals(className)) { // return new AAA(); // }else if("BBB".equals(className)) { // return new BBB(); // }else if("CCC".equals(className)){ // return new CCC(); // }else { // return null; // } try { f = (Father)Class.forName(className).newInstance(); } catch (Exception e) { e.printStackTrace(); } return f; } }
public class Test01 { public static void main(String[] args) { //可从外部获取(properties文件,数据库,或其他方法返回值) String className = "com.dream.springboot.test.clazzTest.BBB"; Father f = Factory.getInstance(className); f.look(); String see = f.see("Hello"); System.out.println(see); } }
在Factory工厂类中,使用全类名获取实例,这样避免了增加类时就修改Factory类中的方法,可减少错误,也减少工作量
总结:反射就是当詹姆斯·高斯林给你关了一扇门,又给你打开的一扇窗
代码上传地址:https://download.csdn.net/download/lijian0420/10803158