1.反射机制是什么
反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性;这种动态获取的信息以及动态调用对象的方法的功能称为java语言的反射机制。
2.反射机制能做什么
-
在运行时判断任意一个对象所属的类;
-
在运行时构造任意一个类的对象;
-
在运行时判断任意一个类所具有的成员变量和方法;
-
在运行时调用任意一个对象的方法;
-
生成动态代理。
3.反射机制的相关API
通过一个对象获得完整的包名和类名
//第一种,任何一个类都有一个隐含的静态成员变量class Class c1 = Person.class; //第二种,已经知道该类的对象,通过getClass()获得 Class c2 = person.getClass(); //第三种,Class类的forName()方法 Class c3 = Class.forName("Person");
public class TestReflect { public static void main(String[] args) throws Exception { Class<?> class1 = null; Class<?> class2 = null; Class<?> class3 = null; //获得类名 一般有三种方式 class1 = Class.forName("com.yuan.TestReflect"); class2 = new TestReflect().getClass(); class3 = TestReflect.class; System.out.println("类名称 " + class1.getName()); System.out.println("类名称 " + class2.getName()); System.out.println("类名称 " + class3.getName()); } }
获取一个对象的父类与实现的接口
clazz.getSuperclass()
clazz.getInterfaces()
import java.io.Serializable; public class TestReflect implements Serializable { private static final long serialVersionUID = -2862585049955236662L; public static void main(String[] args) throws Exception { Class<?> clazz = Class.forName("net.xsoftlab.baike.TestReflect"); // 取得父类 Class<?> parentClass = clazz.getSuperclass(); System.out.println("clazz的父类为:" + parentClass.getName()); // clazz的父类为: java.lang.Object // 获取所有的接口 Class<?> intes[] = clazz.getInterfaces(); System.out.println("clazz实现的接口有:"); for (int i = 0; i < intes.length; i++) { System.out.println((i + 1) + ":" + intes[i].getName()); } // clazz实现的接口有: // 1:java.io.Serializable } }
获取某个类中的全部构造函数
A.1:获取构造方法的数组:
public Constructor<?>[] getConstructors():获得所有公共构造方法
public Constructor<?>[] getDeclaredConstructors():获得所有构造方法
A.2:获取单个构造方法(用于非私有的构造方法)
public Constructor<T> getConstructor(Class<?>... parameterTypes)
参数表示的是:你要获取的构造方法的构造参数个数及数据类型的class字节码文件对象
A.3:获取单个构造方法(用于私有的构造方法)
public Constructor<T> getDeclaredtConstructor(Class<?>... parameterTypes)
B.1:初始化构造方法的实例:(用于非私有的构造方法)
public T newInstance(Object... initargs) 注意:T表示的是泛型,Object... initargs表示的是你要实例化的指定参数
使用此 Constructor对象表示的构造方法来创建该构造方法的声明类的新实例,并用指定的初始化参数初始化该实例。
B.2:初始化构造方法的实例:(用于私有的构造方法)
先用: public void setAccessible(boolean flag):flag的值为 true 则指示反射的对象在使用时应该取消 Java 语言访问检查。
再用: public T newInstance(Object... initargs)
public Constructor<?>[] getConstructors():获得所有公共构造方法
Constructor[] c1 = c.getConstructors(); System.out.println(c1);// 返回的是地址值,证明得到的是个数组对象 // 遍历这个数组 for (Constructor ct : c1) { System.out.println(ct); } //获得Person类中的所有公共构造方法
public Constructor<?>[] getDeclaredConstructors():获得所有构造方法
Constructor[] c1 = c.getDeclaredConstructors(); //遍历数组 for(Constructor ct : c1){ System.out.println(ct); } //获得Person类的所有构造方法
public Constructor<T> getConstructor(Class<?>... parameterTypes):获得指定的构造方法
获得无参构造方法:
Constructor c1 = c.getDeclaredConstructor();
System.out.println(c1);
获得带参构造方法:
//获取指定的构造方法,需要该方法的参数列表 Constructor c1 = c.getDeclaredConstructor(String.class,int.class,String.class); System.out.println(c1);
通过反射机制实例化一个类的对象
通过反射获取公共带参构造方法并使用:
//获取指定的构造方法,需要该方法的参数列表 Constructor c1 = c.getDeclaredConstructor(String.class,int.class,String.class); System.out.println(c1); // 通过带参构造方法对象创建对象 // public T newInstance(Object... initargs): //有泛型,前面没使用泛型,先用Objcet Object b = c1.newInstance("张三",13,"家乡"); System.out.println(b); //Person [name=张三, age=13, address=家乡]
通过反射获取私有带参构造方法并使用:
//获取指定的私有构造方法 Constructor c1 = c.getDeclaredConstructor(String.class); //public void setAccessible(boolean flag): //flag的值为 true 则指示反射的对象在使用时应该取消 Java 语言访问检查。 c1.setAccessible(true); // public T newInstance(Object... initargs) Object ob = c1.newInstance("张三"); System.out.println(ob);
获取某个类的全部属性
getDeclaredFields()获得某个类的所有申明的字段,即包括public、private和proteced,但是不包括父类的申明字段。
getFields()获得某个类的所有的公共(public)的字段,包括父类。
import java.io.Serializable; import java.lang.reflect.Field; import java.lang.reflect.Modifier; public class TestReflect implements Serializable { private static final long serialVersionUID = -2862585049955236662L; public static void main(String[] args) throws Exception { Class<?> clazz = Class.forName("net.xsoftlab.baike.TestReflect"); System.out.println("===============本类属性==============="); // 取得本类的全部属性 Field[] field = clazz.getDeclaredFields(); for (int i = 0; i < field.length; i++) { // 权限修饰符 int mo = field[i].getModifiers(); String priv = Modifier.toString(mo); // 属性类型 Class<?> type = field[i].getType(); System.out.println(priv + " " + type.getName() + " " + field[i].getName() + ";"); } System.out.println("==========实现的接口或者父类的属性=========="); // 取得实现的接口或者父类的属性 Field[] filed1 = clazz.getFields(); for (int j = 0; j < filed1.length; j++) { // 权限修饰符 int mo = filed1[j].getModifiers(); String priv = Modifier.toString(mo); // 属性类型 Class<?> type = filed1[j].getType(); System.out.println(priv + " " + type.getName() + " " + filed1[j].getName() + ";"); } } }
获取某个类的全部方法
import java.io.Serializable; import java.lang.reflect.Method; import java.lang.reflect.Modifier; public class TestReflect implements Serializable { private static final long serialVersionUID = -2862585049955236662L; public static void main(String[] args) throws Exception { Class<?> clazz = Class.forName("net.xsoftlab.baike.TestReflect"); Method method[] = clazz.getMethods(); for (int i = 0; i < method.length; ++i) { Class<?> returnType = method[i].getReturnType(); Class<?> para[] = method[i].getParameterTypes(); int temp = method[i].getModifiers(); System.out.print(Modifier.toString(temp) + " "); System.out.print(returnType.getName() + " "); System.out.print(method[i].getName() + " "); System.out.print("("); for (int j = 0; j < para.length; ++j) { System.out.print(para[j].getName() + " " + "arg" + j); if (j < para.length - 1) { System.out.print(","); } } Class<?> exce[] = method[i].getExceptionTypes(); if (exce.length > 0) { System.out.print(") throws "); for (int k = 0; k < exce.length; ++k) { System.out.print(exce[k].getName() + " "); if (k < exce.length - 1) { System.out.print(","); } } } else { System.out.print(")"); } System.out.println(); } } }
通过反射机制调用某个类的方法
打破封装 实际上setAccessible是启用和禁用访问安全检查的开关,并不是为true就能访问为false就不能访问
由于JDK的安全检查耗时较多.所以通过setAccessible(true)的方式关闭安全检查就可以达到提升反射速度的目的
共有方法
import java.lang.reflect.Method; public class TestReflect { public static void main(String[] args) throws Exception { Class<?> clazz = Class.forName("net.xsoftlab.baike.TestReflect"); // 调用TestReflect类中的reflect1方法 Method method = clazz.getMethod("reflect1"); method.invoke(clazz.newInstance()); // Java 反射机制 - 调用某个类的方法1. // 调用TestReflect的reflect2方法 method = clazz.getMethod("reflect2", int.class, String.class); method.invoke(clazz.newInstance(), 20, "张三"); // Java 反射机制 - 调用某个类的方法2. // age -> 20. name -> 张三 } public void reflect1() { System.out.println("Java 反射机制 - 调用某个类的方法1."); } public void reflect2(int age, String name) { System.out.println("Java 反射机制 - 调用某个类的方法2."); System.out.println("age -> " + age + ". name -> " + name); } }
私有方法
Object person = class1.newInstance(); Method setName = class1.getDeclaredMethod( "setName", String.class ) ; setName.setAccessible( true ); setName.invoke( person , "jack" ) ;
通过反射机制操作某个类的属性
import java.lang.reflect.Field; public class TestReflect { private String proprety = null; public static void main(String[] args) throws Exception { Class<?> clazz = Class.forName("net.xsoftlab.baike.TestReflect"); Object obj = clazz.newInstance(); // 可以直接对 private 的属性赋值 Field field = clazz.getDeclaredField("proprety"); field.setAccessible(true); field.set(obj, "Java反射机制"); System.out.println(field.get(obj)); } }
4.综合训练
反射操作属性和方法
package com.app; import java.lang.reflect.Field; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; public class T1 { public static void main(String[] args) { try { //创建类 Class<?> class1 = Class.forName("com.app.Person"); //创建实例 Object person = class1.newInstance(); //获得id 属性 Field idField = class1.getDeclaredField( "id" ) ; //打破封装 实际上setAccessible是启用和禁用访问安全检查的开关,并不是为true就能访问为false就不能访问 //由于JDK的安全检查耗时较多.所以通过setAccessible(true)的方式关闭安全检查就可以达到提升反射速度的目的 idField.setAccessible( true ); //给id 属性赋值 idField.set( person , "100") ; //获取 setName() 方法 Method setName = class1.getDeclaredMethod( "setName", String.class ) ; //打破封装 setName.setAccessible( true ); //调用setName 方法。 setName.invoke( person , "jack" ) ; //获取name 字段 Field nameField = class1.getDeclaredField( "name" ) ; //打破封装 nameField.setAccessible( true ); //打印 person 的 id 属性值 String id_ = (String) idField.get( person ) ; System.out.println( "id: " + id_ ); //打印 person 的 name 属性值 String name_ = ( String)nameField.get( person ) ; System.out.println( "name: " + name_ ); //获取 getName 方法 Method getName = class1.getDeclaredMethod( "getName" ) ; //打破封装 getName.setAccessible( true ); //执行getName方法,并且接收返回值 String name_2 = (String) getName.invoke( person ) ; System.out.println( "name2: " + name_2 ); } catch (IllegalArgumentException e) { e.printStackTrace(); } catch (InvocationTargetException e) { e.printStackTrace(); } catch (NoSuchMethodException e) { e.printStackTrace(); } catch (InstantiationException e) { e.printStackTrace(); } catch (IllegalAccessException e) { e.printStackTrace(); } catch (NoSuchFieldException e) { e.printStackTrace(); } catch (SecurityException e) { e.printStackTrace() ; } catch (ClassNotFoundException e) { e.printStackTrace(); } } }
id: 100
name: jack
name2: jack
静态属性、静态方法调用
package com.app; public class Util { public static String name = "json" ; /** * 没有返回值,没有参数 */ public static void getTips(){ System.out.println( "执行了---------1111"); } /** * 有返回值,没有参数 */ public static String getTip(){ System.out.println( "执行了---------2222"); return "tip2" ; } /** * 没有返回值,有参数 * @param name */ public static void getTip( String name ){ System.out.println( "执行了---------3333 参数: " + name ); } /** * 有返回值,有参数 * @param id * @return */ public static String getTip( int id ){ System.out.println( "执行了---------4444 参数: " + id ); if ( id == 0 ){ return "tip1 444 --1 " ; }else{ return "tip1 444 --2" ; } } }
package com.app; import java.lang.reflect.Field; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; public class T1 { public static void main(String[] args) { try { //创建类 Class<?> class1 = Class.forName("com.app.Util"); //获取 nameField 属性 Field nameField = class1.getDeclaredField( "name" ) ; //获取 nameField 的值 String name_ = (String) nameField.get( nameField ) ; //输出值 System.out.println( name_ ); //没有返回值,没有参数 Method getTipMethod1 = class1.getDeclaredMethod( "getTips" ) ; getTipMethod1.invoke( null ) ; //有返回值,没有参数 Method getTipMethod2 = class1.getDeclaredMethod( "getTip" ) ; String result_2 = (String) getTipMethod2.invoke( null ) ; System.out.println( "返回值: "+ result_2 ); //没有返回值,有参数 Method getTipMethod3 = class1.getDeclaredMethod( "getTip" , String.class ) ; String result_3 = (String) getTipMethod3.invoke( null , "第三个方法" ) ; System.out.println( "返回值: "+ result_3 ); //有返回值,有参数 Method getTipMethod4 = class1.getDeclaredMethod( "getTip" , int.class ) ; String result_4 = (String) getTipMethod4.invoke( null , 1 ) ; System.out.println( "返回值: "+ result_4 ); } catch (InvocationTargetException e) { e.printStackTrace(); } catch (IllegalArgumentException e) { e.printStackTrace(); } catch (NoSuchMethodException e) { e.printStackTrace(); } catch (IllegalAccessException e) { e.printStackTrace(); } catch (NoSuchFieldException e) { e.printStackTrace(); } catch (SecurityException e) { e.printStackTrace() ; } catch (ClassNotFoundException e) { e.printStackTrace(); } } }
json 执行了---------1111 执行了---------2222 返回值: tip2 执行了---------3333 参数: 第三个方法 返回值: null 执行了---------4444 参数: 1 返回值: tip1 444 --2
注意:
当参数是 int 类型 和 Integer 类型,反射获取方法不一样
- 当参数是 int 类型时。获取方法的时候需要用:
int.class
。不能使用Integer.class
. 会报错。 - 当参数是 Integer类型时。获取方法的时候需要用:
Integer .class
。不能使用int.class
. 会报错。
在泛型为Integer的ArrayList中存放一个String类型的对象。
import java.lang.reflect.Method; import java.util.ArrayList; public class TestReflect { public static void main(String[] args) throws Exception { ArrayList<Integer> list = new ArrayList<Integer>(); Method method = list.getClass().getMethod("add", Object.class); method.invoke(list, "Java反射机制实例。"); System.out.println(list.get(0)); } }
通过反射取得并修改数组的信息
import java.lang.reflect.Array; public class TestReflect { public static void main(String[] args) throws Exception { int[] temp = { 1, 2, 3, 4, 5 }; Class<?> demo = temp.getClass().getComponentType(); System.out.println("数组类型: " + demo.getName()); System.out.println("数组长度 " + Array.getLength(temp)); System.out.println("数组的第一个元素: " + Array.get(temp, 0)); Array.set(temp, 0, 100); System.out.println("修改之后数组第一个元素为: " + Array.get(temp, 0)); } }
通过反射机制修改数组的大小
import java.lang.reflect.Array; public class TestReflect { public static void main(String[] args) throws Exception { int[] temp = { 1, 2, 3, 4, 5, 6, 7, 8, 9 }; int[] newTemp = (int[]) arrayInc(temp, 15); print(newTemp); String[] atr = { "a", "b", "c" }; String[] str1 = (String[]) arrayInc(atr, 8); print(str1); } // 修改数组大小 public static Object arrayInc(Object obj, int len) { Class<?> arr = obj.getClass().getComponentType(); Object newArr = Array.newInstance(arr, len); int co = Array.getLength(obj); System.arraycopy(obj, 0, newArr, 0, co); return newArr; } // 打印 public static void print(Object obj) { Class<?> c = obj.getClass(); if (!c.isArray()) { return; } System.out.println("数组长度为: " + Array.getLength(obj)); for (int i = 0; i < Array.getLength(obj); i++) { System.out.print(Array.get(obj, i) + " "); } System.out.println(); } }
反射机制的动态代理
// 获取类加载器的方法 TestReflect testReflect = new TestReflect(); System.out.println("类加载器 " + testReflect.getClass().getClassLoader().getClass().getName()); package net.xsoftlab.baike; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; //定义项目接口 interface Subject { public String say(String name, int age); } // 定义真实项目 class RealSubject implements Subject { public String say(String name, int age) { return name + " " + age; } } class MyInvocationHandler implements InvocationHandler { private Object obj = null; public Object bind(Object obj) { this.obj = obj; return Proxy.newProxyInstance(obj.getClass().getClassLoader(), obj.getClass().getInterfaces(), this); } public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { Object temp = method.invoke(this.obj, args); return temp; } } /** * 在java中有三种类类加载器。 * * 1)Bootstrap ClassLoader 此加载器采用c++编写,一般开发中很少见。 * * 2)Extension ClassLoader 用来进行扩展类的加载,一般对应的是jrelibext目录中的类 * * 3)AppClassLoader 加载classpath指定的类,是最常用的加载器。同时也是java中默认的加载器。 * * 如果想要完成动态代理,首先需要定义一个InvocationHandler接口的子类,已完成代理的具体操作。 * * @author xsoftlab.net * */ public class TestReflect { public static void main(String[] args) throws Exception { MyInvocationHandler demo = new MyInvocationHandler(); Subject sub = (Subject) demo.bind(new RealSubject()); String info = sub.say("Rollen", 20); System.out.println(info); } }
将反射机制应用于工厂模式
interface fruit { public abstract void eat(); } class Apple implements fruit { public void eat() { System.out.println("Apple"); } } class Orange implements fruit { public void eat() { System.out.println("Orange"); } } class Factory { public static fruit getInstance(String ClassName) { fruit f = null; try { f = (fruit) Class.forName(ClassName).newInstance(); } catch (Exception e) { e.printStackTrace(); } return f; } } /** * 对于普通的工厂模式当我们在添加一个子类的时候,就需要对应的修改工厂类。 当我们添加很多的子类的时候,会很麻烦。 * Java 工厂模式可以参考 * http://baike.xsoftlab.net/view/java-factory-pattern * * 现在我们利用反射机制实现工厂模式,可以在不修改工厂类的情况下添加任意多个子类。 * * 但是有一点仍然很麻烦,就是需要知道完整的包名和类名,这里可以使用properties配置文件来完成。 * * java 读取 properties 配置文件 的方法可以参考 * http://baike.xsoftlab.net/view/java-read-the-properties-configuration-file * * @author xsoftlab.net */ public class TestReflect { public static void main(String[] args) throws Exception { fruit f = Factory.getInstance("net.xsoftlab.baike.Apple"); if (f != null) { f.eat(); } } }
总结
- Class类提供了四个public方法,用于获取某个类的构造方法。
Constructor getConstructor(Class[] params) 根据构造函数的参数,返回一个具体的具有public属性的构造函数
Constructor getConstructors() 返回所有具有public属性的构造函数数组
Constructor getDeclaredConstructor(Class[] params) 根据构造函数的参数,返回一个具体的构造函数(不分public和非public属性)
Constructor getDeclaredConstructors() 返回该类中所有的构造函数数组(不分public和非public属性)
- 四种获取成员方法的方法
Method getMethod(String name, Class[] params) 根据方法名和参数,返回一个具体的具有public属性的方法
Method[] getMethods() 返回所有具有public属性的方法数组
Method getDeclaredMethod(String name, Class[] params) 根据方法名和参数,返回一个具体的方法(不分public和非public属性)
Method[] getDeclaredMethods() 返回该类中的所有的方法数组(不分public和非public属性)
- 四种获取成员属性的方法
Field getField(String name) 根据变量名,返回一个具体的具有public属性的成员变量
Field[] getFields() 返回具有public属性的成员变量的数组
Field getDeclaredField(String name) 根据变量名,返回一个成员变量(不分public和非public属性)
Field[] getDelcaredField() 返回所有成员变量组成的数组(不分public和非public属性)
反射用途https://www.cnblogs.com/panxuejun/p/5874990.html
参考https://www.cnblogs.com/LZL-student/p/5965991.html