JavaSE——反射

一、反射的基本介绍

        Java的反射机制是在程序运行状态中,对于任意一个类,都能够知道这个类的所有的属性和方法。对于任意一个对象,都能够调用它的任意一个方法和属性。这种动态获取的信息以及动态调用对象的方法的功能称之为Java的反射机制。

        要想解剖一个类,必须先要获取这个类的字节码文件对象。解剖这个类,使用的就是Class类中的方法。字节码文件对象就对应到Class类型的对象。

  1. Class也是类,因此也继承Object类
  2. Class类对象不是new出来的,而是系统创建的。
  3. Class对象是存放在堆的。
  4. 类的字节码二进制数据,是放在方法区的,有的地方称为类的元数据(包括方法代码、变量名、方法名、访问权限等等)
  5. 类在第一次加载的时候会执行下面的方法:该方法只会执行一次,所以类只会加载一次。
    public Class<?> loadClass(String name) throws ClassNotFoundException {
        return loadClass(name, false);
    }

二、获取Class类对象的六种方式

方式一:在源代码阶段/编译,即此时只有.java和.class文件;        

               或者已知一个类的全类名,且该类在类路径下;        

               多用于配置文件,读取类全路径,加载类,可能抛出ClassNotFoundException

               使用Class.forName("全类名")

方式二:在加载阶段,若已知具体的类,此时将.class文件加载到内存中,该方式最为安全可靠,程序性能最高

               使用类名.class

方式三:在运行阶段,此时创建了类的实例对象,通过创建好的对象,获取Class对象

               使用对象.getClass()

方式四:ClassLoader cla = 对象.getClass().getClassLoader();

               Class clazz = cla.loadClass("全类名");

方式五:基本数据类型(int、char、boolean、float、double、byte、long、short)按如下方式得到Class对象

               Class clazz = 基本数据类型.class

方式六:基本数据类型对应的包装类,可以通过.TYPE得到Class类对象

               Class clazz = 包装类.TYPE

Student类:

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

    public Student() {
    }

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

    protected Student(int age) {
        this.age = age;
    }

    private Student(String name, int age) {
        this.name = name;
        this.age = age;
    }
}
public class MyReflectDemo1 {
    public static void main(String[] args) throws ClassNotFoundException {
        // 方式一:最常用
        // 字符串必须是全路径类名
        Class<?> aClass2 = Class.forName("com.testdemo.myreflect1.Student");
        System.out.println(aClass2);
        // class com.testdemo.myreflect1.Student     
        Class<Date> aClass1 = (Class<Date>) Class.forName("java.util.Date");
        System.out.println(aClass1);
        // class java.util.Date

        // 方式二:经常当做参数来传递
        Class clazz2 = Student.class;

        // 方式三:有了这个类的对象时,才可以使用
        Student student = new Student();
        Class clazz3 = student.getClass();

        System.out.println(clazz1 == clazz2); // true
        System.out.println(clazz1 == clazz3); // true
        System.out.println(clazz2 == clazz3); // true

        // 方式四:
        ClassLoader cla = student.getClass().getClassLoader();
        Class clazz4 = cla.loadClass("com.testdemo.myreflect1.Student");
        System.out.println(clazz4 == clazz3); // true

        // 通过getClass()这种方式能够获取运行时类型
        List<String> list = new ArrayList<>();
        System.out.println(list.getClass());
        // class java.util.ArrayList

        // 方式五:基本数据类型
        Class<Integer> clazz5 = int.class;
        System.out.println(clazz5); // int
        Class<Boolean> clazz6 = boolean.class;
        System.out.println(clazz6); // boolean
        // 接口
        Class<List> listClass = List.class;
        System.out.println(listClass);
        // interface java.util.List
        // 引用数据类型
        Class<int[]> aClass = int[].class;
        System.out.println(aClass);
        // class [I

        // 方式六:包装类
        Class<Integer> type = Integer.TYPE;
        System.out.println(type); // int
        System.out.println(type == clazz5); // true

        Class<Character> type1 = Character.TYPE;
        System.out.println(type1); // char
    }
}

三、哪些类型有Class对象

  1. 外部类,成员内部类,静态内部类,局部内部类,匿名内部类
  2. interface:接口
  3. 数组
  4. enum:枚举
  5. annotation:注解
  6. 基本数据类型
  7. void
public class AllTypeClass {
    public static void main(String[] args) {

        Class<String> cls1 = String.class;//外部类
        Class<Serializable> cls2 = Serializable.class;//接口
        Class<Integer[]> cls3 = Integer[].class;//数组
        Class<float[][]> cls4 = float[][].class;//二维数组
        Class<Deprecated> cls5 = Deprecated.class;//注解
        //枚举
        Class<Thread.State> cls6 = Thread.State.class;
        Class<Long> cls7 = long.class;//基本数据类型
        Class<Void> cls8 = void.class;//void数据类型
        Class<Class> cls9 = Class.class;//

        System.out.println(cls1);
        System.out.println(cls2);
        System.out.println(cls3);
        System.out.println(cls4);
        System.out.println(cls5);
        System.out.println(cls6);
        System.out.println(cls7);
        System.out.println(cls8);
        System.out.println(cls9);
    }
}

四、类的加载

(一)静态加载与动态加载

反射机制是java实现动态语言的关键,也就是通过反射实现类的动态加载。

1.静态加载:编译时记载相关的类,如果没有则报错,依赖性太强

2.动态加载:运行时加载需要的类,如果运行时不用该类,即使不存在该类,也不报错,降低了依赖性。

(二)类加载的四个时机

  1. 当创建对象时(new)                                        // 静态加载
  2. 当子类被加载时,父类也会被加载                 // 静态加载
  3. 调用类中的静态成员时                                   // 静态加载
  4. 通过反射                                                        // 动态加载

(三)类加载的五个阶段(重要)

1.加载

        JVM在该阶段的主要目的是,将字节码从不同的数据源(可能是class文件、也可能是jar包,甚至网络)转化为二进制字节流加载到内存中,并生成一个代表该类的java.lang.Class对象,作为对方法区中这些数据的访问入口。

2.验证

        目的是为了确保Class文件的字节流中包含的信息符合当前虚拟机的要求,并且不会危害虚拟机自身的安全。验证过程主要包括文件格式验证、元数据验证、字节码验证和符号引用验证等。

  • 文件格式验证:验证字节流是否符合Class文件格式的规范,如是否以0xCAFEBABE开头、主次版本号是否在当前虚拟机处理范围内等。
  • 元数据验证:对字节码描述的信息进行语义分析,以保证其描述的信息符合Java语言规范的要求。
  • 字节码验证:通过数据流和控制流分析,确定程序语义是合法的、符合逻辑的。
  • 符号引用验证:确保解析动作能正确执行,如通过符号引用能找到对应的类和方法等。
  • 可以考虑使用-Xverify:none参数来关闭大部分的类验证措施,缩短虚拟机类加载的时间。

3.准备

  • JVM会在该阶段对静态变量,分配内存并初始化。
  • 内存分配仅包括类变量(static),而不包括实例变量。实例变量会在对象实例化时随着对象一块分配在Java堆中。
  • 所设置的初始值通常是数据类型默认的零值(如0、0L、null、false等),而不是在Java代码中被显式地赋予的值。
  • 这些变量梭式窑的内存都将在方法区中进行分配。
class A {
    // 属性-成员变量-字段
    // 类加载的连接阶段-准备 属性是如何处理
    // 1. n1 是实例属性, 不是静态变量,因此在准备阶段,是不会分配内存
    // 2. n2 是静态变量,分配内存 n2 是默认初始化 0 ,而不是20
    // 3. n3 是static final 是常量, 他和静态变量不一样, 因为一旦赋值就不变 n3 = 30
    public int n1 = 10;
    public static  int n2 = 20;
    public static final  int n3 = 30;
}

4.解析

  • 将常量池内的符号引用替换为直接引用的过程。

        例如:String str = "Hello, World!";

        在上述过程中,str是一个变量引用,它指向一个String对象。

        

        符号引用阶段:在编译时,str变量是通过符号引用来表示的。它被存储在类文件的常量池中,作为符号引用的一部分。这种引用形式仅在编译时存在,用于标识变量的名称和类型。
        直接引用阶段:在运行时,str变量会变成一个实际的内存地址,指向堆内存中的String对象。这种引用形式在程序运行时存在,用于直接访问对象的实际内存位置。

5.初始化

  • 到初始化阶段,才真正开始执行类中定义的Java程序代码,此阶段是执行<clinit>()方法的过程。
  • <clinit>()方法是由编译器按语句在源文件中出现的顺序,依次自动收集类中的所有静态变量的赋值动作和静态代码块中的语句,并进行合并。
//    依次自动收集类中的所有静态变量的赋值动作和静态代码块中的语句,并合并
/*
        clinit() {
            System.out.println("B 静态代码块被执行");
            //num = 300;
            num = 100;
        }
        合并: num = 100
*/
  • 虚拟机会保证一个类的<clinit>()方法在多线程环境中被正确地加锁、同步,如果多个线程同时去初始化一个类,那么只会有一个线程去执行这个类的<clinit>()方法,其他线程都需要阻塞等待,直到活动线程执行<clinit>()方法完毕。
protected Class<?> loadClass (String name,boolean resolve) throws ClassNotFoundException {
    // 正因为有这个机制,才能保证某个类在内存中, 只有一份Class对象
    synchronized (getClassLoadingLock(name)) {
        //....
    }
}

五、使用反射获取构造方法

(一)Class类中用于获取构造方法的方法

方法名作用
Constructor<?>[] getConstructors()返回所有公共构造方法对象的数组
Constructor<?>[] getDeclaredConstructors()返回所有构造方法对象的数组
Constructor<T> getConstructor(Class<?>... parameterTypes)返回单个公共构造方法对象
Constructor<T> getDeclaredConstructor(Class<?>... parameterTypes)返回单个构造方法对象

1.获取所有公共构造方法

Class clazz = Class.forName("com.testdemo.Student");
Constructor[] conn1 = clazz.getConstructors();
for (Constructor constructor : conn1) {
    System.out.println(constructor);
}
/**
 * public com.testdemo.Student(java.lang.String)
 * public com.testdemo.Student()
 */

2.获取所有构造方法

Constructor[] conn2 = clazz.getDeclaredConstructors();
for (Constructor constructor : conn2) {
    System.out.println(constructor);
}
/**
 * private com.testdemo.Student(java.lang.String,int)
 * protected com.testdemo.Student(int)
 * public com.testdemo.Student(java.lang.String)
 * public com.testdemo.Student()

3.获取单个公共构造方法对象

Constructor conn3 = clazz.getConstructor();
System.out.println(conn3);
// public com.testdemo.Student()

Constructor conn4 = clazz.getConstructor(String.class);
System.out.println(conn4);
// public com.testdemo.Student(java.lang.String)

4.获取单个构造方法对象

Constructor conn5 = clazz.getDeclaredConstructor();
System.out.println(conn5);
// public com.testdemo.Student()

Constructor conn6 = clazz.getDeclaredConstructor(String.class);
System.out.println(conn6);
// public com.testdemo.Student(java.lang.String)

System.out.println(conn6.getModifiers());
// public修饰的构造方法,返回1

Constructor conn7 = clazz.getDeclaredConstructor(int.class);
System.out.println(conn7);
// protected com.testdemo.Student(int)

System.out.println(conn7.getModifiers());
// protected修饰的构造方法,返回4

Constructor conn8 = clazz.getDeclaredConstructor(String.class, int.class);
System.out.println(conn8);
// private com.testdemo.Student(java.lang.String,int)

// 获取修饰符
System.out.println(conn8.getModifiers());
// private修饰的构造方法,返回2

// 获取构造方法的每个参数
Parameter[] parameters = conn8.getParameters();
for (Parameter parameter : parameters) {
    System.out.println(parameter);
}
/**
 * java.lang.String arg0
 * int arg1
 * */

修饰符都是2的n次方,如果是多个修饰符,比如private static,那么返回值就是2+8=10 

(二)Constructor类中用于创建对象的方法

方法名作用
T newInstance(Object... initargs)根据指定的构造方法创建对象
setAccessible(boolean flag)设置为true,表示取消访问检查
// 利用获取到的构造方法,创建类的对象
// 暴力反射:临时取消权限校验
conn8.setAccessible(true);
Student newInstance = (Student) conn8.newInstance("张三", 26);
System.out.println(newInstance);
// Student(name=张三, age=26)

(三)通过构造方法来创建对象

1. 通过反射创建Student对象

Student类:

public class Student {
    private String name;

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

    @Override
    public String toString() {
        return "Student{" +
                "name='" + name + '\'' +
                '}';
    }
}
private static void demo1() throws ClassNotFoundException, NoSuchMethodException, 
InstantiationException, IllegalAccessException, InvocationTargetException {
    // 1.先获取class字节码文件对象
    Class<?> aClass = Class.forName("com.testdemo.Student");
    
    // 通过Class创建对象 调用的是无参构造
//  Object instance = aClass.newInstance();
//  System.out.println(instance);
    
    // 如果类中就是没有无参构造
    // 2.获取构造方法   参数就是构造方法的参数类型   
    // 这里的getConstructor不传参表示调用无参构造
    // 如果getConstructor传入参数类名就获取到了有参构造
    Constructor<?> constructor = aClass.getConstructor(String.class);
    System.out.println(constructor);
    // public com.testdemo.Student(java.lang.String)
    
    // 3.创建对象     参数就是构造方法的实参
    Object instance = constructor.newInstance("张三");
    System.out.println(instance);
    // Student{name='张三'}
    
    // 上面的3步就相当于
    Student student = new Student("张三");*/
}

2.通过反射调用String对象的方法

public static void main(String[] args) throws InstantiationException, 
IllegalAccessException, NoSuchMethodException, InvocationTargetException {
    // 例如我们要创建String对象,并调用String的substring方法:
    // 1.获取class字节码文件对象
    Class<String> stringClass = String.class;
    // 2.获取有参构造,传入参数类型
    Constructor<String> constructor = stringClass.getConstructor(byte[].class, int.class, int.class);
    // 3.创建对象,传入实参
    String s = constructor.newInstance(new byte[]{97, 98, 99}, 0, 3);
    System.out.println(s); // abc

    // 获取所有公共的构造方法
    Constructor<?>[] constructors = stringClass.getConstructors();
    for (Constructor<?> constructor1 : constructors) {
        System.out.println(constructor1);
    }

    // 获取指定的构造方法
    Constructor<String> declaredConstructor = stringClass.getDeclaredConstructor(byte[].class, byte.class);
    System.out.println(declaredConstructor);
    // 暴力破解
    declaredConstructor.setAccessible(true);
    String s1 = declaredConstructor.newInstance(new byte[]{97, 98, 99}, (byte) 1);
    System.out.println(s1); // 扡 (乱码)

    // 获取所有的构造方法
    Constructor<?>[] declaredConstructors = stringClass.getDeclaredConstructors();
    for (Constructor<?> declaredConstructor1 : declaredConstructors) {
        System.out.println(declaredConstructor1);
    }
}

3.通过私有的构造方法创建对象

Student类中定义一个私有的构造方法,通过该构造方法来创建对象:

// 通过该方法创建Student对象
private Student(String name,int a){
    this.name = name;
public static void main(String[] args) throws InstantiationException, IllegalAccessException, NoSuchMethodException, InvocationTargetException, ClassNotFoundException {
    // 方式一:
    Class<?> aClass = Class.forName("com.testdemo.Student");
    Constructor<?> declaredConstructor = aClass.getDeclaredConstructor(String.class, int.class);
    // 暴力破解
    declaredConstructor.setAccessible(true);
    // 创建对象,并传入实参
    Object instance = declaredConstructor.newInstance("张三", 18);
    System.out.println(instance);
    // Student{name='张三'}

    // 方式二:
    Class<Student> studentClass = Student.class;
    Constructor<Student> declaredConstructor1 = studentClass.getDeclaredConstructor(String.class, int.class);
    declaredConstructor1.setAccessible(true);
    Student instance1 = declaredConstructor1.newInstance("李四", 20);
    System.out.println(instance1);
    // Student{name='李四'}
}

六、使用反射获取属性

Student类:

@Data
public class Student {
    private String name;
    private int age;
    public String gender;
    public double store;

    public Student() {
    }

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

(一)Class类中用于获取成员变量的方法

方法名作用
Field[] getFields()返回所有公共成员变量对象的数组
Field[] getDeclaredFields()返回所有成员变量对象的数组
Field getField(String name)返回单个公共成员变量对象
Field getDeclaredField(String name)返回单个成员变量对象
Class clazz = Class.forName("com.testdemo.myreflect3.Student");
// 1.获取所有公共的成员变量
Field[] fields1 = clazz.getFields();
for (Field field : fields1) {
    System.out.println(field);
}
/**
 * public java.lang.String com.testdemo.myreflect3.Student.gender
 * public double com.testdemo.myreflect3.Student.store
 */

// 2.获取所有的成员变量
Field[] fields2 = clazz.getDeclaredFields();
for (Field field : fields2) {
    System.out.println(field);
}
/**
 * private java.lang.String com.testdemo.myreflect3.Student.name
 * private int com.testdemo.myreflect3.Student.age
 * public java.lang.String com.testdemo.myreflect3.Student.gender
 * public double com.testdemo.myreflect3.Student.store
 */

// 3.获取单个公共的成员变量
Field field3 = clazz.getField("gender");
System.out.println(field3);
// public java.lang.String com.testdemo.myreflect3.Student.gender

Field field4 = clazz.getField("store");
System.out.println(field4);
// public double com.testdemo.myreflect3.Student.store

// 4.获取单个的成员变量
Field name = clazz.getDeclaredField("name");
System.out.println(name);
// private java.lang.String com.testdemo.myreflect3.Student.name

Field age = clazz.getDeclaredField("age");
System.out.println(age);
// private int com.testdemo.myreflect3.Student.age

Field gender = clazz.getDeclaredField("gender");
System.out.println(gender);
// public java.lang.String com.testdemo.myreflect3.Student.gender

Field store = clazz.getDeclaredField("store");
System.out.println(store);
// public double com.testdemo.myreflect3.Student.store

// 获取成员变量的权限修饰符
System.out.println(store.getModifiers()); // 1

// 获取成员变量的名字
System.out.println(store.getName()); // store

// 获取成员变量的数据类型
System.out.println(store.getType()); // double

(二)Field类中用于创建对象的方法

方法名作用
void set(Object obj, Object value)赋值
Object get(Object obj)获取值
// 获取成员变量记录的值
// 暴力反射:临时取消权限校验
name.setAccessible(true);
Student student = new Student("张三", 18, "男", 98.5);
String value = (String) name.get(student);
System.out.println(value); // 张三

// 修改对象的属性值
name.set(student,"李四");
System.out.println(student);
// Student(name=李四, age=18, gender=男, store=98.5)

修改String的hash值:

Class<String> stringClass = String.class;
Field field = stringClass.getDeclaredField("hash");
field.setAccessible(true);
String str = "abc";
field.set(str,10); // 相当于 "abc".hash = 10;
Object o = field.get(str);
System.out.println(o); // 10

// 获取属性的修饰符
int modifiers = stringClass.getModifiers();
System.out.println(modifiers); // 17

七、使用反射获取方法

private static void demo1() throws NoSuchMethodException, IllegalAccessException, 
InvocationTargetException {
    // 使用反射获取方法
    Class<String> stringClass = String.class;
    Method substring = stringClass.getMethod("substring", int.class, int.class);
    String str = "abcd";
    Object o = substring.invoke(str, 1, 3); // 相当于 "abcd".substring(1,3)
    System.out.println(o); //bc
}

八、使用反射获取枚举常量、接口、父类、包名等

public class TestDemo5 {
    public static void main(String[] args) {
        // 反射获取字节码文件对象
        Class<Level> levelClass = Level.class;
        
        // 获取枚举Level中的所有的枚举常量
        Level[] enumConstants = levelClass.getEnumConstants();
        for (Level enumConstant : enumConstants) {
            System.out.println(enumConstant);
        }

        // 如果不是枚举常量,返回null
        Class<String> stringClass = String.class;
        String[] enumConstants1 = stringClass.getEnumConstants();
        System.out.println(enumConstants1); // null

        // 反射获取接口
        Class<?>[] interfaces = stringClass.getInterfaces();
        for (Class<?> anInterface : interfaces) {
            System.out.println(anInterface);
        }
        /**
         * interface java.io.Serializable
         * interface java.lang.Comparable
         * interface java.lang.CharSequence
         */

        // 反射获取父类
        Class<? super String> superclass = stringClass.getSuperclass();
        System.out.println(superclass);
        // class java.lang.Object

        // 反射获取全路径类名
        String name = stringClass.getName();
        System.out.println(name);
        // java.lang.String

        // 反射获取简单类名
        String simpleName = stringClass.getSimpleName();
        System.out.println(simpleName);
        // String

        // 反射获取包名
        String packageName = stringClass.getPackageName();
        System.out.println(packageName);
        // java.lang

        Package aPackage = stringClass.getPackage();
        System.out.println(aPackage);
        // package java.lang
    }
}

enum Level {
    A, B, C, D, E, F;
}

九、使用反射获取成员方法

Student类:

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

    public void sleep() {
        System.out.println("睡觉");
    }

    public void sleep(String someone) {
        System.out.println(someone + "在睡觉");
    }

    public void eat() {
        System.out.println("吃东西");
    }

    private String eat(String something) throws IOException, NullPointerException, ClassCastException {
        System.out.println("在吃" + something);
        return "菜单";
    }

    private void eat(String something, int a) {
        System.out.println("在吃" + something);
    }
}

(一)Class类中用于获取成员方法的方法

方法作用
Method[] getMethods()返回所有公共成员方法对象的数组,包括继承的
Method[] getDeclaredMethods()返回所有成员方法对象的数组,不包括继承的
Method getMethod(String name, Class<?>... parameterTypes)返回单个公共成员方法对象
Method getDeclaredMethod(String name, Class<?>... parameterTypes)返回单个成员方法对象
Class clazz = Class.forName("com.testdemo.myreflect4.Student");
// 1.获取所有公共的成员方法(包含父类中所有的公共方法)
Method[] methods = clazz.getMethods();
for (Method method : methods) {
    System.out.println(method);
}
/**
 * public java.lang.String com.testdemo.myreflect4.Student.getName()
 * public boolean com.testdemo.myreflect4.Student.equals(java.lang.Object)
 * public java.lang.String com.testdemo.myreflect4.Student.toString()
 * public int com.testdemo.myreflect4.Student.hashCode()
 * public void com.testdemo.myreflect4.Student.sleep()
 * public void com.testdemo.myreflect4.Student.sleep(java.lang.String)
 * public void com.testdemo.myreflect4.Student.setName(java.lang.String)
 * public void com.testdemo.myreflect4.Student.setAge(int)
 * public void com.testdemo.myreflect4.Student.eat()
 * public int com.testdemo.myreflect4.Student.getAge()
 * public final native void java.lang.Object.wait(long) throws java.lang.InterruptedException
 * public final void java.lang.Object.wait(long,int) throws java.lang.InterruptedException
 * public final void java.lang.Object.wait() throws java.lang.InterruptedException
 * public final native java.lang.Class java.lang.Object.getClass()
 * public final native void java.lang.Object.notify()
 * public final native void java.lang.Object.notifyAll()
 * public void com.testdemo.myreflect4.Student.sleep(java.lang.String)
 */

// 2.获取所有的成员方法(不包含父类的成员方法)
Method[] declaredMethods = clazz.getDeclaredMethods();
for (Method declaredMethod : declaredMethods) {
    System.out.println(declaredMethod);
}
/**
 * public java.lang.String com.testdemo.myreflect4.Student.getName()
 * public boolean com.testdemo.myreflect4.Student.equals(java.lang.Object)
 * public java.lang.String com.testdemo.myreflect4.Student.toString()
 * public int com.testdemo.myreflect4.Student.hashCode()
 * public void com.testdemo.myreflect4.Student.sleep()
 * public void com.testdemo.myreflect4.Student.sleep(java.lang.String)
 * public void com.testdemo.myreflect4.Student.setName(java.lang.String)
 * public int com.testdemo.myreflect4.Student.getAge()
 * public void com.testdemo.myreflect4.Student.setAge(int)
 * protected boolean com.testdemo.myreflect4.Student.canEqual(java.lang.Object)
 * private void com.testdemo.myreflect4.Student.eat(java.lang.String,int)
 * public void com.testdemo.myreflect4.Student.eat()
 * private java.lang.String com.testdemo.myreflect4.Student.eat(java.lang.String) throws java.io.IOException,java.lang.NullPointerException,java.lang.ClassCastException
 * public void com.testdemo.myreflect4.Student.sleep(java.lang.String)
 */

// 3.获取单个公共的成员方法对象
Method sleep = clazz.getMethod("sleep", String.class);
System.out.println(sleep);
// public void com.testdemo.myreflect4.Student.sleep(java.lang.String)

// 4.获取单个成员方法对象
Method eat = clazz.getDeclaredMethod("eat", String.class, int.class);
System.out.println(eat);
// private void com.testdemo.myreflect4.Student.eat(java.lang.String,int)

Method eat1 = clazz.getDeclaredMethod("eat", String.class);
System.out.println(eat1);
// private java.lang.String com.testdemo.myreflect4.Student.eat(java.lang.String)
// throws java.io.IOException,java.lang.NullPointerException,java.lang.ClassCastException

// 获取无参的成员方法
Method eat2 = clazz.getDeclaredMethod("eat");
System.out.println(eat2);
// public void com.testdemo.myreflect4.Student.eat()

// 获取方法的修饰符
int modifiers = eat1.getModifiers();
System.out.println(modifiers); // 2

// 获取方法的名字
String name = eat1.getName();
System.out.println(name); // eat

// 获取方法的形参
Parameter[] parameters = eat1.getParameters();
for (Parameter parameter : parameters) {
    System.out.println(parameter);
}
// java.lang.String arg0

// 获取方法抛出的异常
Class[] exceptionTypes = eat1.getExceptionTypes();
for (Class exceptionType : exceptionTypes) {
    System.out.println(exceptionType);
}
/**
 * class java.io.IOException
 * class java.lang.NullPointerException
 * class java.lang.ClassCastException
 */
public class TestDemo7 {
    public static void main(String[] args) throws NoSuchFieldException, 
NoSuchMethodException {
        Class<String> stringClass = String.class;
        
        // 获取属性的声明类型
        Field field = stringClass.getDeclaredField("hash");
        System.out.println(field.getType()); // int

        // 获取指定的成员方法
        Method getBytes = stringClass.getDeclaredMethod("getBytes", String.class);

        // 获取方法的声明类
        System.out.println(getBytes.getDeclaringClass());
        // class java.lang.String

        // 继承的wait方法不可以
//        Method method = stringClass.getDeclaredMethod("wait");
//        System.out.println(method);

        // 获取方法的返回值类型
        Class<?> returnType = getBytes.getReturnType();
        System.out.println(returnType);
        // class [B

        // 获取方法的编译时异常
        Class<?>[] exceptionTypes = getBytes.getExceptionTypes();
        for (Class<?> exceptionType : exceptionTypes) {
            System.out.println(exceptionType);
        }
        // class java.io.UnsupportedEncodingException

        // 获取方法的参数个数
        int parameterCount = getBytes.getParameterCount();
        System.out.println(parameterCount); // 1

        // 获取参数类型
        Class<?>[] parameterTypes = getBytes.getParameterTypes();
        for (Class<?> parameterType : parameterTypes) {
            System.out.println(parameterType);
        }
        // class java.lang.String

        // 判断是否是可变参数
        System.out.println(getBytes.isVarArgs()); // false
    }
}

(二)Method类中用于创建对象的方法

方法作用
Object invoke(Object obj, Object... args)

运行方法

参数一:用obj对象调用该方法

参数二:调用方法的传递的参数(如果没有就不写)

返回值:方法的返回值(如果没有就不写)

// 方法运行
Student student = new Student();
eat1.setAccessible(true);
String result = (String) eat1.invoke(student, "汉堡包");
System.out.println(result);
// 在吃汉堡包
// 菜单

Student类中新增私有成员方法:

private String eat(String str) {
    return this.name + "今天中午吃" + str;
}
private static void demo2() throws NoSuchMethodException, IllegalAccessException,
 InvocationTargetException {
    // 使用反射获取成员方法
    Class<Student> studentClass = Student.class;
    Method eat = studentClass.getDeclaredMethod("eat", String.class);
    eat.setAccessible(true);
    Student student = new Student("张三");
    String str = "黄焖鸡米饭";
    Object invoke = eat.invoke(student, str);
    System.out.println(invoke);
}

十、反射中用于判断的API

public class TestDemo8 {
    public static void main(String[] args) {
        String str = "abc";
        Class<int[]> aClass = int[].class;
        Class<String> stringClass = String.class;

        // 判断是否是一个数组
        System.out.println(aClass.isArray()); // true
        
        // 判断是否是一个枚举
        System.out.println(Level.class.isEnum()); // true
        
        // 判断是否是一个接口
        System.out.println(List.class.isInterface()); // true
        
        // 判断是否是一个基本数据类型
        System.out.println(int.class.isPrimitive()); // true
        
        Object obj = new Object() {};
        // 判断是否是匿名内部类
        System.out.println(obj.getClass().isAnonymousClass()); // true
        
        // 判断是否是方法内部类
        System.out.println(aClass.isLocalClass()); // false
        
        // 判断是否是成员内部类
        System.out.println(aClass.isMemberClass()); // false
        
        // 判断是否是静态内部类
        System.out.println(aClass.isSynthetic()); // false
        
        // 判断类和对象关系
        System.out.println(str instanceof  String); // true
        System.out.println(stringClass.isInstance(str)); // true
        
        // 判断类和类之间的关系
        System.out.println(Object.class.isAssignableFrom(stringClass)); // true
        
        // 判断类和接口之间的关系
        System.out.println(Serializable.class.isAssignableFrom(stringClass)); // true
    }
}

十一、反射的作用

  1. 获取一个类里面所有的信息,获取到了之后,再执行其他的业务逻辑
  2. 结合配置文件,动态地创建对象并调用方法

(一)使用反射保存任意对象数据

对于任意一个对象,都可以把对象所有的字段名和值,保存到文件中去。

创建Cat类:

@Data
@AllArgsConstructor
public class Cat {
    private String ownerName;
    private String catName;
    private int age;
    private String breed;
}

创建Fish类:

@Data
@AllArgsConstructor
public class Fish {
    private int age;
    private String breed;
}

创建测试类:

public class TestDemo {
    public static void main(String[] args) throws IllegalAccessException, IOException {
        Cat cat = new Cat("张三", "咪咪", 2, "奶牛猫");
        Fish fish = new Fish(1, "鲤鱼");

        saveObjects(cat);
        saveObjects(fish);
    }

    private static void saveObjects(Object obj) throws IllegalAccessException, IOException {
        BufferedWriter bw = new BufferedWriter(new FileWriter("chapter19\\src\\com\\testdemo\\myreflect5\\a.txt",true));
        // 获取字节码文件的对象
        Class clazz = obj.getClass();

        // 获取所有的成员变量
        Field[] fields = clazz.getDeclaredFields();
        for (Field field : fields) {
            field.setAccessible(true);
            // 获取属性名字
            String name = field.getName();
            // 获取属性值
            Object value = field.get(obj);
            // 写入文件
            bw.write(name + "=" + value);
            bw.newLine();
        }
        bw.close();
    }
}

运行结果:

(二)反射结合配置文件,动态地创建对象并调用方法

Student类:

@Data
@AllArgsConstructor
@NoArgsConstructor
public class Student {
    private String name;
    private int age;

    public void study(){
        System.out.println("学生在学习!");
    }
}

Teacher类:

@Data
@AllArgsConstructor
@NoArgsConstructor
public class Teacher {
    private String name;
    private double salary;

    public void teach() {
        System.out.println("老师在教书!");
    }
}

prop.properties:

classname=com.testdemo.myreflect6.Student
method=study

测试类:

public class MyReflectDemo {
    public static void main(String[] args) throws IOException, NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException, ClassNotFoundException {
        // 1.加载配置文件
        Properties properties = new Properties();
        FileInputStream fis = new FileInputStream("chapter19\\src\\com\\testdemo\\myreflect6\\prop.properties");
        properties.load(fis);
        fis.close();
        System.out.println(properties);

        // 2.获取全类名和方法名
        String classname = properties.getProperty(Constant.CLASS_NAME);
        String method = properties.getProperty(Constant.METHOD);

        // 3.获取字节码文件
        Class clazz = Class.forName(classname);

        // 4.获取构造方法
        Constructor constructor = clazz.getDeclaredConstructor();

        // 5.通过构造方法拿到对象
        Object obj = constructor.newInstance();
        System.out.println(obj);
        // Student(name=null, age=0)

        // 6.获取成员方法
        Method declaredMethod = clazz.getDeclaredMethod(method);
        // 6.1暴力反射:临时取消权限校验
        declaredMethod.setAccessible(true);

        // 7.执行成员方法:因为该成员方法是无参的,所以不需要传递第二个参数
        declaredMethod.invoke(obj);
        // 学生在学习!
    }
}

interface Constant {
    String CLASS_NAME = "classname";
    String METHOD = "method";
}

要想创建Teacher对象,并调用Teacher的成员方法,只需要修改prop.properties文件中的配置信息即可:

classname=com.testdemo.myreflect6.Teacher
method=teach

修改配置文件后的运行结果:

Teacher(name=null, salary=0.0)
老师在教书!

十二、反射的劣势

  1. 反射打破了封装原则,能够修改私有的成员变量和成员方法
  2. 可以跳过泛型检查:
public static void main(String[] args) throws NoSuchMethodException, 
InvocationTargetException, IllegalAccessException {
    List<String> list = new ArrayList<>();
    // 使用反射跳过泛型检查
    Class<? extends List> aClass = list.getClass();
    Method add = aClass.getMethod("add", Object.class);
    add.invoke(list, 12);
    System.out.println(list); // [12]
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值