反射学习笔记——java

目录

一、常见/反射方法创建对象

1.1静态编译

1.2克隆方法

1.3序列化方法

1.4反射方法

二、反射的关键类

2.1获取Class和设定类型标识信息

2.2关键类

2.2.1获取变量

2.2.2获取方法

2.2.3获取构造方法

2.2.4获取父类和父接口

三、反射应用

3.1数据库连接

3.2动态扩充数组

3.3动态执行方法

3.4json数据转变为对象

3.5其他

四、编译器API

4.1run方法编译.java文件

4.2getTask编译字符串


反射解释:

  • 程序可以访问、检测和修改它本身状态或者行为的能力,即自描述和自控制。
  • 可以在运行时加载、探知和使用编译期间完全未知的类。
  • 给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核心技术》 

发布了55 篇原创文章 · 获赞 17 · 访问量 4992

猜你喜欢

转载自blog.csdn.net/weixin_43698704/article/details/104195870