反射–框架设计的灵魂
反射:将类的各个组成部分封装为其他对象----反射机制
好处:(反射面前无隐私private,下面暴力反射会说)
1.可以在程序运行过程中操作这些对象
2.可以解耦,提高程序的可扩展性
上图就是把Person类中的组成部分-- 成员变量封装成 Fleld对象,构造方法封装成Constructor对象,把成员方法封装成Method对象
==================
声明:用到的Dog类结构
public class Dog {
//构造方法
public Dog(String name){
this.a = name;
this.name = name;
}
protected Dog(int age){
this.age = age;
}
Dog(double price){}
protected Dog(){}
//成员变量
private String name;
private int age;
public String a;
protected String b;
String c;
private String d;
//成员方法
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public void method1(){
System.out.println("干饭中.......");
}
protected int method2(int num){
return num + 3;
}
void method3(){}
private void method4(){}
@Override
public String toString() {
return "Person{ name == "+ name + " " + "age == " + age + " }";
}
}
一.获取成员变量
获取到成员变量后:
1.可赋值:public void set(Object obj, Object value)
说明:因为反射获取到的是某类中的成员变量,所以要set给对应类的对象,因此需要new一个对象
注意:
不能为private修饰的成员变量设值,否则报错。
2.可获取值:public Object get(Object obj)
说明:因为反射获取到的是某类中的成员变量,所以要get到的是对应类的对象的成员变量,
因此需要new一个对象
由于get返回值是Object对象,所以有时候还需要强转类型
//输出name2;
注意:
不可以获取private修饰的成员变量的值,会报错
若想不受访问权限修饰符的影响,可以忽略访问权限修饰符的安全检查----也叫暴力反射
只需添加代码:
name1.setAccessible(true);
添加完setAccessible(true)之后就可以正常访问了,可以使用上面的set和get方法去操作private修饰的对象了
======================================================================================
二.获取构造方法:记住获取构造方法的作用就是用来构造对象
注意:
1.暴力反射是在获取到private对象之后才使用的,用处是不受访问权限符的使用
所以要获取private修饰的构造方法还是需要
public Constructor getDeclaredConstructor(Class<?>… parameterTypes)方法
2.可以使用默认的 xx.class.newInstance来构造无参对象(简便作用)
但有一点要注意的是,这样使用的前提是该无参构造方法不能是private修饰的,否则报错
三.获取成员方法,获取成员方法后利用invoke去操作该方法,以及可以使用setAccessible
public class ReflectDemo3 {
public static void main(String[] args) {
Class<Dog> dogClass = Dog.class;
Method[] methods1 = dogClass.getMethods();//获取所有的pubilc成员方法都打印出来,包括系统隐藏的public默认方法
for(Method method : methods1){
System.out.println(method);
}
System.out.println("===========================");
Method[] methods2 = dogClass.getDeclaredMethods();//获取所有的成员方法,不包括系统的默认方法
for(Method method : methods2){
System.out.println(method);
}
}
}
运行结果:
获取后使用public Object invoke(Object obj, Object… args)方法去执行
Method method2 = dogClass.getDeclaredMethod("method2", int.class);
//参数1是方法名,参数2是具体的参数类型的class对象
System.out.println(method2.getName());
Integer num = (Integer) method2.invoke(new Dog(), 100);//有返回值类型的方法
System.out.println(num);
dogClass.getDeclaredMethod("method1").invoke(new Dog());
运行结果:
四.案例:写一个”框架“,可以帮我们创建任意类的对象,并且执行其中任意方法(要求不修改代码)
实现:
1.配置文件
2.反射
步骤:
1.将需要创建的对象的全类名和需要执行的方法定义在配置文件中
2.在程序中加载读取配置文件
3.使用反射技术来加载类文件进内存
4.创建对象
5.执行方法
=====================================
//框架类
public class ReflectDemo4 {
public static void main(String[] args) throws Exception {
//1.加载配置文件
//1.1创建Properties对象
Properties pro = new Properties();
//1.2j加载配置文件,转换为一个集合
//1.2.1获取class目录下的配置文件
ClassLoader loader = ReflectDemo4.class.getClassLoader();
InputStream in = loader.getResourceAsStream("pro.properties");
pro.load(in);
//2.获取配置文件中定义的数据
String className = pro.getProperty("className");
String methodName1 = pro.getProperty("methodName1");
String methodName2 = pro.getProperty("methodName2");
//3.加载该类j进内存
Class cls = Class.forName(className);
//4.创建对象
Object obj = cls.newInstance();
//5.获取对象方法
Method sleep = cls.getMethod("sleep");
//6.执行
sleep.invoke(obj);
//7.有private修饰的,使用跟反射同理
Method eat = cls.getDeclaredMethod("eat");
eat.setAccessible(true);
eat.invoke(obj);
}
}
className=com.java.fangshe.Cat
methodName1=sleep
methodName2=eat
public class Cat {
public void sleep(){
System.out.println("Sleeping.....");
}
private void eat(){
System.out.println("Eatting.....");
}
}
运行结果:
好处:就是可以任意去调用某个方法,在以后代码量大的时候,我们只需修改配置文件,就不需要修改代码了。 (PS:修改代码的坏处:由于一些设计模式不够好的时候会存在类的耦合,所以修改一处代码可能会影响多处代码,会变得十分繁琐) 而直接修改配置文件就显得方便多,又不存在耦合问题
=======================================================================================
五.获取Class对象的方式:
1.Class.forName(“全类名”):将字节码文件加载进内存,返回Class对象
大多用于配置文件,将类名定义在配置文件中。读取文件后加载类
2.类目.class:通过类名的属性class获取
大多用于参数的传递
3.对象.getClass:getClass()方法在Object类中定义着
大多用于对象获取字节码的方式
public class GetClassDemo {
public static void main(String[] args) throws ClassNotFoundException {
//1.Class.forName("全类名")
Class cls1 = Class.forName("com.java.fangshe.Cat");
System.out.println(cls1);
//2.类目.class
Class cls2 = Cat.class;
System.out.println(cls2);
//.对象.getClass()
Cat cat = new Cat();
Class cls3 = cat.getClass();
System.out.println(cls3);
//比较三个对象是否为同一对象
System.out.println(cls1==cls2); //true
System.out.println(cls1==cls3); //true
}
}
运行结果: