目录
反射解释:
- 程序可以访问、检测和修改它本身状态或者行为的能力,即自描述和自控制。
- 可以在运行时加载、探知和使用编译期间完全未知的类。
- 给java插上动态语言特性的翅膀,弥补强类型语言的不足
- java.lang.reflect,在java2出现,java5中完善
反射功能:
- 在运行中分析类的能力
- 在运行中查看和操作对象
- 实现通用的数组操作代码
- 类似函数指针的功能
一、常见/反射方法创建对象
1.1静态编译
public class A {
public void hello()
{
System.out.println("hello from A");
}
}
//第一种 直接new 调用构造函数
A obj1 = new A();
obj1.hello();
1.2克隆方法
public class B implements Cloneable {
public void hello()
{
System.out.println("hello from B");
}
protected Object clone() throws CloneNotSupportedException
{
return super.clone();
}
}
//第二种 clone
//obj3 是obj2的克隆对象 没有调用构造函数
B obj2 = new B();
obj2.hello();
B obj3 = (B) obj2.clone();
obj3.hello();
1.3序列化方法
import java.io.Serializable;
public class C implements Serializable {
private static final long serialVersionUID = 1L;
public void hello() {
System.out.println("hello from C");
}
}
//第三种 序列化 没有调用构造函数
//序列化会引发安全漏洞,未来将被移除出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(); //obj5是obj4序列化形成的
in.close();
obj5.hello();
1.4反射方法
//第四种 newInstance 调用构造函数
Object obj6 = Class.forName("A").newInstance();
Method m = Class.forName("A").getMethod("hello");
m.invoke(obj6);//调用hello方法
A obj7 = (A) Class.forName("A").newInstance();
//第五种 newInstance 调用构造函数
Constructor<A> constructor = A.class.getConstructor(); //获取构造函数
A obj8 = constructor.newInstance();
obj8.hello();
二、反射的关键类
2.1获取Class和设定类型标识信息
public class ClassTest {
public static void main(String[] args) throws ClassNotFoundException {
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());
}
}
输出:
java.lang.String
java.lang.String
java.lang.String
2.2关键类
- getFields():返回本类和所有父类的public变量的信息
- getDeclaredFields():返回本类自己定义的变量的信息,包括private变量的信息,但不含父类变量的信息
- getMethods():返回本类和所有父类的public方法
- getDeclaredMethods():返回本类自己定义的方法,包括private方法,但不含父类方法
- getPackage():获取这个类在哪个包
- getModifiere():获取前置修饰符有哪些
- getinterface():获取继承的接口
- getSuperClass():获取父类
- getConstructor():获取所有的构造函数
- getAnnotation():获取这个类相应的注解
2.2.1获取变量
import java.lang.reflect.Field;
public class FieldTest {
public static void main(String[] args) throws IllegalArgumentException, IllegalAccessException {
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));
System.out.println("====================");
//获取本类所有声明的字段
Field[] fs2 = c.getDeclaredFields();
for(Field f : fs2)
{
f.setAccessible(true);//会使private属性临时变为public属性
System.out.println(f.getName() + ":" + f.get(obj));
}
}
}
class A
{
public int age;
private String name;
public A(int age, String name)
{
this.age = age;
this.name = name;
}
}
输出:
age:20
====================
age:20
name:Tom
2.2.2获取方法
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import static java.lang.System.out;
public class MethodTest {
public static void main(String[] args)
throws IllegalAccessException, IllegalArgumentException, InvocationTargetException {
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);
}
if ("f2".equals(m.getName())) {
m.setAccessible(true);
String result = (String) m.invoke(obj, "abc");
out.println(result);
}
}
System.out.println("====================");
// 获得该类的所有方法
Method[] ms2 = c.getDeclaredMethods();
for (Method m : ms2) {
if ("f1".equals(m.getName())) {
m.invoke(obj, null);
}
if ("f2".equals(m.getName())) {
m.setAccessible(true);
String result = (String) m.invoke(obj, "abc");
out.println(result);
}
}
}
}
class B {
public void f1() {
out.println("B.f1()...");
}
private String f2(String s) {
out.println("B.f2()...");
return s;
}
}
输出:
B.f1()...
====================
B.f1()...
B.f2()...
abc
2.2.3获取构造方法
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
public class ConstructorTest {
public static void main(String[] args)
throws InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException {
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();
}
}
}
}
class D {
private int num;
public D() {
this.num = 10;
}
public D(int num) {
this.num = num;
}
public void printNum() {
System.out.println(this.num);
}
}
输出:
10
100
2.2.4获取父类和父接口
public class SuperTest {
public static void main(String[] args) {
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());
}
}
}
class Father { }
class Son extends Father
implements Cloneable, Comparable
{
protected Object clone() throws CloneNotSupportedException
{
return super.clone();
}
public int compareTo(Object o) {
return 0;
}
}
输出:
Father
java.lang.Cloneable
java.lang.Comparable
三、反射应用
3.1数据库连接
import java.sql.*;
public class ConnectionTest {
public static void main(String[] args){
//构建Java和数据库之间的桥梁介质
try{
//利用反射
Class.forName("com.mysql.jdbc.Driver");
//Class.forName(className, true, currentLoader)
//通知类加载器加载此类的class文件
System.out.println("注册驱动成功!");
}catch(ClassNotFoundException e1){
System.out.println("注册驱动失败!");
e1.printStackTrace();
return;
}
String url="jdbc:mysql://localhost:3306/test";
Connection conn = null;
try {
//构建Java和数据库之间的桥梁:URL,用户名,密码
conn = DriverManager.getConnection(url, "root", "123456");
//DriverManager将会挑选加载合适的驱动类,并采用getConnection方法连接
//构建数据库执行者
Statement stmt = conn.createStatement();
System.out.println("创建Statement成功!");
//执行SQL语句并返回结果到ResultSet
ResultSet rs = stmt.executeQuery("select bookid, bookname, price from t_book order by bookid");
//开始遍历ResultSet数据
while(rs.next())
{
System.out.println(rs.getInt(1) + "," + rs.getString(2) + "," + rs.getInt("price"));
}
rs.close();
stmt.close();
} catch (SQLException e){
e.printStackTrace();
}
finally
{
try
{
if(null != conn)
{
conn.close();
}
}
catch (SQLException e){
e.printStackTrace();
}
}
}
}
3.2动态扩充数组
import java.lang.reflect.Array;
public class ArrayTest {
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;
}
}
3.3动态执行方法
import java.lang.reflect.Method;
import java.util.Calendar;
import java.util.Date;
import java.util.Timer;
import java.util.TimerTask;
public class MethodTest {
public static void main(String[] args) throws InterruptedException {
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); //每3秒运行一次
// 固定速率
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!");
}
}
3.4json数据转变为对象
import com.google.gson.Gson;
public class JsonToObject {
public static void main(String[] args) {
Gson gson = new Gson();
String s = "{\"name\":\"Jo\""
+ ",\"email\":\"[email protected]\"}";
Person p = gson.fromJson(s, Person.class);
//gson转化为object,这里的第三方库的源码也是基于反射设计的
System.out.println(p.getName());
System.out.println(p.getEmail());
}
}
class Person
{
private String name;
private String email;
public String getName() {
return name;
}
public String getEmail() {
return email;
}
}
3.5其他
举例Tomcat的Servlet对象创建,MyBatis的OR/M,Spring的Bean容器
值得注意的是org.reflections第三方库
四、编译器API
优点:
- 反射需要类(class文件)必须存在,而编译器API可以对.java文件即时编译。
- 可以对字符串即时编译,
- 也可以监听编译过程中产生的错误和警告,
- 在代码中编译运行编译器,而不是Runtime命令行调用javac命令。
4.1run方法编译.java文件
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.UnsupportedEncodingException;
import java.lang.reflect.Method;
import java.net.URI;
import java.net.URISyntaxException;
import java.nio.charset.Charset;
import java.util.Arrays;
import javax.tools.JavaCompiler;
import javax.tools.JavaFileObject;
import javax.tools.SimpleJavaFileObject;
import javax.tools.StandardJavaFileManager;
import javax.tools.ToolProvider;
public class SimpleJavaCompiler {
public static void main(String[] args) throws UnsupportedEncodingException {
successCompile();
failCompile();
}
public static void successCompile() {
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");
}
public static void failCompile() throws UnsupportedEncodingException {
ByteArrayOutputStream err = new ByteArrayOutputStream();
JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
// 第一个参数:输入流,null表示默认使用system.in
// 第二个参数:输出流,null表示默认使用system.out
// 第三个参数:错误流,null表示默认使用system.err
// 第四个参数:String... 需要编译的文件名
// 返回值:0表示成功,其他错误
int result = compiler.run(null, null, err, "F:/temp/Hello3.java");
if (0 == result) {
System.out.println("Success");
} else {
System.out.println("Fail");
System.out.println(new String(err.toByteArray(), Charset.defaultCharset().toString()));
System.out.println(Charset.defaultCharset().toString());
}
}
}
4.2getTask编译字符串
JavaSourceFromString将字符串生成一个虚拟文件。
import java.net.URI;
import javax.tools.SimpleJavaFileObject;
/**
* A file object used to represent source coming from a string.
* 相当于创建一个虚拟的文件对象
* 这个类来自于chm帮助
*/
public class JavaSourceFromString extends SimpleJavaFileObject {
private String code;
public JavaSourceFromString(String name, String code) {
super(URI.create("string:///" + name.replace('.', '/') + Kind.SOURCE.extension), Kind.SOURCE);
this.code = code;
}
public CharSequence getCharContent(boolean ignoreEncodingErrors) {
return code;
}
public String getCode()
{
return code;
}
}
import java.io.File;
import java.io.UnsupportedEncodingException;
import java.lang.reflect.Method;
import java.net.URISyntaxException;
import java.util.Arrays;
import javax.tools.Diagnostic;
import javax.tools.DiagnosticCollector;
import javax.tools.JavaCompiler;
import javax.tools.JavaFileObject;
import javax.tools.StandardJavaFileManager;
import javax.tools.ToolProvider;
public class JavaCompilerTask {
public static void main(String[] args) throws UnsupportedEncodingException {
compileJavaFromString();
}
public static void compileJavaFromString() {
//1.生成字符串
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("}");
//2.将上述源码(字符串)编译
Class<?> c = compile(className, sb.toString());
try {
//3.生成对象并且执行main方法
// 生成对象
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) {
//1.将字符串包装为SimpleJavaFileObject对象
JavaSourceFromString srcObject = new JavaSourceFromString(className, javaCodes);
System.out.println(srcObject.getCode());
//放入一个数组中
Iterable<? extends JavaFileObject> fileObjects = Arrays.asList(srcObject);
JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();//获取JavaCompiler对象
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);//设定class文件输出目录
} 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;
}
}
其他:
参考中国大学mooc《java核心技术》