java反射机制&Annotation详解_I



本文参照大苞米的博客而写,为java反射做一次总结了。

在学习之前,有必要去了解相关的类的API。这里总结了几个主要使用的类。

通俗的讲:  Class对象和实例对象:  类是人类,某个人是对象,你是实例
原理解析:  每个class对象都有一个相应的class对象,当编写一个类,编译完成后,会生成一个class字节码文件,会产生一个class对象,用于表达这个类的类型信息。 

获取class实例有三种方式
  1.使用Class类的静态方法forName获取Person类的class对象
  2.使用类的.class语法
  3.使用对象的getClass()方法(java.lang.Object类中的方法)

[  在运行期间,如果我们要产生某各类的对象,JVM会检查该类型的Class对象是否被加载,如果没有,JVM会根据名称找到.class文件并加载它,一旦某个类型的class对象已被加载到内存,就可以用它来产生该类型的所有对象。 ]


Class详解
public final class Class<T>
    
    
     
     extends 
     
     Object
    
    
    
    
     
     implements 
     
     Serializable, 
     
     GenericDeclaration, 
     
     Type, 
     
     AnnotatedElement
    
    
 GenericDeclaration:声明类型变量的所有实体的公共接口
 Type:所有类型的公共高级接口。它们包括原始类型、参数化类型、数组类型、类型变量和基本类型。
 AnnotatedElement:该接口允许反射性地读取注释。
 
 所有类的对象其实都是Class的实例
 

Class 类的实例表示正在运行的 Java 应用程序中的类和接口。枚举是一种类,注释是一种接口。每个数组属于被映射为 Class 对象的一个类,所有具有相同元素类型和维数的数组都共享该 Class 对象。

forName(String className) 
          返回与带有给定字符串名的类或接口相关联的 Class 对象。
getAnnotations() 
          返回此元素上存在的所有注释。
getClassLoader() 
          返回该类的类加载器。 加载 Java 类到 Java 虚拟机中
getConstructors() 
          返回一个包含某些 Constructor 对象的数组,这些对象反映此 Class 对象所表示的类的所有公共构造方法。
getDeclaredConstructor(Class<?>... parameterTypes) 
          返回一个 Constructor 对象,该对象反映此 Class 对象所表示的类或接口的指定构造方法
getDeclaredConstructors() 
          返回 Constructor 对象的一个数组,这些对象反映此 Class 对象表示的类声明的所有构造方法。
getDeclaredField(String name) 
          返回一个 Field 对象,该对象反映此 Class 对象所表示的类或接口的指定已声明字段。
getDeclaredFields() 
          返回 Field 对象的一个数组,这些对象反映此 Class 对象所表示的类或接口所声明的所有字段。
getDeclaredMethod(String name, Class<?>... parameterTypes) 
          返回一个 Method 对象,该对象反映此 Class 对象所表示的类或接口的指定已声明方法。
getDeclaredMethods() 
          返回 Method 对象的一个数组,这些对象反映此 Class 对象表示的类或接口声明的所有方法,包括公共、保护、默认(包)访问和私有方法,但不包括继承的方法。
isAnnotation() 
          如果此 Class 对象表示一个注释类型则返回 true。
isArray() 
          判定此 Class 对象是否表示一个数组类。
isEnum() 
          当且仅当该类声明为源代码中的枚举时返回 true。

Constructor

java.lang.Object
  java.lang.reflect.AccessibleObject
      java.lang.reflect.Constructor<T>
    
    
public final class Constructor<T>
       
       
        
        extends 
        
        AccessibleObject
       
       
       
       
        
        implements 
        
        GenericDeclaration, 
        
        Member
       
       

Constructor 允许在将实参与带有底层构造方法的形参的 newInstance() 匹配时进行扩展转换,但是如果发生收缩转换,则抛出 IllegalArgumentException


Method

java.lang.Object
  java.lang.reflect.AccessibleObject
      java.lang.reflect.Method
    
    
public final class Method
       
       
        
        extends 
        
        AccessibleObject
       
       
       
       
        
        implements 
        
        GenericDeclaration, 
        
        Member
       
       

Method 提供关于类或接口上单独某个方法(以及如何访问该方法)的信息。所反映的方法可能是类方法或实例方法(包括抽象方法)。

Method 允许在匹配要调用的实参与底层方法的形参时进行扩展转换;但如果要进行收缩转换,则会抛出 IllegalArgumentException


类 Field

java.lang.Object
  java.lang.reflect.AccessibleObject
      java.lang.reflect.Field
    
    
public final class Field
       
       
        
        extends 
        
        AccessibleObject
       
       
       
       
        
        implements 
        
        Member
       
       

Field 提供有关类或接口的单个字段的信息,以及对它的动态访问权限。反射的字段可能是一个类(静态)字段或实例字段。


他们三个有共同的特征

类 AccessibleObject

java.lang.Object
  java.lang.reflect.AccessibleObject

public class AccessibleObject
      
      
       
       extends 
       
       Object
      
      
      
      
       
       implements 
       
       AnnotatedElement
      
      

AccessibleObject 类是 Field、Method 和 Constructor 对象的基类。它提供了将反射的对象标记为在使用时取消默认 Java 语言访问控制检查的能力。对于公共成员、默认(打包)访问成员、受保护成员和私有成员,在分别使用 Field、Method 或 Constructor 对象来设置或获取字段、调用方法,或者创建和初始化类的新实例的时候,会执行访问检查。

在反射对象中设置 accessible 标志允许具有足够特权的复杂应用程序(比如 Java Object Serialization 或其他持久性机制)以某种通常禁止使用的方式来操作对象

setAccessible(AccessibleObject[] array, boolean flag) 
          使用单一安全性检查(为了提高效率)为一组对象设置 accessible 标志的便捷方法
setAccessible(boolean flag) 
          将此对象的 accessible 标志设置为指示的布尔值。

public interface InvocationHandler

InvocationHandler 是代理实例的调用处理程序 实现的接口。

每个代理实例都具有一个关联的调用处理程序。对代理实例调用方法时,将对方法调用进行编码并将其指派到它的调用处理程序的 invoke 方法。


类 Proxy

java.lang.Object
  java.net.Proxy
public class Proxy
      
      
       
       extends 
       
       Object
      
      

此类表示代理设置,通常为类型(http、socks)和套接字地址。Proxy 是不可变对象。


代码部分:   一个JavaBean和一个测试类

public class Person {
	private long id;
	private int age;
	private String name;
        //每个属性的set和get方法,toString方法
        //俩个构造方法
}


测试类:TestPerson.java

import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;

public class TestPerson {

 
	public static void main(String[] args) throws Exception{
		 
		//使用Class类的静态方法forName获取Person类的class对象
		//使用类的.class语法
		//使用对象的getClass()方法(java.lang.Object类中的方法)
		
		//Class<?> classType=Class.forName("day100_reflect.Person");
		//Class<?> classType=Person.class;
		Class<?> classType=new Person().getClass();
		
		//调用Person类的俩个参数构造方法生成对象
		Constructor constructor=classType.getConstructor(new Class[]{String.class,int.class});
		
		//实例化对象
		Object obj=constructor.newInstance(new Object[]{"Jack",25});
		
		//获取类中所有构造方法
		Constructor[] constructors=classType.getDeclaredConstructors();
		for (int i = 0; i < constructors.length; i++) {
			System.out.println(constructors[i].toString());
		}
		
		//获取类中所有方法名称
		Method[] methods=classType.getDeclaredMethods();
		for(int i=0;i<methods.length;i++){
			System.out.println(methods[i].toString());
		}
		
		//获取类中所有变量名称
		Field[] fields=classType.getDeclaredFields();
		for(int i=0;i<fields.length;i++){
			System.out.println(fields[i].toString());
		}
		//获取setId方法
		Method setId=classType.getDeclaredMethod("setId",new Class[]{long.class} );
		
		//调用setId方法设置Id
		setId.invoke(obj, new Object[]{10});
		
		//调用toString方法输出
		Method toString=classType.getDeclaredMethod("toString", new Class[]{});
		String result=(String)toString.invoke(obj, new Object[]{});
		//获取结果
		System.out.println(result);
	}
}


我们再来看一下反射所花费的时间对比吧,反射用的多了会降低系统的性能。

import java.lang.reflect.Constructor;
import java.lang.reflect.Method;

public class NormalWay {

	public static void main(String[] args) throws Exception{
		
		long startTime=System.currentTimeMillis();
		
		for(int i=0;i<100000;i++){
		 Person p=new Person();
		 p.setAge(25);
		 p.setId(15);
		 p.setName("noraml");
		 }
		
		 long endTime=System.currentTimeMillis();
		 System.out.println("普通设置参数需要花费时间为:"+(endTime-startTime)+"ms");
		 
		 startTime=System.currentTimeMillis();
		 for(int i=0;i<100000;i++){
			 
		 Class<?> pclass=Person.class;
		 
		 Constructor<?> constructor=pclass.getConstructor(new Class[]{});
		// Object obj= constructor.newInstance(); 
		 Object obj=pclass.newInstance();  
		 
		 Method setAge=pclass.getMethod("setAge",new Class[]{int.class});
		 setAge.invoke(obj, new Object[]{52});
		 
		 Method setId=pclass.getMethod("setId",new Class[]{long.class});
		 setId.invoke(obj, new Object[]{51});
		 
		 Method setName=pclass.getMethod("setName",new Class[]{String.class});
		 setName.invoke(obj, new Object[]{"special"});
		 }
		 endTime=System.currentTimeMillis();
		 
		 System.out.println("反射设置参数需要花费时间为:"+(endTime-startTime)+"ms");
		 
	}

}
/**
 *错误:  Method setAge=pclass.getMethod("setAge",new Class[]{int.class}); 
 *		setAge.invoke(pclass,new Object[]{22});
 * 激活不是根据类,而是根据对象实例中的方法来激活
 */

运行结果: 

普通设置参数需要花费时间为:17ms
反射设置参数需要花费时间为:728ms



在上面我们使用反射可以获取一个对象的所有属性和方法,构造方法,注解等等信息,还可以通过invoke()方法来激活,设置属性参数等等,但是都是基于public,如果是私有方法怎么办? 

解决方法: Field,Method,Constructor三个类都继承了AccessibleObject这个类,这个类控制权限的。 可以通过set方法来修改权限从而畅通无阻。

setAccessible(boolean flag) 
          将此对象的 accessible 标志设置为指示的布尔值。    

//取得属性
		Field name=dclass.getDeclaredField("name");
		//禁止name的访问检查控制
		name.setAccessible(true);
		name.set(obj, "kenken");


//取得say方法
		Method method=dclass.getDeclaredMethod("say", new Class[]{});
		method.setAccessible(true);
		method.invoke(obj, new Object[]{});
 

	/**
	 * java封装性是给菜鸟看的
	 * 可以使用反射来访问私有的东西
	 * 个人感觉:既然在运行的时候JVM已经加载了class字节码文件,既然可以写入,那么就可以还原
	 * 那么原来的类构造一清二楚,赤裸裸的随便看了 
	 * @param args
	 * @throws Exception
	 */


/*
  错误: 在获取属性和方法时使用的是dclass.getField("name");
 	  这样不行的原因是:
 	 getField(String name) 
          返回一个 Field 对象,它反映此 Class 对象所表示的类或接口的指定公共成员字段。
          而
     getDeclaredField(String name) 
          返回一个 Field 对象,该对象反映此 Class 对象所表示的类或接口的指定已声明字段。   
  */

下面就是代理模式了。还记得我第一次接触代理时是学习三大框架时,搞得很纠结、啥都不懂。java没有深入的研究过。

静态代理是为了对代理的概念有一点理解。

大致是这样的,a通过b来访问c中的服务,实际上就是你通过我这篇博客来学习大苞米写的博客。 这个很通俗了吧。

代码部分: 

public abstract class Subject {
	//空的抽象方法,这个方法需要代理角色和真实角色都去实现它  
	public abstract void request();
}	

//真实角色对象,继承自抽象角色,重写定义的方法。  
public class RealSubject extends Subject {
	//在重写的方法中实现需要的操作,这里只是简单打印一句  
	@Override
	public void request() {
		System.out.println("This is real subject");
	}
}

package day102_reflect_ProxySubject;

//代理角色,同样继承抽象角色,重写抽象角色中的方法  
public class ProxySubject extends Subject {
	
	//在代理角色内部对真实角色的引用  
	private RealSubject realSubject;
	
	//重写的实现抽象角色中的方法  
	@Override
	public void request() {
		
		 this.preRequest();  //调用真实角色操作之前附加的操作
		 
		 if(realSubject==null){
			 realSubject=new RealSubject();
		 }
		 
		 realSubject.request();  //真实角色所完成的操作
		 this.postRequest();    //在真实角色操作之后所附加的操作
	}
	
	private void preRequest(){
		System.out.println("pre request");
	}
	
	private void postRequest(){
		System.out.println("post request");
	}
}

public class Test {

	 
	public static void main(String[] args) {
		Subject subject=new ProxySubject();
		subject.request();
	}
}
/*
抽象角色:
	声明真实对象和代理对象的共同接口
真实对象:
	代理角色所代表的真实对象,是我们最终要引用的对象
代理角色: 
1.代理对象内部含有对真实对象的引用,从而可以操作真实的对象。
2.代理对象提供与真实对象相同的接口以便在任何时刻都能代替真实对象。
3.代理对象可以在执行真实对象操作时,附加其他的操作,相当于对真实对象进行封装
*/

下一篇学习动态代理。


我是菜鸟,我在路上。

2015年5月27日17:47:39



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值