有问有答—JavaSE—反射

1 代码对比

package com.sdust.day37;

public class Student {
    private Integer id;
    private String name;
    private Integer age;
    private String gender;

    private Student(Integer id, String name, Integer age, String gender) {
        this.id = id;
        this.name = name;
        this.age = age;
        this.gender = gender;
    }

    static public String studentInClass = "李四";
    public Student() {
    }
    /*private Student() {
    }*/

    public Student(String name) {
        this.name = name;
    }

    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public Integer getAge() {
        return age;
    }

    public void setAge(Integer age) {
        this.age = age;
    }

    public String getGender() {
        return gender;
    }

    public void setGender(String gender) {
        this.gender = gender;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    @Override
    public String toString() {
        return "Student{" +
                "id=" + id +
                ", name='" + name + '\'' +
                ", age=" + age +
                ", gender='" + gender + '\'' +
                '}';
    }
}
    @Test
    public void test1() {
        Student student2 = new Student();
        student2.setName("张三");
        Student student1 = student2;
        System.out.println(student2);
        System.out.println(student1);

        System.out.println(System.identityHashCode(student1));
        System.out.println(System.identityHashCode(student2));
        System.out.println("-------------------------------");

        String student3 = Student.studentInClass;
        String student4 = student3;
        System.out.println(System.identityHashCode(Student.studentInClass));
        System.out.println(System.identityHashCode(student3));
        System.out.println(System.identityHashCode(student4));
        System.out.println("-------------------------------");

        Class clazz = Student.class;//写Student类(类)的过程,同时也是写(生成)Student.class(实例对象)的过程
        Class clazz2 = clazz;
        System.out.println(System.identityHashCode(Student.class));
        System.out.println(System.identityHashCode(clazz));
        System.out.println(System.identityHashCode(clazz2));
    }

System类提供了一个identityHashCode(Object x)方法,这个方法返回的是指定对象的精确hashCode值,也就是根据该对象的地址计算得到的hashCode值。

当某个类的hashCode()方法被重写之后,该类实例的hashCode方法就不能唯一地标识该对象,但是如果两个对象的identityHashCode值相同,则这两个对象是一个对象。

结果:

Student{name='张三'}
Student{name='张三'}
1170794006
1170794006
-------------------------------
1289479439
1289479439
1289479439
-------------------------------
627185331
627185331
627185331

2 写Student类(类)的过程,同时也是生成Student.class(实例对象)的过程

在 Java 中,类和类对象(Class 对象)是两个重要的概念。编写一个类的过程同时也是生成该类的 Class 对象的过程。下面是详细的解释以及如何编写 Student 类的过程,同时理解 Class 类的实例对象。

编写 Student 类

首先,我们编写一个简单的 Student 类。

public class Student {
    private String name;
    private int age;

    // 构造方法
    public Student(String name, int age) {
        this.name = name;
        this.age = age;
    }

    // 无参构造方法
    public Student() {}

    // Getter 和 Setter 方法
    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    // 重写 toString 方法
    @Override
    public String toString() {
        return "Student{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}

生成 Student.class 对象

【补充说明】每个类加载到内存后,系统都会有唯一的一份字节码对象(Person.class/Student.class字节码对象都是Class这个类的实例)

在编写 Student 类的过程中,Java 编译器会自动生成一个 Class 对象,这个 Class 对象包含了有关这个类的所有信息。你可以通过多种方式获取这个 Class 对象(字节码对象):

  1. 通过类名

    Class<Student> clazz = Student.class;
    
  2. 通过对象的 getClass 方法

    Student student = new Student("张三", 20);
    Class<? extends Student> clazz = student.getClass();
    
  3. 通过 Class.forName 方法

    Class<?> clazz = Class.forName("Student");
    

反射获取类信息

通过反射,你可以获取 Class 对象的信息,如类的名称、字段、方法等。以下是一个示例:

public class ReflectionExample {
    public static void main(String[] args) {
        try {
            // 通过类名获取 Class 对象
            Class<Student> clazz = Student.class;

            // 打印类名
            System.out.println("Class Name: " + clazz.getName());

            // 获取所有声明的字段---public、private等都能获取到
            Field[] fields = clazz.getDeclaredFields();
            for (Field field : fields) {
                System.out.println("Field: " + field.getName() + ", Type: " + field.getType());
            }

            // 获取所有声明的方法---public、private等都能获取到
            Method[] methods = clazz.getDeclaredMethods();
            for (Method method : methods) {
                System.out.println("Method: " + method.getName() + ", Return Type: " + method.getReturnType());
            }

            // 获取所有声明的构造方法---public、private等都能获取到
            Constructor<?>[] constructors = clazz.getDeclaredConstructors();
            for (Constructor<?> constructor : constructors) {
                System.out.println("Constructor: " + constructor.getName() + ", Parameter Count: " + constructor.getParameterCount());
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

运行结果示例

假设你编写了上面的 Student 类和 ReflectionExample 类,运行 ReflectionExample 类的 main 方法将输出类似于以下内容:

Class Name: Student
Field: name, Type: class java.lang.String
Field: age, Type: int

Method: getName, Return Type: class java.lang.String
Method: setName, Return Type: void
Method: getAge, Return Type: int
Method: setAge, Return Type: void
Method: toString, Return Type: class java.lang.String

Constructor: Student, Parameter Count: 2
Constructor: Student, Parameter Count: 0

总结

编写一个类(如 Student 类)的过程也就是生成该类的 Class 对象的过程。Class 对象包含了有关该类的所有信息,Java 提供了反射机制,允许你在运行时获取这些信息。通过反射,你可以获取类的字段、方法和构造方法等信息,从而更灵活地操作对象。

3 Class类里边的Package、Name、Field、Constructor、Method是它的属性吗?还是类里边套了类?

在 Java 的 Class 类中,PackageNameFieldConstructorMethod 等术语涉及的是类的属性或方法,而不是 Java 中的类。这些属性或方法提供对 Java 类结构和成员的反射性访问。下面详细解释每个术语:

Class 类的属性和方法

  1. Package

    • 属性Package 代表一个包,它包含了类的包信息。

    • 方法

      Package pkg = clazz.getPackage();
      

      通过 Class 类的 getPackage 方法可以获取类所属的包。

  2. Name

    • 属性:类的名称,可以是简单名称或全限定名。

    • 方法

      String name = clazz.getName(); // 获取类的全限定名
      String simpleName = clazz.getSimpleName(); // 获取类的简单名称
      

      通过 Class 类的 getNamegetSimpleName 方法可以获取类的名称。

  3. Field

    • java.lang.reflect.Field,代表类的成员变量。Class类中引入了Field这个类,并且将这个类的方法作为Class类的一个方法

    • 方法

      Field[] fields = clazz.getDeclaredFields();
      Field field = clazz.getDeclaredField("fieldName");
      

      通过 Class 类的 getDeclaredFieldsgetDeclaredField 方法可以获取类的成员变量。

  4. Constructor

    • java.lang.reflect.Constructor,代表类的构造方法。Class类中引入了Constructor这个类,并且将这个类的方法作为Class类的一个方法

    • 方法

      Constructor<?>[] constructors = clazz.getDeclaredConstructors();
      Constructor<?> constructor = clazz.getDeclaredConstructor(parameterTypes);
      

      通过 Class 类的 getDeclaredConstructorsgetDeclaredConstructor 方法可以获取类的构造方法。

  5. Method

    • java.lang.reflect.Method,代表类的方法。Class类中引入了Method这个类,并且将这个类的方法作为Class类的一个方法

    • 方法

      Method[] methods = clazz.getDeclaredMethods();
      Method method = clazz.getDeclaredMethod("methodName", parameterTypes);
      

      通过 Class 类的 getDeclaredMethodsgetDeclaredMethod 方法可以获取类的方法。

详细解释

Package
  • 类型:属性

  • 解释Package 类代表 Java 包。通过反射,你可以获取类所属的包信息。

  • 示例

    Class<?> clazz = Student.class;
    Package pkg = clazz.getPackage();
    System.out.println("Package: " + pkg.getName());
    
Name
  • 类型:属性

  • 解释:类的名称。通过反射,你可以获取类的全限定名或简单名。

  • 示例

    Class<?> clazz = Student.class;
    String name = clazz.getName();
    String simpleName = clazz.getSimpleName();
    System.out.println("Full Name: " + name);
    System.out.println("Simple Name: " + simpleName);
    
Field
  • 类型:类

  • 解释Field 类代表类的成员变量。通过反射,你可以获取和操作类的成员变量。

  • 示例

    Class<?> clazz = Student.class;
    Field[] fields = clazz.getDeclaredFields();
    for (Field field : fields) {
        System.out.println("Field: " + field.getName() + ", Type: " + field.getType());
    }
    
Constructor
  • 类型:类

  • 解释Constructor 类代表类的构造方法。通过反射,你可以获取和调用类的构造方法。

  • 示例

    Class<?> clazz = Student.class;
    Constructor<?>[] constructors = clazz.getDeclaredConstructors();
    for (Constructor<?> constructor : constructors) {
        System.out.println("Constructor: " + constructor.getName() + ", Parameter Count: " + constructor.getParameterCount());
    }
    
Method
  • 类型:类

  • 解释Method 类代表类的方法。通过反射,你可以获取和调用类的方法。

  • 示例

    Class<?> clazz = Student.class;
    Method[] methods = clazz.getDeclaredMethods();
    for (Method method : methods) {
        System.out.println("Method: " + method.getName() + ", Return Type: " + method.getReturnType());
    }
    

总结

Class 类中,PackageName 是类的属性,而 FieldConstructorMethodjava.lang.reflect 包中的类,通过这些类的实例方法可以获取类的成员变量、构造方法和普通方法的详细信息。这些工具提供了强大的反射功能,允许在运行时检查和操作类结构。

4 Field、Constructor 和 Method是不是能脱离Class使用?

FieldConstructorMethod 不能脱离 Class 单独使用,因为它们是通过 Class 对象来获取的,反映的是特定类的成员变量、构造方法和普通方法。它们的实例必须通过 Class 对象来创建,以表示特定类的字段、构造方法和方法。

详细解释

  1. Field

    • Field 对象代表某个类的成员变量,必须通过 Class 对象来获取。

    • 示例:

      Class<Student> clazz = Student.class;
      Field field = clazz.getDeclaredField("name");
      
    • Field 对象不能脱离 Class 对象单独存在。

  2. Constructor

    • Constructor 对象代表某个类的构造方法,必须通过 Class 对象来获取。

    • 示例:

      Class<Student> clazz = Student.class;
      Constructor<Student> constructor = clazz.getDeclaredConstructor(String.class, int.class);
      
    • Constructor 对象不能脱离 Class 对象单独存在。

  3. Method

    • Method 对象代表某个类的方法,必须通过 Class 对象来获取。

    • 示例:

      Class<Student> clazz = Student.class;
      Method method = clazz.getDeclaredMethod("getName");
      
    • Method 对象不能脱离 Class 对象单独存在。

示例代码

以下是一个完整的示例,展示如何通过 Class 对象获取 FieldConstructorMethod 对象,并使用它们来操作一个 Student 类的实例。

Student 类
public class Student {
    private String name;
    private int age;

    public Student() {}

    public Student(String name, int age) {
        this.name = name;
        this.age = age;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    @Override
    public String toString() {
        return "Student{name='" + name + "', age=" + age + "}";
    }
}
使用反射操作 Student 类
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;

public class ReflectionExample {
    public static void main(String[] args) {
        try {
            // 获取 Student 类的 Class 对象
            Class<Student> clazz = Student.class;

            // 获取并使用 Constructor 对象
            Constructor<Student> constructor = clazz.getDeclaredConstructor(String.class, int.class);
            Student student = constructor.newInstance("张三", 20);
            System.out.println("Student: " + student);

            // 获取并使用 Field 对象
            Field nameField = clazz.getDeclaredField("name");
            nameField.setAccessible(true); // 如果字段是私有的,设置可访问
            nameField.set(student, "李四");
            System.out.println("Updated Student: " + student);

            // 获取并使用 Method 对象
            Method getNameMethod = clazz.getDeclaredMethod("getName");
            String name = (String) getNameMethod.invoke(student);
            System.out.println("Student's Name: " + name);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

总结

  • Field、Constructor 和 Method 对象:它们表示特定类的成员变量、构造方法和普通方法,必须通过 Class 对象来获取。
  • 不能脱离 Class 使用:这些对象是通过反射机制从 Class 对象中获取的,不能独立存在。
  • 反射操作:通过 Class 对象,可以获取这些反射对象并操作类的实例。

通过 Class 对象获取 FieldConstructorMethod 对象,可以在运行时动态地操作类和对象,这正是 Java 反射机制的强大之处。

5 Class<Student> clazz = Student.class; 所以这个clazz对象里边就有Field类、Method类、Constructor类的实例对象作为它自己的属性吗?

实际上,Class<Student> 对象本身并不直接包含 FieldMethodConstructor 类的实例对象作为它的属性。相反,Class<Student> 对象提供了获取这些反射对象的方法,通过这些方法你可以动态地获取某个类的字段、方法和构造函数的详细信息。

当你调用 Class 对象的方法(如 getDeclaredFieldsgetDeclaredMethodsgetDeclaredConstructors 等)时,Java 反射机制会动态地创建和返回这些反射对象的数组或单个实例。

详细解释

  • FieldMethodConstructor 对象:这些对象代表的是类的成员变量、方法和构造函数。
  • 通过 Class 对象获取反射对象:你需要通过调用 Class 对象的方法来获取这些反射对象,而不是 Class 对象本身直接持有它们。

获取反射对象的方法

  • 获取所有字段

    Field[] fields = clazz.getDeclaredFields();
    
  • 获取所有方法

    Method[] methods = clazz.getDeclaredMethods();
    
  • 获取所有构造函数

    Constructor<?>[] constructors = clazz.getDeclaredConstructors();
    

示例代码

以下是如何通过 Class 对象获取 FieldMethodConstructor 对象的示例。

Student 类
public class Student {
    private String name;
    private int age;

    public Student() {}

    public Student(String name, int age) {
        this.name = name;
        this.age = age;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    @Override
    public String toString() {
        return "Student{name='" + name + "', age=" + age + "}";
    }
}
使用反射获取 Student 类的字段、方法和构造函数
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;

public class ReflectionExample {
    public static void main(String[] args) {
        try {
            // 获取 Student 类的 Class 对象
            Class<Student> clazz = Student.class;

            // 获取所有字段
            Field[] fields = clazz.getDeclaredFields();
            for (Field field : fields) {
                System.out.println("Field: " + field.getName() + ", Type: " + field.getType());
            }

            // 获取所有方法
            Method[] methods = clazz.getDeclaredMethods();
            for (Method method : methods) {
                System.out.println("Method: " + method.getName() + ", Return Type: " + method.getReturnType());
            }

            // 获取所有构造函数
            Constructor<?>[] constructors = clazz.getDeclaredConstructors();
            for (Constructor<?> constructor : constructors) {
                System.out.println("Constructor: " + constructor.getName() + ", Parameter Count: " + constructor.getParameterCount());
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

解释

  • Field 对象

    • Field[] fields = clazz.getDeclaredFields();
    • 通过 Class 对象的 getDeclaredFields 方法获取类的所有字段。
  • Method 对象

    • Method[] methods = clazz.getDeclaredMethods();
    • 通过 Class 对象的 getDeclaredMethods 方法获取类的所有方法。
  • Constructor 对象

    • Constructor<?>[] constructors = clazz.getDeclaredConstructors();
    • 通过 Class 对象的 getDeclaredConstructors 方法获取类的所有构造函数。

总结

  • Class 对象本身并不直接包含 FieldMethodConstructor 类的实例对象作为它的属性。
  • Class 对象提供了方法来动态地获取这些反射对象,这些方法返回的是代表类的字段、方法和构造函数的反射对象。
  • 使用这些方法,你可以在运行时获取类的详细信息,并对其进行操作。

6 你说“它们的实例必须通过 Class 对象来创建,以表示特定类的字段、构造方法和方法。” 是否可以理解为这是Java的规定

是的,你可以理解为这是 Java 的规定。Java 的反射机制设计使得 FieldMethodConstructor 对象必须通过 Class 对象来创建。这是因为 Class 对象表示的是 Java 中的类或接口,反射机制依赖于 Class 对象来获取类的元数据(如字段、方法和构造方法等)。

详细解释

  • Java 的设计:Java 通过 Class 对象提供了访问类元数据的统一接口。这种设计使得反射操作更加结构化和一致。
  • 封装和安全性:通过 Class 对象获取反射对象,可以确保只有在正确上下文中才可以访问类的元数据,从而提供了一定的封装和安全性。

反射机制的设计

Java 的反射机制是通过 Class 类来实现的。以下是一些关键点:

  1. Class:表示类或接口,提供了获取类元数据的方法。
  2. Field:表示类的成员变量,通过 Class 对象获取。
  3. Method:表示类的方法,通过 Class 对象获取。
  4. Constructor:表示类的构造方法,通过 Class 对象获取。

示例

下面是一个示例,展示如何通过 Class 对象获取 FieldMethodConstructor 对象。

Student 类
public class Student {
    private String name;
    private int age;

    public Student() {}

    public Student(String name, int age) {
        this.name = name;
        this.age = age;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    @Override
    public String toString() {
        return "Student{name='" + name + "', age=" + age + "}";
    }
}
使用反射获取 Student 类的字段、方法和构造函数
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;

public class ReflectionExample {
    public static void main(String[] args) {
        try {
            // 获取 Student 类的 Class 对象
            Class<Student> clazz = Student.class;

            // 获取所有字段
            Field[] fields = clazz.getDeclaredFields();
            for (Field field : fields) {
                System.out.println("Field: " + field.getName() + ", Type: " + field.getType());
            }

            // 获取所有方法
            Method[] methods = clazz.getDeclaredMethods();
            for (Method method : methods) {
                System.out.println("Method: " + method.getName() + ", Return Type: " + method.getReturnType());
            }

            // 获取所有构造函数
            Constructor<?>[] constructors = clazz.getDeclaredConstructors();
            for (Constructor<?> constructor : constructors) {
                System.out.println("Constructor: " + constructor.getName() + ", Parameter Count: " + constructor.getParameterCount());
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

总结

  • 设计原则:Java 的反射机制设计为必须通过 Class 对象来获取 FieldMethodConstructor 对象。这种设计确保了对类元数据的访问是结构化和一致的。
  • 封装和安全性:通过 Class 对象访问元数据提供了封装和安全性,确保只有在适当的上下文中才可以获取和操作类的元数据。

可以理解为这是 Java 的设计规定,以确保反射机制的安全性和一致性。

7 字节码对象是Class类的对象吗?

是的,字节码对象在 Java 中是 Class 类的对象。在 Java 中,每个类都有一个对应的 Class 对象,这个对象在运行时被 JVM 用来表示这个类的字节码。这个 Class 对象包含了有关该类的所有信息,例如类的名称、包、字段、方法、构造函数等。

详细解释

  1. Class 对象

    • 在 JVM 中,每个加载的类都有一个 Class 对象与之对应。这个 Class 对象包含了该类的字节码,以及有关该类的所有元数据。
    • 通过这个 Class 对象,Java 程序可以使用反射来获取类的信息和操作类的成员。
  2. 字节码

    • Java 源代码被编译成字节码,这些字节码被存储在 .class 文件中。
    • 当 JVM 加载一个类时,它会读取这个 .class 文件并创建一个 Class 对象来表示这个类的字节码。

示例代码

以下是一个简单的示例,展示如何获取一个类的 Class 对象,并使用反射来获取类的信息。

Student 类
public class Student {
    private String name;
    private int age;

    public Student() {}

    public Student(String name, int age) {
        this.name = name;
        this.age = age;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    @Override
    public String toString() {
        return "Student{name='" + name + "', age=" + age + "}";
    }
}
使用反射获取 Student 类的信息
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;

public class ReflectionExample {
    public static void main(String[] args) {
        try {
            // 获取 Student 类的 Class 对象
            Class<Student> clazz = Student.class;

            // 打印类名
            System.out.println("Class Name: " + clazz.getName());

            // 获取所有声明的字段
            Field[] fields = clazz.getDeclaredFields();
            for (Field field : fields) {
                System.out.println("Field: " + field.getName() + ", Type: " + field.getType());
            }

            // 获取所有声明的方法
            Method[] methods = clazz.getDeclaredMethods();
            for (Method method : methods) {
                System.out.println("Method: " + method.getName() + ", Return Type: " + method.getReturnType());
            }

            // 获取所有声明的构造方法
            Constructor<?>[] constructors = clazz.getDeclaredConstructors();
            for (Constructor<?> constructor : constructors) {
                System.out.println("Constructor: " + constructor.getName() + ", Parameter Count: " + constructor.getParameterCount());
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

解释

  • Class<Student> clazz = Student.class;:通过这种方式获取 Student 类的 Class 对象。
  • clazz.getName():获取类的全限定名。
  • clazz.getDeclaredFields():获取类的所有声明字段。
  • clazz.getDeclaredMethods():获取类的所有声明方法。
  • clazz.getDeclaredConstructors():获取类的所有声明构造函数。

总结

  • Class 对象是字节码对象:在 Java 中,每个类的字节码对象都是 Class 类的实例。这个对象包含了类的所有字节码和元数据。
  • 反射机制:通过 Class 对象,Java 程序可以使用反射来获取和操作类的元数据。这使得 Java 程序可以在运行时动态地获取类的信息和操作类的成员。

因此,可以说字节码对象确实是 Class 类的对象。

  • 7
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值