标签:代理 lang 包含 数据 tcl amp tar font property
什么是反射
JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法;这种动态获取信息以及动态调用对象的方法的功能称为java语言的反射机制。反射 就是将java类中的各种成分映射成一个个的Java对象。
java反射框架提供的功能:
反射的用途
Java 强类型语言,但是我们在运行时有了解、修改信息的需求,包括类信息、成员信息等。反射机制主要用于在运行时动态加载需要加载的对象。例如,在通用框架开发过程中,根据配置文件加载不同的类和对象,调用不同的方法。我们平时使用反射主要用于获取类型的相关信息、动态调用方法、动态构造对象、从程序集中获得类型。
预备知识
在了解java反射之前首先补充几个知识:
Class类:在面向对象编程中,万物皆对象,用户编写的任何一个类都是对象,它是java.lang.class类的实例对象。即在java中存在一个类,叫class类,这个类的实例对象就是我们编写的类。
类类型(class type):用户类对应的class类的对象实例。如下文中的c、c2、c3是Student类的类类型。我们编写的.java文件经过编译后得到.class文件,即对应一个Class实例对象。通过类类型可以获得该类的对象实例以及该类的变量、方法信息。Class类的实例对象是由jvm在类加载的时候创建的,且仅在第一次使用该类时加载。在运行期间,一个用户类只产生一个Class类的对象实例,一个用户类仅有一个Class对象产生,那么我们如何获取这个对象呢?
类的加载方式
1)静态加载类:编译时刻加载类,使用new创建对象,编译时加载所有可能用到的类。
2)动态加载类:运行时刻加载类,使用反射动态加载,通过类类型创建该类对象。
通过动态加载类可以方便进行程序升级,同时为提高程序的适用性,通过c.getInstance()创建对象时的强制类型转换最好转为父接口类型。
反射的基本使用方法
一般情况下,我们在使用类的时候都是创建该类对象,通过对象调用方法。这种方式可以理解为“正”。而反射,则是在程序编写时我们并不知道要创建并初始化的类对象是什么,自然就无法使用new来创建对象,此时可以使用反射来创建对象、调用方法。
其实我们使用反射的目的就是使用某个类、调用类里的方法、获取或设置类中属性的值,这点跟我们传统使用类的目的是一致的。只是 相对于传统使用类的的方式有所区别,所以在学习反射的使用时,无非是学习如何通过反射来创建类对象、调用类的方法和属性等。下面一一作出介绍。
反射就是对任意一个类可以知道他的全部属性和方法,这些信息就存储在该类对应的Class对象实例中,所以如果想要使用某个类,那么必须先获得这个类的类类型,即获取该类对应的Class的对象实例:
获取Class的对象实例的三种方式
1)使用用户类的静态class属性
Class c = Student.class;
任何数据类型(包括基本数据类型)都有一个静态的class属性,通过用户类的类名获得类类型。
2)使用用户类对象实例的getClass()方法
Class c2 = student1.getClass();
getClass()是Object类的成员方法,Java类全部继承自Object类,所以任意一个类对象都有getClass()方法。通过用户类的实例获得类类型。
3)通过Class类的静态方法<常用>
//Class.forName(String className) className为用户类包含包名的完整类名 Class c3 = Class.forName("com.example.reflact.Student")
通过Class类名调用静态方法forName(),传入用户类的完整类名(包含包名)获得用户类。forName()方法在运行时可能会抛出ClassNotFoundException,编程时候需要注意处理异常。
在获得类类型之后,我们就可以进一步获取这个类中的构造方法、成员方法、属性等信息,从而使用该类。
获取用户类的构造方法
前面提到过,反射就是把java类中的各种成分(方法、属性等)映射成一个个的Java对象,其中,构造方法是java.lang.Constructor类的对象。
批量获取构造方法
1 public Constructor[] getConstructors() //获取所有"公有的"构造方法 2 public Constructor[] getDeclaredConstructors() //获取所有的构造方法(包括私有、受保护、默认、公有)
获取单个构造方法,会抛出NoSuchMethodException异常
public Constructor getConstructor(Class... parameterTypes)//获取单个的"公有的"构造方法,
//传入构造方法若干参数的类型,注意传入的必须是类型(如int.class),返回的是描述这个构造方法的类对象 public Constructor getDeclaredConstructor(Class... parameterTypes)//获取"某个构造方法"可以是私有的,或受保护、默认、公有;
调用构造方法
Constructor-->newInstance(Object... initargs) //传入构造方法的若干具体参数
示例:
package com.jing.reflect; import java.lang.reflect.Constructor; import java.lang.reflect.Method; public class Test { public static void main(String[] args){ try { Class c = Class.forName("com.jing.reflect.Student"); Constructor[] cs = c.getDeclaredConstructors();//获取全部构造方法 System.out.println("====批量获取全部构造方法==="); for(Constructor constructor : cs){ System.out.println(constructor); } System.out.println("===获取单个构造方法==="); Constructor constructor = c.getConstructor(String.class);//获取单个构造函数 System.out.println(constructor); } catch (ClassNotFoundException e) { e.printStackTrace(); } catch (NoSuchMethodException e) { e.printStackTrace(); } } }
通过反射获取用户创建的类的对象实例
通过反射创建类对象的方式有两种:
通过Class对象的newInstance()方法,会抛出InstantiationException。只能使用无参构造方法
Student s1 = (Student)c.newInstance();
通过Constructor对象的newInstance()方法,会抛出InvocationTargetException异常。可以选择构造方法
System.out.println("===获取构造方法===");
Constructor constructor = c.getConstructor(String.class);//获取单个构造函数
System.out.println("===创建对象===");
Student s2 = (Student)constructor.newInstance("Jing");
获得用户类中成员方法信息
成员方法是java.lang.reflect.Method类的对象
批量获取:
public Method[] getMethods():获取所有"公有方法";(包含了父类的方法也包含Object类) public Method[] getDeclaredMethods():获取所有的成员方法,包括私有的(不包括继承的)
单个获取:
public Method getMethod(String name,Class<?>... parameterTypes): 参数: name : 方法名; Class ... : 形参的Class类型对象 public Method getDeclaredMethod(String name,Class<?>... parameterTypes)
调用成员方法:
Method --> public Object invoke(Object obj,Object... args): 参数说明: obj : 要调用方法的对象; args:调用方法时所传递的实参;
示例:
System.out.println("===获取全部成员方法==="); Method[] methods = c.getDeclaredMethods(); for(Method method : methods){ Class[] paramTypes = method.getParameterTypes();//获取方法参数列表中参数类型 System.out.print("方法名:" + method.getName() + "--方法返回类型:" + method.getReturnType() + "--方法参数列表:"); for(Class par : paramTypes){ System.out.print(par + " "); } System.out.println(); } System.out.println("===获取单个成员方法==="); Method method = c.getDeclaredMethod("showAge", int.class); System.out.println(method); Object obj = c.getConstructor().newInstance();//实例化一个Student对象 System.out.println("===调用方法==="); method.setAccessible(true);//解除私有限定 Object result = method.invoke(obj, 20);//此处解除了私有限定,所以参数可以使用obj // 或,不解除私有限定,使用s1调用方法(s1被强制类型转换为了Student类型,可以调用private变量) System.out.println("返回结果:" + result);
获取用户类中成员变量信息
成员变量是java.lang.reflect.Field类的对象
批量获取
public Field[] getFields():获取所有的"公有字段" public Field[] getDeclaredFields():获取所有字段,包括:私有、受保护、默认、公有;
单个获取
public Field getField(String fieldName):获取某个"公有的"字段; public Field getDeclaredField(String fieldName):获取某个字段(可以是私有的) ,会抛出NoSuchFieldException 异常
设置字段值
Field --> public void set(Object obj,Object value): 参数说明: obj:要设置的字段所在的对象;
value:要为字段设置的值
示例:
System.out.println("===批量获取成员变量==="); Field[] fields = c.getDeclaredFields(); for(Field f : fields){ System.out.println(f); } System.out.println("===单个获取成员变量==="); Field f = c.getDeclaredField("age"); System.out.println("设置前年龄:" + f.get(obj)); f.set(obj, 11);//设置成员变量值 System.out.println("设置后年龄:" + f.get(obj));
一般情况下我们使用反射获取一个对象的步骤:
而如果要调用某一个方法,则需要经过下面的步骤:
完整示例代码:
用户定义Student类
package com.jing.reflect; public class Student { String name; int age; public Student(){ System.out.println("---调用无参构造方法---"); } public Student(String name){ this.name = name; System.out.println("---调用有参构造方法---"); } public String showName(String name){ System.out.println("show...." + name); return name; } private int showAge(int age){ System.out.println("show...." + age); return age; } }
测试类:
package com.jing.reflect; import java.lang.reflect.Constructor; import java.lang.reflect.Field; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; public class Test { public static void main(String[] args){ try { Class c = Class.forName("com.jing.reflect.Student"); Constructor[] cs = c.getDeclaredConstructors();//获取全部构造方法 System.out.println("====批量获取全部构造方法==="); for(Constructor constructor : cs){ System.out.println(constructor); } System.out.println("===获取单个构造方法==="); Constructor constructor = c.getConstructor(String.class);//获取单个构造函数 System.out.println(constructor); System.out.println("===创建对象==="); Student s1 = (Student)c.newInstance(); Student s2 = (Student)constructor.newInstance("Jing"); System.out.println("===获取全部成员方法==="); Method[] methods = c.getDeclaredMethods(); for(Method method : methods){ Class[] paramTypes = method.getParameterTypes();//获取方法参数列表中参数类型 System.out.print("方法名:" + method.getName() + "--方法返回类型:" + method.getReturnType() + "--方法参数列表:"); for(Class par : paramTypes){ System.out.print(par + " "); } System.out.println(); } System.out.println("===获取单个成员方法==="); Method method = c.getDeclaredMethod("showAge", int.class); System.out.println(method); Object obj = c.getConstructor().newInstance();//实例化一个Student对象 System.out.println("===调用方法==="); method.setAccessible(true);//解除私有限定 Object result = method.invoke(obj, 20);//此处解除了私有限定,所以参数可以使用obj // 或,不解除私有限定,使用s1调用方法(s1被强制类型转换为了Student类型) System.out.println("返回结果:" + result); System.out.println("===批量获取成员变量==="); Field[] fields = c.getDeclaredFields(); for(Field f : fields){ System.out.println("成员变量类型:" + f.getType() + " 成员变量名称" + f.getName()); } System.out.println("===单个获取成员变量==="); Field f = c.getDeclaredField("age"); System.out.println("设置前年龄:" + f.get(obj)); f.set(obj, 11);//设置成员变量值 System.out.println("设置后年龄:" + f.get(obj)); } catch (ClassNotFoundException e) { e.printStackTrace(); } catch (NoSuchMethodException e) { e.printStackTrace(); } catch (IllegalAccessException e) { e.printStackTrace(); } catch (InstantiationException e) { e.printStackTrace(); } catch (InvocationTargetException e) { e.printStackTrace(); } catch (NoSuchFieldException e) { e.printStackTrace(); } } }
运行结果:
反射的应用:
通过反射运行配置文件内容
利用反射和配置文件,可以使:应用程序更新时,对源码无需进行任何修改,只需要添加新类,并修改配置文件使用新类即可。
用户定义类
package com.jing.reflect; public class Student2 { public void show(){ System.out.println("Student2类"); } }
配置文件
className = com.jing.reflect.Student2
methodName = show
测试类
package com.jing.reflect; import java.io.FileReader; import java.io.IOException; import java.lang.reflect.Method; import java.util.Properties; public class Demo { public static void main(String[] args) throws Exception{ //获取Class类对象 Class stuClass = Class.forName(getValue("className")); //获取show方法 Method method = stuClass.getDeclaredMethod(getValue("methodName")); //获取Student构造方法,创建实例,并通过该实例调用show方法 method.invoke(stuClass.getConstructor().newInstance()); } //此方法接收一个key,在配置文件中获取相应的value public static String getValue(String key) throws IOException { Properties pro = new Properties();//获取配置文件的对象 FileReader proIn = new FileReader("I:\\CodePractice\\reflect\\src\\com\\jing\\reflect\\pro.txt");//获取输入流 pro.load(proIn);//将输入流加载到配置文件对象中 proIn.close(); return pro.getProperty(key);//返回根据key获取的value值 } }
运行结果
系统升级时,不再需要Student2类,此时重新写一个Student类,在配置文件中指明,原代码不动,完成系统的升级。修改pro.txt文件,执行程序后结果:
参考:
Java基础之—反射(非常重要)<强烈推荐>
大白话说Java反射:入门、使用、原理<强烈推荐>
标签:代理 lang 包含 数据 tcl amp tar font property
原文地址:https://www.cnblogs.com/Jing-Wang/p/10657664.html