准备工作

创建一个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());
        }
    }
}

image-xnid.png

该代码中通过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();
        }
    }
}

image-embf.png

通过反射获取字段的值

这里通过给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();
  • 适用场景:当你已经有一个对象实例,并希望获取其运行时类型。

  • 特点

    • 返回的是对象的实际运行时类型(可能与声明类型不同,特别是在多态的情况下)。

    • 适用于需要根据对象实例动态获取类信息的场景。