1、认识反射
“反”,有反就有“正”。正常情况先有类,再产生对象。所谓的反就是可以利用对象找到对象的出处:
在Object类里面提供有一个方法:取得Class对象:
public final Class getClass();
可以输出类的完整名称,就找到了对象的出处。
Class类对象的实例化:
java.lang.Class是一个类。这个类是反射操作的源头,所有的反射都要从此类开始进行。最关键的是这个类有三种实例化方式。
1、调用Object类中的getClass()方法:Class cls = mIntent.getClass();需要实例化对象,需要import导入类
2、使用“类.class”取得:Class cls = Intent.class。不需要实例化对象,需要import导入类
3、调用Class类提供的方法:Class.forName(String className)。不需要import语句导入一个明确的类
反射实例化对象。
当拿到一个类的时候,肯定用关键字new进行对象的实例化操作,但是如果有了Class类对象后,就可以不用New关键字也可以进行对象的实例化操作:
public T newInstance()相当于使用new调用无参构造函数。
有了反射之后,进行实例化的操作不再只是单单依靠关键字new完成了。反射也可以。
所以用反射获取一个对象实例化的步骤为:
1、先获取Class类的实例化对象:Class cls = Class.forName(“xxxxxx”);
2、用Class类的实例化对象获取制定类的实例化对象:Book book=(Book)cls.newInstance();
但是本来用关键字new一行代码就可以完成实例化操作,反射需要两步,这样好吗?
2、理解反射的作用
在任何开发中,一起的耦合都起源于new。
看工程模式。如果想要扩展,就必须改动工厂类中的if else。如果一直扩展,就需要一直修改工厂类。因为工厂类中是通过New产生对象实例的,所以New就是问题的关键。
如果工厂中用反射代替new,就不需要if else和new实例化每个if else中的对象。只需要传入类的完整名称,就可以解耦和。扩展性非常的强!
3、利用反射调用类的结构
a、使用反射调用构造:
之前所说的newInstance()方法实际上等于调用了无参构造函数,但是实际中可能么有无参构造函数,
Class中有方法可以取到构造:public Constructor[]getConstructors():取得全部构造。
和:public Constructor getConstructor(Class… paramterTypes):取得一个指定参数顺序的构造函数。
Constructor类是java.lang.reflect,这时候真正到了反射中。
实例化对象方法public T newInstance(Object… initargs)
所以用反射获取一个没有无参构造函数的类的实例化对象步骤为:
1、先获取Class类对象:Class cls = Class.forName(“xxxxx”);
2、获取指定参数类型顺序的构造函数:Constructor con = cls.getConstructor(Sring.class,double.class);
3、使用获取到的构造函数实例化对象:Object obj = con.newInstance(“第一个参数字符串类型”,10086.8);
所以建议,不管有多少个构造方法,都尽量提供一个无参构造函数,不然太麻烦了。
b、反射调用方法:
Class类中提供了一下方法用来获取类的方法:
public Method[]getMethods()
public Method getMethod(String methodName,Class… paramterTypes)
Method类似java.lang.reflect包下的,其中有个
public Object invoke(Object obj,Object… args)方法。
所以用反射调用方法的步骤为:
1、先获取Class对象:Class cls = Class.forName(“xxxxx”);
并获取对象Object object = cls.newInstance();//必须给出实例化对象
2、获取指定方法:Method setTitleMethod = cls.getMethod(“setTitle”,String.class);
3、调用方法:setTitleMethod.invoke(object,“一本书的标题”);//等价于Book对象.setTitle(“一本书的标题”)
但是这个过程中完全没有出现过Book。也就是说,利用反射可以实现任意类的制定方法的调用。
c、反射调用成员:
类中的属性一定要在本类实例化对象产生后才可以分配内存空间。
Class类中提供了取得成员的方法:
1、取得全部成员:public Field[]getDeclaredFields()
2、取得指定成员:public Field getDeclaredField(String fieldName)
Field是java.lang.reflect包下。其中有:
1、取得属性内容:public Object get(Object obj);
2、设置属性内容:public void set(Object obj,Object value)
所以反射调用成员的步骤为:
假设一个Book类,里面只有一个属性private String title;没有getter/setter方法。
1、先获取Class对象:Class cls = Class.forName(“xxx”);
2、获取对象:Object obj = cls.newInstance();
3、获取指定成员:Field titleField = cls.getDeclaredField(“title”);
4、设置属性内容:titleField.set(obj,“书的名字”);//相当于:Book类对象.title =“书的名字“
但是调用get还是会报错。因为封装性。
这时候需要用到:AccessbleObject
在java.lang.reflect.AccessibleObject类下面(JDK1.8修改):
~ Executable//可执行的
~ Constrictor
~Method
~Field
在这个类中有一个方法:
public void setAccessible(boolean flag)设置是否封装,
设置为false后,就是取消封装,这个时候再调用刚才的get就可以正常调用了。
构造方法和普通方法一样可以取消封装,只不过很少这样去做,而且对属性的访问还是应该是getter和setter方法完成。
学习完这些,反射算是入门了。