MOOC 个人学习笔记
# 1. 反射基础
- 反射:reflection
- 程序可以访问、检测和修改它本身状态或行为的能力,即自描述
和自控制。 - 可以在运行时加载、探知和使用编译期间完全未知的类。
- 给 Java 插上动态语言特性的翅膀,弥补强类型语言的不足。
- java.lang.reflect 包,在 Java 2 时代就有,在 Java 5 得到完善
- 程序可以访问、检测和修改它本身状态或行为的能力,即自描述
# 反射功能
- 在运行中分析类的能力
- 在运行中查看和操作对象
- 基于反射自由创建对象
- 反射构建出无法直接访问的类
- set 或者 get 到无法访问的成员变量
- 调用不可访问的方法
- 实现通用的数组操作代码
- 类似函数指针的功能
# 创建对象
- 方法 1:静态编码 & 编译
A obj1 = new A(); |
- 方法 2:克隆 (clone)
public class B implements Cloneable { | |
protected Object clone() throws CloneNotSupportedException | |
{ | |
return super.clone(); | |
} | |
} | |
//obj3 是 obj2 的克隆对象 没有调用构造函数 | |
B obj2 = new B(); | |
B obj3 = (B) obj2.clone(); |
- 方法 3:序列化 (serialization) 和反序列化 (deserialization)
public class C implements Serializable { | |
private static final long serialVersionUID = 1L; | |
} | |
// 没有调用构造函数 | |
// 序列化会引发安全漏洞,未来将被移除出 JDK,请谨慎使用!!! | |
C obj4 = new C(); | |
ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream("data.obj")); | |
out.writeObject(obj4); | |
out.close(); | |
ObjectInputStream in = new ObjectInputStream(new FileInputStream("data.obj")); | |
C obj5 = (C) in.readObject(); | |
in.close(); |
- 方法 4 和 5:反射
public class A { | |
public void hello() | |
{ | |
System.out.println("hello from A"); | |
} | |
} | |
// 第四种 newInstance 调用构造函数 | |
Object obj6 = Class.forName("A").newInstance(); | |
Method m = Class.forName("A").getMethod("hello"); | |
m.invoke(obj6); | |
A obj7 = (A) Class.forName("A").newInstance(); | |
// 第五种 newInstance 调用构造函数 | |
Constructor<A> constructor = A.class.getConstructor(); | |
A obj8 = constructor.newInstance(); | |
obj8.hello(); |
# 2. 反射关键类
- Class:类型标识
- JVM 为每个对象都保留其类型标识信息 (Runtime Type Identification)
- 三种获取方式
String s1 = "abc"; | |
Class c1 = s1.getClass(); | |
System.out.println(c1.getName()); | |
Class c2 = Class.forName("java.lang.String"); | |
System.out.println(c2.getName()); | |
Class c3 = String.class; | |
System.out.println(c3.getName()); |
# 获取详细信息
- getFields()
- 返回本类和所有父类所有的 public 成员变量
- getDeclareFields()
- 返回本类自己定义的成员变量,包括 private 变量,不包括父类变量
A obj = new A(20, "Tom"); | |
Class c = obj.getClass(); | |
// 获取本类及父类所有的 public 字段 | |
Field[] fs = c.getFields(); | |
System.out.println(fs[0].getName() + ":" + fs[0].get(obj)); | |
// 获取本类所有声明的字段 | |
Field[] fs2 = c.getDeclaredFields(); | |
for(Field f : fs2) | |
{ | |
f.setAccessible(true); | |
System.out.println(f.getName() + ":" + f.get(obj)); | |
} |
- getMethods()
- 返回本类和所有父类所有的 public 方法
- getDeclareMethods()
- 返回本类自己定义的方法,包括 private 方法,不包括父类方法
B obj = new B(); | |
Class c = obj.getClass(); | |
// 获取 public 方法 包括父类和父接口 | |
Method[] ms = c.getMethods(); | |
for (Method m : ms) { | |
if ("f1".equals(m.getName())) { | |
m.invoke(obj, null);// 传参调用方法 | |
} | |
} | |
// 获得该类的所有方法 | |
Method[] ms2 = c.getDeclaredMethods(); | |
for (Method m : ms2) { | |
if ("f2".equals(m.getName())) { | |
m.setAccessible(true); | |
String result = (String) m.invoke(obj, "abc");// 传参获得结果 | |
System.out.println(result); | |
} | |
} |
- getPackage()
- 返回本类所在包
- getModifiers()
- 返回本类前缀修饰符
- getInterfaces()
- 返回本类继承的接口
- getSuperClass()
- 返回本类的父类
Son son = new Son(); | |
Class c = son.getClass(); | |
Class father = c.getSuperclass(); | |
System.out.println(father.getName()); | |
Class[] inters = c.getInterfaces(); | |
for(Class inter : inters) { | |
System.out.println(inter.getName()); | |
} |
- getConstructors()
- 返回本类所有的构造函数
D d = new D(); | |
Class c = d.getClass(); | |
Constructor[] cons = c.getConstructors(); | |
for (Constructor con : cons) { | |
if (con.getParameterCount() > 0) { | |
// 有参构造函数 | |
D obj = (D) con.newInstance(100); | |
obj.printNum(); | |
} else { | |
// 无参构造函数 | |
D obj = (D) con.newInstance(); | |
obj.printNum(); | |
} | |
} |
- getAnnotations()
- 返回本类的注解
# 3. 反射应用
# 数据库连接
- JDBC
- Connection,连接到各个不同数据库
try { | |
Class.forName("com.mysql.jdbc.Driver"); | |
// Class.forName(className, true, currentLoader) | |
// 通知类加载器加载此类的 class 文件 | |
} catch (ClassNotFoundException e1) { | |
e1.printStackTrace(); | |
return; | |
} |
# 数组扩充器
- 给定一个数组 (任意类型),将其长度扩大一倍
- Java 的数组一旦创建,其长度是不再更改的
- 新建一个大数组 (相同类型),然后将旧数组的内容拷贝过去
public static void main(String[] args) { | |
int[] a = { 1, 2, 3, 4, 5 }; | |
a = (int[]) goodCopy(a, 10); | |
for (int i : a) { | |
System.out.println(i); | |
} | |
} | |
public static Object goodCopy(Object oldArray, int newLength) { | |
// Array 类型 | |
Class c = oldArray.getClass(); | |
// 获取数组中的单个元素类型 | |
Class componentType = c.getComponentType(); | |
// 旧数组长度 | |
int oldLength = Array.getLength(oldArray); | |
// 新数组 | |
Object newArray = Array.newInstance(componentType, newLength); | |
// 拷贝旧数据 | |
System.arraycopy(oldArray, 0, newArray, 0, oldLength); | |
return newArray; | |
} |
# 动态执行方法
- 给定类名、方法名,即可执行
- 加上定时器,即可做定时任务执行
Timer timer = new Timer(); | |
Calendar now = Calendar.getInstance(); | |
now.set(Calendar.SECOND, | |
now.get(Calendar.SECOND) + 1); | |
Date runDate = now.getTime(); | |
MyTask task2 = new MyTask(); | |
timer.scheduleAtFixedRate(task2, runDate, 3000); | |
// 固定速率 | |
Thread.sleep(15000); | |
timer.cancel(); // 取消定时器 | |
class MyTask extends TimerTask { | |
public void run() { | |
try { | |
Method m = Class.forName("Worker") | |
.getClass().getMethod("hello"); | |
m.invoke(null);// 静态方法可以不用 new 对象 | |
} catch (Exception e) { | |
e.printStackTrace(); | |
} | |
} | |
} | |
class Worker { | |
public static void hello() { | |
System.out.println("Hello java!"); | |
} | |
} |
# Json 和 Java 对象互转
- Json:
Gson gson = new Gson(); | |
String s = "{\"name\":\"Jo\"" | |
+ ",\"email\":\"a@b.com\"}"; | |
Person p = gson.fromJson(s, Person.class); | |
System.out.println(p.getName()); | |
System.out.println(p.getEmail()); |
# Tomcat 的 Servlet 对象创建
# MyBatis 的 OR/M
# Spring 的 Bean 容器
# org.reflections 包介绍
- Reflection 的增强工具包
- https://github.com/ronmamo/reflections
- Java runtime metadata analysis
- 获取某类的所有子类型
- 获取有特殊 annotation 的类型或者成员变量 / 方法
- 根据正则表达式获取资源 (类 / 成员变量 / 方法)・根据组合条件查询相应的方法
- ……
- https://github.com/ronmamo/reflections
# 编译器 API
- 对.java 文件即时编译
- 对字符串即时编译
- 监听在编译过程中产生的警告和错误
- 在代码中运行编译器 (并非:Runtime 命令行调用 javac 命令)
# JavaCompiler
- 自 Java 1.6 推出,位于 javax.tools 包中
- 可用在程序文件中的 Java 编译器接口 (代替 javac.exe)
- 在程序中编译 java 文件,产生 class 文件
- run 方法 (继承自 java.tools.Tools):较简单。可以编译 java 源文件,生成 class 文件,但不能指定输出路径,监控错误信息,调用后就在源码所在目录生成 class 文件
JavaCompiler compiler = ToolProvider.getSystemJavaCompiler(); | |
// 第一个参数:输入流,null 表示默认使用 system.in | |
// 第二个参数:输出流,null 表示默认使用 system.out | |
// 第三个参数:错误流,null 表示默认使用 system.err | |
// 第四个参数:String... 需要编译的文件名 | |
// 返回值:0 表示成功,其他错误 | |
int result = compiler.run(null, null, null, "F:/temp/Hello1.java", "F:/temp/Hello2.java"); | |
System.out.println(0 == result ? "Success" : "Fail"); |
- getTask 方法:更强大的功能。可以编译 java 源文件,包括在内存中的 java 文件 (字符串),生成 class 文件
public static void compileJavaFromString() { | |
StringBuilder sb = new StringBuilder(); | |
String className = "Hello"; | |
//sb.append("package com.test;\n"); | |
sb.append("public class Hello{\n"); | |
sb.append("public static void main(String[]args){\n"); | |
sb.append("System.out.print(\"hello world\"); \n"); | |
sb.append("}\n"); | |
sb.append("}"); | |
// 将上述源码编译 | |
Class<?> c = compile(className, sb.toString()); | |
try { | |
// 生成对象 | |
Object obj = c.newInstance(); | |
// 调用 main 方法 | |
Method m = c.getMethod("main", String[].class); | |
m.invoke(obj, new Object[] { new String[] {} }); | |
} catch (Exception e) { | |
e.printStackTrace(); | |
} | |
} | |
private static Class<?> compile(String className, String javaCodes) { | |
// 将字符串包装为 SimpleJavaFileObject 对象 | |
JavaSourceFromString srcObject = new JavaSourceFromString(className, javaCodes); | |
System.out.println(srcObject.getCode()); | |
Iterable<? extends JavaFileObject> fileObjects = Arrays.asList(srcObject); | |
JavaCompiler compiler = ToolProvider.getSystemJavaCompiler(); | |
StandardJavaFileManager fileManager = compiler.getStandardFileManager(null, null, null); | |
DiagnosticCollector<JavaFileObject> diagnosticCollector = new DiagnosticCollector<JavaFileObject>(); | |
// 设置编译的输出目录,并包装在 options 中 | |
String flag = "-d"; | |
String outDir = ""; | |
try { | |
File classPath = new File(Thread.currentThread().getContextClassLoader().getResource("").toURI()); | |
outDir = classPath.getAbsolutePath() + File.separator; | |
System.out.println(outDir); | |
} catch (URISyntaxException e1) { | |
e1.printStackTrace(); | |
} | |
Iterable<String> options = Arrays.asList(flag, outDir); | |
//JavaCompiler.getTask 方法:以 future 的任务形式 (多线程),来执行编译任务 | |
// 第一个参数:额外输出流,null 表示默认使用 system.err | |
// 第二个参数:文件管理器,null 表示编译器标准文件管理器 | |
// 第三个参数:诊断监听器,null 表示使用编译器默认方法来报告诊断信息 | |
// 第四个参数:编译器参数,null 表示无参数 | |
// 第五个参数:需要经过 annotation 处理的类名,null 表示没有类需要 annotation 处理 | |
// 第六个参数:待编译的类 | |
JavaCompiler.CompilationTask task = | |
compiler.getTask(null, fileManager, diagnosticCollector, options, null, fileObjects); | |
// 等待编译结束 | |
boolean result = task.call(); | |
if (result == true) { | |
try { | |
return Class.forName(className); | |
} catch (ClassNotFoundException e) { | |
e.printStackTrace(); | |
} | |
} | |
else | |
{ | |
//print the Diagnostic's information | |
for (Diagnostic diagnostic : diagnosticCollector | |
.getDiagnostics()) | |
{ | |
System.out.println("Error on line: " | |
+ diagnostic.getLineNumber() + "; URI: " | |
+ diagnostic.getSource().toString()); | |
} | |
} | |
return null; | |
} |
# Java 编译器 API 作用
- Java EE 的 JSP 编译
- 在线编程环境
- 在线程序评判系统 (Online Judge 系统)
- 自动化的构建和测试工具
- ……
# 基于 JavaCompiler 的集成工具
- Janino,http://janino-compiler.github.io/janino
- InMemoryJavaCompile,https://github.com/trung/InMemoryJavaCompiler
- Java-Runtime-Compiler, https://github.com/OpenHFT/Java-Runtime-Compiler
- Apache Commons JCI(Java Compiler Interface), http://commons.apache.org/proper/commons-jci/index.html 适用于 JDK1.5 及以前的版本