一、快速入门
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,使得可以在程序中对其他源文件进行更细粒度的编译控制。