反射
来源:互联网 发布:4g电信网络制式 编辑:程序博客网 时间:2024/06/01 19:49
1 反射
JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法。对于任意一个对象,都能够调用它的任意一个方法和属性, 这种动态获取的信息以及动态调用对象的方法的功能称为Java语言的反射机制。
一个类被类加载器加载到内存中,占用一片存储空间,这个空间里面的内容就是类的字节码,不同的类的字节码是不同的,所以它们在内存中的内容是不同的,这一个个的空间可分别用一个个的对象来表示,这些对象具有相同的类型,这个相同类型的类就是Class类。
反射优点:运行时确定类型绑定对象,发挥了java的灵活性,体现了多态的应用,降低类之间的耦合性。缺点:对性能有影响
2 获取信息
2.1 获取Class对象的方法
每个类被加载后,系统会为该类生成对应的Class 对象,通过Class 对象可以访问到JVM 中的这个类,
①调用某个类的 Class属性来获取该类对应的Class对象,例如:String.class;
②调用 Java.lang.Object 类中的一个方法调用某个对象的getClass()方法 例如:new Date().getClass();
③Class.forName(String className) 传入字符串必须是完整包名 例如: Class.forName("java.lang.String");
注意:基本的Java 类型(boolean、byte、char、short、int、long、float 和double)和关键字void通过class 属性也表示为Class 对象
Class.isPrimitive() :是否基本数据类型
Class.isArray():是否数组类型
(int.class== Integer.TYPE)为true --> Integer.TYPE为包装类的字节码
(int.class== Integer.class)为false -->Integer.class为class java.lang.Integer, int.class为int
2.2 获取Constructor类:构造方法
①获得类中的全部构造函数:
Constructor<?>[] getConstructors() //获得类的所有公共构造函数
Constructor<?>[] getDeclaredConstructors() //获得类的所有构造函数(不限制访问级别)
②得到某个构造方法:
Constructor<T> getConstructor(Class<?>... parameterTypes):表示类的指定的public 构造方法
Constructor<T> getDeclaredConstructor(Class<?>... parameterTypes):表示类的指定的构造方法,和访问权限无关
public class User { private int id; private String name; private User() { } public User(int id, String name) { this.id = id; this.name = name; } void show(String name){ System.out.println("my name is"+name); } public void say(){ System.out.println("say"); } private void study(){ System.out.println("study"); } ......}
public class TestConstructor {public static void main(String[] args) throws SecurityException, NoSuchMethodException {Class<User> clz=User.class; //类的所有公共构造方法 Constructor[] cons=clz.getConstructors();for(Constructor con:cons ){System.out.println("1:"+con);}//获得类的所有构造函数(不限制访问级别) cons=clz.getDeclaredConstructors();for(Constructor con:cons ){System.out.println("2:"+con);}//得到指定的构造器,必须publicConstructor<User> c1=clz.getConstructor(int.class,String.class);System.out.println("3:"+c1);//私有的报错java.lang.NoSuchMethodException: User.<init>()//Constructor<User> c2=clz.getConstructor();//可获取私有的构造器Constructor<User> c2=clz.getDeclaredConstructor();System.out.println("4:"+c2); }
打印输出为
1:public com.User(int,java.lang.String)2:public com.User(int,java.lang.String)2:private com.User()3:public com.User(int,java.lang.String)4:private com.User()
2.3获取Field:成员变量
①获得类中的所有属性:
getFields()://获取所有的公有属性
getDeclaredFields();//获得所有的属性
②得到某个属性:
getField(String name);//获得指定名字的公有的属性
getDeclaredField(String name);//获得指定名字的公有的属性,不限制访问级别
Field[] fields = clz.getFields(); for (Field field : fields) { System.out.println("getFields:" + field); } // 得到不受修饰符限定的字段 fields = clz.getDeclaredFields(); for (Field field : fields) {// System.out.println("getDeclaredFields:" + field); } // 得到public类型的特定字段 Field fi = clz.getField("sex");//不是对象上的变量,而是类上的,要用它通过get方法取某个对象上对应的值 System.out.println("getField:" + fi);
2.4 获取Method:类中成员方法
①获得类中的所有方法:
Method[] getMethods():返回该Class 对象表示类和其父类的所有public 方法;
Method[] getDeclaredMethods():获得类所有的方法,包括公共、保护、默认(包)访问和私有方法,但不包括继承的方法;
②获取类中的某个方法:
Method getMethod(String name, Class<?> ... parameterTypes):返回该Class 对象表示类和其父类的指定的public 方法;
Method getDeclaredMethod(String name, Class<?>... parameterTypes):返回该Class 对象表示类的指定的方法。和访问权限无关,但不包括继承的方法;
Class<User> clz = User.class; // 获取所有的public修饰的方法,包含父类的方法 Method[] m = clz.getMethods(); for (Method method : m) { System.out.println("getMethods:" + method); } // 访问所有方法,不受访问权限影响 m = clz.getDeclaredMethods(); for (Method method : m) { System.out.println("getDeclaredMethods:" + method); } // 获取指定的方法,只有public方法 Method say = clz.getMethod("say", null); System.out.println("getMethod: " + say); // 获取指定的方法,不受访问权限影响 Method show = clz.getDeclaredMethod("show", String.class); System.out.println("getDeclaredMethod:" + show); // getDeclaredMethod不能访问父类方法,报错java.lang.NoSuchMethodException Method toString = clz.getDeclaredMethod("toString"); System.out.println("访问父类方法:" + toString);
输出
getMethods:public void com.User.say()getMethods:public final native void java.lang.Object.wait(long) throws java.lang.InterruptedExceptiongetMethods:public final void java.lang.Object.wait() throws java.lang.InterruptedExceptiongetMethods:public final void java.lang.Object.wait(long,int) throws java.lang.InterruptedExceptiongetMethods:public boolean java.lang.Object.equals(java.lang.Object)getMethods:public java.lang.String java.lang.Object.toString()getMethods:public native int java.lang.Object.hashCode()getMethods:public final native java.lang.Class java.lang.Object.getClass()getMethods:public final native void java.lang.Object.notify()getMethods:public final native void java.lang.Object.notifyAll()getDeclaredMethods:public void com.User.say()getDeclaredMethods:void com.User.show(java.lang.String)getDeclaredMethods:private void com.User.study()getMethod: public void com.User.say()getDeclaredMethod:void com.User.show(java.lang.String)
2.5 获取Interface:接口信息
①getGenericInterfaces()//返回表示某些接口的Type,这些接口由此对象所表示的类或接口直接实现。
②getInterfaces//确定此对象所表示的类或接口实现的接口。
2.6获取SuperClass:父类信息
①getSuperclass// 返回表示此Class
所表示的实体(类、接口、基本类型或 void)的超类的class
。
2.7获取Annotation注解
①对Class、Method、Field以及Constructor对象调用getAnnotation()方法,可以获得与对象关联的特定信息
<A extends Annotation> getAnnotation(Class<A> annoType)
定义注解
@Retention(RetentionPolicy.RUNTIME)@interface MyAnno { String str(); int val();}@Retention(RetentionPolicy.RUNTIME)@interface What { String description();}
@MyAnno(str = "example", val = 200) public static void myMeth() { Meta meta = new Meta(); Class clz = meta.getClass(); try { Method m = clz.getMethod("myMeth"); // MyAnno类型的Class对象,即注解 MyAnno anno = m.getAnnotation(MyAnno.class); System.out.println(anno.str() + "--" + anno.val());//输出example--200 } catch (SecurityException e) { e.printStackTrace(); } catch (NoSuchMethodException e) { e.printStackTrace(); } }
②Annotation[ ] getAnnotations( ) 可以获取与某个条目关联的具有RUNTIME保留策略的所有注解
@MyAnno(str = "example", val = 200) @What(description = "An annotation test method") public static void myMeth() { Meta meta = new Meta(); Class clz = meta.getClass(); try { Method m = clz.getMethod("myMeth"); // MyAnno类型的Class对象,即注解 Annotation[] anno = m.getAnnotations(); for (Annotation a : anno) { System.out.println(a); } } catch (SecurityException e) { e.printStackTrace(); } catch (NoSuchMethodException e) { e.printStackTrace(); } }
输出 @annotation.MyAnno(str=example, val=200)@annotation.What(description=An annotation test method)</span>
2.8获取泛型信息
(1)普通Field ,通过指定对应的Class 对象,程序可以获得该类里面所有的Field,再用getType()来获取其类型。
Class<?> type = field.getType();//获得字段的类型
(2)Field 有泛型修饰,如Map<String,Integer>使用;Type gType = f.getGenericType()得到泛型类型
然后将Type 对象强转为ParameterizedType,其表示增加泛型后的类型
Type getRawType()//返回被泛型限制的类型;
Type[] getActualTypeArguments()//返回泛型参数类型;
步骤:
1.获取当前类
2.获取目标字段
3.获取包含泛型类型的类型getGenericType()
4.强转至子类ParameterizedType 因为Type 没有任何对应的方法
5.获得泛型真正的类型getActualTypeArguments()
public class TestField {Map<Integer, String> map = new HashMap<Integer, String>();public static void main(String[] args) throws SecurityException,NoSuchFieldException {Class clz = TestField.class;Field field = clz.getDeclaredField("map");// 返回一个Class 对象,它标识了此Field 对象表示字段的声明类型。System.out.println("获得其类型:" + field.getType());Type type = field.getGenericType();// 包含泛型的类型System.out.println("getGenericType:" + type);// Type里面没有任何的方法,所以需要调用子类的方法ParameterizedType pt = (ParameterizedType) type;System.out.println("parameterizedType:" + type);// 返回Type 对象,表示此类型是其成员之一的类型。type = pt.getOwnerType();System.out.println("getOwnerType:" + type);// 返回Type 对象,表示声明此类型的类或接口。type = pt.getRawType();System.out.println("getRawType:" + type);// 返回表示此类型实际类型参数的Type对象的数组。Type[] types = pt.getActualTypeArguments();for (Type t : types) {System.out.println("getActualTypeArguments:" + t);} }}
输出
获得其类型:interface java.util.MapgetGenericType:java.util.Map<java.lang.Integer, java.lang.String>parameterizedType:java.util.Map<java.lang.Integer, java.lang.String>getOwnerType:nullgetRawType:interface java.util.MapgetActualTypeArguments:class java.lang.IntegergetActualTypeArguments:class java.lang.String
3 反射调用
3.1 创建对象
1、使用Class 对象的newInstance()方法创建该Class 对象的实例,此时该Class 对象必须要有无参数的构造方法且不能为私有,否则抛出 java.lang.IllegalAccessException异常
2、使用Class 对象获取指定的Constructor 对象,再调用Constructor 的newInstance()方法创建对象类的实例,此时可以选择使用某个构造方法。如果这个构造方法为私有,那么必须设置setAccessible(true)
java枚举类型不能利用 new、clone()、de-serialization、以及 Reflection API 来产生enum 的 对象
public enum WeekDay {Monday, Tuesday, Wednesday, Thursday, Friday, Saturday, Sunday ;private WeekDay(){};}
// 报错:java.lang.NoSuchMethodException Class<WeekDay> c = WeekDay.class; Constructor<WeekDay> con = c.getDeclaredConstructor();// 编译可以通过,但是运行就通不过了! WeekDay w = con.newInstance(); System.out.println(w);
3.2调用方法
获得Method 对象后,程序可以使用Method里面的invoke 方法来执行该底层方法
Object invoke(Object obj,Object ... args):obj 表示调用底层方法的对象,后面的args 表示传递的实际参数。
1) 如果底层方法是静态的,那么可以忽略指定的obj 参数。该参数可以为null
2)如果底层方法所需的形参个数为0,则所提供的args 数组长度可以为0 或null。不写,null,或new Object[]{}
Class<User> clz = User.class; // 调用公有方法 Method method = clz.getMethod("show", String.class); method.invoke(clz.newInstance(), "张三"); // 调用私有方法 method = clz.getDeclaredMethod("privateshow"); method.setAccessible(true); method.invoke(clz.newInstance()); // 静态方法的调用 method = clz.getMethod("staticshow"); method.invoke(null);
使用反射调用可变参数方法
要把可变参数都当做是其对应的数组类型参数;
1)若可变参数元素类型是引用类型:
JDK 内部接收到参数之后,会自动拆包取出参数再分配给该底层方法,需要把这个数组实参先包装成一个Object 对象或把实际参数作为一个Object 一维数组的元素再传递。
2)若可变参数元素类型是基本类型:
JDK 内部接收到参数之后,不会拆包,可以不必再封装.
Method m=TestArg.class.getMethod("main", String[].class);
m.invoke(null, new String[]{"111","222"});
报错:java.lang.IllegalArgumentException: wrong number of arguments
可打包成一个数组:m.invoke(null, new Object[]{new String[]{"111","222"}});
或转为object对象:m.invoke(null, (Object)new String[]{"111","222"});
3.3操作字段
Field 提供两组方法操作字段:
①xxx getXxx(Object obj):获取obj 对象该Field 的字段值,此处的xxx 表示8 个基本数据类型。
若该字段的类型是引用数据类型则使用,Object get(Object obj);
②void setXxx(Object obj,xxx val):将obj 对象的该Field 字段设置成val 值,xxx 表示8个基本数据类型。
若该字段的类型是引用数据类型则使用,void set(Object obj, Object value);
Class<User> clz = User.class; User u = clz.newInstance(); // 引用数据类型 Field fi = clz.getDeclaredField("name"); fi.setAccessible(true); fi.set(u, "张三"); System.out.println(fi); System.out.println(fi.getName()); System.out.println(fi.get(u)); // 基本数据类型 Field f2 = clz.getDeclaredField("id"); f2.setAccessible(true); f2.set(u, 1); System.out.println(f2.getInt(u));
输出
private java.lang.String com.User.namename张三1
4、数组的反射
(1)具有相同的元素类型和相同的维度表示同一个类型,有相同的class实例对象
在内存中数组用 [ 表示,相应的字符表示类型 通过 数组名.getClass().getName()查看
[I 表示int[]
[J 表示long[]
[S 表示short[]
[Z 表示boolean[]
[B 表示byte[]
[C 表示char[]
[D 表示double[]
[F 表示float[]
(2)通过Class实例 的getSuperClass()方法,返回父类为Object类对应的class
(3)基本类型的一维数组可以当作Object类型使用,但不能当作Object[]使用,非基本类型的都可以
这里可以通过 Arrays.asList()方法处理 int[] 和String[]类型数组看到差异。