反射机制
通过Java中的反射机制可以操作字节码文件。
可以在类加载(classloader )的时候动态获取类的信息(字节码)
java.lang.Class:代表整个字节码,代表一个类型,代表整个类。
java.lang.reflect. Method:代表字节码中的方法字节码,代表类中的方法。
java.lang.reflect.Constructor:代表字节码中的构造方法字节码,代表类中的构造方法
java.lang.reflect.Field:代表字节码中的属性字节码,代表类中的成员变量(静态变量+实例变量)。
反射的好处:可以通过操作字节码的方式侧面的去操作类,会使得程序的灵活性和可扩展性增强!!
- 双亲委派机制(可能被面试官问到):在类加载的时候, 实际上jvm调用类加载器classloader去加载类
- 双亲:jdk提供的包 rat, reft
- 类加载的时候,首先会去这两个包下面去找对应的字节码文件,如果双亲包找不到就去classpath下面找。
反射的用途:最重要的用途就是开发各种框架,比如在Spring中,我们将所有的类Bean 交给Spring容器管理,无论是XML配置Bean还是注入配置,当我们从容器中获取Bean来依赖注入时,容器会读取配置,而配置中给的就是类的信息,Spring根据这些信息,需要创建那些Bean,Spring就动态的创建这些类。还有在Struts2的struts.xml中配置action,也是通过反射调用的action。
一、获取Class(字节码)的三种方式
1)Class c = Class.forName(“完整类名”);
2)Class c = 对象.getClass();
3)Class c = String.class;(String指的是类型,可以是int.class,double.class等等)
PS:Java中所以的属性类型都有一个隐式属性class。
/*** 获取字节码的三种方式* 1、Class.forName("全类名")* 2、对象.getClass();* 3、类型.class** 反射机制的概念:在类加载的时候动态获取类的信息,也可以通过反射去操作类的方法、属性等。* 还可以通过反射去创建对象**/public class Reflect01 {public static void main(String[] args) {try {//获取字节码文件(加载字节码或者说使得类加载)Class threadClass1 = Class.forName("java.lang.Thread");//对象.getClass();Thread thread = new Thread();Class threadClass2 = thread.getClass();//类型.classClass threadClass3 = Thread.class;System.out.println("获取的字节码对象为"+threadClass1);System.out.println("获取的字节码对象为"+threadClass2);System.out.println("获取的字节码对象为"+threadClass3);System.out.println(threadClass1 == threadClass2);} catch (ClassNotFoundException e) {e.printStackTrace();}}}
二、具体操作
1、通过反射机制访问对象的某个属性。
public class Reflect02 {public static void main(String[] args) {try {//通过反射获取user类的字节码Class userClass = Class.forName("com.jy.pojo.User");//通过反射创建user对象 newInstance() 这个方法在底层会调用无参构造//强转后就可以操作属性User o = (User)userClass.newInstance();System.out.println(o);//通过字节码直接获取属性 这个方法只能获取公共的属性Field sex = userClass.getField("sex");Field id1 = userClass.getDeclaredField("id");//获取属性的类型System.out.println(sex.getType());//获取属性的访问权限修饰符 sex.getModifiers() 返回的是当前属性所对应的修饰符代号int modifiers = sex.getModifiers();//将修饰符代号转换成修饰符String s = Modifier.toString(modifiers);System.out.println(s);//getDeclaredField() 可以获取所有属性Field id = userClass.getDeclaredField("id");System.out.println(id.getType());//获取所有属性Field[] declaredFields = userClass.getDeclaredFields();for (Field declaredField : declaredFields) {System.out.print("属性名称为"+declaredField.getName());System.out.print("属性类型为"+declaredField.getType());System.out.println("属性修饰符为"+Modifier.toString(declaredField.getModifiers()));}} catch (ClassNotFoundException | NoSuchFieldException e) {e.printStackTrace();}}}
2、通过反射机制调用对象的某个方法。
/*** 反射获取方法*/public class Reflect03 {public static void main(String[] args) {//获取字节码对象Class studentClass = Student.class;//获取方法try {//不传参数,即无参方法Method study1 = studentClass.getDeclaredMethod("study");System.out.println(study1.getName());//获取所有类型的方法 getDeclaredMethod()方法后面的是参数类型.class,是可变长参数Method study = studentClass.getDeclaredMethod("study",int.class);//获取方法的返回值类型Class returnType = study.getReturnType();//returnType.getName() 获得的是全类名 returnType.getSimpleName() 获得简类名System.out.println("方法的返回值类型为"+returnType.getSimpleName());//获取方法的修饰符int modifiers = study.getModifiers();//将代号转为修饰符String s = Modifier.toString(modifiers);System.out.println("方法的修饰符为"+s);//获取方法所有形参的类型Class<?>[] parameterTypes = study.getParameterTypes();for (Class<?> parameterType : parameterTypes) {System.out.println(parameterType);}} catch (NoSuchMethodException e) {e.printStackTrace();}}}
PS:可变长参数
/*** 可变长参数 ...只能是三个点,实质上传的是数组* 只能出现在参数列表的末尾,可传可不传*/public class Test01 {public static void main(String[] args) {Test01 test01 = new Test01();test01.study("a","b","c");}public void study(String...arg){for (String s : arg) {System.out.println(s);}}}
3、通过反射机制获取某个构造方法实例化对象。
/*** 通过反射获取一个类的构造*/public class Reflect04 {public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException {//获取字节码对象Class aClass = Class.forName("com.jy.pojo.Student");//获取公共的空参构造//Constructor constructor = aClass.getConstructor();//获取公共的有参构造//Constructor constructor = aClass.getConstructor(int.class, String.class, int.class);//获取所有构造方法//Constructor[] constructors = aClass.getConstructors();//获取空参构造Constructor declaredConstructor1 = aClass.getDeclaredConstructor();//获取有参构造Constructor declaredConstructor2 = aClass.getDeclaredConstructor(int.class, String.class, int.class);//获取参数列表Class[] parameterTypes = declaredConstructor2.getParameterTypes();for (Class parameterType : parameterTypes) {System.out.println(parameterType);}//获取修饰符System.out.println(Modifier.toString(declaredConstructor2.getModifiers()));}}
4、如何通过反射给属性赋值
思考:如果通过反射给私有属性赋值的话,需要调用set方法吗?
答:不需要,直接打破封装!!这个算是反射的缺点。
package com.jy.reflect;import java.lang.reflect.Field;/*** 如何通过反射给属性赋值* 1、如果通过反射给私有属性赋值的话,需要调用set方法吗?* 不需要,直接打破封装!!这个算是反射的缺点*/public class Reflect06 {public static void main(String[] args) throws ClassNotFoundException, IllegalAccessException, InstantiationException, NoSuchFieldException {//获取字节码对象Class aClass = Class.forName("com.jy.pojo.Student");//必须通过反射先创建对象Object obj = aClass.newInstance();//先要获取被赋值的属性Field sex = aClass.getDeclaredField("sex");//给属性赋值 obj 赋值属性所属对象 value 具体要赋的值sex.set(obj,true);//获取属性值Object o = sex.get(obj);System.out.println(o);/*** 操作私有属性*/Field name = aClass.getDeclaredField("name");/*如果需要给私有属性赋值,需要打破封装,setAccessible(true)是用于打破封装的方法*/name.setAccessible(true);name.set(obj,"韩信");Object o1 = name.get(obj);System.out.println(o1);}}
5、如何通过反射调用方法(比较重要)
/*** 如何通过反射调用方法(比较重要 动态代理)*/public class Reflect07 {public static void main(String[] args) {try {//获取字节码对象Class userClass = User.class;//创建对象Object o = userClass.newInstance();//获取需要被调用的方法Method login = userClass.getDeclaredMethod("login", String.class, String.class);//打破封装后,可以调用私有方法login.setAccessible(true);//调用方法 login.invoke()通过反射调用普通方法的方法//o 方法所属对象//args 需要传的参数//返回值指的就是方法的返回值Object shizi = login.invoke(o, "柿子", "123456");System.out.println(shizi);} catch (Exception e) {e.printStackTrace();}}}
6、通过反射调用构造方法及获取一个类的父类和接口
/*** 通过反射调用构造方法*/public class Reflect08 {public static void main(String[] args) {try {//获取字节码对象Class aClass = Class.forName("com.jy.pojo.User");//这种写法也是调用空参构造,如果没有无参构造就会报错//Object o1 = aClass.newInstance();//调用空参构造Constructor declaredConstructor = aClass.getDeclaredConstructor();Object o = declaredConstructor.newInstance();Constructor declaredConstructor1 = aClass.getDeclaredConstructor(int.class, String.class, boolean.class, int.class);Object o1 = declaredConstructor1.newInstance(1, "asf", true, 2);if(o1 instanceof User){User user = (User)o1;System.out.println(user);}//获取一个类的父类Class superclass = aClass.getSuperclass();System.out.println(superclass);//获取一个类的所有接口Class[] interfaces = aClass.getInterfaces();for (Class anInterface : interfaces) {System.out.println(anInterface);}} catch (Exception e) {e.printStackTrace();}}}
