准备工作
创建一个Person类
package com.oop.demo9;
public class Person {
public String name;
int age;
}
创建两个接口,一个Move接口一个Study接口,接口中定义方法。
Move.java,该文件中定义moveType()方法。
package com.oop.demo9;
public interface Move {
void moveType();
}
Study.java,该文件中定义一个studyInfo()方法。
package com.oop.demo8;
public interface Study {
void studyInfo();
}
然后创建一个Student.java类继承Person类和Move、Study接口,继承接口必须要实复写接口中的方法。
package com.oop.demo9;
public class Student extends Person implements Study, Move {
@Override
public void moveType() {
}
@Override
public void studyInfo() {
}
}
给Students类中设置字段,实现构造方法,并复写toString()方法,设置get,set方法,并设置注解。
设置上述完整的Student.java
package com.oop.demo9;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Table_a("db_student")
public class Student extends Person implements Study, Move {
@Field_a(columnName = "db_id",type = "int",length = 10)
int id;
@Field_a(columnName = "db_school",type = "varchar",length = 10)
String school;
@Field_a(columnName = "db_age",type = "int",length = 10)
private int age;
//有参构造方法
public Student(int age, int id, String school) {
this.age = age;
this.id = id;
this.school = school;
}
//无参构造方法
public Student() {}
//获取id的值
public int getId() {return id;}
//设置id的值
public void setId(int id) {this.id = id;}
//获取学校的值
public String getSchool() {return school;}
//获取年龄的值
public int getAge() {return age;}
//设置年龄的值
public void setAge(int age) {this.age = age;}
//设置学校的值
public void setSchool(String school) {this.school = school;}
@Override
public void moveType() {System.out.println("开车上学");}
@Override
public void studyInfo() {System.out.println("学习的是大学的知识");}
public void showInfo(){
System.out.println("学校是:"+this.school);
}
@Override
public String toString() {
return "Student{" +
"age=" + age +
", id=" + id +
", school='" + school + '\'' +
'}';
}
}
//创建注解
//类名的注解
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@interface Table_a{
String value();
}
//属性的注解
@Target({ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
@interface Field_a{
String columnName();
String type();
int length();
}
现在Student类有父类、接口、注解、构造方法、属性(字段)、方法。
反射操作
1、通过反射获取父类
新建一个getFather.java文件类进行获取父类
通过forName("com.oop.demo9.Student")来实例化Class对象,然后使用getSuperclass()方法类获取父类的Class对象并通过getName()方法获取父类名称。
package com.oop.demo9;
import java.lang.reflect.InvocationTargetException;
public class getFather {
public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, IllegalAccessException, InstantiationException {
Class<?> clazz = Class.forName("com.oop.demo9.Student");
// 获取父类
Class<?> superclass = clazz.getSuperclass();
// 输出父类名称
if (superclass != null) {
System.out.println("父类是: " + superclass.getName());
} else {
System.out.println("该类没有父类");
}
}
}
2、通过反射获取类属性字段
package com.oop.demo9;
import java.lang.reflect.Field;
public class getFields {
public static void main(String[] args) throws ClassNotFoundException {
// 获取Student类的Class对象
Class<?> clazz = Class.forName("com.oop.demo9.Student");
// 获取所有字段
Field[] fields = clazz.getDeclaredFields();
// 输出字段名称
System.out.println("Student类的字段有:");
for (Field field : fields) {
System.out.println(field.getName());
}
}
}
该代码中通过Class<?> clazz = Class.forName("com.oop.demo9.Student");实例化Class对象通过getDeclaredFields()方法来获取Public和Private属性的Field字段名,而getFields()方法只能获取Public属性的字段名。
通过循环遍历获取类以及其父类字段名称
package com.oop.demo9;
import java.lang.reflect.Field;
public class getFields {
public static void main(String[] args) throws ClassNotFoundException {
// 获取Student类的Class对象
Class<?> clazz = Class.forName("com.oop.demo9.Student");
// 遍历类及其父类
while (clazz != null) {
// 获取当前类的所有字段
Field[] fields = clazz.getDeclaredFields();
// 输出字段名称
System.out.println(clazz.getName() + "类的字段有:");
for (Field field : fields) {
System.out.println(field.getName());
}
// 获取父类
clazz = clazz.getSuperclass();
}
}
}
通过反射获取字段的值
这里通过给Student类的school属性进行赋值,然后设置成私有,并通过反射来获取这个私有属性的值。
通过反射获取school私有属性的值代码实例:
package com.oop.demo9;
import java.lang.reflect.Field;
public class getFieldValue {
public static void main(String[] args) throws Exception {
// 获取Student类的Class对象
Class<?> clazz = Class.forName("com.oop.demo9.Student");
// 创建Student类的实例
Student studentInstance = (Student)clazz.getDeclaredConstructor().newInstance();
//获取Studnet的Field的值
Field schoolField = clazz.getDeclaredField("school");
schoolField.setAccessible(true);
String schoolValue = (String)schoolField.get(studentInstance);
System.out.println("school的值是:"+schoolValue);
}
}
可以看到通过反射成功的获取到了Student的私有属性school的值,主要实现就是通过实例化Class对象并通过,实例化的Class对象通过getDeclaredConstructor().newInstance()方法来实例化Student对象,并通过getDeclaredField("school")获取对应的属性,setAccessible(true)设置私有属性可访问,通过get(studentInstance)方法来获取对应字段实例化对象的对应的属性的值。getDeclaredField()方法可以获取全部的字段,包括私有和共有。getField()只能获取共有字段。
3、通过反射获取方法
创建getMethods.java来获取对象的方法
package com.oop.demo9;
import java.lang.reflect.Method;
public class getMethods {
public static void main(String[] args) throws ClassNotFoundException {
// 获取Student类的Class对象
Class<?> clazz = Class.forName("com.oop.demo9.Student");
// 获取所有方法,包括继承的方法
Method[] methods = clazz.getDeclaredMethods();
// 输出方法名称
System.out.println("Student类的方法有:");
for (Method method : methods) {
System.out.println(method.getName());
}
}
}
这里通过Class<?> clazz = Class.forName("com.oop.demo9.Student");实例化Class对象,通过getDeclaredMethods()方法获取方法名数组,该方法可以获取Public和Private属性的方法,而getMethods()方法只能获取Public属性的方法名数组。
通过反射调用函数方法,创建invokeMethod.java文件来调用getId()和setId()方法
invokeMedthod.java
package com.oop.demo9;
import java.lang.reflect.Method;
public class invokeMedthod {
public static void main(String[] args) throws Exception {
// 获取Student类的Class对象
Class<?> clazz = Class.forName("com.oop.demo9.Student");
// 创建Student类的实例
Object studentInstance = clazz.getDeclaredConstructor().newInstance();
// 获取setId方法
Method setIdMethod = clazz.getMethod("setId", int.class);
// 调用setId方法,传入参数
setIdMethod.invoke(studentInstance, 123);
// 获取getId方法
Method getIdMethod = clazz.getMethod("getId");
// 调用getId方法,获取返回值
Object idValue = getIdMethod.invoke(studentInstance);
// 输出返回值
System.out.println("调用getId方法返回的值: " + idValue);
}
}
这里通过Class<?> clazz = Class.forName("com.oop.demo9.Student")创建Class对象实例,通过clazz.getDeclaredConstructor().newInstance()来创建Student对象实例。通过getMethod("setId", int.class)方法来获取对应的方法名和参数类型(通过函数名和参数类型确定调用那个函数),通过invoke()方法来调用对象的函数,invoke()的第一个参数为调用方法的实例对象第二个为参数,后续的就是调用Stendent的实例对象的getId()方法来获取设置的id值。
如果调用的方法有多个参数,那么getMedthod()方法中第一个是方法名,后面依次是参数的类型。例如有一个studentInfo(String school,int id),这个方法有两参数,那么使用getMedthod()获取这个函数就需要getMedthod("studentInfo",String.class,int.class)来获取这个指定的函数方法。如果方法是Private私有的那么就需要使用getDeclaredMethod()方法来获取私有方法,并设置setAccessible(true),然后就可以通过反射调用私有属性的方法了
这里Student类中添加了一个私有的方法
通过反射调用这个私有的方法
这里可以看到私有方法成功的通过反射进行调用。
4、通过反射获取构造方法
获取构方法名、构造方法参数、调用构造方法的实例代码如下:
package com.oop.demo9;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
public class getConstructor {
public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {
//获取Class实例化对象
Class clazz = Class.forName("com.oop.demo9.Student");
//获取所有构造方法到Constructor[]数组对象中
Constructor[] declaredConstructors = clazz.getDeclaredConstructors();
int i = 0;
for (Constructor declaredConstructor : declaredConstructors) {
String out = String.format("构造方法[%d]: %s", i, declaredConstructor);
System.out.println(out);
i++;
}
//获取带有参数的构造函数
Constructor constructor = clazz.getConstructor(int.class, int.class, String.class);
Class<?>[] parameterTypes = constructor.getParameterTypes();
System.out.println("构造方法的参数类型:");
for (Class<?> paramType : parameterTypes) {
System.out.println(paramType.getName());
}
//实例化通过构造参数对象实例化Student对象
Student student = (Student)constructor.newInstance(1, 123, "张三");
System.out.println(student);
}
}
通过实例化Class对象,通过getDeclaredConstructors()获取所有构造方法包括私有和共有,那么getConstructors()只能获取到共有的构造方法,通过实例化的Class对象的getConstructor(int.class, int.class, String.class)方法,该方法通过构造方法里面的参数(int.class, int.class, String.class)来确定调用的构造方法且该方法只能获取共有的构造方法,则也有一个对应的getDeclaredConstructor()方法来获取共有和私有的构造方法。实例化的Constructor对象通过调用getParameterTypes()方法来获取构造方法内需要要的所有参数类型。通过反射调用Student类构造方法只需要实例化Student类对象,则通过Constructor实例化对象调用newInstance()方法,在newInstance()方法内输入构造参数需要的参数即可调用Student类指定的构造方法。
执行情况如下图:
5、通过反射获取注解值
通过反射获取注解,并获取注解中的值
实例代码:
package com.oop.demo9;
import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
public class getAnnote {
public static void main(String[] args) throws ClassNotFoundException {
//实例化Class对象
Class<?> clazz = Class.forName("com.oop.demo9.Student");
//获取类中的注解
Annotation[] annotations = clazz.getAnnotations();
for (Annotation annotation : annotations) {
System.out.println("类中的注解:"+annotation);
Table_a annotation1 = (Table_a) annotation;
System.out.println("类中的注解value的值:"+annotation1.value());
}
Field[] fields = clazz.getDeclaredFields();
for (Field field : fields) {
System.out.println("字段名"+field.getName());
if (field.isAnnotationPresent(Field_a.class)) {
Field_a fieldAnnotation = field.getAnnotation(Field_a.class);
System.out.println("字段注解: columnName:"+fieldAnnotation.columnName() +"\ttype:"+fieldAnnotation.type()+"\tlength:"+fieldAnnotation.length());
}
}
}
}
Student类中的注解
通过反射获取注解,并获取注解中的值
通过实例化Class对象为clazz,然后调用getAnnotations()获取类对应的注解,如果要获取类对应的注解需要将类对象的getAnnotations()获取到的Annotation对象强制换回成对应的注解对象然后使用注解对象的value()方法来获取类对象注解的值。
如果要获取字段的注解的话,要先获取字段对象,然后通过字段对象获取字段的注解对象,然后再使用注解对象中的对应接口调用对应的值。
6、通过反射获取接口
通过反射获取类的接口,可以根据获取的接口来获取接口中的方法和接口中的字段
代码实例:
package com.oop.demo9;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
public class getImp {
public static void main(String[] args) throws ClassNotFoundException {
//实例化Class对象
Class<?> clazz = Class.forName("com.oop.demo9.Student");
//获取类实现的接口名并通过for循环打印输出
Class<?>[] interfaces = clazz.getInterfaces();
for (Class<?> anInterface : interfaces) {
System.out.println(anInterface.getName());
//获取接口中的方法名并通过for循环打印输出
Method[] declaredMethods = anInterface.getDeclaredMethods();
for (Method declaredMethod : declaredMethods) {
System.out.println(declaredMethod.getName());
}
//获取接口中的字段名并通过for循环打印输出
Field[] declaredFields = anInterface.getDeclaredFields();
for (Field declaredField : declaredFields) {
declaredField.setAccessible(true);
System.out.println(declaredField.getName());
}
}
}
}
执行结果:
这里成功打印了反射对应的类继承的接口,以及接口中定义的方法,应为这里接口中没有设置字段,所以没有字段输出。
实例化Class的几种方法
1. 使用 .class
语法
这是最直接的方式,用于在编译时已知类的情况下获取其 Class
对象。
Class<?> clazz = MyClass.class;
适用场景:当你已经知道类的名称,并且类在编译时是确定的。
特点:
简单、直观。
不会抛出异常。
适用于静态上下文。
2. 使用 Class.forName()
方法
通过类的全限定名(包括包名)动态加载类并获取其 Class
对象。
Class<?> clazz = Class.forName("com.example.MyClass");
适用场景:当类的名称是动态的(例如从配置文件或用户输入中读取)。
特点:
动态性较强。
如果类不存在,会抛出
ClassNotFoundException
。常用于框架和插件系统。
3. 使用 Object.getClass()
方法
对于已经存在的对象实例,可以通过调用 getClass()
方法获取其运行时类的 Class
对象。
MyClass obj = new MyClass();
Class<?> clazz = obj.getClass();
适用场景:当你已经有一个对象实例,并希望获取其运行时类型。
特点:
返回的是对象的实际运行时类型(可能与声明类型不同,特别是在多态的情况下)。
适用于需要根据对象实例动态获取类信息的场景。