zoukankan      html  css  js  c++  java
  • Java 之 使用反射生成并操作对象

    一、使用反射创建对象

       通过反射来生成对象有如下两种方式:

      方式一:

        使用 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
        •     使得原本无法访问的私有成员也可以访问。

       (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     }
  • 相关阅读:
    冒泡排序
    Window中常见的dos命令
    spring boot 重定向
    阿里云轻量级服务器使用
    网络知识
    spring boot security 登出
    深入理解java虚拟机
    jsp内置对象与servlet的关系
    求一个有向无换图中,最长简单路径。动态规划问题15-1
    一些动态规划问题的java实现
  • 原文地址:https://www.cnblogs.com/niujifei/p/12313684.html
Copyright © 2011-2022 走看看