Java日志十八「反射与两个简单的框架」

**

反射

**
在介绍反射之前,我先简单说一下Java代码的三个阶段与一些必要的概念。
Java代码的三个阶段:
1.Source源代码阶段(此时的代码还没有进入内存)
2.Class类对象阶段(通过类加载器把class文件加载入内存,此时会封装类的各个组成部分)
3.Runtime运行时阶段(创建对象)

框架:半成品的软件。在框架的基础上进行软件开发,简化编码
反射:框架设计的灵魂,将类的各个组成部分封装为其他对象(在内存中封装)
所有的成员变量封装为Field[] fields
所有的构造方法封装为Constructor[] constructors
所有的成员方法封装成Method[] methods

Java的反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用其任意一个方法和属性。总而言之,反射就是将类的各个组成部分封装为其他对象

通过上面的描述,我们来获取任意的一个类(这里以一个Person类作为示例)

Person类:

package Reflection;

public class Person {

	private String name;
	private int age;
	public String a;
	public String b;
	
	public Person()
	{
		
	}
	
	public Person(String name,int age)
	{
		this.name=name;
		this.age=age;
	}
	
	public String getName() 
	{
		return name;
	}
	
	public void setName(String name)
	{
		this.name=name;
	}
	
	public int getAge() 
	{
		System.out.println("getAge方法");
		return age;
	}
	
	public void setAge(int age)
	{
		this.age=age;
	}
	
	@Override
	public String toString() 
	{
		return name+" "+age;
	}
}

获取一个类的Class对象(三种方式):

	//获取Class对象的第一种方式,多数用于配置文件,读取文件,加载类
	Class<?> cls1=Class.forName("Reflection.Person");//参数传递全类名(包名.类名)
	System.out.println(cls1);
		
	//获取Class对象的第二种方式,多用于参数的传递
	Class<?> cls2=Person.class;
	System.out.println(cls2);
		
	//获取Class对象的第三种方式,多用于通过对象获取
	Person p=new Person();
	Class<?> cls3=p.getClass();
	System.out.println(cls3);

虽然有三种方式来获取Class对象,但不管用第几种方式获取的Class对象都是同一个,Class字节码文件只会被加载一个

获取了Class对象,我们来看一下Class对象的功能

Class对象的功能:
1.获取成员变量们
2.获取构造方法们
3.获取成员方法们
4.获取类名

1.获取成员变量们:

	Person p1=new Person();
	Class<? extends Person> personClass=p1.getClass();
	Field[] fields=personClass.getFields();//获取所有public修饰的成员变量
	for(Field f:fields) 
	{
		System.out.println(f);
	}
		
	System.out.println();
		
	Field a=personClass.getField("a");//获取指定名称的public成员变量
	System.out.println(a);
		
	System.out.println();
		
	Field[] declaredFields=personClass.getDeclaredFields();//获取所有的成员变量
	for(Field f:declaredFields) 
	{
		System.out.println(f);
	}
		
	System.out.println();
		
	Field declaredField=personClass.getDeclaredField("name");//获取指定的成员变量
	System.out.println(declaredField);
		
	System.out.println();
		
	a.set(p1,"小明");//获取成员变量后,set方法可以设置值
	System.out.println(a.get(p1));//返回成员变量的值
		
	System.out.println();
		
	declaredField.setAccessible(true);//正常情况下不可以set于get私有成员变量,但这句话可以无视修饰符,称为暴力反射
	declaredField.set(p1, "gfx");
	System.out.println(declaredField.get(p1));

结果

public java.lang.String Reflection.Person.a
public java.lang.String Reflection.Person.b

public java.lang.String Reflection.Person.a

private java.lang.String Reflection.Person.name
private int Reflection.Person.age
public java.lang.String Reflection.Person.a
public java.lang.String Reflection.Person.b

private java.lang.String Reflection.Person.name

小明

gfx

2.获取构造方法们:

	Constructor	constructor=personClass.getConstructor(String.class,int.class);//获取public的有参数的构造方法
	System.out.println(constructor);
		
	Object p2=constructor.newInstance("小红",18);//用来创建对象
	System.out.println(p2);
	//还有getConstructor(),getDeclaredConstructor(.....),getDeclaredConstructor()三种,不多赘述

结果

public Reflection.Person(java.lang.String,int)
小红 18

3.获取成员方法

	Method[] methods=personClass.getDeclaredMethods();//获取所有自己声明的的成员方法
	for(Method m:methods) 
	{
		System.out.println(m);
	}
		
	methods[3].invoke(p2);//某method.invoke可以调用某成员方法

结果

	public java.lang.String Reflection.Person.toString()
	public java.lang.String Reflection.Person.getName()
	public void Reflection.Person.setName(java.lang.String)
	public int Reflection.Person.getAge()
	public void Reflection.Person.setAge(int)
	getAge方法

因为通过Class对象获取构造方法或者成员方法的方式,与获取成员变量的方式都很类似,所以我只在获取成员变量的时候完整的写了四个方法。

我们来写两个简单的框架作为练习。

第一个框架要求: 可以创建任意的类,执行其中的方法

因为这是一个框架,所以像前面那样在main方法中获取Class对象显然是行不通的,所以我们用配置文件,里面写上要用的类与其中的方法就可以了。

配置文件内容:
className=Reflection.Person
methodName=getAge

代码实现这个框架:

public class A_Simple_Frame {

	public static void main(String[] args) throws Exception 
	{
		//1.加载配置文件
		Properties pro=new Properties();
		ClassLoader classLoader=A_Simple_Frame.class.getClassLoader();//创建一个类加载器的对象
		InputStream is=classLoader.getResourceAsStream("pro.properties");//用创建的一个类加载器对象寻找我们的配置文件的路径并且返回一个字节
		pro.load(is);
		
		//2.获取配置文件中定义的数据
		String className=pro.getProperty("className");
		String methodName=pro.getProperty("methodName");
		
		//3.加载该类进内存
		Class cls=Class.forName(className);
		
		//4.创建对象
		Object obj=cls.newInstance();
		
		//5.获取方法对象
		Method method=cls.getMethod(methodName);//若有参数就写到第二个参数位置上
		
		//6.执行方法
		method.invoke(obj);
	}
}

结果

getAge方法

这样一个简单的框架就完成了,若要使用其他类的方法,只需要把配置文件改一下就可以,主要代码不需要改变。

第二个框架要求:一个简单的测试框架,可以测试任何类
当main方法执行的时候,会自动检测所有加了Check注解的方法,记录异常到桌面的文件中

这个框架包含了上一篇关于注解的知识,同样也是使用配置文件完成的框架,先来看一下我们要测试的类:

public class Caculator {

	@Check//自定义的一个注解
	public int add() 
	{
		return 1+0;
	}
	@Check
	public int sub() 
	{
		return 0-1;
	}
	@Check
	public int mul() 
	{
		return 1*0;
	}
	@Check
	public int div() 
	{
		return 1/0;//将来这个错误要被记录到文件中
	}
}

Check注解:

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface Check {

}

配置文件
className=Annotation_in_check.Caculator

代码实现这个框架

public class TestCheck {

	@SuppressWarnings("all")
	public static void main(String[] args) throws IOException, ClassNotFoundException, InstantiationException, IllegalAccessException {
		
		int ececeptionNum=0;//错误数量
		BufferedWriter bw=new BufferedWriter(new FileWriter("/Users/ahlahmlahlah/Desktop/bug.txt"));
		
		Properties pro=new Properties();
		ClassLoader classLoader=TestCheck.class.getClassLoader();
		InputStream is=classLoader.getResourceAsStream("pro.properties");
		pro.load(is);
		String className=pro.getProperty("className");
		Class cls=Class.forName(className);
		Object obj=cls.newInstance();
		//上面的代码获取要测试的类并且创建此类的对象
		
		//获取所有方法
		Method[] methods=cls.getMethods();
		for(Method m:methods)
		{
			if(m.isAnnotationPresent(Check.class))//这一句话判断方法前也没有注解
			{
				try {
					m.invoke(obj);
				} catch (Exception e) {
					//捕获异常并把它记录到文件中
					ececeptionNum++;
					
					bw.write(m.getName()+"方法出现异常");
					bw.newLine();
					bw.write("异常的名称是:"+e.getCause().getClass().getSimpleName());
					bw.newLine();
					bw.write("异常的原因是:"+e.getCause().getMessage());
					bw.newLine();
					bw.write("------------------------");
					bw.newLine();
				} 
			}
		}
		bw.write("本次测试一共出现"+ececeptionNum+"次异常");
		bw.flush();
		bw.close();
	}
}

结果:
在这里插入图片描述
这样这个框架就完成了,下次更新完JDBC的知识,就要进入JavaWeb的部分了,博主尽量在开学前更新完。。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值