Java 反射学习笔记

举报
江南一点雨 发表于 2021/08/16 23:29:17 2021/08/16
【摘要】 要学反射,先要了解Class这个类,Class是所有Java类的一个总称,Class的实例中存储的是一个类的字节码,获取Class的实例有三种方式: System.classnew Date().getClass()Class.forName(“java.lang.String”); Java不允许使用Class cla = new Class()这种方式获得一个C...

要学反射,先要了解Class这个类,Class是所有Java类的一个总称,Class的实例中存储的是一个类的字节码,获取Class的实例有三种方式:

  1. System.class
  2. new Date().getClass()
  3. 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); }
  
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

输出结果为:
这里写图片描述

根据这结果我们可以推论出,用这三种方式获得的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
  
 
  • 1
  • 2
  • 3
  • 4

那么反射是什么?这是别人总结的“反射就是把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(); } }
  
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

输出结果:
这里写图片描述

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!"); }

}
  
 
  • 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

方式二,先获得一个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
  • 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

输出结果:

这里写图片描述

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(); } }
  
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24

输出结果:

这里写图片描述

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(); } }
  
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20

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(); } }
  
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23

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 + "]"; }
}
  
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15

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(); } }
  
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18

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(); } }
  
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19

ReflectMain.java

class ReflectMain{ public static void main(String[] args) { for(String str:args) System.out.println(str); }
}
  
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

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); } }
  
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22

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(); } }
  
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19

config.properties

className=lenve.test.Utils
  
 
  • 1

方式二:

 //配置文件为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(); } }
  
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18

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>
  
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

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(); } }
  
 
  • 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

文章来源: wangsong.blog.csdn.net,作者:_江南一点雨,版权归原作者所有,如需转载,请联系作者。

原文链接:wangsong.blog.csdn.net/article/details/45337249

【版权声明】本文为华为云社区用户转载文章,如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。