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
对象(字节码对象):
-
通过类名:
Class<Student> clazz = Student.class;
-
通过对象的
getClass
方法:Student student = new Student("张三", 20); Class<? extends Student> clazz = student.getClass();
-
通过
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
类中,Package
、Name
、Field
、Constructor
和 Method
等术语涉及的是类的属性或方法,而不是 Java 中的类。这些属性或方法提供对 Java 类结构和成员的反射性访问。下面详细解释每个术语:
Class
类的属性和方法
-
Package:
-
属性:
Package
代表一个包,它包含了类的包信息。 -
方法:
Package pkg = clazz.getPackage();
通过
Class
类的getPackage
方法可以获取类所属的包。
-
-
Name:
-
属性:类的名称,可以是简单名称或全限定名。
-
方法:
String name = clazz.getName(); // 获取类的全限定名 String simpleName = clazz.getSimpleName(); // 获取类的简单名称
通过
Class
类的getName
或getSimpleName
方法可以获取类的名称。
-
-
Field:
-
类:
java.lang.reflect.Field
,代表类的成员变量。Class类中引入了Field这个类,并且将这个类的方法作为Class类的一个方法 -
方法:
Field[] fields = clazz.getDeclaredFields(); Field field = clazz.getDeclaredField("fieldName");
通过
Class
类的getDeclaredFields
或getDeclaredField
方法可以获取类的成员变量。
-
-
Constructor:
-
类:
java.lang.reflect.Constructor
,代表类的构造方法。Class类中引入了Constructor这个类,并且将这个类的方法作为Class类的一个方法 -
方法:
Constructor<?>[] constructors = clazz.getDeclaredConstructors(); Constructor<?> constructor = clazz.getDeclaredConstructor(parameterTypes);
通过
Class
类的getDeclaredConstructors
或getDeclaredConstructor
方法可以获取类的构造方法。
-
-
Method:
-
类:
java.lang.reflect.Method
,代表类的方法。Class类中引入了Method这个类,并且将这个类的方法作为Class类的一个方法 -
方法:
Method[] methods = clazz.getDeclaredMethods(); Method method = clazz.getDeclaredMethod("methodName", parameterTypes);
通过
Class
类的getDeclaredMethods
或getDeclaredMethod
方法可以获取类的方法。
-
详细解释
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
类中,Package
、Name
是类的属性,而 Field
、Constructor
和 Method
是 java.lang.reflect
包中的类,通过这些类的实例方法可以获取类的成员变量、构造方法和普通方法的详细信息。这些工具提供了强大的反射功能,允许在运行时检查和操作类结构。
4 Field、Constructor 和 Method是不是能脱离Class使用?
Field
、Constructor
和 Method
不能脱离 Class
单独使用,因为它们是通过 Class
对象来获取的,反映的是特定类的成员变量、构造方法和普通方法。它们的实例必须通过 Class
对象来创建,以表示特定类的字段、构造方法和方法。
详细解释
-
Field:
-
Field
对象代表某个类的成员变量,必须通过Class
对象来获取。 -
示例:
Class<Student> clazz = Student.class; Field field = clazz.getDeclaredField("name");
-
Field
对象不能脱离Class
对象单独存在。
-
-
Constructor:
-
Constructor
对象代表某个类的构造方法,必须通过Class
对象来获取。 -
示例:
Class<Student> clazz = Student.class; Constructor<Student> constructor = clazz.getDeclaredConstructor(String.class, int.class);
-
Constructor
对象不能脱离Class
对象单独存在。
-
-
Method:
-
Method
对象代表某个类的方法,必须通过Class
对象来获取。 -
示例:
Class<Student> clazz = Student.class; Method method = clazz.getDeclaredMethod("getName");
-
Method
对象不能脱离Class
对象单独存在。
-
示例代码
以下是一个完整的示例,展示如何通过 Class
对象获取 Field
、Constructor
和 Method
对象,并使用它们来操作一个 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
对象获取 Field
、Constructor
和 Method
对象,可以在运行时动态地操作类和对象,这正是 Java 反射机制的强大之处。
5 Class<Student> clazz = Student.class;
所以这个clazz对象里边就有Field类、Method类、Constructor类的实例对象作为它自己的属性吗?
实际上,Class<Student>
对象本身并不直接包含 Field
、Method
、Constructor
类的实例对象作为它的属性。相反,Class<Student>
对象提供了获取这些反射对象的方法,通过这些方法你可以动态地获取某个类的字段、方法和构造函数的详细信息。
当你调用 Class
对象的方法(如 getDeclaredFields
、getDeclaredMethods
、getDeclaredConstructors
等)时,Java 反射机制会动态地创建和返回这些反射对象的数组或单个实例。
详细解释
Field
、Method
和Constructor
对象:这些对象代表的是类的成员变量、方法和构造函数。- 通过
Class
对象获取反射对象:你需要通过调用Class
对象的方法来获取这些反射对象,而不是Class
对象本身直接持有它们。
获取反射对象的方法
-
获取所有字段:
Field[] fields = clazz.getDeclaredFields();
-
获取所有方法:
Method[] methods = clazz.getDeclaredMethods();
-
获取所有构造函数:
Constructor<?>[] constructors = clazz.getDeclaredConstructors();
示例代码
以下是如何通过 Class
对象获取 Field
、Method
和 Constructor
对象的示例。
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
对象本身并不直接包含Field
、Method
、Constructor
类的实例对象作为它的属性。Class
对象提供了方法来动态地获取这些反射对象,这些方法返回的是代表类的字段、方法和构造函数的反射对象。- 使用这些方法,你可以在运行时获取类的详细信息,并对其进行操作。
6 你说“它们的实例必须通过 Class 对象来创建,以表示特定类的字段、构造方法和方法。” 是否可以理解为这是Java的规定
是的,你可以理解为这是 Java 的规定。Java 的反射机制设计使得 Field
、Method
和 Constructor
对象必须通过 Class
对象来创建。这是因为 Class
对象表示的是 Java 中的类或接口,反射机制依赖于 Class
对象来获取类的元数据(如字段、方法和构造方法等)。
详细解释
- Java 的设计:Java 通过
Class
对象提供了访问类元数据的统一接口。这种设计使得反射操作更加结构化和一致。 - 封装和安全性:通过
Class
对象获取反射对象,可以确保只有在正确上下文中才可以访问类的元数据,从而提供了一定的封装和安全性。
反射机制的设计
Java 的反射机制是通过 Class
类来实现的。以下是一些关键点:
Class
类:表示类或接口,提供了获取类元数据的方法。Field
类:表示类的成员变量,通过Class
对象获取。Method
类:表示类的方法,通过Class
对象获取。Constructor
类:表示类的构造方法,通过Class
对象获取。
示例
下面是一个示例,展示如何通过 Class
对象获取 Field
、Method
和 Constructor
对象。
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
对象来获取Field
、Method
和Constructor
对象。这种设计确保了对类元数据的访问是结构化和一致的。 - 封装和安全性:通过
Class
对象访问元数据提供了封装和安全性,确保只有在适当的上下文中才可以获取和操作类的元数据。
可以理解为这是 Java 的设计规定,以确保反射机制的安全性和一致性。
7 字节码对象是Class类的对象吗?
是的,字节码对象在 Java 中是 Class
类的对象。在 Java 中,每个类都有一个对应的 Class
对象,这个对象在运行时被 JVM 用来表示这个类的字节码。这个 Class
对象包含了有关该类的所有信息,例如类的名称、包、字段、方法、构造函数等。
详细解释
-
Class
对象:- 在 JVM 中,每个加载的类都有一个
Class
对象与之对应。这个Class
对象包含了该类的字节码,以及有关该类的所有元数据。 - 通过这个
Class
对象,Java 程序可以使用反射来获取类的信息和操作类的成员。
- 在 JVM 中,每个加载的类都有一个
-
字节码:
- Java 源代码被编译成字节码,这些字节码被存储在
.class
文件中。 - 当 JVM 加载一个类时,它会读取这个
.class
文件并创建一个Class
对象来表示这个类的字节码。
- Java 源代码被编译成字节码,这些字节码被存储在
示例代码
以下是一个简单的示例,展示如何获取一个类的 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
类的对象。