文章目录
惊喜都在后面
一、获取class对象的方式
//第1种,通过Class.forName("全类名")获取
Class c1 = Class.forName("reflect.Student");
System.out.println(c1);
//第2种,通过类名.class获取
Class c2 = Student.class;
System.out.println(c2);
//第3种,通过对象.getClass获取
Student st = new Student();
Class c3 = st.getClass();
System.out.println(c3);
如果把这三个拿到的值做个比较的话:
System.out.println(c1==c2);
System.out.println(c1==c3);
会发现这三种拿到的值,都是相等的。
可以得出一个结论,在当前的jdk运行环境中,同一个.class字节码文件只被加载一次,被加载之后,第二次再去拿的话,就会直接引用之前的,所以是一样的。
二、Class类有什么功能
(一)分析一个类中都有什么?
我们先简单的分析一下在一个类中都有什么:
public class Student {
private String name;
public Student() {
}
public Student(String name) {
this.name = name;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public void printName(){
System.out.println(this.name);
}
}
我们看下图:
我们很常见的就有:
类名,变量,无参构造方法,有参构造方法,get和set方法,自定义方法等
(二)我们想要什么
先来看一下我们的测试类:
package reflect;
/**
* @Auther: truedei
* @Date: 2020 /20-6-16 20:19
* @Description:
*/
public class Student {
private String name;
public String a;
protected String b;
String c;
private String d;
public Student() {}
public Student(String name) {
this.name = name;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public void printName(){
System.out.println(this.name);
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", a='" + a + '\'' +
", b='" + b + '\'' +
", c='" + c + '\'' +
", d='" + d + '\'' +
'}';
}
}
我们当然都想要了。
例如:
- 1、获取类名
- 2、获取成员变量
- 3、获取构造方法
- 4、获取成员方法
1、获取类名功能
String getName()返回由 类对象表示的实体(类,接口,数组类,原始类型或空白)的名称,作为 String 。
2、获取成员变量功能
设置和获取变量的值
(1)获取所有的成员变量
Field[] getFields() 获取Public修饰的成员变量。
例如我们定义四个级别的成员变量:
public String a;
protected String b;
String c;
private String d;
再去获取一下试试:
(2)获取单个指定名称的成员变量
Field getField(String name) 返回一个指定名字的public修饰的 Field对象。
到现在可以想一下,我们拿到了成员变量要干什么呢?
答:无非就是两个操作
- 1、获取值
- 2、设置值
Filed类有两个方法,分别是get和set:
Object get(Object obj)返回该所表示的字段的值 Field ,到指定的对象上。
void set(Object obj, Object value) 为某个Object所在的Field设置新值value。
1、获取值
Field a = studentClass.getField("a");
//获取公共成员变量的值
Student student = new Student();
Object value = a.get(student);
System.out.println("获取公共成员变量的值--->"+value);
2、设置值
Field a = studentClass.getField("a");
//设置成员变量的值
a.set(student,"aaaaa");
System.out.println("设置成员变量的值--->"+student);
(3)忽略权限获取所有成员变量
Field[] getDeclaredFields() 获取所有字段 。
//1.获取Student的Class对象
Class studentClass = Student.class;
Field[] declaredFields = studentClass.getDeclaredFields();
for (Field declaredField : declaredFields) {
System.out.println(declaredField);
}
(4)获取指定名称的变量
Field getDeclaredField(String name) 获取单个指定名字的成员变量。
在这里如果我们直接获取的话,就会提示没有权限。
我们可以忽略权限进行获取d.setAccessible(true);//忽略权限,通常也被称为暴力反射
//1.获取Student的Class对象
Class studentClass = Student.class;
Field[] declaredFields = studentClass.getDeclaredFields();
for (Field declaredField : declaredFields) {
System.out.println(declaredField);
}
System.out.println("-------");
Student stu = new Student();
Field d = studentClass.getDeclaredField("d");
d.setAccessible(true);//忽略权限
Object o = d.get(stu);
System.out.println(o);
3、获取构造方法功能
创建对象
Constructor<?>[] getConstructors()返回包含一个数组 Constructor对象反射由此表示的类的所有公共构造 类对象。
Constructor getConstructor(类... parameterTypes)返回一个 Constructor对象,该对象反映 Constructor对象表示的类的指定的公共 类函数。
Constructor<T> getDeclaredConstructor(类<?>... parameterTypes)返回一个 Constructor对象,该对象反映 Constructor对象表示的类或接口的指定 类函数。
Constructor<?>[] getDeclaredConstructors()返回一个反映 Constructor对象表示的类声明的所有 Constructor对象的数组 类 。
(1)获取指定参数构造方法
//1.获取Student的Class对象
Class studentClass = Student.class;
//参数的类型
Constructor constructor = studentClass.getConstructor(String.class);
Object a = constructor.newInstance("a");
System.out.println(constructor);
System.out.println(a);
结果:
public reflect.Student(java.lang.String)
Student{name='a', a='null', b='null', c='null', d='null'}
源码如下:
在源码中可以看出,这里的参数可以有多个
(2)获取所有的构造方法
//1.获取Student的Class对象
Class studentClass = Student.class;
Constructor[] constructors = studentClass.getConstructors();
for (Constructor constructor : constructors) {
System.out.println(constructor);
}
结果:
public reflect.Student()
public reflect.Student(java.lang.String)
其余两个差不多,可以自行测试
(3)获取无参构造并创建对象方法1
//1.获取Student的Class对象
Class studentClass = Student.class;
Constructor constructor = studentClass.getConstructor();
Object o = constructor.newInstance();
System.out.println(o);
(4)获取无参构造并创建对象方法2
//1.获取Student的Class对象
Class studentClass = Student.class;
Object o1 = studentClass.newInstance();
System.out.println(o1);
4、获取成员方法功能
作用:
获取方法、执行方法
Method getMethod(String name, 类<?>... parameterTypes)返回一个 方法对象,它反映此表示的类或接口的指定公共成员方法 类对象。
Method[] getMethods()返回包含一个数组 方法对象反射由此表示的类或接口的所有公共方法 类对象,包括那些由类或接口和那些从超类和超接口继承的声明。
Method getDeclaredMethod(String name, 类<?>... parameterTypes)返回一个 方法对象,它反映此表示的类或接口的指定声明的方法 类对象。
Method[] getDeclaredMethods()返回包含一个数组 方法对象反射的类或接口的所有声明的方法,通过此表示 类对象,包括公共,保护,默认(包)访问和私有方法,但不包括继承的方法。
(1)获取所有方法
//1.获取Student的Class对象
Class studentClass = Student.class;
Method[] methods = studentClass.getMethods();
for (Method method : methods) {
System.out.println(method);
}
结果:
public void reflect.Student.printName()
public java.lang.String reflect.Student.toString()
public java.lang.String reflect.Student.getName()
public void reflect.Student.setName(java.lang.String)
public final void java.lang.Object.wait(long,int) throws java.lang.InterruptedException
public final native void java.lang.Object.wait(long) throws java.lang.InterruptedException
public final void java.lang.Object.wait() throws java.lang.InterruptedException
public boolean java.lang.Object.equals(java.lang.Object)
public native int java.lang.Object.hashCode()
public final native java.lang.Class java.lang.Object.getClass()
public final native void java.lang.Object.notify()
public final native void java.lang.Object.notifyAll()
(2)获取指定方法并执行
//1.获取Student的Class对象
Class studentClass = Student.class;
//获取对象
Constructor constructor = studentClass.getConstructor(String.class);
//创建对象 new
Object trueDei = constructor.newInstance("TrueDei");
//获取方法
Method printName = studentClass.getMethod("printName");
//调用方法 执行方法
printName.invoke(trueDei);
结果:
TrueDei
(3)获取有参构造方法
//1.获取Student的Class对象
Class studentClass = Student.class;
//获取对象
Constructor constructor = studentClass.getConstructor(String.class);
//创建对象 new
Object trueDei = constructor.newInstance("TrueDei");
//获取方法
Method printName = studentClass.getMethod("printName");
//调用方法 执行方法
printName.invoke(trueDei);
//获取有参数的set方法
Method setName = studentClass.getMethod("setName", String.class);
setName.invoke(trueDei,"不是TrueDei了,又修改了");
//再次执行打印方法
printName.invoke(trueDei);
结果:
TrueDei
不是TrueDei了,又修改了
三、实战一:手写一个自己的框架
需求:
在不改动java代码的情况下,创建任意一个对象并执行任意一个方法
项目目录结构:
请忽略我其他的包
我们现在有两个类:
package reflect;
/**
* @Auther: truedei
* @Date: 2020 /20-6-16 23:40
* @Description:
*/
public class P {
public void aaa(){
System.out.println("这是P下的aaaaaaaaaaaa");
}
}
和;
package reflect;
/**
* @Auther: truedei
* @Date: 2020 /20-6-16 20:19
* @Description:
*/
public class Student {
private String name="我是默认的名字";
public Student() {}
public Student(String name) {
this.name = name;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public void printName(){
System.out.println(this.name);
}
}
application.properties配置文件内容:
className=reflect.P #要创建的类的全类名
methdName=aaa #要执行的方法
核心文件:”
package reflect;
import java.io.InputStream;
import java.lang.reflect.Method;
import java.util.Properties;
/**
* @Auther: truedei
* @Date: 2020 /20-6-16 20:18
* @Description:反射
*/
public class ReflectTest {
public static void main(String[] args) throws Exception {
//1.加载配置文件
//1.1 创建Properties对象
Properties properties = new Properties();
//1.2加载配置文件,转换为一个集合
//1.2.1获取class目录下的配置文件
ClassLoader classLoader = ReflectTest.class.getClassLoader();
//获取class下的文件的字节流
InputStream resourceAsStream = classLoader.getResourceAsStream("application.properties");
//加载该字节流文件
properties.load(resourceAsStream);
//2.获取配置文件中定义的数据
String className = properties.getProperty("className");
String methdName = properties.getProperty("methdName");
//3.加载该类进内存
Class cls = Class.forName(className);
//4.创建对象 (使用无参构造方法创建)
Object o = cls.newInstance();
//5.获取方法
Method method = cls.getMethod(methdName);
//6.执行方法
method.invoke(o);
}
}
测试:
修改一下一下配置文件:
className=reflect.Student
methdName=printName
就可以看到这种效果了。
四、哪些地方使用了反射
写过java开发的同学来说,对于下面这个图片的内容肯定很熟悉,以后看到这种,就一定是反射。
五、实战二:使用反射原理,手写一个jdbc通用结果封装框架
(一)需求
我有这么三个表:
这三个表的内容我在:《爬取淘宝商品数据使用Java实现商品推荐系统(含sql文件、算法推导等)》
这篇文章已经使用了,想要表结构的话,可以去这篇文章去找。
在《爬取淘宝商品数据使用Java实现商品推荐系统(含sql文件、算法推导等)》这个试验中我使用了JDBC操作数据库。
代码如下:
/**
* 获取所有的商品信息
* @return
* @param sqlId
*/
public static List<ProductTable> getProductList(String sqlId){
List<ProductTable> productTables = new ArrayList<>();
try {
st = conn.createStatement();
rs = st.executeQuery("select * from product_table where productID in ("+sqlId+")");
while (rs.next()){
productTables.add(new ProductTable(
rs.getInt("productID"),
rs.getString("product_name"),
rs.getDouble("price"),
rs.getInt("volume"),
rs.getString("shopp_name"),
rs.getString("location"),
rs.getInt("evaluate"),
rs.getInt("collect")));
}
} catch (SQLException e) {
e.printStackTrace();
}
return productTables;
}
//获取用户订单信息
public static List<ProductOrder> getProductOrderList(Integer userId){
List<ProductOrder> productTables = new ArrayList<>();
String sql = "select * from product_order "+(userId==null?"":"where USER_ID="+userId);
// System.out.println("执行的 sql: "+sql);
try {
st = conn.createStatement();
rs = st.executeQuery(sql);
while (rs.next()){
productTables.add(new ProductOrder(
rs.getInt("order_id"),
rs.getInt("user_id"),
rs.getInt("product_id"),
rs.getInt("gwcount")));
}
} catch (SQLException e) {
e.printStackTrace();
}
return productTables;
}
//获取用户信息
public static List<MemberUser> getMemberUserList(){
List<MemberUser> productTables = new ArrayList<>();
try {
st = conn.createStatement();
rs = st.executeQuery("select * from member_user");
while (rs.next()){
productTables.add(new MemberUser(
rs.getInt("user_id"),
rs.getString("user_name")));
}
} catch (SQLException e) {
e.printStackTrace();
}
return productTables;
}
初学的时候感觉很好,我只要把结果集重新封装一下就可以拿到一个对象形式的数据了。
随着技术的不断增长,也想着这种办法太低级了,有没有一种更好的办法来实现呢?
就想到了反射可以实现。经过实践结果是可以的。下面一起来看一下。
(二)创建一个类作为工具类(PEU)
1、名字由来
暂且起名字为:PEU
P:Public
E:Entity
U:Util
寓意为:公共的实体类创建工具
2、PEU说明
(1)createEntity方法详解
功能
通过类的字节码和数据库结果集返回单个查询结果
代码结构:
public Object createEntity(Class cls, ResultSet rs) {}
参数说明:
这个方法需要传递两个参数,分别为
Class和ResultSet类型的数据;
Class cls:我们最终要通过反射封装实体类的类名,可以有三种办法拿到,这不不在说明。前面已经说的很清楚了。
ResultSet rs:通过sql查询的结果集,需要拿到里面的数据,所以必须要有此参数。
返回值:
Object:最终要把一个封装好的实体类(包含结果)返回
该方法全部代码:
代码注释很详细,就不说了,都是反射的基本用法
/**
* 返回一个实体类数据(单个结果)
* @param cls
* @param rs
* @return
*/
public Object createEntity(Class cls, ResultSet rs) {
Object entity = null;
try {
//1.获取对象
Constructor constructor = cls.getConstructor();
//2.获取无参构造方法,通过无参构造方法创建一个对象
entity = constructor.newInstance();
//ResultSet 结果集移动一下个指针
rs.next();
//获取所有的变量
Field[] declaredFields = cls.getDeclaredFields();
for (Field declaredField : declaredFields) {
//忽略权限
declaredField.setAccessible(true);
//通过变量名字,设置值,值在entity对象中
//rs.getObject(字段名):通过这个拿到数据库中的数据
declaredField.set(entity,rs.getObject(declaredField.getName()));
}
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
} catch (SQLException e) {
e.printStackTrace();
}
//把Object对象强转成T泛型
return entity;
}
(2)createEntityList方法详解
功能
通过类的字节码和数据库结果集返回一个List集合
代码结构:
List<Object> createEntityList(Class cls, ResultSet rs) {}
参数说明:
这个方法需要传递两个参数,分别为
Class和ResultSet类型的数据;
Class cls:我们最终要通过反射封装实体类的类名,可以有三种办法拿到,这不不在说明。前面已经说的很清楚了。
ResultSet rs:通过sql查询的结果集,需要拿到里面的数据,所以必须要有此参数。
返回值:
List:最终要把一个封装好的集合(包含结果)返回
/**
* 返回一个集合数据
* @param cls
* @param rs
* @return
*/
public List<Object> createEntityList(Class cls, ResultSet rs) {
List<Object> entityList = new ArrayList<>();
try {
//1.获取对象
Constructor constructor = cls.getConstructor();
//ResultSet 结果集移动一下个指针
while (rs.next()){
//2.获取无参构造方法,通过无参构造方法创建一个对象
Object entity = constructor.newInstance();
//获取所有的变量
Field[] declaredFields = cls.getDeclaredFields();
for (Field declaredField : declaredFields) {
//忽略权限
declaredField.setAccessible(true);
//通过变量名字,设置值,值在entity对象中
//rs.getObject(字段名):通过这个拿到数据库中的数据
declaredField.set(entity,rs.getObject(declaredField.getName()));
}
//塞到list中
entityList.add(entity);
}
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
} catch (SQLException e) {
e.printStackTrace();
}
return entityList;
}
(三)PEU全部代码
package cn.util;
import cn.entity.MemberUser;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
/**
*
* PEU(Public Entity Util)公共的实体类创建工具
* @Auther: truedei
* @Date: 2020 /20-6-16 23:53
* @Description: 通用实体类创建工具
*/
public class PEU{
/**
* 返回一个实体类数据(单个结果)
* @param cls
* @param rs
* @return
*/
public Object createEntity(Class cls, ResultSet rs) {
Object entity = null;
try {
//1.获取对象
Constructor constructor = cls.getConstructor();
//2.获取无参构造方法,通过无参构造方法创建一个对象
entity = constructor.newInstance();
//ResultSet 结果集移动一下个指针
rs.next();
//获取所有的变量
Field[] declaredFields = cls.getDeclaredFields();
for (Field declaredField : declaredFields) {
//忽略权限
declaredField.setAccessible(true);
//通过变量名字,设置值,值在entity对象中
//rs.getObject(字段名):通过这个拿到数据库中的数据
declaredField.set(entity,rs.getObject(declaredField.getName()));
}
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
} catch (SQLException e) {
e.printStackTrace();
}
//把Object对象强转成T泛型
return entity;
}
/**
* 返回一个集合数据
* @param cls
* @param rs
* @return
*/
public List<Object> createEntityList(Class cls, ResultSet rs) {
List<Object> entityList = new ArrayList<>();
try {
//1.获取对象
Constructor constructor = cls.getConstructor();
//ResultSet 结果集移动一下个指针
while (rs.next()){
//2.获取无参构造方法,通过无参构造方法创建一个对象
Object entity = constructor.newInstance();
//获取所有的变量
Field[] declaredFields = cls.getDeclaredFields();
for (Field declaredField : declaredFields) {
//忽略权限
declaredField.setAccessible(true);
//通过变量名字,设置值,值在entity对象中
//rs.getObject(字段名):通过这个拿到数据库中的数据
declaredField.set(entity,rs.getObject(declaredField.getName()));
}
//塞到list中
entityList.add(entity);
}
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
} catch (SQLException e) {
e.printStackTrace();
}
return entityList;
}
}
(四)测试
1、测试1:查询单条数据
/**
* 测试1:自动封装单个实体结果
* @param userId
*/
public static void tongyongTest1(Integer userId){
PEU peu = new PEU();
List<MemberUser> productTables = new ArrayList<>();
try {
st = conn.createStatement();
rs = st.executeQuery("select * from product_order where ORDER_ID="+userId);
ProductOrder memberUser = (ProductOrder)peu.createEntity(ProductOrder.class,rs);
System.out.println(memberUser);
} catch (SQLException e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
tongyongTest1(1);
}
测试结果:
ProductOrder{order_id=1, user_id=1, product_id=1, gwcount=15}
ProductOrder{order_id=2, user_id=2, product_id=3, gwcount=42}
ProductOrder{order_id=3, user_id=3, product_id=4, gwcount=2}
2、测试2:切换一个表查询
可以看到我只修改了一点代码就可以了
public static void tongyongTest1(Integer userId){
PEU peu = new PEU();
try {
st = conn.createStatement();
rs = st.executeQuery("select * from member_user where USER_ID="+userId);
MemberUser memberUser = (MemberUser)peu.createEntity(MemberUser.class,rs);
System.out.println(memberUser);
} catch (SQLException e) {
e.printStackTrace();
}
}
结果图:
3、测试3:查询一个集合的数据
/**
* 测试2:自动封装单个实体结果
*/
public static void tongyongTest2(){
PEU peu = new PEU();
try {
st = conn.createStatement();
rs = st.executeQuery("select * from member_user ");
List<MemberUser> entityList = (List)peu.createEntityList(MemberUser.class, rs);
for (MemberUser memberUser : entityList) {
System.out.println(memberUser);
}
} catch (SQLException e) {
e.printStackTrace();
}
}
结果:
MemberUser{user_id=1, user_name='郑成功'}
MemberUser{user_id=2, user_name='小红'}
MemberUser{user_id=7, user_name='小李'}
MemberUser{user_id=19, user_name='郑晖'}
MemberUser{user_id=10, user_name='张三'}
MemberUser{user_id=11, user_name='二龙湖浩哥'}
MemberUser{user_id=12, user_name='张三炮'}
MemberUser{user_id=13, user_name='赵四'}
MemberUser{user_id=14, user_name='刘能'}
MemberUser{user_id=15, user_name='刘能逗'}
4、测试4:切换一个表查询集合数据
/**
* 测试2:自动封装单个实体结果
*/
public static void tongyongTest2(){
PEU peu = new PEU();
try {
st = conn.createStatement();
rs = st.executeQuery("select * from product_order ");
List<ProductOrder> entityList = (List)peu.createEntityList(ProductOrder.class, rs);
for (ProductOrder productOrder : entityList) {
System.out.println(productOrder);
}
} catch (SQLException e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
tongyongTest2();
}
结果:
结果:
ProductOrder{order_id=1, user_id=1, product_id=1, gwcount=15}
ProductOrder{order_id=2, user_id=2, product_id=3, gwcount=42}
ProductOrder{order_id=3, user_id=3, product_id=4, gwcount=2}
ProductOrder{order_id=4, user_id=4, product_id=4, gwcount=20}
ProductOrder{order_id=5, user_id=1, product_id=2, gwcount=21}
ProductOrder{order_id=6, user_id=5, product_id=1, gwcount=null}
ProductOrder{order_id=7, user_id=5, product_id=2, gwcount=null}
ProductOrder{order_id=8, user_id=5, product_id=3, gwcount=null}
ProductOrder{order_id=9, user_id=6, product_id=2, gwcount=null}
ProductOrder{order_id=10, user_id=6, product_id=5, gwcount=null}
ProductOrder{order_id=11, user_id=7, product_id=1, gwcount=null}
ProductOrder{order_id=12, user_id=7, product_id=2, gwcount=null}
ProductOrder{order_id=13, user_id=7, product_id=5, gwcount=null}
ProductOrder{order_id=14, user_id=3, product_id=1, gwcount=null}
六、有秘密告诉你
如果对你有帮助,可以分享给你身边的朋友。或者给俺点个大大的赞和大大的评论,点赞和评论就是给我最大的支持,感谢。
水平有限,难免会有疏漏或者书写不合理的地方,欢迎交流讨论。
作者:TrueDei
作者唯一博客CSDN:truedei.blog.csdn.net/
转载说明:如需转载请注明原地址和作者名。
如果喜欢我的文章,还没看够可以关注我,我会用心写好每一篇文章。
我已加入CSDN合伙人计划
亲爱的各位粉丝:可以添加我的CSDN官方企业微信号,和我近距离互动聊天,为您答疑解惑。
直接使用微信扫码即可,不用下载企业微信。
也可以加入此群:到期后,可以加我上面微信哦