Java反射说的是在运行状态中,对于任何一个类,我们都能够知道这个类有哪些方法和属性。对于任何一个对象,我们都能够对它的方法和属性进行调用。我们把这种动态获取对象信息和调用对象方法的功能称之为反射机制。
所谓反射其实是获取类的字节码文件,也就是.class文件,通过Class这个对象进行获取。
Class 类的实例表示正在运行的 Java类和接口,jvm中每个类都有且只有一个class对象。(包括基本数据类型)
Class 没有公共构造方法。Class 对象是在加载类时由 Java 虚拟机以及通过调用类加载器中的defineClass 方法自动构造的。也就是这不需要我们自己去处理创建,JVM已经帮我们创建好了。
获取类的字节码文件有三种方式:
getClass()是Object的一个方法,Class继承了Object,所以我们可以直接使用
Travel travel=new Travel();
Class c=travel.getClass();
Class c=Travel.class;
try {
Class c=Class.forName("com.suixing.pojo.Travel")
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
因为运行期间一个类只会有一个class对象产生,所以用不同方式多次获取同一个类的class对象,所获取到是同一个class对象
1)批量的方法
public Constructor[] getConstructors()
:所有”公有的”构造方法
public Constructor[] getDeclaredConstructors()
:获取所有的构造方法(包括私有、受保护、默认、公有)
2)获取单个的方法,并调用
public Constructor getConstructor(Class… parameterTypes)
:获取单个的”公有的”构造方法:
public Constructor getDeclaredConstructor(Class… parameterTypes)
:获取”某个构造方法”可以是私有的,或受保护、默认、公有;
Constructor对象.newInstance(Object… initargs)
newInstance(Object… initargs):使用此 Constructor 对象表示的构造方法来创建该构造方法的声明类的新实例,并用指定的初始化参数初始化该实例。
它的返回值是T类型,所以newInstance是创建了一个构造方法的声明类的新实例对象。并为之调用
public static void main(String[] args) throws NoSuchMethodException {
try {
Class c=Class.forName("com.suixing.pojo.TestPojo");
System.out.println("***************所有公有构造方法******************");
Constructor[] constructors=c.getConstructors();
for(Constructor constructor : constructors){
System.out.println(constructor);
}
System.out.println("*********所有的构造方法(包括:私有、受保护、默认、公有)*********");
constructors = c.getDeclaredConstructors();
for(Constructor constructor : constructors){
System.out.println(constructor);
}
System.out.println("**********获取公有、无参的构造方法***************");
Constructor constructor = c.getConstructor(null);//因为是无参的构造方法所以类型是一个null,不写也可以
System.out.println(constructor);
System.out.println("***********获取私有构造方法,并调用*************");
constructor = c.getDeclaredConstructor(Integer.class);
System.out.println(constructor);
constructor.setAccessible(true);//暴力访问(忽略掉访问修饰符)
Object o= constructor.newInstance(21);
System.out.println(o.toString());
System.out.println("**************获取并调用多参数的公有构造方法***********");
constructor=c.getConstructor(new Class[]{String.class,String.class,Integer.class});
o= constructor.newInstance("ximou","661",21);
System.out.println(o.toString());
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
}
//c是Class类的对象
System.out.println("************获取所有公有的字段********************");
Field[] fields=c.getFields();
for(Field field:fields)
System.out.println(field);
System.out.println("************获取所有的字段********************");
fields=c.getDeclaredFields();
for(Field field:fields)
System.out.println(field);
System.out.println("**************获取私有字段并调用**************");
Field field=c.getDeclaredField("name");
field.setAccessible(true);
field.set(o,"change name");
System.out.println(o.toString());
设置字段时:需要传递两个参数,一个是实体类对象,一个是要设置的值
//f是Field对象
Object obj = stuClass.getConstructor().newInstance();
f.set(obj, “刘德华”);
System.out.println("***********获取所有的”公有“方法*************");
c.getMethods();
Method[] methodArray = c.getMethods();
for(Method m : methodArray){
System.out.println(m);
}
System.out.println("*************获取所有的方法,包括私有的***********");
methodArray = c.getDeclaredMethods();
for(Method m : methodArray){
System.out.println(m);
}
System.out.println("***************获取公有的show1()方法*******************");
Method method=c.getMethod("show1",String.class);
Object result=method.invoke(o,("123456"));
System.out.println("return "+result);
System.out.println("***************获取私有的show4()方法******************");
method=c.getDeclaredMethod("show4",int.class);
method.setAccessible(true);
result=method.invoke(o,123);
System.out.println("return "+result);
System.out.println("***************获取默认的、多个参数的show3()方法******************");
method=c.getDeclaredMethod("show3",String.class,int.class);
method.setAccessible(true);
result=method.invoke(o,"sss",123);
System.out.println("return "+result);
需要传入至少两个参数,第一个是调用的方法名称,第二个到第n个是方法的形参类型,切记是类型。
m = stuClass.getDeclaredMethod(“show4”, int.class);
需要至少两个参数,一个是要调用的对象,其他的是实参
m.setAccessible(true);//对非公有方法解除私有限定
Object result = m.invoke(obj, 20);
Method method=c.getMethod("main",String[].class);
method.invoke(null,(Object)new String[]{"a","b"});
假设有两个业务类:
public class Service1 {
public void doService1(){
System.out.println("业务方法1");
}
}
public class Service2 {
public void doService2(){
System.out.println("业务方法2");
}
}
当需要从第一个业务方法切换到第二个业务方法的时候,若使用非反射方式,则必须修改代码,并且重新编译运行,才可以达到效果
public class CommonTest {
public static void main(String[] args) {
//new Service1().doService1();
//必须重新修改代码
new Service2().doService2();
}
}
而使用反射方式则方便很多
spring.txt
class=reflection.Service1
method=doService1
测试类
public class ReflectTest {
@SuppressWarnings({ "rawtypes", "unchecked" })
public static void main(String[] args) throws Exception {
Properties pro= new Properties();
FileReader in = new FileReader("spring.txt");//获取输入流,从spring.txt中获取类名称和方法名称
pro.load(in);
in.close();
String className = (String) pro.get("class");
String methodName = (String) pro.get("method");
//根据类名称获取类对象
Class clazz = Class.forName(className);
//根据方法名称,获取方法对象
Method m = clazz.getMethod(methodName);
//获取构造器
Constructor c = clazz.getConstructor();
//根据构造器,实例化出对象
Object service = c.newInstance();
//调用对象的指定方法
m.invoke(service);
}
}
泛型是在编译期间起作用的。在编译后的.class文件中是没有泛型的。所有比如T或者E类型啊,本质都是通过Object处理的。所以可以通过使用反射来越过泛型。
public class GenericityTest {
public static void main(String[] args) throws Exception{
ArrayList<String> list = new ArrayList<>();
list.add("this");
list.add("is");
//list.add(5);报错
/********** 越过泛型检查 **************/
//获取ArrayList的Class对象,反向的调用add()方法,添加数据
Class listClass = list.getClass();
//获取add()方法
Method m = listClass.getMethod("add", Object.class);
//调用add()方法
m.invoke(list, 5);
//遍历集合
for(Object obj : list){
System.out.println(obj);
}
}
}