反射

来源:互联网 发布: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[]{}

3)若底层方法返回的是数组类型,invoke 方法返回的不是底层方法的值,而是底层方法的返回类型;    

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[]类型数组看到差异。   

0 0