Java反射入门
来源:互联网 发布:一朝得道天下知 编辑:程序博客网 时间:2024/09/21 13:46
一直听说反射,但在实际开发过程中却没怎么接触过,最近在学习代理模式时,发现动态代理就是用的反射,于是学习一下。
一、反射定义
Java反射是指在程序运行状态中,获取一个类所有属性和方法,调用一个对象的任意一个属性和方法;这种动态的获取信息和动态调用对象的功能就是Java的反射机制。也就是在程序运行时,通过class文件对象来使用该文件的变量、构造方法以及成员方法。
JavaAPI提供了以下几个类,一些常用的方法通可以通过这几个类获取:
java.lang.Class;相当于.class文件对象java.lang.reflect.Constructor;相当于类中构造方法对象java.lang.reflect.Field;相当于类中成员变量对象java.lang.reflect.Method;相当于类中成员方法对象
二、利用反射获取信息
使用反射,必须先得到类的class文件对象,Java中获取class文件对象有三种方式:
(1)Student s = new Student();Class c = s.getClass();(2)Class c = Student.class;(3)Class c = Class.forName("cn.reflect.demo.Student");//这里必须使用全类名
常用的是2、3两种方式;下面是一个简单的Student类,供测试使用。
package proxy.reflect.demo;public class Student { private String name ; Integer age ; public Double money; public Student() {} Student(String name){ this.name = name; } private Student(String name, Integer age) { this.name = name; this.age = age; } public static void learn(String name,String address){ System.out.println(name+"在"+address+"学习"); } private void learn(){ System.out.println("学生正在学习"); } public String learn(String name){ return name+"正在学习"; } public String toString() { return "Student [name=" + name + ", age=" + age + "]"; }}
创建对象实例
Object obj = c.newInstance();//调用的是Student的public修饰的无参构造方法,相当于Object obj = new Student();
有了对象之后就可以获取想要的信息了。
2.1获取构造方法
/** Constructor[] getConstructors();只能获取public构造方法 Constructor[] getDeclaredConstructors();获取所有构造方法(包括private修饰的) Constructor getConstructor(Class<?>... params);根据传递参数获取public修饰的构造方法对象 Constructor getDeclaredConstructor(Class<?>... params);根据传递参数获取构造方法对象(包括private修饰的)*/public static void main(String[] args) throws Exception{ //获取字节码文件对象 Class c = Class.forName("proxy.reflect.demo.Student"); //获取所有构造方法 Constructor[] constructors = c.getDeclaredConstructors(); for (Constructor constructor : constructors) { System.out.println(constructor); //带参数的构造方法,传递的参数类型对应构造方法的参数 Constructor constructor = c.getDeclaredConstructor(String.class,Integer.class); //取消Java访问检查,只有设置为true后才可以调用private修饰的构造方法 constructor.setAccessible(true); //相当于Object obj = new Student("小明",24); Object obj = constructor.newInstance("小明",24); //使用System的打印流时自动调用obj对象的toString()方法 System.out.println(obj);}
打印结果
private proxy.reflect.demo.Student(java.lang.String,java.lang.Integer)proxy.reflect.demo.Student(java.lang.String)public proxy.reflect.demo.Student()Student [name=小明, age=24]
可以看到Student的所有构造方法都可以取到,还可以调用private修饰的构造方法;
2.2获取成员变量
获取成员变量和获取构造函数一样。首先获取到Constructor对象,然后使用Constructor创建新对象实例,然后调用get(),set()方法获取和修改与该Field对象关联的字段。
/** Field[] getFields();得到所有带public修饰的成员方法对象数组 Field[] getDeclaredFields();得到所有带修饰的成员方法对象数组 Field getField(String name);获取对应name的Field对象,必须是public修饰的 Field getDeclaredField(String name);获取对应name的Field对象 field.get(Object obj)返回指定对象obj在此Field对象表示的值 field.set(Object obj, Object value)将指定对象变量上此 Field 对象表示的字段设置为指定的新值。*/public static void main(String[] args) throws Exception{ //获取字节码文件对象 Class c = Class.forName("proxy.reflect.demo.Student"); //创建对象实例,默认调用的是无参构造,相当于Object ojb = new Student(); Object obj = c.newInstance(); //获取属性为name成员变量的Field对象 Field field = c.getDeclaredField("name"); //取消Java访问检查 field.setAccessible(true); //为obj对象的field属性赋值"小明",相当于ojb.name="小明"; field.set(obj, "小明"); //打印结果:小明 System.out.println(field.get(obj)); }
2.3获取成员方法
使用Constructor创建新对象,然后用invoke()方法调用与Method对象关联的而方法。成员方法的获取和其他两个属性获取还是有一点区别的,看代码。
/** Method[] c.getMethods();得到本类和父类所有public修饰的方法Method对象数组 Method[] c.getDeclaredMethods();得到所有方法Method对象数组 Method getMethod(String name, Class<?>... params) 得到某一方法method对象(只能是public修饰的),name是方法名,params是参数.class Method getDeclaredMethod(String name, Class<?>... params) 得到某一方法method对象,name是方法名,params是参数.class Object invoke(Object obj, Object... args)调用obj对象的method方法传递参数是args*/public static void main(String[] args) throws Exception { Class c = Class.forName("proxy.reflect.demo.Student"); Object object = c.newInstance(); //可以获取本类和父类的public方法 Method[] methods = c.getMethods(); //只获取自己的所有的方法 //Method[] methods = c.getDeclaredMethods(); for (Method m : methods) { System.out.println(m); } //获取方法名为learn并且没有形参的方法对象 Method m = c.getDeclaredMethod("learn"); m.setAccessible(true); //调用object对象的与m对象关联方法 m.invoke(object); //拿到方法名为learn参数是String类型的方法对象 Method m2 = c.getMethod("learn",String.class); //调用object对象的方法并给参数赋值为"小明",该方法有返回值 Object result = m2.invoke(object, "小明"); Method m3 = c.getMethod("learn",String.class,String.class); //调用静态方法,因为静态方法是随着类的加载而加载的,无需使用对象调用,所有这里传null,当然传对象也没有问题 m3.invoke(null, "-----\n小明","教室");}
2.4一些其他的常用方法
以下几种方法也是比较常用的,不再一一描述,可以通过JavaAPI详细了解,它才是我们最好的老师!
三、一些应用
反射的应用是非常广泛的,合理的利用反射可以很大程度上节省我们的开发时间。反射是在运行时期获取类信息的,我们利用这一特性,可以做很多事情。
3.1加载配置文件
在软件开发过程中,使用配置文件是必不可少的,通常把一些多变的数据写在配置文件,在修改时候只需需修改配置文件,增加了代码的可维护性。在学习jdbc的时候,将访问数据库的一些参数放到配置文件中,比如数据库链接、用户名、密码等,在更换数据库或者修改密码之后,只需修改配置文件即可。来看一下反射如何加载config.properties文件的。
dbName=jdbc:mysql://localhost:3306/reflectusername=jiekepassword=rose
使用反射获取数据
public static void main(String[] args) throws Exception { //加载文件中的键值对数据 Properties p = new Properties(); /** JavaAPI:查找与给定类相关的资源的规则是通过定义类的 class loader 实现的,此方法委托此对象的类加载器, 如果此对象通过引导类加载器加载,则此方法将委托给 ClassLoader.getSystemResourceAsStream(String)。 通过以上说明此处可以填写任意类,因为这些类都会有由加载器加载,最后都是调用类加载器的方法; 所以此处写Object.class、String.class、ReflectProperty.class等等都时可以的。 不可以使用this(本类对象)、super(父类对象),在jvm加载一个类时,首先为static属性分配内存空间,而对象是在new时候才会被加载的 */ InputStream is = ReflectProperty.class.getResourceAsStream("/config.properties"); p.load(is); is.close(); System.out.println("数据库链接:"+p.getProperty("dbName")+"\n用户名:"+p.getProperty("username")+"\n密码:"+p.getProperty("password"));}
打印结果
数据库链接:jdbc:mysql://localhost:3306/reflect用户名:jieke密码:rose
3.2泛型”擦除”
泛型”擦除”就是越过泛型检查,泛型是在程序编译期检查程序的,通过反射获取到程序运行期的class文件对象进行操作,即可越过泛型检查。不过该知识点在开发过程中基本上不可能用到,只是在一些面试题上会出现,简单了解即可。
public static void main(String[] args) { //泛型简写,Java7之后出现的 List<Integer> list = new ArrayList<>(); Class c = list.getClass(); try { //通过class对象得到方法名为add参数类型是Object的方法对象 //通过查询源码发现List的add()方法传递的就是Object Method method = c.getMethod("add",Object.class); method.invoke(list, "小明"); System.out.println(list); } catch (Exception e) { e.printStackTrace(); }}
四、总结
Java反射的灵活性已经体验到了,很多框架都有用到反射,Spring的AOP,代码自动生成工具、mybatis等等。不过在我们平时开发过程中,是很少使用反射的,它会使我们的程序性能变低,因为使用反射调用类的构造方法、变量、方法的效率远低于直接代码。总之,有利有弊,开发过程中应尽量避开反射,假如想自己写框架,那么反射是必不可少的。
- java 反射入门
- java反射入门备忘
- java反射入门
- java 反射入门
- Java反射入门
- Java反射入门
- JAVA反射机制入门
- Java入门_反射
- 简单入门Java反射
- Java反射机制入门
- java入门:反射
- Java反射入门
- Java反射入门
- Java反射入门
- JAVA反射入门
- java反射入门基础
- java反射入门
- Java 入门 八 (Java 反射)
- <转>Spark-Mllib 数据类型
- 设计模式之单例模式-java
- Unsupported major.minor version 52.0
- Mysql5.7 的错误日志中最常见的note级别日志解释
- 双链表的建立、测长、打印、删除、插入
- Java反射入门
- JSPatch OC->JS学习(1)
- window下的wamp散装
- ECharts 自定义动态tooltip
- springmvc文件上传代码
- 高精度小数
- java权限设计大纲
- ubuntu16.04 没有/var/log/messages
- 内部类访问局部变量的时候,为什么变量必须加上final修饰(转)