反射

 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);
	}
	
	//得到指定的构造器,必须public
	Constructor<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.InterruptedException
getMethods:public final void java.lang.Object.wait() throws java.lang.InterruptedException
getMethods:public final void java.lang.Object.wait(long,int) throws java.lang.InterruptedException
getMethods: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.Map
getGenericType:java.util.Map<java.lang.Integer, java.lang.String>
parameterizedType:java.util.Map<java.lang.Integer, java.lang.String>
getOwnerType:null
getRawType:interface java.util.Map
getActualTypeArguments:class java.lang.Integer
getActualTypeArguments: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.name
name
张三
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
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值