zoukankan      html  css  js  c++  java
  • java反射机制学习

    java的反射机制是Java特性之一,反射机制是构建框架技术的基础所在。灵活掌握Java反射机制,对大家以后学习框架技术有很大的帮助

    1、什么是反射

    只要提供引用对象或者代表类的字符串信息,就可以得到该对象或者该字符串对应的类型信息,利用这些信息,在运行时候动态去调用类里面的各种方法和各种属性;换句话说只要有类的实例对象或者类的完整路径信息,就可以通过反射获得该类所有的属性、方法、构造方法,并使用;所以,反射就会突出下面两个特点:

    1)灵活性高:编译的时候都不清楚会生成哪个类的对象,直到运行调用的时候,才知道哪个类;

    2)牺牲效率:一般正常的jvm工作是将.java编译成.class运行,反射则是将.class变成.java,读取相关信息后,再编译运行;

    2、反射的实现

    第一步:首先获得class类对象:有两种方法

      ①通过对象:class类对象.getClass()

      ②通过类名(路径):Class.forName(String className)

    第二步:通过获得的class类对象获取属性、方法

      获得private属性:class类对象. getDeclaredFields()

      获得普通属性:class类对象. getFields()

      获得构造方法组:Constructor[] 数组名 = class类对象.getConstructors()

      获得方法组:Method[] 数组名 = class类对象.getMethods()

    第三步:通过构造方法创建对象

      1)调用无参构造方法(下面c代表Class类对象

        先通过Class类对象获得无参构造方法对象(只要对象就可以,不要数组):

          Constructor constructor=c.getConstructor();

        再利用构造方法对象的newInstance()创建出实例

          Object obj = constructor.newInstance();

      2) 调用有参构造方法创建对象(指定参数类型、个数、顺序)

        先通过class类对象获得有参构造方法对象,还要知道有参参数类型、个数:

          Constructor constructor = c.getConstructor(包装类1.class, 包装类2.class,...)

           再利用构造方法对象的newInstance(对应类型赋值)创建出实例

          Object obj = constructor.newInstance(赋值);

    另外:在获得对象的基础上,可以调用里面任意方法

    如下一个简单例子:

    新建一个User类,这边我是放在fanshe包下面(反射的一般都要是包装类型,即UserInfo里面属性及方法参数类型

     1 package fanshe;
     2 
     3 public class User {
     4     private Integer id; // 包装类
     5     private String name;
     6     public Integer age;
     7 
     8     public Integer getId() {
     9         return id;
    10     }
    11 
    12     public Integer getAge() {
    13         return age;
    14     }
    15 
    16     public void setAge(Integer age) {
    17         this.age = age;
    18     }
    19 
    20     public void setId(Integer id) {
    21         this.id = id;
    22     }
    23 
    24     public String getName() {
    25         return name;
    26     }
    27 
    28     public void setName(String name) {
    29         this.name = name;
    30     }
    31 
    32     // 无参构造
    33     public User() {
    34         // TODO Auto-generated constructor stub
    35     }
    36 
    37     // 有参构造
    38     public User(Integer id, String name) { // 参数包装类
    39         this.id = id;
    40         this.name = name;
    41     }
    42 
    43     public String toString() {
    44         System.out.println("重写toString");
    45         return "id:" + this.id + ":" + "用户名:" + this.name;
    46     }
    47 
    48     public void show() {
    49         System.out.println(this.toString());
    50     }
    51 
    52     public Double getSalary() {
    53         System.out.println("无参有返回值");
    54         return (double) (100 * id);
    55     }
    56 
    57     public Double getSalay(int year, double baseSalary) {
    58         System.out.println("有参有返回值");
    59         return year * baseSalary;
    60     }
    61 }

    1)获取属性、方法、构造方法 java代码

     1 package fanshe;
     2 
     3 import java.lang.reflect.Constructor;
     4 import java.lang.reflect.Field;
     5 import java.lang.reflect.Method;
     6 
     7 import org.junit.Test;
     8 
     9 public class FansheTest {
    10     @Test
    11     public void getInfo() {
    12         // 已知类,先获得实例对象
    13         User user = new User();
    14         // 获得class类对象
    15         Class c = user.getClass();
    16         // 利用 class类对象 得到属性数组
    17         Field[] fieldArr = c.getDeclaredFields();// 这个属性可以得到包括访问权限为
    18         for(Field field : fieldArr) {
    19             System.out.println("所有属性:" + field.getName());
    20         }
    21         Field[] fields = c.getFields();// 这个属性可以得到公有属性
    22         for(Field field : fields) {
    23             System.out.println("public属性:" + field.getName());
    24         }
    25         // 利用 class类对象 得到构造方法(每个类至少有一个默认的无参构造方法)
    26         Constructor[] constructorArr = c.getConstructors();
    27         System.out.println("有" + constructorArr.length + "个构造方法:");
    28         // 利用 class类对象 得到普通方法
    29         Method[] methodArr = c.getMethods();
    30         for(Method method : methodArr) {
    31             System.err.println("方法名:" + method.getName());
    32         }
    33 
    34     }
    35 
    36 }

    执行结果

     1 所有属性:id
     2 所有属性:name
     3 所有属性:age
     4 public属性:age
     5 有2个构造方法:
     6 方法名:toString
     7 方法名:getName
     8 方法名:getId
     9 方法名:setName
    10 方法名:getAge
    11 方法名:getSalay
    12 方法名:show
    13 方法名:setAge
    14 方法名:getSalary
    15 方法名:setId
    16 方法名:wait
    17 方法名:wait
    18 方法名:wait
    19 方法名:equals
    20 方法名:hashCode
    21 方法名:getClass
    22 方法名:notify
    23 方法名:notifyAll

    2)利用反射得到实例对象:已知类的路径信息:fanshe.User,

    分析无参:获得class类对象→通过class类对象获得无参构造方法→利用构造方法的newInstance方法创建出实例

    java代码

     1 // 反射获得实例对象
     2     @Test
     3     public void getObject() throws Exception {
     4         String className = "fanshe.User";
     5         // 得到class对象
     6         Class clazz = Class.forName(className);
     7 
     8         // 先得到无参构造方法对象,不是构造方法数组
     9         // Constructor constructor = clazz.getConstructor();
    10         // 利用构造方法对象的newInstance()创建出user实例
    11         // Object user = constructor.newInstance();//无参默认就有
    12 
    13         // 也可以用有参构造(前提是User里面要有这个构造方法)
    14         Constructor constructor = clazz.getConstructor(Integer.class, String.class, Integer.class);
    15         Object user = constructor.newInstance(1, "zm", 25);
    16         System.out.println(user);
    17     }

    执行结果

    1 重写toString
    2 id:1:用户名:zm

    3)利用反射调用类里面的普通方法

    3.1)利用反射调用无参无返回值的普通方法

    已知:已知类的路径信息:fanshe.User,要调用的方法名:show

    java代码

     1 // 利用反射调用普通方法
     2     @Test
     3     public void getMethod() {
     4         // 通过反射调用无参无返回值的普通方法
     5         // 已知含有类名字符串、方法名
     6         String className = "fanshe.User";
     7         String methodName = "show";
     8         try {
     9             // 通过含有类名字符串得到class对象
    10             Class clazz = Class.forName(className);
    11             // 通过class对象创建出有参的构造方法Constructor对象
    12             Constructor constructor = clazz.getConstructor(Integer.class, String.class,Integer.class);
    13             // 通过有参构造方法对象创建出实例对象
    14             Object user = constructor.newInstance(2, "hzm",26);
    15             // 通过class对象创建出方法Method对象
    16             // 提供方法名,要调用无参所以没有参数
    17             Method method = clazz.getMethod(methodName);
    18             // 调用指定的对象要执行指定的无参方法:
    19             // user是根据字符串实例出对象,method是根据方法名实例出的对象
    20             Object object = method.invoke(user);
    21             System.out.println(object);
    22         } catch(Exception e) {
    23             // TODO Auto-generated catch block
    24             e.printStackTrace();
    25         }
    26 
    27     }

    执行结果

    1 重写toString
    2 id:2:用户名:hzm

    3.2)利用反射调用返回值的普通方法(这个主要是展示另外一种创建实例对象的方式)

    java代码

     1 package fanshe;
     2 
     3 import java.lang.reflect.Method;
     4 
     5 import org.junit.Test;
     6 
     7 public class FansheTest {
     8 
     9     @Test
    10     public void getMethod1() {
    11         // 已知含有类名字符串、方法名
    12         String className = "fanshe.User";
    13         String methodName = "getSalay";
    14         try {
    15             // 通过含有类名字符串得到class对象
    16             Class clazz = Class.forName(className);
    17             // 创建实例对象的另外一种方式
    18             // 利用class对象直接clazz.newInstance()(默认无参构造)
    19             Object user = clazz.newInstance();
    20             // 通过class对象创建出方法Method对象
    21             // 提供方法名,要调用无参所以没有参数
    22             Method method = clazz.getMethod(methodName, int.class, double.class);
    23             // 调用指定的对象要执行指定的无参方法:
    24             // user是根据字符串实例出对象,method是根据方法名实例出的对象
    25             Object object = method.invoke(user, 3, 1000);
    26             System.out.println(object);
    27         } catch(Exception e) {
    28             // TODO Auto-generated catch block
    29             e.printStackTrace();
    30         }
    31 
    32     }
    33 }

    执行结果

    1 有参有返回值
    2 3000.0

    4)反射得到get/set方法

    java代码

     1 // 反射得到get/set方法
     2     @Test
     3     public void getOrSet() {
     4         // 首先用反射生成UserInfo实例对象,调用的是有参的构造方法
     5 
     6         // 已知含有类名字符串、方法名
     7         String className = "fanshe.User";
     8         try {
     9             // 通过含有类名字符串得到class对象
    10             Class clazz = Class.forName(className);
    11             // 得到类名:User
    12             System.out.println(clazz.getSimpleName());
    13             // 通过class对象创建出有参构造方法
    14             Constructor con = clazz.getConstructor(Integer.class, String.class,Integer.class);
    15             Object user = con.newInstance(3, "hong",27);
    16             // 通过get方法显示出所有属性的值
    17             // 得到所有属性对象
    18             Field[] fields = clazz.getDeclaredFields();
    19             // 遍历fields
    20             for(Field field : fields) {
    21                 // PropertyDescriptor(属性名,class对象)
    22                 PropertyDescriptor pd = new PropertyDescriptor(field.getName(), clazz);
    23                 // 利用pd对象得到get对象方法getReadMethod()
    24                 Method getMethod = pd.getReadMethod();
    25                 System.out.println(getMethod);
    26                 // invoke(实例对象)可以获得属性值
    27                 System.out.println("属性名:" + field.getName() + " 属性值:" + getMethod.invoke(user));
    28             }
    29         } catch(Exception e) {
    30 
    31         }
    32 
    33     }

    执行结果

    1 User
    2 public java.lang.Integer fanshe.User.getId()
    3 属性名:id 属性值:3
    4 public java.lang.String fanshe.User.getName()
    5 属性名:name 属性值:hong
    6 public java.lang.Integer fanshe.User.getAge()
    7 属性名:age 属性值:null

    反射的应用 

    处理数据新增:实现通用版本的新增处理,默认约定:类名与表名一致,属性与列名一致,最大多是大小写区别,默认自增列为id,仅适用于类与表结构完全一致;

     1 package fanshe;
     2 
     3 import java.beans.PropertyDescriptor;
     4 import java.lang.reflect.Field;
     5 import java.lang.reflect.Method;
     6 
     7 public class TransaSql {
     8     /***
     9      * 实现通用版本的新增处理 该方法使用注意事项:默认的约定:类名与表名一致,最多就是大小写区别, 属性名与列名一致,自增列默认名称为id
    10      * 目前仅适用于类与表结构完全一致的情况
    11      * 
    12      * @param obj——就是要保存到数据库的对象
    13      * @return >0表示新增成功,<=0表示没有新增成功
    14      * 
    15      */
    16     public static int doAdd(Object object) {
    17         int i = 0;
    18         // 关键点是sql语句的构造
    19         // 构造的关键点1表名/2列名/3值
    20         Class clazz = object.getClass();
    21         // 得到表名
    22         String tableName = clazz.getSimpleName();
    23         String sql = " insert into " + tableName + "(";
    24         String colStr = "";// 保存列名
    25         String valStr = "";// 保存值
    26         // 接下来遍历得到列名,就先要得到属性名
    27         Field[] fields = clazz.getDeclaredFields();
    28         Object[] objArr = new Object[fields.length - 1];
    29         // 增加一个变量用来指示数组下标
    30         int arrIndex = 0;
    31         try {
    32             for(Field field : fields) {
    33                 String colName = field.getName();// 保存字段名
    34                 if("id".equals(colName.toLowerCase()) == false) {
    35                     colStr += colName + ",";// 构造列名
    36                     valStr += "?,";// 构造值
    37                     // 得到相应的值
    38                     PropertyDescriptor pd = new PropertyDescriptor(field.getName(), clazz);
    39                     // 利用pd对象得到get对象
    40                     Method getMethod = pd.getReadMethod();
    41                     objArr[arrIndex] = getMethod.invoke(object);
    42                     arrIndex++;
    43                 }
    44             }
    45             // 遍历完毕,构造出sql
    46             colStr = colStr.substring(0, colStr.length() - 1);
    47             valStr = valStr.substring(0, valStr.length() - 1);
    48             sql += colStr + ")";
    49             sql += " values(" + valStr + ")";
    50             System.out.println(sql);
    51             // 省略数据jdbc部分
    52         } catch(Exception e) {
    53             // TODO Auto-generated catch block
    54             e.printStackTrace();
    55         }
    56 
    57         return i;
    58     }
    59 }
    作者:howtosay
             
    放牛娃的个人笔记整理,每天记录一点点,进步一点点
  • 相关阅读:
    Codeforces Round #274 (Div. 2)
    codeforces 477C
    ZOJ 3822 Domination
    Codeforces Round #271 (Div. 2)
    进程
    线程
    udp和tcp特点 实现文件上传
    面向对象补1
    socket基本语法和粘包
    网络编程
  • 原文地址:https://www.cnblogs.com/hongzm/p/8276994.html
Copyright © 2011-2022 走看看