反射机制

    通过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. /**
    2. * 获取字节码的三种方式
    3. * 1、Class.forName("全类名")
    4. * 2、对象.getClass();
    5. * 3、类型.class
    6. *
    7. * 反射机制的概念:在类加载的时候动态获取类的信息,也可以通过反射去操作类的方法、属性等。
    8. * 还可以通过反射去创建对象
    9. *
    10. */
    11. public class Reflect01 {
    12. public static void main(String[] args) {
    13. try {
    14. //获取字节码文件(加载字节码或者说使得类加载)
    15. Class threadClass1 = Class.forName("java.lang.Thread");
    16. //对象.getClass();
    17. Thread thread = new Thread();
    18. Class threadClass2 = thread.getClass();
    19. //类型.class
    20. Class threadClass3 = Thread.class;
    21. System.out.println("获取的字节码对象为"+threadClass1);
    22. System.out.println("获取的字节码对象为"+threadClass2);
    23. System.out.println("获取的字节码对象为"+threadClass3);
    24. System.out.println(threadClass1 == threadClass2);
    25. } catch (ClassNotFoundException e) {
    26. e.printStackTrace();
    27. }
    28. }
    29. }

    二、具体操作

    1、通过反射机制访问对象的某个属性。

    1. public class Reflect02 {
    2. public static void main(String[] args) {
    3. try {
    4. //通过反射获取user类的字节码
    5. Class userClass = Class.forName("com.jy.pojo.User");
    6. //通过反射创建user对象 newInstance() 这个方法在底层会调用无参构造
    7. //强转后就可以操作属性
    8. User o = (User)userClass.newInstance();
    9. System.out.println(o);
    10. //通过字节码直接获取属性 这个方法只能获取公共的属性
    11. Field sex = userClass.getField("sex");
    12. Field id1 = userClass.getDeclaredField("id");
    13. //获取属性的类型
    14. System.out.println(sex.getType());
    15. //获取属性的访问权限修饰符 sex.getModifiers() 返回的是当前属性所对应的修饰符代号
    16. int modifiers = sex.getModifiers();
    17. //将修饰符代号转换成修饰符
    18. String s = Modifier.toString(modifiers);
    19. System.out.println(s);
    20. //getDeclaredField() 可以获取所有属性
    21. Field id = userClass.getDeclaredField("id");
    22. System.out.println(id.getType());
    23. //获取所有属性
    24. Field[] declaredFields = userClass.getDeclaredFields();
    25. for (Field declaredField : declaredFields) {
    26. System.out.print("属性名称为"+declaredField.getName());
    27. System.out.print("属性类型为"+declaredField.getType());
    28. System.out.println("属性修饰符为"+Modifier.toString(declaredField.getModifiers()));
    29. }
    30. } catch (ClassNotFoundException | NoSuchFieldException e) {
    31. e.printStackTrace();
    32. }
    33. }
    34. }

    2、通过反射机制调用对象的某个方法。

    1. /**
    2. * 反射获取方法
    3. */
    4. public class Reflect03 {
    5. public static void main(String[] args) {
    6. //获取字节码对象
    7. Class studentClass = Student.class;
    8. //获取方法
    9. try {
    10. //不传参数,即无参方法
    11. Method study1 = studentClass.getDeclaredMethod("study");
    12. System.out.println(study1.getName());
    13. //获取所有类型的方法 getDeclaredMethod()方法后面的是参数类型.class,是可变长参数
    14. Method study = studentClass.getDeclaredMethod("study",int.class);
    15. //获取方法的返回值类型
    16. Class returnType = study.getReturnType();
    17. //returnType.getName() 获得的是全类名 returnType.getSimpleName() 获得简类名
    18. System.out.println("方法的返回值类型为"+returnType.getSimpleName());
    19. //获取方法的修饰符
    20. int modifiers = study.getModifiers();
    21. //将代号转为修饰符
    22. String s = Modifier.toString(modifiers);
    23. System.out.println("方法的修饰符为"+s);
    24. //获取方法所有形参的类型
    25. Class<?>[] parameterTypes = study.getParameterTypes();
    26. for (Class<?> parameterType : parameterTypes) {
    27. System.out.println(parameterType);
    28. }
    29. } catch (NoSuchMethodException e) {
    30. e.printStackTrace();
    31. }
    32. }
    33. }

    PS:可变长参数

    1. /**
    2. * 可变长参数 ...只能是三个点,实质上传的是数组
    3. * 只能出现在参数列表的末尾,可传可不传
    4. */
    5. public class Test01 {
    6. public static void main(String[] args) {
    7. Test01 test01 = new Test01();
    8. test01.study("a","b","c");
    9. }
    10. public void study(String...arg){
    11. for (String s : arg) {
    12. System.out.println(s);
    13. }
    14. }
    15. }

    3、通过反射机制获取某个构造方法实例化对象。

    1. /**
    2. * 通过反射获取一个类的构造
    3. */
    4. public class Reflect04 {
    5. public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException {
    6. //获取字节码对象
    7. Class aClass = Class.forName("com.jy.pojo.Student");
    8. //获取公共的空参构造
    9. //Constructor constructor = aClass.getConstructor();
    10. //获取公共的有参构造
    11. //Constructor constructor = aClass.getConstructor(int.class, String.class, int.class);
    12. //获取所有构造方法
    13. //Constructor[] constructors = aClass.getConstructors();
    14. //获取空参构造
    15. Constructor declaredConstructor1 = aClass.getDeclaredConstructor();
    16. //获取有参构造
    17. Constructor declaredConstructor2 = aClass.getDeclaredConstructor(int.class, String.class, int.class);
    18. //获取参数列表
    19. Class[] parameterTypes = declaredConstructor2.getParameterTypes();
    20. for (Class parameterType : parameterTypes) {
    21. System.out.println(parameterType);
    22. }
    23. //获取修饰符
    24. System.out.println(Modifier.toString(declaredConstructor2.getModifiers()));
    25. }
    26. }

    4、如何通过反射给属性赋值
    思考:如果通过反射给私有属性赋值的话,需要调用set方法吗?

    答:不需要,直接打破封装!!这个算是反射的缺点。

    1. package com.jy.reflect;
    2. import java.lang.reflect.Field;
    3. /**
    4. * 如何通过反射给属性赋值
    5. * 1、如果通过反射给私有属性赋值的话,需要调用set方法吗?
    6. * 不需要,直接打破封装!!这个算是反射的缺点
    7. */
    8. public class Reflect06 {
    9. public static void main(String[] args) throws ClassNotFoundException, IllegalAccessException, InstantiationException, NoSuchFieldException {
    10. //获取字节码对象
    11. Class aClass = Class.forName("com.jy.pojo.Student");
    12. //必须通过反射先创建对象
    13. Object obj = aClass.newInstance();
    14. //先要获取被赋值的属性
    15. Field sex = aClass.getDeclaredField("sex");
    16. //给属性赋值 obj 赋值属性所属对象 value 具体要赋的值
    17. sex.set(obj,true);
    18. //获取属性值
    19. Object o = sex.get(obj);
    20. System.out.println(o);
    21. /**
    22. * 操作私有属性
    23. */
    24. Field name = aClass.getDeclaredField("name");
    25. /*如果需要给私有属性赋值,需要打破封装,setAccessible(true)是用于打破封装的方法*/
    26. name.setAccessible(true);
    27. name.set(obj,"韩信");
    28. Object o1 = name.get(obj);
    29. System.out.println(o1);
    30. }
    31. }

    5、如何通过反射调用方法(比较重要)

    1. /**
    2. * 如何通过反射调用方法(比较重要 动态代理)
    3. */
    4. public class Reflect07 {
    5. public static void main(String[] args) {
    6. try {
    7. //获取字节码对象
    8. Class userClass = User.class;
    9. //创建对象
    10. Object o = userClass.newInstance();
    11. //获取需要被调用的方法
    12. Method login = userClass.getDeclaredMethod("login", String.class, String.class);
    13. //打破封装后,可以调用私有方法
    14. login.setAccessible(true);
    15. //调用方法 login.invoke()通过反射调用普通方法的方法
    16. //o 方法所属对象
    17. //args 需要传的参数
    18. //返回值指的就是方法的返回值
    19. Object shizi = login.invoke(o, "柿子", "123456");
    20. System.out.println(shizi);
    21. } catch (Exception e) {
    22. e.printStackTrace();
    23. }
    24. }
    25. }

    6、通过反射调用构造方法及获取一个类的父类和接口

    1. /**
    2. * 通过反射调用构造方法
    3. */
    4. public class Reflect08 {
    5. public static void main(String[] args) {
    6. try {
    7. //获取字节码对象
    8. Class aClass = Class.forName("com.jy.pojo.User");
    9. //这种写法也是调用空参构造,如果没有无参构造就会报错
    10. //Object o1 = aClass.newInstance();
    11. //调用空参构造
    12. Constructor declaredConstructor = aClass.getDeclaredConstructor();
    13. Object o = declaredConstructor.newInstance();
    14. Constructor declaredConstructor1 = aClass.getDeclaredConstructor(int.class, String.class, boolean.class, int.class);
    15. Object o1 = declaredConstructor1.newInstance(1, "asf", true, 2);
    16. if(o1 instanceof User){
    17. User user = (User)o1;
    18. System.out.println(user);
    19. }
    20. //获取一个类的父类
    21. Class superclass = aClass.getSuperclass();
    22. System.out.println(superclass);
    23. //获取一个类的所有接口
    24. Class[] interfaces = aClass.getInterfaces();
    25. for (Class anInterface : interfaces) {
    26. System.out.println(anInterface);
    27. }
    28. } catch (Exception e) {
    29. e.printStackTrace();
    30. }
    31. }
    32. }