少女祈祷中...

1.反射基础

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

反射功能

  • 在运行中分析类的能力
  • 在运行中查看和操作对象
    • 基于反射自由创建对象
    • 反射构建出无法直接访问的类
    • set或者get到无法访问的成员变量
    • 调用不可访问的方法
  • 实现通用的数组操作代码
  • 类似函数指针的功能

创建对象

  • 方法1:静态编码&编译
1
A obj1 = new A();
  • 方法2:克隆(clone)
1
2
3
4
5
6
7
8
9
10
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)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
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:反射
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
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)
    • 三种获取方式
1
2
3
4
5
6
7
8
9
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变量,不包括父类变量
1
2
3
4
5
6
7
8
9
10
11
12
13
14
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方法,不包括父类方法
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
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()
    • 返回本类的父类
1
2
3
4
5
6
7
8
9
10
11
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()
    • 返回本类所有的构造函数
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
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,连接到各个不同数据库
1
2
3
4
5
6
7
8
try{            
Class.forName("com.mysql.jdbc.Driver");
//Class.forName(className, true, currentLoader)
//通知类加载器加载此类的class文件
}catch(ClassNotFoundException e1){
e1.printStackTrace();
return;
}

数组扩充器

  • 给定一个数组(任意类型),将其长度扩大一倍
    • Java的数组一旦创建,其长度是不再更改的
    • 新建一个大数组(相同类型),然后将旧数组的内容拷贝过去
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
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;
}

动态执行方法

  • 给定类名、方法名,即可执行
    • 加上定时器,即可做定时任务执行
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
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对象互转

1
2
3
4
5
6
7
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的类型或者成员变量/方法
      • 根据正则表达式获取资源(类/成员变量/方法) • 根据组合条件查询相应的方法
      • ……

编译器API

  • 对.java文件即时编译
  • 对字符串即时编译
  • 监听在编译过程中产生的警告和错误
  • 在代码中运行编译器(并非:Runtime命令行调用javac命令)

JavaCompiler

  • 自Java 1.6 推出,位于javax.tools包中
  • 可用在程序文件中的Java编译器接口(代替javac.exe)
  • 在程序中编译java文件,产生class文件
  • run方法(继承自java.tools.Tools):较简单。可以编译java源文件,生成class文件,但不能指定输出路径,监控错误信息, 调用后就在源码所在目录生成class文件
1
2
3
4
5
6
7
8
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文件
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
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的集成工具