Java 反射学习笔记
要学反射,先要了解Class这个类,Class是所有Java类的一个总称,Class的实例中存储的是一个类的字节码,获取Class的实例有三种方式:
- System.class
- new Date().getClass()
- Class.forName(“java.lang.String”);
Java不允许使用Class cla = new Class()这种方式获得一个Class的新实例,因为Class的构造方法是私有的,看一段源代码:
这段源码里说的很明白,只有JVM才可以创建一个Class对象。
那么这三种获取Class实例的方式有什么区别呢?
@Test
public void test1() throws ClassNotFoundException{
String str = "abc";
Class cla1 = str.getClass();
Class cla2 = String.class;
Class cla3 = Class.forName("java.lang.String");
System.out.println(cla1==cla2);
System.out.println(cla2==cla3);
}
输出结果为:
根据这结果我们可以推论出,用这三种方式获得的Class实例是一模一样的,但是在实际的开发中我们更多的是使用第三种方式来获得一个Class实例,比如spring框架,我们先在配置文件中写好类名,然后在程序运行的过程中动态加载,获得该类的实例,再执行方法(spring的工作原理基本就这样)。
System.out.println(String.class.isPrimitive());//false
System.out.println(int.class.isPrimitive());//true,判断是否为基本类型
System.out.println(int.class==Integer.class);//false,Integer是类,而int是基本类型
System.out.println(int[].class.isArray());//true
那么反射是什么?这是别人总结的“反射就是把Java类中的各种成分映射成相应的Java类(比如 属性–>Field、方法–>Method、构造方法–>Contructor、包–>Package)”。拿到这些相应的Java类之后该怎么用?这是反射学习的重点。
1.Constructor类
1.1 如果想调用一个类的默认无参构造方法,有以下两种方式:
方式一,直接实例化一个Class(这种方式只能调用无参构造方法):
@Test
public void test2(){
try {
Class c = Class.forName("lenve.test.Utils");
c.newInstance();
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
输出结果:
Utils.java
package lenve.test;
public class Utils {
private String username;
private String password;
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public int add(int a, int b) {
return a + b;
}
public Utils(String str) {
System.out.println(str);
}
public Utils(int a, int b) {
System.out.println(a + b);
}
public Utils() {
System.out.println("this is default constructor!");
}
}
方式二,先获得一个Constructor类,再根据这个类调用无参构造方法。
@Test
public void test3(){
try {
/**
Class.newInstance() 只能够调用无参的构造函数,即默认的构造函数;
Constructor.newInstance() 可以根据传入的参数,调用任意构造构造函数。
*/
Class c = Class.forName("lenve.test.Utils");
Constructor constructor = c.getConstructor(null);
Utils util = (Utils) constructor.newInstance();
System.out.println(util.add(3, 4));
} catch (ClassNotFoundException e) {
e.printStackTrace();
}catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (SecurityException e) {
e.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (IllegalArgumentException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
}
输出结果:
1.2 调用一个有参的构造方法
@Test
public void test4(){
try {
Class c = Class.forName("lenve.test.Utils");
//根据参数的类型来确定调用的是哪一个构造方法
Constructor constructor = c.getConstructor(String.class);
//传入该构造方法需要的参数
constructor.newInstance("today is a good day!");
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (SecurityException e) {
e.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (IllegalArgumentException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
}
输出结果:
2.获得Field并查看相应的实例对象的值,细节都已经在注释中说明:
@Test
public void test5(){
try {
Point p1 = new Point(3, 7);
Field fY = p1.getClass().getField("y");
//fY是字节码中Field对象的一个实例,并不属于某个具体的实例,因此它的值不是7
//这样才是得到p1中y的值
System.out.println(fY.get(p1));
//因为x是私有的,所以不能通过下面的方式获得
// Field fX = p1.getClass().getField("x");
//正确的获得方式应该是这样的
Field fX = p1.getClass().getDeclaredField("x");
//上面的方式拿到x后并不能获得其值,还需要做如下处理
fX.setAccessible(true);
System.out.println(fX.get(p1));
} catch (NoSuchFieldException | SecurityException
| IllegalArgumentException | IllegalAccessException e) {
e.printStackTrace();
}
}
3.获取一个类中的所有String类型的属性,如果该字段的值中有’a’,则全部替换为’b’:
@Test
public void test6(){
Point p = new Point(3, 4);
changeValue(p);
System.out.println(p);
}
private void changeValue(Object obj) {
try {
Field[] fields = obj.getClass().getFields();
for(Field field:fields){
//因为对一个Java类来说,它只有一个字节码,是单例的,所以用==比较就可以了
if(field.getType()==String.class){
String oldStr = (String) field.get(obj);
String newStr = oldStr.replace('a', 'b');
field.set(obj, newStr);
}
}
} catch (SecurityException | IllegalArgumentException
| IllegalAccessException e) {
e.printStackTrace();
}
}
Point.java
public class Point {
private int x;
public int y;
public String username = "zhangsan";
public String password = "lisi";
public Point(int x,int y) {
this.x = x;
this.y = y;
}
@Override
public String toString() {
return "Point [username=" + username + ", password=" + password + "]";
}
}
4.通过反射调用方法
@Test
public void test7() {
try {
Class c = Class.forName("lenve.test.Utils");
//通过方法名以及参数类型、参数个数来确定具体是得到那个方法
Method m = c.getMethod("add", int.class, int.class);
/**
* 第一个参数表示方法是在哪个实例中调用,后面的表示该方法的参数,
* 如果该方法是静态方法,则第一个参数可以为null
*/
System.out.println(m.invoke(c.newInstance(), 3, 4));
} catch (ClassNotFoundException | NoSuchMethodException
| SecurityException | IllegalAccessException
| IllegalArgumentException | InvocationTargetException
| InstantiationException e) {
e.printStackTrace();
}
}
5.通过反射调用一个方法的main(String[] args)方法:
@Test
public void test8(){
try {
Method m = Class.forName("lenve.test.ReflectMain").getMethod("main", String[].class);
/**
* 由于编译器会将传入的参数拆包,因此要设法使参数以一个数据类型出现
* 下面一共有两种调用方式
* 第一种:告诉编译器不要拆包
* 第二种:再包一层,这样即使拆包后还是一个参数
*/
m.invoke(null, (Object)new String[]{"111","222","333"});
System.out.println("---------------------");
m.invoke(null, new Object[]{new String[]{"111","222","333"}});
} catch (NoSuchMethodException | SecurityException
| ClassNotFoundException | IllegalAccessException
| IllegalArgumentException | InvocationTargetException e) {
e.printStackTrace();
}
}
ReflectMain.java
class ReflectMain{
public static void main(String[] args) {
for(String str:args)
System.out.println(str);
}
}
6.使用反射类打印一个普通对象或者数组对象:
@Test
public void test10(){
int[] a1 = new int[]{1,2,3};
String[] a2 = new String[]{"a","b","c"};
Object obj = null;
printObject(a1);
printObject(a2);
printObject("xyz");
}
private void printObject(Object obj) {
Class c = obj.getClass();
if(c.isArray()){
int len = Array.getLength(obj);
for (int i = 0; i < len; i++) {
System.out.println(Array.get(obj, i));
}
}else{
System.out.println(obj);
}
}
7.利用反射实现一个间的spring框架
方式一:
//配置文件为config.properties
@Test
public void test1(){
try {
InputStream is = new FileInputStream(new File("config.properties"));
Properties prop = new Properties();
prop.load(is);
is.close();
String className = prop.getProperty("className");
Class c = Class.forName(className);
Method m = c.getMethod("add", int.class,int.class);
System.out.println(m.invoke(c.newInstance(), 3,4));
} catch (ClassNotFoundException | NoSuchMethodException
| SecurityException | IllegalAccessException
| IllegalArgumentException | InvocationTargetException
| InstantiationException | IOException e) {
e.printStackTrace();
}
}
config.properties
className=lenve.test.Utils
方式二:
//配置文件为config.xml
@Test
public void test2(){
try {
SAXReader reader = new SAXReader();
Document document = reader.read(new File("config.xml"));
Element root = document.getRootElement();
List<Element> elementList = root.elements();
for(Element es:elementList){
String className = es.element("name").getText();
Class c = Class.forName(className);
System.out.println(c.newInstance());
}
} catch (ClassNotFoundException | InstantiationException
| IllegalAccessException | DocumentException e) {
e.printStackTrace();
}
}
config.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans>
<bean>
<name>lenve.test.Utils</name>
</bean>
<bean>
<name>java.util.Date</name>
</bean>
</beans>
8.使用classLoader加载一个配置文件:
//配置文件为config.properties
@Test
public void test1(){
try {
//把config.properties文件放入工程目录下
// InputStream is = new FileInputStream(new File("config.properties"));
//使用classLoader获得配置文件,注意文件路径
// InputStream is = ReflectTest2.class.getClassLoader().getResourceAsStream("lenve/test/config.properties");
//也可以直接使用class提供的方法获得,这里的路径地址则是相对地址(这里我们把配置文件和java文件放入同一个包中)
// InputStream is = ReflectTest2.class.getResourceAsStream("config.properties");
//如果把配置文件放入lenve.test.resources包中(注意ReflectTest2.java在lenve.test包中)
// InputStream is = ReflectTest2.class.getResourceAsStream("resources/config.properties");
//还可以通过绝对路径来访问
InputStream is = ReflectTest2.class.getResourceAsStream("/lenve/test/resources/config.properties");
Properties prop = new Properties();
prop.load(is);
is.close();
String className = prop.getProperty("className");
Class c = Class.forName(className);
Method m = c.getMethod("add", int.class,int.class);
System.out.println(m.invoke(c.newInstance(), 3,4));
} catch (ClassNotFoundException | NoSuchMethodException
| SecurityException | IllegalAccessException
| IllegalArgumentException | InvocationTargetException
| InstantiationException | IOException e) {
e.printStackTrace();
}
}
Java 反射学习笔记的更多相关文章
- java反射学习笔记
1.java反射概念 JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法:对于任意一个对象,都能够调用它的任意一个方法和属性:这种动态获取的信息以及动态调用对象的方法的功 ...
- java 反射---学习笔记
一.java的动态性 反射机制 动态编译 动态执行JavaScript代码 动态字节码操作 二.动态语言 动态语言 程序运行时,可以改变程序结构或变量类型,典型的语言:python.ruby.java ...
- [新手学Java]反射学习笔记
示例类 @SuppressWarnings("unused") public class Person { public String Name; private int Age; ...
- Java反射学习笔记01
- [原创]java WEB学习笔记66:Struts2 学习之路--Struts的CRUD操作( 查看 / 删除/ 添加) 使用 paramsPrepareParamsStack 重构代码 ,PrepareInterceptor拦截器,paramsPrepareParamsStack 拦截器栈
本博客的目的:①总结自己的学习过程,相当于学习笔记 ②将自己的经验分享给大家,相互学习,互相交流,不可商用 内容难免出现问题,欢迎指正,交流,探讨,可以留言,也可以通过以下方式联系. 本人互联网技术爱 ...
- Java基础学习笔记总结
Java基础学习笔记一 Java介绍 Java基础学习笔记二 Java基础语法之变量.数据类型 Java基础学习笔记三 Java基础语法之流程控制语句.循环 Java基础学习笔记四 Java基础语法之 ...
- 20145213《Java程序设计学习笔记》第六周学习总结
20145213<Java程序设计学习笔记>第六周学习总结 说在前面的话 上篇博客中娄老师指出我因为数据结构基础薄弱,才导致对第九章内容浅尝遏止地认知.在这里我还要自我批评一下,其实我事后 ...
- [原创]java WEB学习笔记95:Hibernate 目录
本博客的目的:①总结自己的学习过程,相当于学习笔记 ②将自己的经验分享给大家,相互学习,互相交流,不可商用 内容难免出现问题,欢迎指正,交流,探讨,可以留言,也可以通过以下方式联系. 本人互联网技术爱 ...
- java JDK8 学习笔记——助教学习博客汇总
java JDK8 学习笔记——助教学习博客汇总 1-6章 (by肖昱) Java学习笔记第一章——Java平台概论 Java学习笔记第二章——从JDK到IDEJava学习笔记第三章——基础语法Jav ...
随机推荐
- [Android应用]《花界》V1.0 正式版隆重发布!
http://www.cnblogs.com/qianxudetianxia/archive/2012/04/05/2433669.html 1. 软件说明(1). 花界是一款看花软件:“看花,议花, ...
- 用 NSURProtocol 注入测试数据
在之前的几篇博文中,笔者介绍过访问异步网络的单元测试方法及如何使用模拟对象来进一步控制单元测试的范围.在今天的教程中,笔者将展示另一种方法,即:通过自定义 NSURProtocol 类来获取静态测试数 ...
- CF_225B _Well-known Numbers
Numbers k-bonacci (k is integer, k > 1) are a generalization of Fibonacci numbers and are determi ...
- 使用Spring AOP预处理Controller的参数
实际编程中,可能会有这样一种情况,前台传过来的参数,我们需要一定的处理才能使用,比如有这样一个Controller @Controller public class MatchOddsControll ...
- PHP curl传输文件的版本兼容性
/** * 存储文件到远程服务器 * * @param string $filePath * 文件绝对路径 * @param string $fileSaveUrl * 存储的远程目标地址 * @pa ...
- 【HDOJ】1198 Farm Irrigation
其实就是并查集,写麻烦了,同样的代码第一次提交wa了,第二次就过了. #include <stdio.h> #include <string.h> #define MAXNUM ...
- VM Depot 登陆中国!
发布于 2014-03-24 作者 陈 忠岳 今天我很高兴地向大家宣布,来自微软开放技术(上海)有限公司的首个产品 VM Depot 正式在中国发布!VM Depot是为Windows Azur ...
- Tag file directory /struts-tags does not start with "/WEB-INF/tags"
使用自定义标签,记得引用路径 <%@taglib prefix="s" uri="/struts-tags" %>
- HDU-2561 第二小整数
http://acm.hdu.edu.cn/showproblem.php?pid=2561 第二小整数 Time Limit: 3000/1000 MS (Java/Others) Memor ...
- bootstrap easyUI 的选型
最近开始搭建新的项目后台,开始在Bootstrap 和 EasyUI两个框架上选型,个人还是倾向于Bootstrap,毕竟这个响应式效果是个趋势,并且可以自动兼容不同的屏幕分辨率和设备.