反射


反射

1 定义

反射是Java提供的一套API。是Java提供的一套动态执行机制。

2 静态执行

案例:

Person one = new Person();
one.sayHello();

过程:
1) 根据类名和方法名编写固定的静态代码
2) 编译为class文件
3) 执行class文件期间按照第1步约定的顺序执行

> 特点:静态执行是根据事先约定的类名和方法名执行。

3 动态执行

过程:
1) 利用反射API编写代码,代码中没有类名和方法名
2) 编译为class文件
3) 在运行期间根据动态获得的类名和方法名执行。

> 特点: 动态执行是依据运行期间动态获取的类名和方法名来确定执行过程的。

4 静态执行与动态执行的比较

见Reflect.day01

5 需求

JUnit3 原型:执行给定的一个类中全部以test为开头的方法

详细需求:
1) 类有无参数构造器
2) 方法是非静态方法
3) test开头的方法都是无参数方法

6 反射API的功能

6.1 动态加载类

语法:

Class cls = Class.forName(className);

意义:

根据给定的类名将类信息加载到方法区。返回值就是方法区中的类信息。

详细流程见Reflect.day01

案例:

public static void main(String[] args) throws Exception{
//className是类全名
String className = "reflect.Foo";
Class cls=Class.forName(className);
System.out.println(cls);
}

6.2 动态创建对象

语法:

Object obj = cls.newInstance();

意义:

调用cls代表类型信息中的 **无参数构造器** 创建对象,如果没有无参数构造器将抛出异常!!返回值是新创建的对象。

案例:

public static void main(String[] args) throws Exception{
Scanner in = new Scanner(System.in);
System.out.print("输入类名:");
//在运行期间动态获取类名
String className=in.nextLine();
//动态加载类
Class cls=Class.forName(className);
//动态创建对象
Object obj = cls.newInstance();
System.out.println(obj);
}

6.3 获取类的相关信息

1) 获取方法的相关信息
2) 获取属性相关信息
3) 获取构造器信息
4) 动态执行方法

6.3.1 获取方法的相关信息

调用反射API获取一个类方法信息:

Method[] methods=cls.getDeclaredMethods();

> 提示:

1) getDeclaredMethods()用于获取当前类中定义的方法信息
2) getMethods()用于获取当前类中已经从父类中继承的全部公有方法信息。

案例1:

public static void main(String[] args)throws Exception{
Scanner in = new Scanner(System.in);
System.out.print("输入类名:");
String className=in.nextLine();
Class cls = Class.forName(className);
//获取类中声明的全部方法信息
Method[] methods = cls.getDeclaredMethods();
//methods 包含类中全部的方法信息
for (Method method : methods) {
System.out.println(method);
}
}


案例2:

//查找一个类中全部以test为开头的方法
public static void main(String[] args) throws Exception {
Scanner in=new Scanner(System.in);
System.out.println("输入类名:");
String className=in.nextLine();
Class cls=Class.forName(className);
//获取类中声明的全部方法
Method[] methods=cls.getDeclaredMethods();
//挑选以test为开头的方法
for(Method method:methods){
String name=method.getName();
if(name.startsWith("test")){
//找到以test为开头的方法
System.out.println(method);
}
}
}

6.3.2 获取一个类的属性信息

调用反射API获取一个类属性信息:

Field[] flds=cls.getDeclaredFields();

> 提示:

1) getDeclaredFields()用于获取当前类中定义的属性信息
2) getFields()用于获取当前类中已经从父类中继承的全部公有属性信息。

案例1:

public static void main(String[] args) throws Exception{
Scanner in = new Scanner(System.in);
System.out.print("输入类名:");
String className=in.nextLine();
Class cls=Class.forName(className);
//获取类中全部的属性信息
Field[] fields=cls.getDeclaredFields();
//cls.getDeclaredConstructors()
//显示每个属性信息
for(Field field:fields){
System.out.println(field);
}
}

6.3.3 获取构造器信息

语法:

Constructor[] cs=cls.getDeclaredConstructors();

案例:

public static void main(String[] args) throws Exception{
Scanner in = new Scanner(System.in);
System.out.print("输入类名:");
String className=in.nextLine();
Class cls=Class.forName(className);
//获取类中全部的构造器信息
Constructor[] cs=cls.getDeclaredConstructors();
//显示每个属性信息
for(Constructor c:cs){
System.out.println(c);
}
}

6.3.4 动态执行方法

语法:

method.invoke(对象, 参数...)

解释:

执行method代表的那个动态方法,
其中参数 **对象** 中一定包含这个方法!!
其中参数 **参数** 对应于方法实际的参数。违反上述规则将抛出异常。

如:
method 代表 Foo 的hello(int)方法
则执行 method.invoke() 时候,对象必须是Foo的实例,参数一定是int参数,否则将出现异常!

method 代表 Foo 的testA()方法
则执行 method.invoke() 时候,对象必须是Foo的实例,参数一定是无参数,否则将出现异常!

案例:

//执行一个类中以test为开头的方法
public static void main(String[] args) throws Exception {
Scanner in=new Scanner(System.in);
System.out.println("输入类名:");
String className=in.nextLine();
Class cls=Class.forName(className);
//获取全部的方法
Method[] methods=cls.getDeclaredMethods();
//挑选以test为开头的方法
Object obj = cls.newInstance();
for(Method method:methods){
String name=method.getName();
if(name.startsWith("test")){
//找到以test为开头的方法
System.out.println(method);
//method -> testA()
Object val=method.invoke(obj);
System.out.println(val);
}
}
}


7 利用反射解析注解

7.1 软件中的注解使用反射API进行解析的。

案例: 执行一个类中标记了@Test的方法。

> 提示:很多使用注解的框架都使用 **反射API** 解析的注解。

> 在Class上解析类的注解,Method上解析方法的注解,Field上解析属性的注解。

如:

public class TestCase{
@Test
public void hello(){
System.out.println("Hello World!");
}
}

7.2 获取方法的注解

//获取方法上全部的注解
Annotation[] ary=method.getAnnotations();
//获取方法上一个特定类型的注解,找到返回注解对象,找不到返回 null
Annotation ann=method.getAnnotation(注解类型);

案例:

//执行一个类中全部以@Test注解标识的方法

/*
* 声明一个Test注解
* RUNTIME 注解保持到运行期间
* METHOD 注解只对方法有效
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface Test {
}

//声明用注解标识方法
public class TestCase {
@Test
public void hello(){
System.out.println("Hello WOrld!");
}
@Test
public void kitty(){
System.out.println("Hello Kitty!");
}
}

/**
* 执行一个类中以@Test注解标识的方法
*/
public class Demo08 {

public static void main(String[] args)throws Exception {
Scanner in=new Scanner(System.in);
System.out.print("输入类名:");
String className=in.nextLine();
Class cls=Class.forName(className);
Method[] methods = cls.getDeclaredMethods();
Object obj = cls.newInstance();
for(Method method:methods){
if(method.getAnnotation(Test.class)!=null){
//执行方法
method.invoke(obj);
}
}
}
}


8 利用反射API读取对象的属性

语法:

field.get(对象)

意义:

获取 **对象** 上field代表的属性的值,其中对象类型上一定有field属性值,如果没有抛出异常。

> 注意:

getDeclaredField() 返回类中声明的属性, getField() 返回类中声明的公有属性

案例:

/**
* 利用反射动态调用对象的属性值
*/
public class Demo09 {
public static void main(String[] args)
throws Exception {
Scanner in=new Scanner(System.in);
System.out.print("类名:");
String className=in.nextLine();
Class cls = Class.forName(className);
Object obj = cls.newInstance();
//输入属性名:
System.out.print("属性名:");
String name = in.nextLine();
//getField 在类中查找属性信息
Field fld= cls.getDeclaredField(name);
//获取属性的值:
Object value = fld.get(obj);
System.out.println(value);
}
}

9 利用反射API读取对象的私有成员

在访问私有成员之前调用如下API即可:

fld.setAccessible(true);

案例:


/**
* 利用反射动态调用对象的私有属性值
*/
public class Demo09 {
public static void main(String[] args) throws Exception {
Scanner in=new Scanner(System.in);
System.out.print("类名:");
String className=in.nextLine();
Class cls = Class.forName(className);
Object obj = cls.newInstance();
//输入属性名:
System.out.print("属性名:");
String name = in.nextLine();
//getField 在类中查找属性信息
Field fld= cls.getDeclaredField(name);
//打开属性的访问权限(取消私有)
fld.setAccessible(true);
//获取属性的值:
Object value = fld.get(obj);
System.out.println(value);
}
}

> 提示:反射也可以调用私有方法,具体的方式请学员执行摸索。

> 反射API提供的这个功能可以实现一些特殊情形编程,但是这个API也造成封装被打破了,请慎用!!!

 

posted @ 2018-01-14 15:33  Leo_Messi  阅读(98)  评论(0编辑  收藏  举报