java反射

java反射

一、反射的概述

反射是什么?
反射就是在运行时才知道要操作的类是什么,并且可以在运行时获取类的完整构造,并获得类的信息。
反射机制
JAVA反射机制是,对于任意一个对象,都能够调用它的任意一个方法和属性;这种动态获取的信息以及动态调用对象的方法的功能称为java语言的反射机制。
下面我们来说说反射原理
反射的原理是在于Class对象,那么Class对象是什么呢?Class对象是JAVA虚拟机将class文件读入内存,并为之创建一个Class对象。而在这个对象里边它包含着许多信息,例如一个类的成员变量、方法、构造方法、包等等信息。接下来就是利用反射技术对一个类进行解剖,把个个组成部分映射成一个个对象。
下图呢是class文件读入内存或者说是加载的过程,
在这里插入图片描述
“java的类在第一次需要创建类的实例(对象)时被加载”这个说法不全面,应该是java中类被使用就会被加载到内存
其中这个Class对象很特殊。我们先了解一下这个Class类。

二、Class类

Class类在API文档中有具体的描述,大家可以去看看,我在这大致的说一下,
Class 类的实例表示正在运行的 Java 应用程序中的类和接口。也就是jvm中有N多的实例每个类都有该Class对象。(包括基本数据类型)
Class 没有公共构造方法。Class 对象是在加载类时由 Java 虚拟机自动构造的。也就是这不需要我们自己去处理创建,JVM已经帮我们创建好了。
下图是API文档中的Class类的方法的一部分,非常多,我在这举例几个我经常用的。
在这里插入图片描述

三、反射的使用

(在这啰嗦的说一句,要想解剖一个类,必须先要获取到该类的字节码文件对象。而解剖使用的就是Class类中的方法.所以先要获取到每一个字节码文件对应的Class类型的对象)

1、Class对象有三种获取方式

1.1 Object ——> getClass();
1.2 任何数据类型(包括基本数据类型)都有一个“静态”的class属性
1.3 通过Class类的静态方法:forName(String className)

先写一个Student类,然后如下

package com.jiazhong.fanshe;

/**
 * @author:王先生
 * @date:公元2019/8/26 22:41
 */
public class main {
    public static void main(String[] args) {
        //第一种方式获取Class对象
        Student stu = new Student();//这个new,它产生了俩个对象,一个是Student对象,另一个是Class对象
        Class stuClass = stu.getClass();//获取Class对象
        System.out.println(stuClass);

        //第二种方式获取Class对象
        Class stuClass1 = Student.class;
        System.out.println(stuClass1 == stuClass);//判断俩种获取的结果是否一致

        //第三种方式获取Class对象,有空指针异常,需要捕捉或者抛出
        try {
            Class stuClass2 = Class.forName("com.jiazhong.fanshe.Student");//注意此字符串必须是真实路径,就是带包名的类路径,包名.类名
            System.out.println(stuClass2 == stuClass1);//判断三种获取的结果是否一致
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }

    }
}


注意:在运行期间,一个类,只有一个Class对象产生

看到这,有些小伙伴肯定想问三种方法用哪一种好呢或者说哪一种常用呢,就我目前阶段,经常使用的是第二种。(我目前略懂一点)

2、通过反射获取构造方法并使用

Student类:

package com.jiazhong.fanshe;

/**
 * @author:王先生
 * @date:公元2019/8/26 22:40
 */
public class Student {
    private String id;
    private String name;
    private float score;

    public Student(String id, float score) {
        System.out.println("有俩个参数的构造方法");
    }

    public Student(String id) {
        System.out.println("公有的,有一个参数的构造方法");
    }

    public Student() {
        System.out.println("无参数的构造方法");
    }

    private Student(float score) {
        System.out.println("私有的构造方法 分数:" + score);
    }

    @Override
    public String toString() {
        return "Student{" +
                "id='" + id + '\'' +
                ", name='" + name + '\'' +
                ", score='" + score + '\'' +
                '}';
    }
}

测试类:

package com.jiazhong.fanshe;

import java.lang.reflect.Constructor;

/**
 * @author:王先生
 * @date:公元2019/8/26 23:37
 */
public class main1 {
    public static void main(String[] args) {
        //加载Class对象
        Class stuClass = Student.class;
        //获取所有的构造方法
        System.out.println("************所有的构造方法(包括:私有、受保护、默认、公有)***************");
        Constructor[] con = stuClass.getDeclaredConstructors();
        for (Constructor c : con) {
            System.out.println(c);
        }
        //获取所有的共有的构造方法
        System.out.println("**********************所有的公有的构造方法*********************************");
        con = stuClass.getConstructors();
        for (Constructor c : con) {
            System.out.println(c);
        }
        System.out.println("*****************获取公有、无参的构造方法*******************************");
        try {
            //获取指定参数类型的构造方法
            Constructor con1 = stuClass.getConstructor(null);
            //1.因为是无参的构造方法所以类型是一个null,不写也可以:这里需要的是一个参数的类型,切记是类型
            //2.返回的是描述这个无参构造函数的类对象。
            System.out.println(con1);
            //获取有参数类型的格式是 数据类型.class
            System.out.println("*****************获取公有、有参的构造方法******************************");
            con1 = stuClass.getConstructor(String.class);
            System.out.println(con1);
            //调用构造方法 返回值是Object类型
            Object obj = con1.newInstance("1");
            System.out.println(obj);//这是输出了一个空对象
            con1 = stuClass.getDeclaredConstructor(float.class);
            //调用私有的构造方法
            con1.setAccessible(true);//暴力访问(忽略掉访问修饰符)
            con1.newInstance(66);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

结果:在这里插入图片描述
注意:Class类通过反射实例化类对象的时候,只能够调用类中的无参构造。如果现在的类中没有无参构造则无法使用Class类调用,只能够通过明确的构造调用实例化处理。
比如说上面所写到的Class对象stuClass,它可以调用自己类的newInstance方法,但调用的是无参构造;需要调用有参构造,就需要用到Constructor类了

3、获取成员变量并调用

Student类:

package com.jiazhong.fanshe;

/**
 * @author:王先生
 * @date:公元2019/8/30 18:16
 */
public class Student1 {
    public String name;
    protected String id;
    char sex;
    private String phone;

    @Override
    public String toString() {
        return "Student1{" +
                "name='" + name + '\'' +
                ", id='" + id + '\'' +
                ", sex=" + sex +
                ", phone='" + phone + '\'' +
                '}';
    }
}

测试类:

package com.jiazhong.fanshe;

import java.lang.reflect.Field;

/**
 * @author:王先生
 * @date:公元2019/8/30 18:18
 */
public class main2 {
    public static void main(String[] args) {
        Class stuClass = Student1.class;
        //获取所有的字段
        System.out.println("*****************获取所有的字段*******************************");
        Field[] field = stuClass.getDeclaredFields();
        for (Field f : field) {
            System.out.println(f);
        }
        System.out.println("*****************获取所有的公有的字段*******************************");
        //获取所有的共有的字段
        field = stuClass.getFields();
        for (Field f : field) {
            System.out.println(f);
        }
        System.out.println("*****************获取公有的name字段并赋值*******************************");
        try {
            Field f = stuClass.getField("name");
            System.out.println(f);
            //为字段赋值,首先得产生一个对象
            Object obj = stuClass.getConstructor().newInstance();//相当于是  Student stu=new Student();
            //第一个参数:要传入设置的对象,第二个参数:要传入实参
            f.set(obj, "王先生");//相当于是 obj.name="王先生";
            //验证
            System.out.println(obj);
            Student1 stu = (Student1) obj;
            System.out.println("姓名:" + stu.name);
            System.out.println("*****************获取默认的sex字段并赋值*****************************");
            f = stuClass.getDeclaredField("sex");
            System.out.println(f);
            f.set(obj, '男');
            System.out.println(obj);
            System.out.println("*****************获取私有的phone字段并赋值***************************");
            f = stuClass.getDeclaredField("phone");
            System.out.println(f);
            //暴力反射,解除私有限定
            f.setAccessible(true);
            f.set(obj, "11111");
            System.out.println(obj);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

结果:
在这里插入图片描述

4、获取成员方法并调用

Student类:

package com.jiazhong.fanshe;

/**
 * @author:王先生
 * @date:公元2019/8/30 23:17
 */
public class Student3 {
    public void show1(String name) {
        System.out.println("调用了公有的,String参数类型的方法show1(),name=" + name);
    }

    protected void show2() {
        System.out.println("调用了受保护的无参方法show2()");
    }

    void show3() {
        System.out.println("调用了默认的无参的方法show3()");
    }

    private void show4(int age) {
        System.out.println("调用了私有的,int型参数类型的方法show4()");
    }
}

测试类:

package com.jiazhong.fanshe;

import java.lang.reflect.Method;

/**
 * @author:王先生
 * @date:公元2019/8/30 23:18
 */
public class main3 {
    public static void main(String[] args) {
        Class stuClass = Student3.class;
        System.out.println("*****************获取所有的方法*******************************");
        //获取这个类声明的所有方法
        Method[] method = stuClass.getDeclaredMethods();
        for (Method m : method) {
            System.out.println(m);
        }
        System.out.println("*****************获取所有的公有的方法*******************************");
        //不仅会获得这个类的公有的方法,而且也会获得从父类(Object类)继承的方法
        method = stuClass.getMethods();
        for (Method m : method) {
            System.out.println(m);
        }
        System.out.println("*****************获取公有的有参的方法并调用*******************************");
        try {
            //需要传入两个参数,第一个是调用的方法名称,第二个是方法的形参类型,切记是类型。
            Method m = stuClass.getDeclaredMethod("show1", String.class);
            Object obj = stuClass.getConstructor().newInstance();
            System.out.println(m);
            //需要传入俩个参数,第一个是方法的调用者(此类对象),第二个是实参
            m.invoke(obj, "王先生");
            System.out.println("*****************获取默认的无参的方法并调用****************************");
            m = stuClass.getDeclaredMethod("show3");
            m.invoke(obj);
            System.out.println("*****************获取私有的有参的方法并调用****************************");
            m = stuClass.getDeclaredMethod("show4", int.class);
            //暴力访问(忽略掉访问属性)
            m.setAccessible(true);
            m.invoke(obj, 20);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

结果:
在这里插入图片描述

4、反射的应用

例1  先写一个mysql数据库的增删查改通用类吧。(注意:实体类的字段和数据库中的字段应保持一致)

package com.jiazhong.fanshe;

import java.lang.reflect.Field;
import java.sql.*;
import java.util.ArrayList;
import java.util.List;


/**
 * @author:王先生
 * @date:公元2019/8/31 0:15
 */
public class Ty {
    private String driver = "com.mysql.jdbc.Driver";
    private String url = "jdbc:mysql://localhost:3306/jz";
    private String root = "root";
    private String pwd = "wang";
    private Connection con;
    private PreparedStatement psmt;
    private ResultSet rs;

    //获取连接对象
    public Connection getCon() {
        try {
            //注册驱动
            Class.forName(driver);
            //获取连接
            con = DriverManager.getConnection(url, root, pwd);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return con;
    }

    //增加,删除,修改
    public int updateTy(String sql, Object... obj) {
        con = this.getCon();
        int n = 0;
        try {
            //带参sql
            psmt = con.prepareStatement(sql);
            //如果传递的参数不为空
            if (obj != null) {
                //赋值
                int length = obj.length;
                for (int i = 0; i < length; i++) {
                    psmt.setObject(i + 1, obj[i]);
                }
            }
            //执行操作
            n = psmt.executeUpdate();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            closeAll(null, psmt, con);
        }
        return n;
    }

    //查询
    public <T> List<T> query(String sql, Class<T> cla, Object... obj) {
        List<T> list = list = new ArrayList<>();
        con = this.getCon();
        try {
            //带参sql
            psmt = con.prepareStatement(sql);
            //如果传递的参数不为空
            if (obj != null) {
                //赋值
                int length = obj.length;
                for (int i = 0; i < length; i++) {
                    psmt.setObject(i + 1, obj[i]);
                }
            }
            //返回一个结果集
            rs = psmt.executeQuery();
            //返回的是与(全部字段)字段有关的全部信息
            ResultSetMetaData rsmd = rs.getMetaData();
            //获取字段的数量
            int len = rsmd.getColumnCount();
            while (rs.next()) {
                //使用反射创建无参数对象
                T t = (T) cla.newInstance();
                for (int i = 0; i < len; i++) {
                    //获取字段名称
                    String name = rsmd.getColumnName(i + 1);
                    //获取字段的值
                    Object value = rs.getObject(i + 1);
                    //通过反射获取实体类的成员变量
                    Field field = cla.getDeclaredField(name);
                    //给成员变量赋值
                    field.setAccessible(true);//解除私有限定
                    field.set(t, value);
                }
                //给集合中添加对象
                list.add(t);
            }
            for (T t1 : list) {
                System.out.println(t1);
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            closeAll(rs, psmt, con);
        }
        return list;
    }

    //释放资源,正向打开,逆向关闭
    public void closeAll(ResultSet rs, PreparedStatement psmt, Connection con) {
        try {
            if (rs != null) {
                rs.close();
            }
            if (psmt != null) {
                psmt.close();
            }
            if (con != null) {
                con.close();
            }
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }
}

调用查询方法时,第二个实参应写成 实体类.class,例如 Student.class

例2 写一个javaweb中的通用Servlet吧

package servlet;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.lang.reflect.Method;

/**
 * @author:王先生 description: 通用Servlet。继承此类,子类的其中一个方法就是Servlet
 * @time: 公元2019/8/31 13:13
 */
public class BaseServlet extends HttpServlet {
    //EmpServlet?method=login
    @Override
    protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        req.setCharacterEncoding("UTF-8");
        resp.setContentType("UTF-8");
        String method = req.getParameter("method");
        try {
            Method met = this.getClass().getDeclaredMethod(method, HttpServletRequest.class, HttpServletResponse.class);
            met.invoke(this, req, resp);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

}

就先写到这吧,后期学到了新知识再往上加吧!

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值