Java入坑之反射研究

一、快速入门

1.1反射概述

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


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

1.2反射功能 

反射:reflection
-程序可以访问、检测和修改它本身状态或行为的能力,即自描述
和自控制。
-可以在运行时加载、探知和使用编译期间完全未知的类。
-给Java插上动态语言特性的翅膀,弥补强类型语言的不足。
- java.lang.reflect包,在Java 2时代就有,在Java 5得到完善

 1.3创建对象的方式

1.3.1静态编码&编译

效率最好,方法写死无法实现A类换B类(硬性编码) 

1.3.2克隆

 JAVA四大接口

Comparable:用于对象比较的接口;
Runnable :用于对象线程化的接口;
Serializable:用于对象序列化的接口;
Clonable:用于对象克隆的接口。

1.3.3序列化

 1.3.4反射

二、反射关键类

2.1Class类型标识

-JVM为每个对象都保留其类型标识信息(RuntimeTypeIdentification)

package org.example;

// 导入 java.lang 包,该包包含了基本的类,如 String, Class, System 等
import java.lang.*;
// 定义一个公共类 Main,包含主方法
public class a {
    // 主方法,程序的入口
    public static void main(String[] args) {
        // 调用上面给出的代码段,作为一个静态方法
        testClass();
    }

    // 定义一个静态方法 testClass,用来测试获取 Class 对象的三种方式
    public static void testClass() {
        // 创建一个字符串对象 s1
        String s1 = "abc";
        // 通过调用 s1 的 getClass 方法,获取其对应的 Class 对象 c1
        Class c1 = s1.getClass();
        // 打印 c1 的名称,即 java.lang.String
        System.out.println(c1.getName());
        // 通过调用 Class 类的静态方法 forName,传入一个类的全限定名,获取其对应的 Class 对象 c2
        try {
            Class c2 = Class.forName("java.lang.String");
            // 打印 c2 的名称,也是 java.lang.String
            System.out.println(c2.getName());
        } catch (ClassNotFoundException e) {
            // 如果找不到指定的类,捕获异常并打印错误信息
            e.printStackTrace();
        }
        // 通过使用类名后面加上 .class 的语法,获取其对应的 Class 对象 c3
        Class c3 = String.class;
        // 打印 c3 的名称,同样是 java.lang.String
        System.out.println(c3.getName());
    }
}

 常见方法

getMethods () :返回本类和所有父类所有的public方法;
getDec lar edMethods() :返回本类自己定义的方法,包括pr ivate的方法,但不包括父类的方法。 

2.2Field方法

package org.example;// 导入 java.lang 包,该包包含了基本的类,如 String, Class, System 等
import java.lang.*;
// 导入 java.lang.reflect 包,该包包含了反射相关的类,如 Field, Method 等
import java.lang.reflect.*;
// 定义一个类 b
class b {
    // 定义两个字段 age 和 name
    public int age;
    private String name;
    // 定义一个构造方法,接收两个参数 age 和 name,并赋值给字段
    public b(int age, String name) {
        this.age = age;
        this.name = name;
    }

  

    // 定义一个静态方法 main,程序的入口
    // 在方法上添加 throws 子句,声明可能抛出的 IllegalAccessException 异常
    public static void main(String[] args) throws IllegalAccessException {
        // 创建一个 b 类的对象 obj,并传入参数 20 和 "Tom"
        b obj = new b(20, "Tom");
        // 通过调用 obj 的 getClass 方法,获取其对应的 Class 对象 c
        Class c = obj.getClass();
        // 获取本类及父类所有的 public 字段,并存储在数组 fs 中
        Field[] fs = c.getFields();
        // 打印 fs 数组中第一个元素(即 age 字段)的名称和值
        System.out.println(fs[0].getName() + ":" + fs[0].get(obj));
        // 获取本类所有声明的字段,并存储在数组 fs2 中
        Field[] fs2 = c.getDeclaredFields();
        // 使用 for-each 循环遍历 fs2 数组中的每个元素 f
        for (Field f : fs2) {
            // 设置 f 可访问,以便访问私有字段
            f.setAccessible(true);
            // 打印 f 的名称和值
            System.out.println(f.getName() + ":" + f.get(obj));
        }
    }
}

 2.3Method:成员方法

package org.example;

// 导入 java.lang 包,该包包含了基本的类,如 String, Class, System 等
import java.lang.*;
// 导入 java.lang.reflect 包,该包包含了反射相关的类,如 Field, Method 等
import java.lang.reflect.*;
// 导入 java.io 包,该包包含了输入输出相关的类,如 PrintStream, PrintWriter 等
import java.io.*;
// 定义一个类 B
class B {
    // 定义一个公共方法 f1,无参数,无返回值
    public void f1() {
        // 定义一个局部变量 t1,并赋值为 0
        int t1 = 0;
        // 使用 System 类的 out 字段,调用 println 方法,打印一条信息
        System.out.println("B.f1()...");
    }
    // 定义一个私有方法 f2,接收一个字符串参数 s,返回一个字符串值
    private String f2(String s) {
        // 使用 System 类的 out 字段,调用 println 方法,打印一条信息
        System.out.println("B.f2()...");
        // 返回参数 s
        return s;
    }
    // 定义一个静态方法 main,程序的入口
    // 在方法上添加 throws 子句,声明可能抛出的异常
    public static void main(String[] args) throws Exception {
        // 创建一个 B 类的对象 obj,并传入参数 20 和 "Tom"
        B obj = new B();
        // 通过调用 obj 的 getClass 方法,获取其对应的 Class 对象 c
        Class c = obj.getClass();
        // 获取本类及父类所有的 public 方法,并存储在数组 ms 中
        Method[] ms = c.getMethods();
        // 使用 for-each 循环遍历 ms 数组中的每个元素 m
        for (Method m : ms) {
            // 如果 m 的名称等于 "f1"
            if ("f1".equals(m.getName())) {
                // 调用 m 的 invoke 方法,传入 obj 和 null 作为参数,执行 f1 方法
                m.invoke(obj, null);
            }
        }
        // 获取本类所有声明的方法,并存储在数组 ms2 中
        Method[] ms2 = c.getDeclaredMethods();
        // 使用 for-each 循环遍历 ms2 数组中的每个元素 m
        for (Method m : ms2) {
            // 如果 m 的名称等于 "f2"
            if ("f2".equals(m.getName())) {
                // 设置 m 可访问,以便访问私有方法
                m.setAccessible(true);
                // 调用 m 的 invoke 方法,传入 obj 和一个包含 "abc" 的 Object[] 类型的数组作为参数,执行 f2 方法,并接收返回值 result
                String result = (String) m.invoke(obj, new Object[]{"abc"});
                // 使用 System 类的 out 字段,调用 println 方法,打印返回值 result
                System.out.println(result);
            }
        }
    }
}

2.4Constructor:构造函数

package org.example;

// 导入 java.lang 包,该包包含了基本的类,如 String, Class, System 等
import java.lang.*;
// 导入 java.lang.reflect 包,该包包含了反射相关的类,如 Field, Method, Constructor 等
import java.lang.reflect.*;
// 定义一个类 D
class D {
    // 定义一个私有字段 num
    private int num;
    // 定义一个无参构造函数
    public D() {
        // 给 num 赋值为 0
        this.num = 0;
    }
    // 定义一个有参构造函数
    public D(int num) {
        // 给 num 赋值为参数 num
        this.num = num;
    }
    // 定义一个公共方法 printNum,无参数,无返回值
    public void printNum() {
        // 使用 System 类的 out 字段,调用 println 方法,打印 num 的值
        System.out.println(this.num);
    }
    // 定义一个静态方法 main,程序的入口
    // 在方法上添加 throws 子句,声明可能抛出的异常
    public static void main(String[] args) throws Exception {
        // 创建一个 D 类的对象 d,并使用无参构造函数
        D d = new D();
        // 通过调用 d 的 getClass 方法,获取其对应的 Class 对象 c
        Class c = d.getClass();
        // 获取本类所有声明的构造函数,并存储在数组 cons 中
        Constructor[] cons = c.getConstructors();
        // 使用 for-each 循环遍历 cons 数组中的每个元素 con
        for (Constructor con : cons) {
            // 如果 con 的参数个数为 1
            if (con.getParameterCount() == 1) {
                // 有参构造函数
                // 调用 con 的 newInstance 方法,传入一个包含 100 的 Object[] 类型的数组作为参数,创建一个 D 类的对象 obj,并强制转换为 D 类型
                D obj = (D) con.newInstance(new Object[]{100});
                // 调用 obj 的 printNum 方法,打印 num 的值
                obj.printNum();
            } else {
                // 无参构造函数
                // 调用 con 的 newInstance 方法,传入一个空的 Object[] 类型的数组作为参数,创建一个 D 类的对象 obj,并强制转换为 D 类型
                D obj = (D) con.newInstance(new Object[]{});
                // 调用 obj 的 printNum 方法,打印 num 的值
                obj.printNum();
            }
        }
    }
}

2.5父类/父类接口

package org.example;

// 导入 java.lang 包,该包包含了基本的类,如 String, Class, System 等
import java.lang.*;
// 定义一个类 Father
class Father {
}
// 定义一个类 Son,并继承了 Father 类,并实现了 Cloneable 和 Comparable 接口
class Son extends Father implements Cloneable, Comparable {
    // 定义一个受保护的方法 clone,并抛出了 CloneNotSupportedException 异常
    protected Object clone() throws CloneNotSupportedException {
        // 返回父类的 clone 方法的结果
        return super.clone();
    }
    // 定义一个公共的方法 compareTo,并接收了一个 Object 类型的参数 o
    public int compareTo(Object o) {
        // 返回 0
        return 0;
    }
    // 定义一个静态方法 main,程序的入口
    public static void main(String[] args) {
        // 创建一个 Son 类的对象 son,并使用无参构造函数
        Son son = new Son();
        // 通过调用 son 的 getClass 方法,获取其对应的 Class 对象 c
        Class c = son.getClass();
        // 获取父类的 Class 对象,并存储在变量 father 中
        Class father = c.getSuperclass();
        // 使用 System 类的 out 字段,调用 println 方法,打印 father 的名称
        System.out.println(father.getName());
        // 获取实现的接口,并存储在数组 inters 中
        Class[] inters = c.getInterfaces();
        // 使用 for-each 循环遍历 inters 数组中的每个元素 inter
        for (Class inter : inters) {
            // 使用 System 类的 out 字段,调用 println 方法,打印 inter 的名称
            System.out.println(inter.getName());
        }
    }
}

三、反射应用

3.1数组扩容

package org.example;

import java.lang.reflect.Array;

public class ArrayCopyExample {
    public static void main(String[] args) {
        int[] oldArray = {1, 2, 3, 4, 5};
        int newLength = 10;

        int[] newArray = (int[]) goodCopy(oldArray, newLength);

        System.out.println("Original array:");
        printArray(oldArray);

        System.out.println("New array after copying:");
        printArray(newArray);
    }

    // 用于将旧数组的内容复制到新数组中
    public static Object goodCopy(Object oldArray, int newLength) {
        // 获取数组类型
        Class<?> c = oldArray.getClass();
        // 获取数组中的元素类型
        Class<?> componentType = c.getComponentType();
        // 获取旧数组的长度
        int oldLength = Array.getLength(oldArray);
        // 创建一个新数组
        Object newArray = Array.newInstance(componentType, newLength);
        // 使用 System.arraycopy 方法将旧数组的内容复制到新数组中
        System.arraycopy(oldArray, 0, newArray, 0, Math.min(oldLength, newLength));
        return newArray;
    }

    // 打印数组内容
    public static void printArray(Object array) {
        int length = Array.getLength(array);
        for (int i = 0; i < length; i++) {
            System.out.print(Array.get(array, i) + " ");
        }
        System.out.println();
    }
}

3.2动态执行方法

package org.example;

import java.util.Timer;
import java.util.TimerTask;
import java.util.Calendar;
import java.util.Date;

public class TimerExample {
    public static void main(String[] args) {
        // 创建一个定时器
        Timer timer = new Timer();

        // 获取当前时间
        Calendar now = Calendar.getInstance();
        // 设置任务开始时间,推迟1秒
        now.set(Calendar.SECOND, now.get(Calendar.SECOND) + 1);
        Date runDate = now.getTime();

        // 创建任务实例
        MyTask task = new MyTask();
        // 将任务安排在指定的开始时间开始执行,每隔3秒执行一次
        timer.scheduleAtFixedRate(task, runDate, 3000);
    }
}

class MyTask extends TimerTask {
    public void run() {
        try {
            // 调用 Worker 类的 hello 方法
            Worker.hello();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

class Worker {
    public static void hello() {
        System.out.println("Hello java!");
    }
}

3.3Json和Java对象互转

3.4Tomcat的Servlet创建

 3.5第三方库

四、编译器API 

4.1相关知识

 JDK提供javac工具来编译.java源文件,但我们没法深入javac工具了解其内部,只能通过Runtime命令行调用javac工具来进行动态编译(仅仅调用,无法深入控制编译过程)。而编译器API提供了一套完整的API,使得可以在程序中对其他源文件进行更细粒度的编译控制。

4.2JavaCompiler

4.3 编译器API 作用

4.4相关集成工具 

猜你喜欢

转载自blog.csdn.net/qq_62377885/article/details/132591066
今日推荐