反射的概念:
要想解剖一个类,必须先要获取到该类的字节码文件对象。而解剖使用的就是Class类中的方法.所以先要获取到每一个字节码文件对应的Class类型的对象.
反射就是把java类中的各种成分映射成一个个的Java对象
例如:一个类有:成员变量、方法、构造方法、包等等信息,利用反射技术可以对一个类进行解剖,把个个组成部分映射成一个个对象。(其实:一个类中这些成员方法、构造方法、在加入类中都有一个类来描述)
Class反射类一直是Java中较为重要的一部分,说深入了解不敢说,基础的操作其他类的公有属性/方法,私有的属性/方法做个操作,什么是反射类,请参考其他更为详细的文章。
首先创建一个类,Student类:
public class Student { private String name; private String sex; private int age; public String getName() { return name; } public void setName(String name) { this.name = name; } public String getSex() { return sex; } public void setSex(String sex) { this.sex = sex; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } public void run(){ System.out.println("奔跑"); } private String say(String str){ return "他说"+str; } @Override public String toString() { return "Student{" + "name='" + name + '\'' + ", sex='" + sex + '\'' + ", age=" + age + '}'; } }在这个类中,有三个私有的属性,一个无返回值无参的方法,一个有返回值有参的方法,现在要利用Class反射类要做一个操作,获取这个Student类中的属性并赋值,调用这个类的方法
介绍一下获取Class对象的三种方式:
1、 Object ——> getClass();(封装时常用)
2、 任何数据类型(包括基本数据类型)都有一个“静态”的class属性
3、 通过Class类的静态方法:forName(String className)(常用)
第一种:
Student student = new Student();//这一new 产生一个Student对象,一个Class对象。 Class cls = student.getClass();//获取Class对象第二种:
Class sls = Student.class;
第三种:
Class sls = Class.forName("com.uhope.uip.item.constants.Student");第三种就是类所在包的完整路径
三种写法,看自己的需求来写,我这里要介绍的是第一种,因为我对这个反射类进行了一次的封装,封装成工具类方便调用。
创建好了Student类以后,创建一个测试类来进行测试一下看看效果:
在不知道类名的情况下,不把代码写死,想要写的灵活点,那就让使用这个工具类的人自己传一个类进来,那方法中的参数就放一个Object,Object是所有类的父类,所以都是没问题的:
public static void ClassUtil(Object obj){ Class cls = obj.getClass(); }这样就获取到了别人传入的类
接着要操作类的方法,使用Method:
public static void ClassUtil(Object obj){ try { Class cls = obj.getClass(); Method method = cls.getMethod("setName",String.class); method.invoke(obj,"测试名称"); System.out.println(obj); } catch (NoSuchMethodException e) { e.printStackTrace(); } catch (IllegalAccessException e) { e.printStackTrace(); } catch (InvocationTargetException e) { e.printStackTrace(); } }这是操作公有方法的写法,接下来是操作私有方法的写法:
public static void ClassUtil(Object obj){ try { Class cls = obj.getClass(); Method method = cls.getDeclaredMethod("say",String.class); method.setAccessible(true); method.invoke(obj, "哈喽"); } catch (NoSuchMethodException e) { e.printStackTrace(); } catch (IllegalAccessException e) { e.printStackTrace(); } catch (InvocationTargetException e) { e.printStackTrace(); } }say是private封装的方法,setName是公有方法,操作私有的,需要 setAccessible(true) 来解除私有状态,还有一个不一样的是需要getDeclaredMethod而不是getMethod方法了,getDeclaredMethod是为了私有方法准备的,由于say是有返回值的方法,这里就不需要System.out.println了,在打印中就会展示出来
接下来是操作Student类中的run方法,run是一个无返回值无参的方法,但是里面写好了内容,这样的话只需要如下代码:
public static void ClassUtil(Object obj){ try { Class cls = obj.getClass(); Method method = cls.getMethod("run"); method.invoke(obj); } catch (NoSuchMethodException e) { e.printStackTrace(); } catch (IllegalAccessException e) { e.printStackTrace(); } catch (InvocationTargetException e) { e.printStackTrace(); } }三种操作方法的演示好了,需要注意的就是getDeclaredMethod和getMethod方法的使用区别,以及有无参数的区别,有参数的话要多写两个地方,一个是Method的第二个参数类型,一个是invoke的第二个指定的值。
接下来看调用私有的属性(一般项目中都是私有封装的,所以就讲解一下私有操作),调用方法是Method,调用属性是用Field:
public static void ClassUtil(Object obj){ try { Class cls = obj.getClass(); Field field = cls.getDeclaredField("name"); field.setAccessible(true); field.set(obj,"测试名称"); System.out.println(field.get(obj)); }catch (IllegalAccessException e) { e.printStackTrace(); }catch (NoSuchFieldException e) { e.printStackTrace(); } }和获取私有方法相同,都有一个getDeclared,也需要解除私有状态,通过Field中的set方法,将要给与的值赋进去,查看有没有赋值成功,就用Field的get方法即可,也可以通过Method获取setName方法,将参数写进去,再用Field获取值:
public static void ClassUtil(Object obj){ try { Class cls = obj.getClass(); Method method = cls.getMethod("setName",String.class); method.invoke(obj,"Method测试名称"); Field field = cls.getDeclaredField("name"); field.setAccessible(true); System.out.println(field.get(obj)); } catch (NoSuchFieldException e) { e.printStackTrace(); } catch (IllegalAccessException e) { e.printStackTrace(); } catch (NoSuchMethodException e) { e.printStackTrace(); } catch (InvocationTargetException e) { e.printStackTrace(); } }