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();
}
}
145

被折叠的 条评论
为什么被折叠?



