Java反射的常见用法
反射的常见用法有三类,第一类是“查看”,比如输入某个类的属性方法等信息,第二类是“装载“,比如装载指定的类到内存里,第三类是“调用”,比如通过传入参数,调用指定的方法。
1 查看属性的修饰符、类型和名字
通过反射机制,我们能从.class文件里看到指定类的属性,比如属性的修饰符,属性和类型和属性的变量名。通过下面的ReflectionReadVar.java,我们看演示下具体的做法。
1 import java.lang.reflect.Field;
2 import java.lang.reflect.Modifier;
3 class MyValClass{
4 private int val1;
5 public String val2;
6 final protected String val3 = "Java";
我们在第3行定义了一个MyValCalss的类,并在第4到第6行里,定义了三个属性变量。
8 public class ReflectionReadVar {
9 public static void main(String[] args) {
10 Class<MyValClass> clazz = MyValClass.class;
11 //获取这个类的所有属性
12 Field[] fields = clazz.getDeclaredFields();
13 for(Field field : fields) {
14 //输出修饰符 System.out.print(Modifier.toString(field.getModifiers()) + "\t");
15 //输出属性的类型
16 System.out.print(field.getGenericType().toString() + "\t");
17 //输出属性的名字
18 System.out.println(field.getName());
19 }
20 }
21 }
在main函数的第10行里,通过MyValClass.class,得到了Class<MyValClass>类型的变量clazz,在这个变量中,存储了MyValClass这个类的一些信息。
在第12行里,通过了clazz.getDeclaredFields()方法得到了MyValClass类里的所有属性的信息,并把这些属性的信息存入到Field数组类型的fields变量里。
通过了第13行的for循环依次输出了这些属性信息。具体来讲,通过第14行的代码输出了该属性的修饰符,通过第16行的代码输出了该属性的类型,通过第18行的代码输出了该属性的变量名。这段代码的输出如下,从中我们能看到各属性的信息。
1 private int val1
2 public class java.lang.String val2
3 protected final class java.lang.String val3
2 查看方法的返回类型,参数和名字
通过ReflectionReadFunc.java,我们能通过反射机制看到指定类的方法。
1 import java.lang.reflect.Constructor;
2 import java.lang.reflect.Method;
3 class MyFuncClass{
4 public MyFuncClass(){}
5 public MyFuncClass(int i){}
6 private void f1(){}
7 protected int f2(int i){return 0;}
8 public String f2(String s) {return "Java";}
在第3行定义的MyFuncClass这个类里,我们定义了2个构造函数和3个方法。
10 public class ReflectionReadFunc {
11 public static void main(String[] args) {
12 Class<MyFuncClass> clazz = MyFuncClass.class;
13 Method[] methods = clazz.getDeclaredMethods();
14 for (Method method : methods)
15 { System.out.println(method); }
16 //得到所有的构造函数
17 Constructor[] c1 = clazz.getDeclaredConstructors();
18 //输出所有的构造函数
19 for(Constructor ct : c1)
20 { System.out.println(ct); }
21 }
22 }
在main函数的第12行,我们同样是通过了类名.class的方式(也就是MyFuncClass.class的方式)得到了Class<MyFuncClass>类型的clazz对象。
在第13行里,是通过了getDeclaredMethods方法得到了MyFuncClass类的所有方法,并在第14行的for循环里输出了各方法。在第17行里,是通过了getDeclaredConstructors方法得到了所有的构造函数,并通过第19行的循环输出。
本代码的输出结果如下所示,其中第1到第3行输出的是类的方法,第4和第5行输出的是类的构造函数。
1 private void MyFuncClass.f1()
2 protected int MyFuncClass.f2(int)
3 public java.lang.String MyFuncClass.f2(java.lang.String)
4 public MyFuncClass()
5 public MyFuncClass(int)
不过在实际的项目里,我们一般不会仅仅“查看”类的属性和方法,在更多的情况里,我们是通过反射装载和调用类里的方法。
3 通过forName和newInstance方法加载类
在前文JDBC操作数据库的代码里,我们看到在创建数据库连接对象(Connection)之前,需要通过Class.forName("com.mysql.jdbc.Driver");的代码来装载数据库(这里是MySQL)的驱动。
可以说,Class类的forName方法最常见的用法就是装载数据库的驱动,以至于不少人会错误地认为这个方法的作用是“装载类”。
其实forName方法的作用仅仅是返回一个Class类型的对象,它一般会和newInstance方法配套使用,而newInstance方法的作用才是加载类。
通过下面的ForClassDemo.java这段代码,我们来看下综合使用forName和newInstance这两个方法加载对象的方式。
1 class MyClass{
2 public void print()
3 { System.out.println("Java"); }
4 }
5 public class ForClassDemo {
6 public static void main(String[] args) {
7 //通过new创建类和使用类的方式
8 MyClass myClassObj = new MyClass();
9 myClassObj.print();//输出是Java
10 //通过forName和newInstance加载类的方式
11 try {
12 Class<?> clazz = Class.forName("MyClass");
13 MyClass myClass = (MyClass)clazz.newInstance();
14 myClass.print();//输出是Java
15 } catch (ClassNotFoundException e) {
16 e.printStackTrace();
17 } catch (InstantiationException e) {
18 e.printStackTrace();
19 } catch (IllegalAccessException e) {
20 e.printStackTrace();
21 }
22 }
23 }
在第1行定义的MyClass这个类里,我们在其中的第2行定义了一个print方法。
Main函数的第8和第9行里,我们演示了通过常规new的方式创建和使用类的方式,通过第9行,我们能输出“Java”这个字符串。
在第12行,我们通过Class.forName("MyClass")方法返回了一个Class类型的对象,请注意,forName方法的作用不是“加载MyClass类”,而是返回一个包含MyClass信息的Class类型的对象。这里我们是通过第13行的newInstance方法,加载了一个MyClass类型的对象,并在第14行调用了其中的print方法。
既然forName方法的作用仅仅是“返回Class类型的对象”,那么在JDBC部分的代码里,为什么我们能通过Class.forName("com.mysql.jdbc.Driver");代码来装载MySQL的驱动呢?在MySQL的com.mysql.jdbc.Driver驱动类中有如下的一段静态初始化代码。
1 static {
2 try {
3 java.sql.DriverManager.registerDriver(new Driver());
4 } catch (SQLException e) {
5 throw new RuntimeException(“Can’t register driver!”);
6 }
7 }
也就是说,当我们调用Class.forName方法后,会通过执行这段代码会新建一个Driver的对象,并调用第3行的DriverManager.registerDriver把刚创建的Driver对象注册到DriverManager里。
在上述的代码里,我们看到了除了new之外,我们还能通过newInstance来创建对象。
其实这里说“创建”并不准确,虽然说通过new和newInstance我们都能得到一个可用的对象,但newInstance的作用其实是通过Java虚拟机的类加载机制把指定的类加载到内存里。
我们在工厂模式中,经常会通过newInstance方法来加载类,但这个方法只能是通过调用类的无参构造函数来加载类,如果我们在创建对象时需要传入参数,那么就得使用new来调用对应的带参的构造函数了。
4 通过反射机制调用类的方法
如果我们通过反射机制来调用类的方式,那么就得解决三个问题,第一,通过什么方式来调?第二,如何传入参数,第三,如何得到返回结果?
通过下面的CallFuncDemo.java代码,我们将通过反射来调用类里的方法,在其中我们能看下上述三个问题的解决方法。
1 import java.lang.reflect.Constructor;
2 import java.lang.reflect.InvocationTargetException;
3 import java.lang.reflect.Method;
4 class Person {
5 private String name;
6 public Person(String name)
7 {this.name = name;}
8 public void saySkill(String skill) {
9 System.out.println("Name is:"+name+",skill is:" + skill);
10 }
11 public int addSalary(int current)
12 { return current + 100;}
13 }
在第4行里,我们定义了一个Person类,在其中的第6行里,我们定义了一个带参的构造函数,在第8行里,我们定义了一个带参但无返回值得saySkill方法,在第11行里,我们定义了一个带参而且返回int类型的addSalary方法。
14 public class CallFuncDemo {
15 public static void main(String[] args) {
16 Class c1azz = null;
17 Constructor c = null;
18 try {
19 c1azz = Class.forName("Person");
20 c = c1azz.getDeclaredConstructor(String.class);
21 Person p = (Person)c.newInstance("Peter");
22 //output: Name is:Peter, skill is:java
23 p.saySkill("Java");
24 // 调用方法,必须传递对象实例,同时传递参数值
25 Method method1 = c1azz.getMethod("saySkill", String.class);
26 //因为没返回值,所以能直接调
27 //输出结果是Name is:Peter, skill is:C#
28 method1.invoke(p, "C#");
29 Method method2 = c1azz.getMethod("addSalary", int.class);
30 Object invoke = method2.invoke(p, 100);
31 //输出200
32 System.out.println(invoke);
33 } catch (ClassNotFoundException e) {
34 e.printStackTrace();
35 } catch (NoSuchMethodException e1) {
36 e1.printStackTrace();
37 } catch (InstantiationException e) {
38 e.printStackTrace();
39 } catch (IllegalAccessException e) {
40 e.printStackTrace();
41 } catch (InvocationTargetException e) {
42 e.printStackTrace();
43 }
44 }
45 }
在第19行里,我们通过Class.forName得到了一个Class类型的对象,其中包含了Person类的信息。在第20行里,通过传入String.class参数,得到了Person类的带参的构造函数,并通过了第21行的newInstance方法,通过这个带参的构造函数创建了一个Person类型的对象。随后在第23行里调用了saySkill方法。这里我们演示通过反射调用类的构造函数来创建对象的方式。
在第25行里,我们通过了getMethod方法,得到了带参的saySkill方法的Method类型的对象,随后通过第28行的invoke方法调用了这个saySkill方法,这里第一个参数是由哪个对象来调用,通过第二个参数,我们传入了saySkill方法的String类型的参数。
用同样的方式,我们在第29和30行通过反射调用了Person类的addSalary方法,由于这个方法有返回值,所以我们在30行用了一个Object类型的invoke对象来接收返回值,通过第32行的打印语句,我们能看到200这个执行结果。
Java反射的常见用法的更多相关文章
- java 反射与常用用法
java通常是先有类再有对象,有对象我就可以调用方法或者属性. 反射其实是通过Class对象来调用类里面的方法.通过反射可以调用私有方法和私有属性.大部分框架都是运用反射原理. 如何获得Class对象 ...
- Java反射之Field用法
在Java反射中Field用于获取某个类的属性或该属性的属性值 一:如何通过Field反射获取类的属性 Field提供如下几种方法: :1:Class.getDeclaredField(String ...
- Java jar命令 常见用法
一.jar命令作用: 进行打包 -- 把多个文件打包成一个压缩包 -- 这个压缩包和Winzip的压缩格式是一样的. 区别在于jar压缩的文件默认多一个META-INF的文件夹,该文件夹下包含一个Ma ...
- Java stream的常见用法
不讲原理,只说用法. 1,集合遍历 public class StreamTest { public static void main(String[] args) { //1 遍历 List< ...
- Java 反射之Class用法
下面示范如果通过Class对象获取对应类的信息: package com.reflect; import java.lang.annotation.Annotation; import java.la ...
- collections在java中的常见用法
1. 工具类collections用于操作集合类,如List,Set,常用方法有: 1) 排序(Sort) 使用sort方法可以根据元素的自然顺序 对指定列表按升序进行排序.列表中的所有元素都必须实现 ...
- java位运算符常见用法
1. 判断int型变量a是奇数还是偶数 a&1 = 0 偶数 a&1 = 1 奇数 2. 求平均值,比如有两个int类型变量x.y,首先要求x+y的和,再除以2,但是有可能x+y的结果 ...
- Java 基础之详解 Java 反射机制
一.什么是 Java 的反射机制? 反射(Reflection)是Java的高级特性之一,是框架实现的基础,定义:JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法: ...
- Java反射01 : 概念、入门示例、用途及注意事项
1.Java反射定义 本文转载自:https://blog.csdn.net/hanchao5272/article/details/79360452 官方定义如下: Reflection enabl ...
随机推荐
- 【2016常州一中夏令营Day5】
小 W 拼图[问题描述]小 W 和小 M 一起玩拼图游戏啦~小 M 给小 M 一张 N 个点的图,有 M 条可选无向边,每条边有一个甜蜜值,小 W 要选K 条边,使得任意两点间最多有一条路径,并且选择 ...
- LDO
1.出现原因:便携式设备的发展,使得人们对电源的要求越来越高,而以前一直使用的三段稳压电源无法满足需求. 2.特点:内部的PNP管导通压降很小,自耗很低. 3.应用场合: (1)不同电压输出级别的应用 ...
- 编写自己的JDBC框架(转)
一.元数据介绍 元数据指的是"数据库"."表"."列"的定义信息. 1.1.DataBaseMetaData元数据 Connection.g ...
- vue新增属性响应式更新的问题
根据官方文档定义: 如果在实例创建之后添加新的属性到实例上,它不会触发视图更新. 受现代 JavaScript 的限制 (以及废弃 Object.observe),Vue 不能检测到对象属性的添加或删 ...
- hadoop中 namenode的持久化
一.为什么namenode持久化 namenode通过内存存储hdfs集群的元数据(目录结构 文件信息 块对应关系),如果内存出现问题,那么会数据丢失,需要通过持久化,把内存中的数据定期的存储在硬盘中 ...
- eclipse中如何配置tomcat
1.打开eclipse上面的Windows选项,选择Preferences==>Server==>Runtime Environments==>Add 2.选择你电脑中安装的tomc ...
- HashMap、lru、散列表
HashMap HashMap的数据结构:HashMap实际上是一个数组和链表("链表散列")的数据结构.底层就是一个数组结构,数组中的每一项又是一个链表. hashCode是一个 ...
- POJ-1741 树上分治--点分治(算法太奇妙了)
给你1e5个节点的树,(⊙﹏⊙) 你能求出又几对节点的距离小于k吗??(分治NB!) 这只是一个板子题,树上分治没有简单题呀!(一个大佬说的) #include<cstdio> #incl ...
- $Poj2083/AcWing118\ Fractal$ 模拟
$AcWing$ $Sol$ 一年前做过差不多的南蛮图腾,当时做出来还是很有成就感的$OvO$ $N<=7$,就是模拟模拟,预处理一下,$over$ $Code$ #include<bit ...
- AdapterPattern(适配器模式)-----Java/.Net
适配器模式(Adapter Pattern)是作为两个不兼容的接口之间的桥梁.这种类型的设计模式属于结构型模式,它结合了两个独立接口的功能. 这种模式涉及到一个单一的类,该类负责加入独立的或不兼容的接 ...