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();
}
}
}
就先写到这吧,后期学到了新知识再往上加吧!