一、使用反射创建对象
通过反射来生成对象有如下两种方式:
方式一:
使用 Class 对象的 newInstance() 方法来创建 Class 对象对应类的实例,这种方法要求该 Class 对象的对应类有默认构造器,而执行 newInstance() 方法实际上是利用默认构造器来创建该类的实例。
方式二:
先使用 Class 对象获取指定的 Constructor 对象,再调用 Constructor 对象的newInstance(Object... args) 方法来创建该 Class 对象对应类的实例。通过这种方式可以选择使用某个类的指定构造器来创建实例。
通过第一种方式来创建对象是比较常见的情形,因为很多 JavaEE 框架中都需要根据配置文件信息来创建实例对象,从配置文件读取的只是某个类的字符串类名,程序就需要根据该字符串来创建对应的实例,就必须使用反射。
代码示例:
1 import java.lang.reflect.Constructor;
2
3 import org.junit.Test;
4
5 public class TestNewInstance {
6 @Test
7 public void test1() throws Exception{
8 Class<?> clazz = Class.forName("com.ks.reflect.Student");
9 Object obj = clazz.newInstance();
10 System.out.println(obj);
11 }
12
13 @Test
14 public void test2() throws Exception{
15 Class<?> clazz = Class.forName("com.ks.reflect.Student");
16 Constructor<?> constructor = clazz.getDeclaredConstructor(String.class);
17 Object obj = constructor.newInstance("张三");
18 System.out.println(obj);
19 }
20 }
21 class Student{
22 private String name;
23
24 public Student(String name) {
25 super();
26 this.name = name;
27 }
28
29 public Student() {
30 super();
31 }
32
33 @Override
34 public String toString() {
35 return "Student [name=" + name + "]";
36 }
37 }
二、获取或设置某个对象的属性值
通过 Class 对象的 getFields() 等方法可以获取该类所包括的全部 Field(属性)或指定 Field。
而Field类除了提供获取属性的修饰符、属性类型、属性名等方法外,还提供了如下两组方法来访问属性:
public xxx getXxx(Object obj):获取obj对象该Field的属性值
public void setXxx(Object obj,Xxx value):设置obj对象该Field的属性值为value。
此处的XXX 对应 8 种基本数据类型,如果该属性的类型是引用数据类型,则直接使用get(Object obj) 方法或 set(Object obj, Object value) 方法。
当属性是 private不可访问时,还需要下面的方法:
public void setAccessible(boolean flag)启动和禁用访问安全检查的开关。
(1)值 true 则指示反射的对象在使用时应该取消 Java 语言访问检查。
-
-
- 提高反射的效率,如果代码中必须用反射,而该句代码需要频繁的呗调用,那么请设置为 true
- 使得原本无法访问的私有成员也可以访问。
- 提高反射的效率,如果代码中必须用反射,而该句代码需要频繁的呗调用,那么请设置为 true
-
(2)值 false 则指示反射的对象应该实施 Java 语言访问检查。
Demo:获取或设置某个对象的属性值示例代码
1 import java.lang.reflect.Field;
2
3 public class TestField {
4
5 public static void main(String[] args)throws Exception {
6 Class<?> clazz = Class.forName("com.ks.reflect.Circle");
7 Object obj = clazz.newInstance();
8 Field field = clazz.getDeclaredField("radius");
9 field.setAccessible(true);
10 field.set(obj, 1.2);
11 Object value = field.get(obj);
12 System.out.println(value);
13 }
14
15 }
16 class Circle{
17 private double radius;
18 }
三、调用方法
当获得某个类对应的 Class 对象后,就可以通过该 Class 对象的 getMethods() 等方法获取全部方法或指定方法。
每个 Method 对象对应一个方法,获取 Method 对象后,程序就可以通过该 Method 对象的 invoke 方法来调用对应方法。
Object invoke(Object obj, Object... args):对带有指定参数的指定对象调用由此 Method 对象表示的底层方法
Demo:
1 import java.lang.reflect.Method;
2
3 public class TestMethod {
4
5 public static void main(String[] args) throws Exception {
6 Class<?> clazz = Class.forName("com.ks.reflect.Utils");
7 Object obj = clazz.newInstance();
8 Method method = clazz.getMethod("check", String.class,String.class);
9 Object value = method.invoke(obj, "tong","666");
10 System.out.println(value);
11 }
12
13 }
14 class Utils{
15 public boolean check(String user,String password){
16 if("admin".equals(user) && "123".equals(password)){
17 return true;
18 }else{
19 return false;
20 }
21 }
22 }
注意: 如果调用某个类的某个方法时,并不是传入对象,而是 null,表示调用静态方法。(即 invoke(null,参数列表))
四、操作数组
在java.lang.reflect包下还提供了一个Array类,Array对象可以代表所有的数组。程序可以通过使用Array类来动态的创建数组,操作数组元素等。
Array 类提供了如下几个方法:
public static Object newInstance(Class<?> componentType, int... dimensions):创建一个具有指定的组件类型和维度的新数组
public static void setXxx(Object array,int index,xxx value):将array数组中[index]元素的值修改为value。
public static xxx getXxx(Object array,int index,xxx value):将array数组中[index]元素的值返回。
此处的 Xxx 对应 8 种基本数据类型,如果该属性的类型是引用数据类型,则直接使用 set(object arr,int index, Object value)方法或 get(Object array, int index) 方法。
Demo:
1 import java.lang.reflect.Array;
2
3 public class TestArray {
4 public static void main(String[] args) {
5 Object arr = Array.newInstance(String.class, 5);
6 Array.set(arr, 0, "Hello");
7 Array.set(arr, 1, "World");
8 System.out.println(Array.get(arr, 0));
9 System.out.println(Array.get(arr, 1));
10 System.out.println(Array.get(arr, 2));
11 }
12 }
五、运行时获取注解信息
一个完整的注解,有三个要素:
(1)声明
(2)使用
(3)读取
像@Override,@SuppressWarings,@Deprecated等这些是JRE中声明的,也是由编译器读取的
像@Test,@Before...等这些注解是JUnit声明和读取的
像@author,@param...等这些注解是JRE中声明的,由javadoc.exe读取的
对于自定义的注解,声明和读取需要自己完成
1、声明注解
Demo:
1 //声明注解
2 @Target({TYPE,FIELD})
3 @Retention(RetentionPolicy.RUNTIME)//只有生命周期是运行时,那么才可以被反射读取
4 @interface MyAnnotation{
5 String value(); //如果配置参数只有一个,名称是value,在使用时,赋值的话就可以省略"value="
6 String name(); // 也可以使用 default 给它们指定默认值
7 }
2、使用注解
Demo:使用自定义的注解
1 //使用注解
2 @MyAnnotation(value = "Annotation",name="Java")
3 class MyClass{
4 @MyAnnotation(value="String",name="Field")
5 private String info;
6
7 }
3、读取注解
主要分为三步:
① 获取 Class 对象
② 获取注解对象
③ 获取注解的配置参数的值
Demo:
1 public class TestAnnotation {
2 @SuppressWarnings("unchecked")
3 @Test
4 public void test01() {
5 //(1)获取Class对象
6 Class clazz = MyClass.class;//四种方式之一
7
8 //(2)获取注解对象
9 MyAnnotation annotation = (MyAnnotation) clazz.getAnnotation(MyAnnotation.class);
10
11 //(3)获取注解的配置参数的值
12 String value = annotation.value();
13 System.out.println("value = " + value);
14
15 String name = annotation.name();
16 System.out.println("name =" + name);
17 }
18
19 @SuppressWarnings("unchecked")
20 @Test
21 public void test02() throws NoSuchFieldException, SecurityException {
22 //(1)获取Class对象
23 Class clazz = MyClass.class;//四种方式之一
24
25 //(2)获取属性对象
26 Field infoField = clazz.getDeclaredField("info");
27
28 //(3)获取注解对象
29 MyAnnotation annotation = (MyAnnotation) infoField.getAnnotation(MyAnnotation.class);
30
31 //(3)获取注解的配置参数的值
32 String value = annotation.value();
33 System.out.println("value = " + value);
34
35 String name = annotation.name();
36 System.out.println("name =" + name);
37 }
38
39 }
六、运行时读取某个类的泛型实参
读取某个类的泛型实参主要分为三步:
(1)获取 Class 对象
(2)获取泛型父类
Type type = clazz.getGenericSuperclass();
ParameterizedType type = (ParameterizedType) clazz.getGenericSuperclass();
(3)获取类型实参
Type:代表Java的所有类型
(1)Class:代表的是普通的类型,没有泛型信息的
(2)ParameterizedType:参数化类型 例如:Father<Integer, String>
(3)GenericArrayType:泛型数组类型 例如:T[]
(4)TypeVariable:类型变量 例如:T
(5)WildcardType:带?通配符的泛型的类型 例如:ArrayList<?> 或ArrayList<? super 下限> 或ArrayList<? extends 上限>
Demo:
1 //泛型类型形参:<T,U>
2 class Father<T,U>{
3
4 }
5 //泛型类型实参:<Integer, String>
6 class Son extends Father<Integer, String>{
7
8 }
9
10 @Test
11 public void test01() {
12 //获取Son类的泛型父类的类型实参
13 //(1)获取Class对象
14 Class clazz = Son.class;
15
16 //(2)获取泛型父类
17 //获取普通父类
18 // Class fu = clazz.getSuperclass();
19 // System.out.println(fu);
20
21 //获取泛型父类的参数
22 ParameterizedType type = (ParameterizedType) clazz.getGenericSuperclass();
23
24 //(3)获取类型实参
25 Type[] types = type.getActualTypeArguments();
26 for (Type t : types) {
27 System.out.println(t);
28 }
29 }
对于泛型类、接口的类型形参,什么时候才能确定具体的类型呢?
(1)创建它的对象
(2)继承泛型类
(3)实现泛型接口
Demo:
1 class Tools<T>{
2 private Class type;
3
4 public Tools() {
5 //在创建子类对象时,来确定type的代表类型
6 //(1)获取正在new的对象的类型Class对象
7 Class clazz = this.getClass();
8
9 //(2)获取泛型父类的信息
10 ParameterizedType t = (ParameterizedType) clazz.getGenericSuperclass();
11
12 //(3)获取类型实参
13 type = (Class) t.getActualTypeArguments()[0];
14 }
15
16
17 public void test(){
18 //这个方法中需要用到T的类型对象,即T的Class对象
19 // Class c = T.class;//此时无法确定T的类型
20 System.out.println(type);
21 }
22
23 }
24 class MyTools extends Tools<String>{
25
26 }
27
28 @Test
29 public void test02() {
30 MyTools my = new MyTools();
31 my.test(); //class java.lang.String
32
33 }