• Java反射基础


           Java反射机制是Java语言的一个重要特性,在实际生产生活中,Java反射都得到了广泛的应用。例如,在服务器端,往往需要根据客户的请求,动态调用某一个对象的特定方法;此外,在ORM中间件的实现中,运用Java反射机制可以读取任意一个JavaBean的所有属性,或者给这些属性赋值,这都是Java反射的实际应用。

            Java反射机制主要提供了以下功能:

            (1)在运行时判断任意一个对象所属的类

            (2)在运行时构造任意一个类的对象

            (3)在运行时判断任意一个类所具有的成员变量和方法

            (4)在运行时调用任意一个对象的方法

            (5)生成动态代理

            要想实现上述功能,必须要先能获取到一个类的Class对象,以String类为例,一般有三种方法可以获取String类的Class类对象

            (1)通过String类的对象调用对象的getClass()方法

    1. String str = "";
    2. Class clazz = str.getClass();

            (2)通过String类.class来获取Class对象

    Class clazz = String.class;

            (3)通过Class类的静态方法forName()获取,方法内传入String类的完全限定名

    1. try {
    2. Class clazz = Class.forName("Java.lang.String");
    3. } catch (ClassNotFoundException e) {
    4. e.printStackTrace();
    5. }

            得到了Class对象后我们可以使用Java反射技术的一些功能,例如,获取该类的构造方法、继承关系、访问字段、调用方法等等

            (1)首先看如何来得到该类的构造方法,Java的反射API提供了Constructor对象,它包含一个构造方法的所有信息。

    1. public class Main {
    2. public static void main(String[] args) throws Exception {
    3. // 获取构造方法Integer(int):
    4. Constructor cons1 = Integer.class.getConstructor(int.class);
    5. // 调用构造方法:
    6. Integer n1 = (Integer) cons1.newInstance(123);
    7. System.out.println(n1);
    8. // 获取构造方法Integer(String)
    9. Constructor cons2 = Integer.class.getConstructor(String.class);
    10. Integer n2 = (Integer) cons2.newInstance("456");
    11. System.out.println(n2);
    12. }
    13. }

            (2)再看如何获取某个类的继承关系

    1. public class Main {
    2. public static void main(String[] args) throws Exception {
    3. Class i = Integer.class;
    4. Class n = i.getSuperclass();
    5. System.out.println(n);
    6. Class o = n.getSuperclass();
    7. System.out.println(o);
    8. System.out.println(o.getSuperclass());
    9. }
    10. }

            也可以获取这个类继承的接口

    1. public class Main {
    2. public static void main(String[] args) throws Exception {
    3. Class s = Integer.class;
    4. Class[] is = s.getInterfaces();
    5. for (Class i : is) {
    6. System.out.println(i);
    7. }
    8. }
    9. }

            如果一个类没有继承任何接口,那么调用getInterfaces()会得到一个空数组

            (3)同样的,要想获取到一个类的字段,也可以使用下面这些方法

    1. public class Main {
    2. public static void main(String[] args) throws Exception {
    3. Class stdClass = Student.class;
    4. // 获取public字段"score":
    5. System.out.println(stdClass.getField("score"));
    6. // 获取继承的public字段"name":
    7. System.out.println(stdClass.getField("name"));
    8. // 获取private字段"grade":
    9. System.out.println(stdClass.getDeclaredField("grade"));
    10. }
    11. }
    12. class Student extends Person {
    13. public int score;
    14. private int grade;
    15. }
    16. class Person {
    17. public String name;
    18. }

            输出结果

    1. public int Student.score
    2. public java.lang.String Person.name
    3. private int Student.grade

            当我们拿到一个field字段实例时,我们还可以拿到一个实例对应的该字段的值,例如,对于一个Person实例,我们可以先拿到name字段对应的Field,再获取这个实例的name字段值

    1. import java.lang.reflect.Field;
    2. public class Main {
    3. public static void main(String[] args) throws Exception {
    4. Object p = new Person("贝吉塔");
    5. Class c = p.getClass();
    6. Field f = c.getDeclaredField("name");
    7. Object value = f.get(p);
    8. System.out.println(value); // "贝吉塔"
    9. }
    10. }
    11. class Person {
    12. private String name;
    13. public Person(String name) {
    14. this.name = name;
    15. }
    16. }

            既然通过Field实例可以拿到指定实例的字段值,那么也可以设置字段的值,设置字段的值通过Field.set(Object,Object)实现的,其中第一个Object参数是指定的实例,第二个参数是待修改的值。

    1. import java.lang.reflect.Field;
    2. public class Main {
    3. public static void main(String[] args) throws Exception {
    4. Person p = new Person("Xiao Ming");
    5. System.out.println(p.getName()); // "Xiao Ming"
    6. Class c = p.getClass();
    7. Field f = c.getDeclaredField("name");
    8. f.setAccessible(true);
    9. f.set(p, "Xiao Hong");
    10. System.out.println(p.getName()); // "Xiao Hong"
    11. }
    12. }
    13. class Person {
    14. private String name;
    15. public Person(String name) {
    16. this.name = name;
    17. }
    18. public String getName() {
    19. return this.name;
    20. }
    21. }

            (4)我们既然能通过Class实例获取所有的Field对象,同样的,也就可以通过Class实例来获取所有方法(Method类型的对象)

    1. public class Main {
    2. public static void main(String[] args) throws Exception {
    3. Class stdClass = Student.class;
    4. // 获取public方法getScore,参数为String:
    5. System.out.println(stdClass.getMethod("getScore", String.class));
    6. // 获取继承的public方法getName,无参数:
    7. System.out.println(stdClass.getMethod("getName"));
    8. // 获取private方法getGrade,参数为int:
    9. System.out.println(stdClass.getDeclaredMethod("getGrade", int.class));
    10. }
    11. }
    12. class Student extends Person {
    13. public int getScore(String type) {
    14. return 99;
    15. }
    16. private int getGrade(int year) {
    17. return 1;
    18. }
    19. }
    20. class Person {
    21. public String getName() {
    22. return "Person";
    23. }
    24. }

            输出结果

    1. public int Student.getScore(java.lang.String)
    2. public java.lang.String Person.getName()
    3. private int Student.getGrade(int)

            既然我们可以通过反射调用Field对象,那么同样的我们也可以通过反射来调用Method类的对象,即方法。如果我们来调用String类的substring()方法,代码如下

    1. public class Main {
    2. public static void main(String[] args) throws Exception {
    3. // String对象:
    4. String s = "Hello world";
    5. // 获取String substring(int)方法,参数为int:
    6. Method m = String.class.getMethod("substring", int.class);
    7. // 在s对象上调用该方法并获取结果:
    8. String r = (String) m.invoke(s, 6);
    9. // 打印调用结果:
    10. System.out.println(r);
    11. }
    12. }

            同样的我们也可以调用非public方法以及静态方法

            调用非public方法代码如下:

    1. public class Main {
    2. public static void main(String[] args) throws Exception {
    3. Person p = new Person();
    4. Method m = p.getClass().getDeclaredMethod("setName", String.class);
    5. m.setAccessible(true);
    6. m.invoke(p, "Bob");
    7. System.out.println(p.name);
    8. }
    9. }
    10. class Person {
    11. String name;
    12. private void setName(String name) {
    13. this.name = name;
    14. }
    15. }

            调用静态方法代码如下:

            

    1. public class Main {
    2. public static void main(String[] args) throws Exception {
    3. // 获取Integer.parseInt(String)方法,参数为String:
    4. Method m = Integer.class.getMethod("parseInt", String.class);
    5. // 调用该静态方法并获取结果:
    6. Integer n = (Integer) m.invoke(null, "12345");
    7. // 打印调用结果:
    8. System.out.println(n);
    9. }
    10. }

    Java的反射虽然可以对于一些功能可以进行很好的展开,但是,反射它违反了Java三大特性中的封装性,但是不会影响Java的多态性。在我们调用方法时它仍会遵循多态原则。

  • 相关阅读:
    函数式接口概述、作为方法的参数、作为方法的返回值及函数式接口Supplier介绍
    关于 Flink 状态与容错机制
    大二Web课程设计——张家界旅游网站设计与实现(HTML+CSS+JavaScript)
    宝塔部署nginx遇到的400错误和502错误
    google cloud DevOps Engineer
    如何设计静态资源缓存方案
    KMP算法(详解加图解)
    驼峰式与下划线命名规则
    el-select 远程搜索下,添加下拉箭头
    子不语IPO下限定价:预计2022年全年净利润下滑,华丙如为实控人
  • 原文地址:https://blog.csdn.net/qq_17845335/article/details/126165061