Educoder--9.1 Java反射基础

 第1关:3种方式获取类class字节码对象

任务描述

本关任务:编写一个3种反射方法获取相应对象。

相关知识

1.1 什么是 Java 的反射

Java 反射是可以让我们在运行时获取类的函数、属性、父类、接口等 Class 内部信息的机制。通过反射还可以让我们在运行期实例化对象,调用方法,通过调用 get/set 方法获取变量的值,即使方法或属性是私有的的也可以通过反射的形式调用,这种“看透 class”的能力被称为内省,这种能力在框架开发中尤为重要。 有些情况下,我们要使用的类在运行时才会确定,这个时候我们不能在编译期就使用它,因此只能通过反射的形式来使用在运行时才存在的类(该类符合某种特定的规范,例如 JDBC),这是反射用得比较多的场景。

还有一个比较常见的场景就是编译时我们对于类的内部信息不可知,必须得到运行时才能获取类的具体信息。比如 ORM 框架,在运行时才能够获取类中的各个属性,然后通过反射的形式获取其属性名和值,存入数据库。这也是反射比较经典应用场景之一。

1.2 Class 类

那么既然反射是操作 Class 信息的,Class 又是什么呢?

当我们编写完一个 Java 项目之后,所有的 Java 文件都会被编译成一个.class 文件,这些 Class 对象承载了这个类型的父类、接口、构造函数、方法、属性等原始信息,这些 class 文件在程序运行时会被 ClassLoader 加载到虚拟机中。当一个类被加载以后,Java 虚拟机就会在内存中自动产生一个 Class 对象。我们通过 new 的形式创建对象实际上就是通过这些 Class 来创建,只是这个过程对于我们是不透明的而已。

下面的章节中我们会为大家演示反射的一些常用 api,从代码的角度理解反射。

1.3 反射获取 Class 对象(3种方法)

在你想检查一个类的信息之前,你首先需要获取类的 Class 对象。Java 中的所有类型包括基本类型,即使是数组都有与之关联的 Class 类的对象。如果你在编译期知道一个类的名字的话,那么你可以使用如下的方式获取一个类的 Class 对象。

 
  1. Class<?> myObjectClass = MyObject.class;

如果你已经得到了某个对象,但是你想获取这个对象的 Class 对象,那么你可以通过下面的方法得到:

 
  1. Student me = new Student("mr.simple");
  2. Class<?> clazz = me.getClass();

如果你在编译期获取不到目标类型,但是你知道它的完整类路径,那么你可以通过如下的形式来获取 Class 对象:

 
  1. Class<?> myObjectClass = Class.forName("com.simple.User");

在使用 Class.forName()方法时,你必须提供一个类的全名,这个全名包括类所在的包的名字。例如 User 类位于 com.simple 包,那么他的完整类路径就是 com.simple.User。

如果在调用 Class.forName()方法时,没有在编译路径下(classpath)找到对应的类,那么将会抛出 ClassNotFoundException。

接口说明

 
  1. // 加载指定的 Class 对象,参数 1 为要加载的类的完整路径,例如"com.simple.Student". ( 常用方式 )
  2. public static Class<?> forName (String className)
  3. // 加载指定的 Class 对象,参数 1 为要加载的类的完整路径,例如"com.simple.Student";
  4. // 参数 2 为是否要初始化该 Class 对象,参数 3 为指定加载该类的 ClassLoader.
  5. public static Class<?> forName (String className, boolean shouldInitialize, ClassLoader classLoader)

编程要求

根据提示,在右侧编辑器补充代码,使用3种方法通过反射获取相应的对象。获取的class对象分别赋值给studentClass1,studentClass2,studentClass3

测试说明

平台会对你编写的代码进行测试,用户无法查看输出,系统自动判断代码的正误;

开始你的任务吧,祝你成功!

package step1;


public  class  Reflection{

    public static void main(String[] args) throws ClassNotFoundException {
        Class <?> studentClass1 = null;
        Class <?> studentClass2 = null;
        Class <?> studentClass3 = null;
        // ---------------------Begin------------------------
        // Studnet 类的路径为 "step1.Student"
studentClass1 = Student.class;
studentClass2 = new Student().getClass();
studentClass3 = Class .forName("step1.Student") ;

        // ---------------------End------------------------
        System.out.println(studentClass1);
        System.out.println(studentClass2);
        System.out.println(studentClass3);
    }

}

第2关:反射获取类中函数 

任务描述

本关任务:

1.使用字节码对象获取该类中定义公用方法。遍历输出该方法名。

相关知识

1.1 获取当前类中定义的所有方法

要获取当前类中定义的所有方法可以通过 Class 中的 getDeclaredMethods 函数,它会获取到当前类中的 public、default、protected、private 的所有方法。代码示例如下 :

 
  1. private static void showDeclaredMethods() {
  2. Student student = new Student("mr.simple");
  3. Method[] methods = student.getClass().getDeclaredMethods();
  4. for (Method method : methods) {
  5. System.out.println("declared method name : " + method.getName());
  6. }
  7. try {
  8. Method learnMethod = student.getClass().getDeclaredMethod("learn", String.class);
  9. // 获取方法的参数类型列表
  10. Class<?>[] paramClasses = learnMethod.getParameterTypes() ;
  11. for (Class<?> class1 : paramClasses) {
  12. System.out.println("learn 方法的参数类型 : " + class1.getName());
  13. }
  14. // 是否是 private 函数,属性是否是 private 也可以使用这种方式判断
  15. System.out.println(learnMethod.getName() + " is private "
  16. + Modifier.isPrivate(learnMethod.getModifiers()));
  17. learnMethod.invoke(student, "java ---> ");
  18. } catch (Exception e) {
  19. e.printStackTrace();
  20. }
  21. }

1.2 获取当前类中的指定的方法

getDeclaredMethod(String name, Class...<?> parameterTypes)则是获取某个指定的方法,代码示例如下 :

 
  1. private static void showDeclaredMethods() {
  2. Student student = new Student("mr.simple");
  3. try {
  4.         // 获取Student中名为getName的方法
  5. Method getAge = studentClass.getMethod("getName");
  6. System.out.println(getAge);
  7. } catch (Exception e) {
  8. e.printStackTrace();
  9. }
  10. }

接口说明

 
  1. // 获取 Class 对象中指定函数名和参数的函数,参数一为函数名,参数 2 为参数类型列表
  2. public Method getDeclaredMethod (String name, Class...<?> parameterTypes)
  3. // 获取该 Class 对象中的所有函数( 不包含从父类继承的函数 )
  4. public Method[] getDeclaredMethods ()
  5. // 获取指定的 Class 对象中的**公有**函数,参数一为函数名,参数 2 为参数类型列表
  6. public Method getMethod (String name, Class...<?> parameterTypes)
  7. // 获取该 Class 对象中的所有**公有**函数 ( 包含从父类和接口类集成下来的函数 )
  8. public Method[] getMethods ()

这里需要注意的是 getDeclaredMethod 和 getDeclaredMethods 包含 private、protected、default、public 的函数,并且通过这两个函数获取到的只是在自身中定义的函数,从父类中集成的函数不能够获取到。而 getMethod 和 getMethods 只包含 public 函数,父类中的公有函数也能够获取到。

编程要求

根据提示,在右侧编辑器补充代码,完成以下任务:

1.使用字节码对象获取该类中定义公用方法。遍历输出改方法名。

2.学会使用反射获取类中指定方法

测试说明

平台会对你编写的代码进行测试:

无输入,系统自动判断用户代码的正误。


开始你的任务吧,祝你成功!

package step2;

import java.lang.reflect.Method;
public class ReflectFunc {

    public static void main(String[] args) {
        Class<Student> studentClass = Student.class;
        // ---------------------Begin------------------------
        // 根据上方的字节码对象,获取定义的方法并遍历输出
Method[] declaredMethods = studentClass.getDeclaredMethods();
for (Method declaredMethod: declaredMethods){ 
System.out.println(declaredMethod.getName());
    }


        // 获取 Student 类中的getAge方法并将其打印输出
    try {
    Method getAge = studentClass.getMethod("getAge");
    System.out.println(getAge);
    }
    catch (Exception e){
e.printStackTrace();
    }

        // ---------------------End------------------------

    }


}


第3关:反射获取类中的属性 

任务描述

本关任务:

1.使用字节码对象获取该类中定义公用属性。遍历输出该属性名。

2.学会使用反射获取类中指定属性,并输出该属性的值,Student类中的 name属性

相关知识

获取属性和第2关 中获取方法是非常相似的,只是从 getMethod 函数换成了 getField,从 getDeclaredMethod 换成了 getDeclaredField 罢了。

1.1 获取当前类中定义的属性

要获取当前类中定义的所有属性可以通过 Class 中的 getDeclaredFields 函数,它会获取到当前类中的 public、default、protected、private 的所有属性。代码示例如下 :

 
  1. private static void showDeclaredFields() {
  2. Student student = new Student("mr.simple");
  3. // 获取当前类和父类的所有公有属性
  4. Field[] publicFields = student.getClass().getDeclaredFields();
  5. for (Field field : publicFields) {
  6. System.out.println("declared field name : " + field.getName());
  7. }
  8. try {
  9. // 获取当前类和父类的某个公有属性
  10. Field gradeField = student.getClass().getDeclaredField("mGrade");
  11. // 获取属性值
  12. System.out.println(" my grade is : " + gradeField.getInt(student));
  13. // 设置属性值
  14. gradeField.set(student, 10);
  15. System.out.println(" my grade is : " + gradeField.getInt(student));
  16. } catch (Exception e) {
  17. e.printStackTrace();
  18. }
  19. }

1.2 获取指定属性

getDeclaredField 则是获取某个指定的属性。代码示例如下 :

 
  1. private static void showDeclaredFields() {
  2. Student student = new Student("mr.simple");
  3. try {
  4. // 获取当前类和父类的某个公有属性
  5. Field gradeField = student.getClass().getDeclaredField("age");
  6. // 获取属性值
  7. System.out.println(" my grade is : " + gradeField.getInt(student));
  8. } catch (Exception e) {
  9. e.printStackTrace();
  10. }
  11. }

接口说明

 
  1. // 获取 Class 对象中指定属性名的属性,参数一为属性名
  2. public Method getDeclaredField (String name)
  3. // 获取该 Class 对象中的所有属性( 不包含从父类继承的属性 )
  4. public Method[] getDeclaredFields ()
  5. // 获取指定的 Class 对象中的**公有**属性,参数一为属性名
  6. public Method getField (String name)
  7. // 获取该 Class 对象中的所有**公有**属性 ( 包含从父类和接口类集成下来的公有属性 )
  8. public Method[] getFields ()

这里需要注意的是 getDeclaredField 和 getDeclaredFields 包含 private、protected、default、public 的属性,并且通过这两个函数获取到的只是在自身中定义的属性,从父类中集成的属性不能够获取到。而 getField 和 getFields 只包含 public 属性,父类中的公有属性也能够获取到。

编程要求

根据提示,在右侧编辑器补充代码,完成以下任务:

1.使用字节码对象获取该类中定义公用属性。遍历输出该属性名。

2.学会使用反射获取类中指定属性,并输出该属性的值,Student类中的 name属性

测试说明

平台会对你编写的代码进行测试:

无输入,系统自动判断用户代码的正误。


开始你的任务吧,祝你成功!

package step3;
import java.lang.reflect.Field;
public class ReflectField {

    public static void main(String[] args) throws NoSuchFieldException,IllegalAccessException {
        Student student = new Student();
        Class<? extends Student> studentClass = student.getClass();
// ---------------------Begin------------------------
//        使用字节码对象获取该类中定义公用属性。遍历输出该属性名。
Field[] declaredFields = studentClass.getDeclaredFields();
for(Field declaredField:declaredFields){
    System.out.println(declaredField.getName());
}


//        学会使用反射获取类中指定属性,并输出该属性的值,Student类中的 name属性
Field age = studentClass.getDeclaredField("name");
System.out.println(age);
System.out.println(age.get(student));

// ---------------------End------------------------

    }

}

第4关:反射获取父类及其接口 

任务描述

本关任务:

1.使用字节码对象获取该父类,遍历输出上一父类名,直到父类为null。

2.使用字节码对象获取该父类接口,遍历输出上一父类接口名

相关知识

1.1 获取父类

获取 Class 对象的父类。

 
  1. Student student = new Student("mr.simple");
  2. Class<?> superClass = student.getClass().getSuperclass();
  3. while (superClass != null) {
  4. System.out.println("Student's super class is : " + superClass.getName());
  5. // 再获取父类的上一层父类,直到最后的 Object 类,Object 的父类为 null
  6. superClass = superClass.getSuperclass();
  7. }

1.2 获取接口

获取 Class 对象中实现的接口。

 
  1. private static void showInterfaces() {
  2. Student student = new Student("mr.simple");
  3. Class<?>[] interfaceses = student.getClass().getInterfaces();
  4. for (Class<?> class1 : interfaceses) {
  5. System.out.println("Student's interface is : " + class1.getName());
  6. }
  7. }

编程要求

根据提示,在右侧编辑器补充代码,完成以下任务:

1.使用字节码对象获取该父类,遍历输出上一父类名,直到父类为null。

2.使用字节码对象获取该父类接口,遍历输出上一父类接口名

测试说明

平台会对你编写的代码进行测试:

无输入,系统自动判断用户代码的正误。


开始你的任务吧,祝你成功!

package step4;

public class ReflectSuperClass {

    public static void main(String[] args) {

        Student student = new Student();
        // ---------------------Begin------------------------
        //通student对象获取字节码对象,并遍历输出该父类的名字
Class<?> superclass = student.getClass().getSuperclass();
    while (superclass != null){
        System.out.println(superclass.getName());
        superclass = superclass.getSuperclass();
    }



        //通student对象获取字节码对象,并遍历输出该父类的接口名

Class<?>[] interfaces = student.getClass().getInterfaces();
for (Class<?> clas:interfaces
){
    System.out.println(clas.getName());
        }
    
    
    

        // ---------------------End------------------------
    

}
}

  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值