zoukankan      html  css  js  c++  java
  • java学习--基础知识进阶第十三天--反射机制的概述和字节码对象的获取方式、反射操作构造方法、成员方法、成员属性、JavaBean的概述&BeanUtils的使用、自定义BeanUtils工具类

    今日内容介绍

    u 反射机制的概述和字节码对象的获取方式

    u 反射操作构造方法、成员方法、成员属性

    u JavaBean的概述&BeanUtils的使用

    u 自定义BeanUtils工具类

    第1章 反射机制概述、字节码对象的获取方式、反射操作构造方法、成员方法、成员属性

    1.1 反射机制的概述和字节码对象的获取方式

    1.1.1 反射介绍

    JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法,对于任意一个对象,都能够调用它的任意一个方法
            这种动态获取的以及动态调用对象的方法的功能称为java语言的反射机制.

      简单来说, 就可以把.class文件比做动物的尸体, 而反射技术就是对尸体的一种解剖.
      通过反射技术, 我们可以拿到该字节码文件中所有的东西, 例如成员变量, 成员方法, 构造方法, 而且还包括私有

    1.1.2 字节码文件获取的三种方式

    1. 对象名.getCalss(); // 此方法来自于Object对象已经存在的情况下, 可以使用这种方式

    2. 类名.class // 类名.class这是一个静态的属性, 只要知道类名, 就可以获取 

    3. Class.forName(“com.itheima_01.Student”);// 通过Class类中的静态方法, 指定字符串, 该字符串是类的全类名(包名+类名)

    // 此处将会抛出异常都系 ClassNotFoundException 防止传入错误的类名

    1.1.3 案例代码

    package com.itheima_01;

    /*

     * 反射:

     * 在运行时,我们可以获取任意一个类的所有方法和属性(所有包括私有的)

     * 在运行时,让我们调用任意一个对象的所有方法和属性

     *

     * 反射的前提:

     * 要获取类的对象(Class对象)(字节码对象)

     *

     */

    public class ReflectDemo {

    public static void main(String[] args) throws ClassNotFoundException {

    //三种获取字节码对象的方式,推荐第三种

    * // 通过Object的getClass()方法获取,必须要有对象,弊端是需要有对象

    Student s = new Student();//创建对象

    Class clazz = s.getClass();

    // 通过类名.class获取字节码对象

    Class clazz2 = Student.class;//弊端是需要有类名

    * //Class类里面找forName

    // static Class<?> forName(String className)返回值class对象

    Class clazz3 = Class.forName("com.itheima_01.Student");//需要抛出异常:ClassNotFoundException类名可能找不到 推荐这种方式获取

    System.out.println(clazz == clazz2);//true

    System.out.println(clazz == clazz3);//true

    System.out.println(clazz);//class com.itheima_01.Student

    }

    }

    1.1.4 问题: 字节码对象是用来描述什么的?

    用来描述.class文件的.
             面向对象阶段的时候讲过java中描述事物都是通过类的形式
             而字节码文件也可以看做为一种事物, 如何描述这种事物? 那就看看这个事物是由什么组成的了

                1. 成员变量
                2. 成员方法
                3. 构造方法:构造方法的用处:创建对象,给对象初始化

    1.2 反射操作构造方法             

    1.2.1 通过获取的构造创建对象

    步骤:

    1.获得Class对象

    2获得构造

    3.通过构造对象获得实例化对象

    package com.itheima_01;

    import java.lang.reflect.Constructor;

    import java.lang.reflect.InvocationTargetException;

    /*

     * 通过反射获取构造方法并使用(构造方法的作业创建对象时候调用,给成员变量初始化)

     * Constructor<?>[] getConstructors()  //以数组的形式返回所有的构造方法(以对象的形式返回)

     * Constructor<T> getConstructor(Class<?>... parameterTypes)  //不给参数就是无参构造,给了参数就是有参构造

    * Class<?>...可变参数是JDK1.5的新特性

    * 格式:数据类型...变量名

    * 位置:只能在方法的形式参数的列表中

    * 特点:可变参数本质上是个数组

    * 可变参数可以传递0~n个参数,也就是说可以传递参数,也可以不传递参数,

    * 一个方法中如果有多个形式参数,而且包含可变参数,那么可变参数一定要位于最后一个,一个方法中的可变参数只能有一个(因为要位于最后一个),

    *

     * T newInstance()   //这个方法不能传递参数,只能返回无参构造

     *

     *Constructor:

     * T newInstance(Object... initargs)  这个方法属于Constructor类

    */

    public class ReflectDemo2 {

    public static void main(String[] args) throws ReflectiveOperationException {

    Class clazz = Class.forName("com.itheima_01.Student");

    //method(clazz);

    //Constructor<T> getConstructor(Class<?>... parameterTypes)

    //method2(clazz);

    //method3(clazz);

    Object obj = clazz.newInstance();//使用的就是无参构造,便捷创建对象的方式,

    System.out.println(obj);

    }

    private static void method3(Class clazz)

    throws NoSuchMethodException, InstantiationException, IllegalAccessException, InvocationTargetException {

    Constructor c = clazz.getConstructor(String.class,int.class);//获取有参构造,参数1类型为String,参数2类型为int。参数1和参数2都是要获取字节码对象

    System.out.println(c);

    Object obj = c.newInstance("lisi",30);//调用有参构造。获得的是有参构造,那么newInstance()就需要给和上面类型一样的参数。

    System.out.println(obj);

    }

    private static void method2(Class clazz)

    throws NoSuchMethodException, InstantiationException, IllegalAccessException, InvocationTargetException {

    //Constructor<T> getConstructor(Class<?>... parameterTypes) (可以给参数,也可以不给参数,不给参数就获得是无参构造)

    * Constructor c = clazz.getConstructor();//获取无参构造

    System.out.println(c);

    Object obj = c.newInstance();//调用无参构造。获得的是无参构造,那么newInstance()也不用给参数。

    System.out.println(obj);

    }

    private static void method(Class clazz) {

    //Constructor<?>[] getConstructors() :获取所有public修饰的构造方法 

    Constructor[] cs = clazz.getConstructors();

    for (int i = 0; i < cs.length; i++) {

    System.out.println(cs[i]);

    }

    }

    }

    1.2.2 问题: 直接通过Class类中的newInstance()和获取getConstructor()有什么区别?

    newInstance()方法, 只能通过空参的构造方法创建对象

    getConstructor(Class<T>… parameterTypes)方法, 方法接受一个可变参数, 可以根据传入的类型来匹配对应的构造方法

    总结

    Constructor<?>[] getConstructors()
                     Constructor<T> getConstructor(Class<?>... parameterTypes) 
                
                     方法1: 获取该类中所有的构造方法, 返回的是一个数组
                     方法2: 方法接受一个可变参数, 可以根据传入的类型, 来匹配对应的构造方法

    1.3 反射操作公共成员变量

    1.3.1 反射public成员变量(字段) 

    通过反射运行public变量流程

    1. 通过反射获取该类的字节码对象

    Class clazz = Class.forName("com.heima.Person");

    2. 创建该类对象

    Object p = clazz.newInstance();

    3. 获取该类中需要操作的字段(成员变量)

    getField(String name) --> 方法传入字段的名称.

    注意: 此方法只能获取公共的字段

    Field f = clazz.getField("age");

    4. 通过字段对象中的方法修改属性值

    void set(Object obj, Object value)  --> 参数1): 要修改那个对象中的字段, 参数2): 将字段修改为什么值.

    f.set(p, 23);

    1.3.2 案例代码

    package com.itheima_01;

    import java.lang.reflect.Field;

    /*

     * 通过反射获取成员变量并使用  首先需要有字节码对象,在获取对象

     * Field[] getFields() :获取公共的成员变量

    * Field[] getDeclaredFields() 获取所有的成员变量

     *

     *

     * Field getField(String name)  //根据字段名称获取公共的字段对象

     * Field getDeclaredField(String name)根据字段名称获取所有的字段对象 

     *

     * Field类的方法:

     * Object get(Object obj)  通过对象获取成员变量的值

     * void set(Object obj, Object value) //通过成员变量对象,修改指定对象为指定的值

    以前的步骤:

    先给成员变量赋值-->再通过成员变量对象,修改制定对象为指定的值-->在获取成员变量对象???????

    通过反射获取成员变量并使用的步骤:

     先获取成员变量对象-->通过成员变量对象,修改指定对象为指定的值-->通过对象获取成员变量的值

     */

    public class ReflectDemo3 {

    public static void main(String[] args) throws ReflectiveOperationException {

    //获取学生类的字节码对象

    Class clazz = Class.forName("com.itheima_01.Student");

    //获取学生类的对象(三种方式)

    Object stu = clazz.newInstance();

    System.out.println(stu);//Student [name = zhangsan, age = 18]

    //Field getField(String name)  :根据字段名称获取公共的字段对象

    Field f = clazz.getField("age");//获取成员变量对象,可以去赋值

    //System.out.println(f);

    //void set(Object obj, Object value)

    f.set(stu,28);//通过成员变量对象,修改指定为指定的值

    //Object get(Object obj)

    Object age = f.get(stu);//通过对象获取成员变量的值

    System.out.println(age);//28

    System.out.println(stu);//Student [name = zhangsan, age = 28]

    }

    private static void method(Class clazz) {

    //Field[] getFields()  :获取公共的成员变量

    Field[] fs = clazz.getFields();

    for (int i = 0; i < fs.length; i++) {

    System.out.println(fs[i]);

    }

    System.out.println("----------");

    //getDeclaredFields()  :获取所有的成员变量

    Field[] fs2 = clazz.getDeclaredFields();

    for (int i = 0; i < fs2.length; i++) {

    System.out.println(fs2[i]);

    }

    }

    }

    1.3.3 方法总结

                 通过反射获取成员变量并使用  
                         Field[] getFields()              --> 返回该类所有(公共)的字段
                         Field getField(String name)      --> 返回指定名称字段
                
                        Field[] getDeclaredFields()      --> 暴力反射获取所有字段(包括私有) 
                        Field getDeclaredField(String name) --> 暴力反射获取指定名称字段

                        ---------------马上讲-----------------

                  Field:
                          Object get(Object obj)          --> Field对象调用, 返回传入对象的具体字段
                          void set(Object obj, Object value) -->  Field对象调用
                                                            参数1: 要修改的对象
                                                            参数2: 将此对象的字段修改为什么值.

    1.4 反射操作私有成员变量

    1.4.1 反射private成员变量(字段)

    反射private属性执行流程

    1. 获取学生类字节码对象
    2. 获取学生对象
    3. 通过getDeclaredField方法获取私有字段
    4. 通过setAccessible让jvm不检查权限
    5. 通过set方法设置对象为具体的值

    1.4.2 案例代码

    package com.itheima_01;

    import java.lang.reflect.Field;

    /*

     * 通过反射获取私有成员变量并使用  

     * Field[] getDeclaredFields()  

     * Field getDeclaredField(String name) 可以看见私有的,但是不能访问

     */

    public class ReflectDemo4 {

    public static void main(String[] args) throws ReflectiveOperationException {

    //获取学生类的字节码对象

    Class clazz = Class.forName("com.itheima_01.Student");

    //获取学生对象

    Object stu = clazz.newInstance();

    //获取私有的字段对象

    Field f = clazz.getDeclaredField("name");

    //Object name = f.get(stu);

    //System.out.println(name);//访问失败,不能访问私有的

    f.setAccessible(true);//设置反射时取消Java的访问检查(设置为true时取消),暴力访问

    //System.out.println(f);

    f.set(stu, "lisi");

    Object name = f.get(stu);

    System.out.println(name);//访问成功

    }

    }

    1.4.3 方法总结

                   Field[] getDeclaredFields()      --> 暴力反射获取所有字段(包括私有) 
                   Field getDeclaredField(String name) --> 暴力反射获取指定名称字段
                   void setAccessible(boolean flag) --> 让jvm不检查权限

    1.5 通过反射获取成员方法并使用

    1.5.1 反射获取普通成员方法

    反射public方法执行流程

    1. 获取学生类字节码对象
    2. 反射手段创建学生对象
    3. 调用getMethod方法获取Method对象, 方法形参接受方法的名字
    4. 调用Method方法中的invoke()将方法运行

    1.5.2 案例代码

    package com.itheima_01;

    import java.lang.reflect.InvocationTargetException;

    import java.lang.reflect.Method;

    /*

     * 通过反射获取成员方法并使用  

     * Method getMethod(String name, Class<?>... parameterTypes)  

     * Method类的方法:

     * Object invoke(Object obj, Object... args)  //调用方法

     *

     */

    public class ReflectDemo5 {

    public static void main(String[] args) throws ReflectiveOperationException {

    //获取学生类的字节码对象

    Class clazz = Class.forName("com.itheima_01.Student");

    //获取学生类的对象

    Object stu = clazz.newInstance();

    //获取无参有返回值的方法

    Method m = clazz.getMethod("getName");//没有传递参数进去

    Object obj = m.invoke(stu);//不用传递参数进去

    System.out.println(obj);//zhangsan

    }

    private static void method2(Class clazz, Object stu)

    throws NoSuchMethodException, IllegalAccessException, InvocationTargetException {

    //获取有参无返回值的方法

    * //Method getMethod(String name, Class<?>... parameterTypes)

    Method m = clazz.getMethod("setName", String.class);//传递了参数(String.class)进去

    * System.out.println(stu);//zhangsan

    m.invoke(stu, "lisi");//传递了参数(lisi)进去了

    System.out.println(stu);//lisi

    }

    private static void method(Class clazz, Object stu)

    throws NoSuchMethodException, IllegalAccessException, InvocationTargetException {

    //获取无参无返回值的方法

    ////Method getMethod(String name, Class<?>... parameterTypes)

    Method m = clazz.getMethod("method");//没有给参数

    m.invoke(stu);//不用传递参数进去

    }

    }

    1.5.3 方法总结

    Class:
        Method getMethod(String name, Class<?>... parameterTypes)  
                // 此方法由字节码对象调用
                // 参数1: 要反射的方法名称
                // 参数2: 此方法需要接受的参数类型(注意,传入的都是字节码)
    Method:
        Object invoke(Object obj, Object... args)  
                // 方法由Method对象调用
                // 参数1: 要由那个对象调用方法
                // 参数2: 方法需要的具体实参(实际参数)

    1.5.4 问题: 私有的成员方法怎么玩?

    // 获取字节码对象

    Class clazz = Class.forName("com.heima.Student");

    // 创建学生对象

    Object stu = clazz.newInstance();

    // 暴力反射获取方法

    Method method = clazz.getDeclaredMethod("method");

    // 让jvm不检查权限

    method.setAccessible(true);

    // 执行方法

    method.invoke(stu);

    第2章 JavaBean的概述、BeanUtils的使用

    2.1 JavaBean的概述和规范

    JavaBean(标准类)的概述:

    将需要操作的多个属性封装成JavaBean, 简单来说就是用于封装数据的

    规范:

    类使用公共进行修饰

    提供私有修饰的成员变量

    为成员变量提供公共getter和setter方法

    提供公共无参的构造

    2.1.1 实例代码

    package com.itheima_02;

    import java.io.Serializable;

    /*创建一个标准的Person类,在主方法中穿件对象并使用

     *

     * JavaBean(标准类):(主要作用)用于封装数据

     * 书写规范:

    * 类使用公共进行修饰

     * 提供私有修饰的成员变量

     * 为成员变量提供公共getter和setter方法 (提供公共的访问方式)

     * 提供公共无参的构造 (只提供无参的即可,方便对成员变量进行初始化)

     * 实现序列号接口 (可以被持久化,可以被写到文件中)

     *

     */

    public class Person implements Serializable {//实现序列化的接口

    private static final long serialVersionUID = 1049712678750452511L;//给一个序列化的ID

    private String name;

    private int age;

    private String gender;

    public Person() {

    super();

    // TODO Auto-generated constructor stub

    }

    public String getName() {

    return name;

    }

    public void setName(String name) {

    this.name = name;

    }

    public int getAge() {

    return age;

    }

    public void setAge(int age) {

    this.age = age;

    }

    public String getGender() {

    return gender;

    }

    public void setGender(String gender) {

    this.gender = gender;

    }

    @Override

    public String toString() {

    return "Person [name=" + name + ", age=" + age + ", gender=" + gender + "]";

    }

    }

    2.2 BeanUtils的概述

    BeanUtils(为JavaBean服务)的由来

    Utils(工具包)

    主要功能就是为了简化JavaBean封装数据的操作

    之前我们使用的类都是来自Java编写好的源代码

    而这个BeanUtils却是一个叫做Apache(阿帕奇)的组织编写.

    那么这个组织编写的代码当中, 有一个系列可以很方便的提高我们今后的开发效率.这个系列为Commons,              

     BeanUtils就是其中之一

    Ant

    Hadoop

    Tomcat

    Hive

    Maven

    Commens

    bin是字节码文件

    Src是源文件

    2.2.1

    2.2.2 准备工作

    1. 导入两个jar包

    commons-beanutils-1.8.3.jar

    commons-logging-1.1.1.jar

    2. 将jar包Build path 配置到当前的classpath环境变量中

    2.3 BeanUtils的常用方法

    static void    setProperty(Object bean, String name, Object value) 
    static String getProperty(Object bean, String name) 
    static void    populate(Object bean, Map properties) 

    setProperty  用来给对象中的属性赋值(了解)

    参数1: 需要设置属性的对象

    参数2: 需要修改的属性名称

    参数3: 需要修改的具体元素

    getProperty 用来获取对象中的属性(了解)

    参数1: 要获取的javaBean对象

    参数2: 对象中的哪个属性

    Populate 用来给对象中的属性赋值(掌握)

    参数1: 要设置属性的对象

    参数2: 将属性以Map集合的形式传入

    Key : 属性的名称

    Value:  属性具体的值

    2.3.1 实例代码

    package com.itheima_02;

    import java.lang.reflect.InvocationTargetException;

    import java.util.HashMap;

    import java.util.Map;

    import org.apache.commons.beanutils.BeanUtils;

    /*用那个类,那个文档,问API

     * BeanUtils:Apache commons提供的一个组件,主要功能就是为了简化JavaBean封装数据的操作

     * static void setProperty(Object bean, String name, Object value)

     * static String getProperty(Object bean, String name)//

     * static void populate(Object bean, Map properties)//

     *

     * 注意:BeanUtils的setProperty和getProperty方法底层并不是直接操作成员变量,而是操作和成员变量名有关的get和set方法

     */

    public class BeanUtilsDemo {

    public static void main(String[] args) throws ReflectiveOperationException  {

    //static void populate(Object bean, Map properties)

    Person p = new Person();

    Map<String,Object> map = new HashMap<String,Object>();

    map.put("name", "lisi");

    map.put("age", 18);

    map.put("gender", "male");

    BeanUtils.populate(p,map);

    System.out.println(p);

    }

    private static void method() throws IllegalAccessException, InvocationTargetException, NoSuchMethodException {

    Person p = new Person();//JavaBean

    //System.out.println(p);

    //static void setProperty(Object bean, String name, Object value) :给JavaBean对象的成员变量进行赋值

    BeanUtils.setProperty(p, "name", "zhangsan");

    //BeanUtils.setProperty(p, "age", 18);

    //System.out.println(p);

    //static String getProperty(Object bean, String name)

    String name = BeanUtils.getProperty(p, "name");

    System.out.println(name);

    }

    }

    2.3.2 方法总结

    三个方法底层是通过反射实现, 而且反射操作的是setXxx方法和getXxx方法.

    所以编写JavaBean的时候一定要注意格式

    2.4 自定义BeanUtils的赋值和获取方法实现.

    2.4.1 功能分析

    定义MyBeanUtils工具类, 实现与BeanUtils相同的功能

    public static void setProperty(Object bean,String name,Object value)

    // 设置任意对象的, 任意属性, 为任意的值

    public static String getProperty(Object bean,String name)

    // 获取任意对象的任意属性

    public static void populate(Object bean,Map map)

    // 修改任意对象中的属性, 为传入Map集合中的键和值 

    Ps: 下个知识点

    2.4.2 实例代码

    package com.itheima_03;

    import java.lang.reflect.Field;

    public class MyBeanUtils {

    private MyBeanUtils() {}

    //public static void setProperty(Object bean,String name,Object value)

    public static void setProperty(Object bean,String name,Object value) throws ReflectiveOperationException {

    //根据JavaBean对象获取对应的字节码对象

    Class clazz = bean.getClass();

    //根据字节码对象获取对应的Field对象

    Field f = clazz.getDeclaredField(name);

    //设置权限,让虚拟机不进行访问的检查

    f.setAccessible(true);

    //赋值

    f.set(bean, value);

    }

    //public static String getProperty(Object bean,String name)

    public static String getProperty(Object bean,String name) throws ReflectiveOperationException {

    Class clazz = bean.getClass();

    Field f = clazz.getDeclaredField(name);

    f.setAccessible(true);

    Object obj = f.get(bean);

    return obj.toString();

    }

    }

    2.5 自定义BeanUtils的populate方法实现

    2.5.1 功能分析

    public static void populate(Object bean,Map map)

    // 修改任意对象中的属性, 为传入Map集合中的键和值 

    思路:

    1. 获取传入对象的字节码对象

    2. 获取map集合中所有的键和值

    3. 调用Class中的getDeclaredField()方法将每一个键传入, 得到Field对象

    4. 通过Field对象中的set方法赋值

    5. Try catch捕获getDeclaredField方法可能发生的异常.(为了方式传入错误的值)

    2.5.2 实例代码

    //public static void populate(Object bean,Map map)

    public static void populate(Object bean,Map map) throws ReflectiveOperationException {

    //通过JavaBean对象来获取对应的字节码对象

    Class clazz = bean.getClass();

    //获取Map中所有的key

    Set keys = map.keySet();

    for (Object key : keys) {

    try {

    //根据key来获取对应的Field对象

    Field f = clazz.getDeclaredField(key.toString());

    //根据key来获取Map中对应的value

    Object value = map.get(key);

    f.setAccessible(true);

    f.set(bean, value);

    } catch(NoSuchFieldException e) {

    //e.printStackTrace();

    }

    }

    }

  • 相关阅读:
    SharePoint 2010 User Profile Sync Service自动停止
    如何区别多个svchost.exe?
    Log Parser分析IIS log的一个简单例子
    Timeout expired. The timeout period elapsed prior to obtaining a connection from the pool. This may have occurred because all pooled connections were in use and max pool size was reached.
    Windows中右键点击文件夹, 结果找不到共享选项卡, 怎么办?
    介绍SOS中的SaveModule命令
    SharePoint中Draft版本的文档不会收到document added的Alert Email
    和我一起学Windows Workflow Foundation(1)创建和调试一个WF实例
    门户网站
    C#基础—— check、lock、using语句归纳
  • 原文地址:https://www.cnblogs.com/bigorangehasc/p/8639735.html
Copyright © 2011-2022 走看看