Java反射

JAVA中的反射

一.反射的基本概念

1.1.Java在计算机中经历的三个阶段:

在这里插入图片描述

1.2.Java反射百度百科定义:

JAVA反射机制是在运行状态中(即RunTime阶段);

对于任意一个实体类(如Person类),都能够知道这个类的所有属性(name)和方法(eat()、构造方法);

对于任意一个对象,都能够调用它的任意方法和属性;

这种动态获取信息以及动态调用对象方法(方法名.invoke(类对象),类似于p.eat())的功能称为java语言的反射机制。

1.3.反射的作用、好处:

定义Person类和Student类以及测试类Test

		public class Person {
		    public void eat(){
		        	System.out.println("eat.....");
	    	}
	}
	
	public class Stutent {
		    public void study(){
			        System.out.println("study.....");
	    	}
	}

当测试类Test执行Person类的方法eat()时,需要

	public class Test {
		    public static void main(String[] args){
		    	    Person p = new Person();
			        p.eat();
	    	}
	}

同时,当想要执行Studen类的study()方法时,则需要注释掉Person类,然后重写

public class Test {
    	public static void main(String[] args){
	        	//Person p = new Person();
	        	//p.eat();
		
	        	Student s = new Student();
	        	s.study();
	    }
}

传统的方式执行类的方法时,无疑需要对源代码进行修改,这在工程应用中跟不方面,尤其是代码量很大时;
而Java的反射机制,在不修改源码的条件下,可以执行不同类的方法或者获取不同类的属性(成员变量),以及构造方法。

二.反射获取类的属性(成员)、方法

2.1. 获取Class类对象(字节码文件(Person.class)对象)的三种方式:
						                             **为了方便理解,这里统一采取Person类作为例子**

2.1.1).Class类的静态方法获取:

对应Java代码的第一阶段(Source源代码阶段)

    	Class c = Class.ForName("包名.类名(Person)")

2.1.2).类名(Person)获取:

对应Java代码的第二阶段(Class类对象阶段)

    	Class c = Person.Class;

2.1.3).类的对象获取:

对应Java代码的第三阶段(RunTime运行时阶段)

	    Person p = new Person();
	    Class c = p.getClass();

注意:上述三种方式获得的字节码文件对象c为同一个对象,因为一个字节码文件在一次程序运行过程中,只会被加载一次。

2.2.反射获取成员变量的四种方法
    	public class Person {
	        	public String name;
	        	public String address;
	        	private String age;
	    	}

2.2.1).获取类(Person)的public访问修饰的全部成员变量:

	    Field[] field = c.getFields();
    	field返回结果为:Person.age和Person.address

2.2.2).获取类(Person)的public访问修饰的指定成员变量:

	    Field field = c.getField("name");

获取name成员变量后,可以进行的操作为:获取成员变量值和设置成员变量值。

获取:

	    Person p = new Person();
	    Object value = field.get(p);value值为null,因为name	未设置值

设置:

    	Person p = new Person();
	    field.set(p,"Asmita"); 则name的值设置为:Asmita

2.2.3).获取类(Person)的全部成员变量(与修饰符无关):

	Field[] field = c.getDeclatedFields();field结果为name、address、age

2.2.4)获取类(Person)的任意指定的成员变量:

前提:需要暴力反射。

若要访问private修饰的成员变量,需暴力反射,即忽略访问权限修饰符的安全检查。

	    Field field = c.getDeclaredField("age");
	    
	    暴力反射:field.setAccessible(true);
	    
    	Person p = new Person();
	    获取age值:Object value = field.get(p);value = 0,因为age初始值为0。
	    设置age值:field.set(p,18); age属性设置为age = 18。

2.3.反射获取构造方法

	public class Person {
	    	public Person(){};

	    	public Person(String name , String address){
		        	this.name = name;
			        this.address = address;
	    	}

	    	private Person(String name , String address){
		       	this.name = name;
		       	this.address = address;
    		}
	} 

2.3.1).获取类(Person)的public修饰的全部构造方法:

	    Constructor[] con = c.getConstructors();

2.3.2).获取类(Person)的public修饰的指定的构造方法:

有参构造器:

    	Constructor con = c.getConstructor(String.class , String.class);

获取构造器的目的是创建对象,设置初始值

	    Object obj = con.newInstance("Asmita" , "Nanjing");
	    则obj的值 obj=[name = "Asmita" , age = 0 ,address = "Nanjing"]

同理,可以获得空参构造器:

	Constructor con = C.getConstructor();

获取空参构造器,创建对象

    	Object obj = con.newInstance();

改进:不需要获取构造器,直接创建类对象

    	Object obj = c.newInstance();

2.3.3).获取类(Person)的全部构造方法:

    	Constructor[] con = c.getDeclaredConstructors();

2.3.4).获取类(Person)的指定构造方法(比如private修饰的):

创建对象,及对象属性初始化值的前提:需要暴力反射

    Constructor con = c.getDeclaredConstructor(String.class , String.class);
    con.setAccessible(true);
    Object obj = con.newInstance("Asmita" , "Nanjing");

2.4.反射获取成员方法

public class Person {
		    public void eat(){
		        	System.out.println("eat.....");
	    	}

		    public void eat(String something){
		        	System.out.println("eat...."+something);
		    }
	}

2.4.1).获取类(Person)的所有public访问修饰的成员方法:

    	Method[] method = c.getMethods();

	    获取全部public修饰的成员方法,遍历输出

    	for(Method func : meyhod){
	    	    Syso(func);
	    }
结果,除了本类的方法外,还有父类Object的toString()等等方法

**2.4.2).**获取类(Person)的指定的public访问修饰的成员方法:

    	Method method = c.getMethod("eat",String.class);

获取指定成员方法后,可以执行此方法,设置方法参数

	    Person p = new Person();
	    method.incoke(p , "好吃的东西呀~");

2.5.补充获取方法名称和获取类名

**2.5.1).**获取方法名:

    String methodName = method.getName();

比如2.4.1中获取了全部的public修饰的方法,method结果为:eat()、toString()…等等,而此方法,获取的methodName结果为:
eat、toString…

**2.5.2).**获取类名:

    Person p = new Person();
    String className = p.getName();类名的全路径(包括包名)

三.通过反射-创建类对象,继而执行类方法##

3.1.通过配置文件的方法

步骤:

step1:创建一个配置文件,将全类名(包名.类名),和类的方法,写入配置文件。step2:加载配置文件。

step3:反射获取Class类对象(字节码文件对象)

step4:获取类对象

step5:执行类方法

1.new-File(pro.properties):

ClassName = Demo.Person
MethodName = eat

public class TestDemo {
    public static void main(String[] args) throws Exception {
        	//1.创建配置文件pro.properties
        		
        	//2.加载配置文件
        		//2.1利用类加载器,将字节码文件加载到内存中
	        ClassLoader classLoader = TestDemo.class.getClassLoader();
	        //2.2classLoader可以找到.class文件,也可以找到src目录下的pro.perties文件
	        //并将配置文件转换为IO流。
	        InputStream is = classLoader.getResourceAsStream("pro.perties");
	        //2.3以键值对的形式(className-Demo.Person)存储到Properties集合中
	        Properties pro = new Properties();
	        pro.load(is);
	        is.close();
	        
        		//2.4键获取Properties集合中的值
	        String classNameValue = pro.getProperty("className");
	        String methodNameValue = pro.getProperty("methodName");
	
	        //3.反射获取Class类对象
	        //使用第一阶段.获取方法
	        Class c = Class.forName(classNameValue);
	
	        //4.快捷方法创建类对象、获取方法名
        	Object obj = c.newInstance();
	        Method method = c.getMethod(methodNameValue);
	
        	//5.执行方法
	        method.invoke(obj);
    		}
}

3.2.通过注解的方式执行方法

//自定义的注解,作用在类上
@Target({ElementType.TYPE})

//自定义的注解,作用在JVM运行时阶段
@Retention(RetentionPolicy.RUNTIME)

public @interface MyAnnotation {
    //1.定义连两个属性(抽象方法)
    	public abstract String className();
    //public abstract 可省略,在注解(接口)(抽象类)中
    String methodName();
}


@MyAnnotation(className = "AnnotationReflectDemo.Student" , methodName = "study")
public class Test {
    public static void main(String[] args) throws Exception {
	        //1.自定义MyAnnotation注释(接口),作用在Test类、运行时阶段
	        //注释(接口)包含了两个属性(抽象方法)
	
	        //2.解析注释
	        //2.1获取被注释修饰的类 class文件对象
	        Class<Test> c = Test.class;
	        //2.2获取注释(接口)的实现类的对象
	        /*public class MyAnnotationImpl implements MyAnnotation(){
	        重写实现接口(注释)的抽象方法
        	public String className(){
		        return "AnnotationReflectDemo.Person";
	        */
	        MyAnnotation anno = c.getAnnotation(MyAnnotation.class);
	        //2.3 接口实现类对象,对象调用方法,获取注释(接口)的属性(抽象方法)
	        String classNameValue = anno.className();
	        String methodNameValue = anno.methodName();
	
	        //3.反射,获取Class类对象
	        Class c1 = Class.forName(classNameValue);
	
	        //4.获取类名,获取方法名
	        String className = c1.getName();
	        Method methodName = c1.getMethod(methodNameValue);

	        //5.执行方法
	        //获取类的对象快捷方法,反射
	        Object obj = c1.newInstance();
	        methodName.invoke(obj);
	
    }
}

四.反射的应用

可以利用反射结合注解,对声明好的方法进行简单的测试。

step1:创建测试对象:Calculator类

step2:获取字节码文件对象

step3:反射获取全部方法

step4:判断方法,是否被注释修饰

step5:有则执行注释进行方法检查

step6:有异常则捕获异常

public class Calculator {
    	@check
	    public void(){
	        	System.out.println("1+0=" +(1+0));;
	    }

	    @check
    	public int div(){
	        	System.out.println("1/0 = " + (1/0));;
	    }
}			

很明显,div()方法出现了错误,执行step(2-6)

public class CalculatorTest {
    public static void main(String[] args) {
	        //1.创建需要被检查的Calculator类
	
	        //2.获取字节码文件对象(第三种方式RunTime阶段)
	        Calculator cal = new Calculator();
	        Class  c = cal.getClass();
	        //2.1获取类对象,类的全部方法
	        Object obj = c.newInstance();
	        Method[] methods = c.getMethods();
	
	        //定义异常次数
	        int number = 0;
	        //定义记录异常的流对象和文件
	        BufferedWriter bw = new BufferedWriter(new FileWriter("bug.txt"));
	
	        //3.循环遍历判断获取的方式是否被@check注解修饰
	        for(Method method : methods){
		            //4.判断是否被注解修饰
		            if(method.isAnnotationPresent(check.class)){
			                //5.修饰,则执行方法
			                try{
				                     //执行类方法
				                     method.invoke(obj);
			                	//6.捕获异常
			                 }catch(Exception ex){
			                     	number++;
			                     	bw.write(method.getName()+"方法出现了异常");
			                     	bw.newLine();
			                     	//获取异常简短名
		                     		bw.write("异常名称为:"+ex.getCause().getClass().getSimpleName());
		                     		bw.newLine();
				                     bw.write("异常原因"+ex.getCause().getMessage());
		                  	}
	               	}
           	}
           	
        	bw.write("异常总共出现 "+number+"次异常");
        	bw.flush();
	        bw.close();
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值