【JavaLearn】#(19)反射、Class类、使用反射创建对象-操作属性-执行方法、泛型与反射、反射案例
1. 反射
1.1 反射引入
编译时
,知道类或对象的具体信息,此时直接对类和对象进行操作即可编译时不知道类或对象的具体信息,只有
运行时
知道,需要使用反射来实现==> 比如驱动的类的名称放在 XML 文件中,属性和属性值也放在XML文件中,需要在运行时读取XML文件,动态获取类的信息
// 编码、编译时知道要创建哪个类的对象
Dog dog = new Dog(); // 创建对象
dog.age = 12; // 操作属性
dog.eat(); // 执行方法// 编译时不知道类或对象的具体信息,只有`运行时`知道
String className = "com.lwclick.demo.Dog";
Class aClass = Class.forName(className);
// 使用反射创建对象,相当于 new Dog();
aClass.getConstructor().newInstance();
反射的作用:
- 动态创建对象
- 动态操作属性
- 动态调用方法
在JDK中,由以下类来实现 Java 的反射机制,都位于 java.lang.reflect 包中
- Class类:代表一个类
- Constructor类:代表类的构造方法
- Field类:类的成员变量
- Method类:类的成员方法
1.2 反射的入口—Class类
- Class类是Java反射机制的起源和入口
- 继承自 Object 类
- 存放类的结构信息
- 是所有类的共同的图纸
- 所有的类都有类名、属性、方法、构造方法、包、父类、父接口等
- Class类的对象称为
类对象
2. 认识 Class 类
// 获取一个类的类对象:Class信息(结构信息)
Class aClass = Class.forName("com.lwclick.java.Dog");// 从类对象中获取具体的结构信息:get
// 1. 获取最基本的信息=================【类信息】
System.out.println(aClass.getName()); // 全限定类名
System.out.println(aClass.getSimpleName()); // 类名
System.out.println(aClass.getSuperclass()); // 父类的类对象 class com.lwclick.java.Animal
System.out.println(Arrays.toString(aClass.getInterfaces()));// 2. 获取============================【成员变量】
Field[] fields = aClass.getFields(); // 获取所有的 public 属性,包括上级的
Field[] declaredFields = aClass.getDeclaredFields(); // 获取本类中,所有的属性(私有的也可获取)Field field = aClass.getField("nickName"); // public java.lang.String com.lwclick.java.Animal.nickName
Field declaredField = aClass.getDeclaredField("type"); // 获取本类中的,私有的属性// 3. 获取============================【成员方法】
Method[] methods = aClass.getMethods(); // 所有公共的
Method[] declaredMethods = aClass.getDeclaredMethods(); // 获取本类中,所有的方法
for (Method m : declaredMethods) {String methodName = m.getName(); // 方法名Class returnType = m.getReturnType(); // 返回值类型Class[] parameterTypes = m.getParameterTypes(); // 参数类型
}Method method = aClass.getMethod("guard");
Method method = aClass.getMethod("shout", String.class); // 参数列表可变// 4. 获取============================【构造方法】
Constructor[] constructors = aClass.getConstructors(); // 获取【本类】中,所有public的构造方法
Constructor[] declaredConstructors = aClass.getDeclaredConstructors(); 获取【本类】中,所有的构造方法Constructor constructor = aClass.getConstructor(); // 获取无参构造方法
Constructor declaredConstructor = aClass.getDeclaredConstructor(String.class, Integer.class); // 获取有参构造方法
Class类的常用方法:
方法名 | 说 明 |
---|---|
getFields() | 获得类的public类型的属性。 |
getDeclaredFields() | 获得类的所有属性 |
getField(String name) | 获得类的指定属性 |
getMethods() | 获得类的public类型的方法 |
getMethod (String name,Class [] args) | 获得类的指定方法 |
getConstrutors() | 获得类的public类型的构造方法 |
getConstrutor(Class[] args) | 获得类的特定构造方法 |
newInstance() | 通过类的无参构造方法创建对象 |
getName() | 获得类的完整名字 |
getPackage() | 获取此类所属的包 |
getSuperclass() | 获得此类的父类对应的Class对象 |
获取一个类的类对象方法:
- Class.forName(类的完整路径字符串) 【当编码时还不知道要操作的具体类,只能使用Class.forName()】
- 类名.class 【已经知道了要操作的类】
- 对象名.getClass() 【已经知道了要操作的类】
3. 使用反射创建对象
使用反射调用无参构造方法,创建对象
- 使用 Constructor 的
newInstance()
方法 - 使用 Class 的 newInstance() 方法
// 不使用反射
Dog dog = new Dog();// =======================================================================// 使用反射创建
// 1. 获取类的完整路径字符串(Properties、DOM4J等)
String className = "com.lwclick.domain.Dog";// 2. 根据完整路径字符串获取类的类对象
Class aClass = Class.forName(className);// 3. 从类对象中获取无参构造方法
Constructor constructor = aClass.getConstructor();// 4. 【使用反射创建对象】
Object o = constructor.newInstance();// 当为无参构造方式时,3 4步也可以合并为一步操作
Object o1 = aClass.newInstance();
使用反射调用有参构造方法,创建对象
- 只能使用 Constructor 的 newInstance(参数列表) 方法
// 不使用反射
Dog dog = new Dog("旺财", 4, "柴犬");// =======================================================================// 使用反射创建
String className = "com.lwclick.domain.Dog";
Class aClass = Class.forName(className);// 3. 从类对象中获取有参构造方法【公共的】
Constructor constructor = aClass.getConstructor(String.class, int.class, String.class);// 获取非 public 的构造方法 !!!!
Constructor declaredConstructor = aClass.getDeclaredConstructor(String.class, int.class, String.class);
// 突破私有的限制
declaredConstructor.setAccessible(true);// 4. 【使用反射创建对象】
Object o = constructor.newInstance("旺财", 4, "柴犬");
4. 使用反射操作属性
- 先获取类对象
- 再使用反射操作属性,
get()
,set()
// 不使用反射
Dog dog = new Dog();
dog.nickName = "旺财"; // 赋值
String nickName = dog.nickName; // 取值(使用getter操作,是调用方法)// =======================================================================String className = "com.lwclick.domain.Dog";
Class aClass = Class.forName(className);// 3. 使用反射创建对象
Object o = aClass.newInstance();// 4. 使用反射操作对象
// 先获取对象的属性
Field f = aClass.getDeclaredField("type"); // 非 public 的属性
f.setAccessible(true);
// 对属性进行操作
f.set(o, "柴犬"); // 赋值
Object type = f.get(o); // 取值
5. 使用反射执行方法
invoke()
常用
String className = "com.lwclick.domain.Dog";
Class aClass = Class.forName(className);// 3. 使用反射创建对象
Object o = aClass.newInstance();// 4. 获取对象的方法(无参的)
Method shout = aClass.getMethod("shout");
// 执行方法
shout.invoke(o);// 有参的方法
Method add = aClass.getMethod("add", int.class, int.class);
Object res = add.invoke(o, 10, 20);
使用反射执行方法时,操作的类对象,也可以不通过反射创建
Dog dog = new Dog(); // 先创建对象
Class aClass = dog.getClass(); // 获取对象的classMethod shout = aClass.getMethod("shout");
6. 使用反射操作泛型
没有泛型之前,Java的所有数据类型包括:
- primitive types:基本类型
- raw type:原始类型(不单指类,还包括数据、接口、注解、枚举等)
==》 Class类的一个具体对象代表一个指定的原始类型和基本类型
泛型出现之后,扩充了数据类型:
- parameterized types(参数化类型,可以大概理解为带了泛型的原始类型):就是平时用到的泛型
List<T>、Map<K, v>
的 List 和 Map - type variables(类型变量):比如
List<T>
中的 T 等 - array types(数组类型):带泛型的数组,比如
List<T> []
- WildcardType(通配符类型):例如
List<? extends Number>
所以就无法通过 Class 来进行操作,为此Java就新增了 ParameterizedType、TypeVariable、GenericArrayType、WildcardType 几种类型来操作泛型的反射
6.1 获取方法的参数中的泛型
getGenericParameterTypes()
, ((ParameterizedType) parameterType).getActualTypeArguments()
public void method1(Map<Integer, String> map, List<Integer> list, String str) {}public static void main(String[] args) throws NoSuchMethodException {Class aClass = TestGeneric.class; // TestGeneric为类名// 获取 method1 方法的参数中的泛型类型Method m1 = aClass.getMethod("method1", Map.class, List.class, String.class);// 不带泛型的参数Type[] types = m1.getParameterTypes();System.out.println(types.length);for (Type type : types) {System.out.println(type);}// interface java.util.Map// interface java.util.List// class java.lang.String// 带泛型的参数Type[] parameterTypes = m1.getGenericParameterTypes();for (Type parameterType : parameterTypes) {System.out.println(parameterType);if (parameterType instanceof ParameterizedType) {// 【重点!!!需要强制类型转换】Type[] typeArguments = ((ParameterizedType) parameterType).getActualTypeArguments(); for (Type typeArgument : typeArguments) {System.out.println(" " + typeArgument);}}}// java.util.Map<java.lang.Integer, java.lang.String>// class java.lang.Integer// class java.lang.String// java.util.List<java.lang.Integer>// class java.lang.Integer// class java.lang.String
}
6.2 获取方法的返回值类型中的泛型
getGenericReturnType()
, ((ParameterizedType) genericReturnType).getActualTypeArguments()
public Map<Integer, String> method2() {return null;
}public static void main(String[] args) throws NoSuchMethodException {Class aClass = TestGeneric.class;Method m2 = aClass.getMethod("method2");// 不带泛型的Class returnType = m2.getReturnType();System.out.println(returnType);// interface java.util.Map// 带泛型的Type genericReturnType = m2.getGenericReturnType();System.out.println(genericReturnType);Type[] actualTypeArguments = ((ParameterizedType) genericReturnType).getActualTypeArguments();for (Type typeArgument : actualTypeArguments) {System.out.println(" " + typeArgument);}// java.util.Map<java.lang.Integer, java.lang.String>// class java.lang.Integer// class java.lang.String
}
6.3 使用反射突破泛型限制
List<Integer> list = new ArrayList<>();
list.add(123);
list.add(456);
list.add(789);
list.add(123);System.out.println(list); // [123, 456, 789, 123]// list.add("lwclick"); 无法加入,有泛型限制// 使用反射突破泛型
Class aClass = list.getClass();
Method add = aClass.getMethod("add", Object.class);
add.invoke(list, "lwclick");
add.invoke(list, "true");System.out.println(list); // [123, 456, 789, 123, lwclick, true]
7. 反射案例—提取 DBUtil 的 select()
提取员工管理系统案例中的 DBUtil 类的 select() 方法
7.1 认识ResultSetMetaData
利用 ResultSet 的 getMetaData() 方法可以获得 ResultSetMeta 对象,它存储了结果集 ResultSet 的 MetaData(“描述数据的数据”,一般翻译为“元数据”)
// 获得 ResultSetMetaData 对象
ResultSetMetaData rsmd = rs.getMetaData();// 返回此 ResultSet 对象中的列数
int num = rsmd.getColumnCount();// 获取每一列(数据库中的字段)的属性
for (int i = 0; i < num; i++) {rsmd.getColumnClassName(i + 1); // 返回Java中对应的数据库字段的数据类型(全限定名称)rsmd.getColumnName(i + 1); // 数据库中指定列的名字
}
7.2 提取 DBUtil 类中的 select() 方法
/*** 之前Dao层的,查询所有*/
public List<Employee> findAll() {Connection conn = null;PreparedStatement pstmt = null;ResultSet rs = null;List<Employee> employeeList = new ArrayList<>();try {conn = DBUtil.getConnection();String sql = "SELECT * FROM emp";pstmt = conn.prepareStatement(sql);rs = pstmt.executeQuery();while (rs.next()) {Employee emp = new Employee(rs.getInt("empno"), rs.getString("ename"),rs.getString("job"), rs.getInt("mgr"),rs.getDate("hireDate"), rs.getDouble("sal"),rs.getDouble("comm"), rs.getInt("deptno"));employeeList.add(emp);}} catch (SQLException throwables) {throwables.printStackTrace();} finally {DBUtil.closeAll(rs, pstmt, conn);}return employeeList;
}/** * 之前Dao层的,通过 empno 查询*/
public Employee findById(int empno) {Connection conn = null;PreparedStatement pstmt = null;ResultSet rs = null;Employee emp = null;try {conn = DBUtil.getConnection();String sql = "SELECT * FROM emp WHERE empno = ?";pstmt = conn.prepareStatement(sql);pstmt.setInt(1, empno);rs = pstmt.executeQuery();if (rs.next()) {emp = new Employee(rs.getInt("empno"), rs.getString("ename"),rs.getString("job"), rs.getInt("mgr"),rs.getDate("hireDate"), rs.getDouble("sal"),rs.getDouble("comm"), rs.getInt("deptno"));}} catch (SQLException throwables) {throwables.printStackTrace();} finally {DBUtil.closeAll(rs, pstmt, conn);}return emp;
}// ====================== 修改之后 ========================
public List<Employee> findAll() {Connection conn = null;PreparedStatement pstmt = null;ResultSet rs = null;List<Employee> employeeList = new ArrayList<>();try {conn = DBUtil.getConnection();String sql = "SELECT * FROM emp";pstmt = conn.prepareStatement(sql);rs = pstmt.executeQuery();while (rs.next()) {Employee emp = new Employee();// 获取每一列的值,并通过 setter 方法赋给 emp 对象 !!!!!!修改的地方int empno = rs.getInt("empno");emp.setEmpno(empno);String ename = rs.getString("ename");emp.setEname(ename);String job = rs.getString("job");emp.setJob(job);// 。。。。。。。employeeList.add(emp);}} catch (SQLException throwables) {throwables.printStackTrace();} finally {DBUtil.closeAll(rs, pstmt, conn);}return employeeList;
}public Employee findById(int empno1) {Connection conn = null;PreparedStatement pstmt = null;ResultSet rs = null;// 统一用list接收List<Employee> employeeList = new ArrayList<>();try {conn = DBUtil.getConnection();String sql = "SELECT * FROM emp WHERE empno = ?";pstmt = conn.prepareStatement(sql);pstmt.setInt(1, empno1);rs = pstmt.executeQuery();while (rs.next()) {Employee emp = new Employee();// 获取每一列的值,并通过 setter 方法赋给 emp 对象 !!!!!!修改的地方int empno = rs.getInt("empno");emp.setEmpno(empno);String ename = rs.getString("ename");emp.setEname(ename);String job = rs.getString("job");emp.setJob(job);// 。。。。。。。employeeList.add(emp);}} catch (SQLException throwables) {throwables.printStackTrace();} finally {DBUtil.closeAll(rs, pstmt, conn);}Employee emp = null;if (employeeList.size() > 0) {// 只取第一个即可emp = employeeList.get(0);}return emp;
}
可以看到每次查询数据时,都是使用不同的sql,然后传递某些参数,因此可以提取出一个公共的查询方法放到 DBUtil 类中
/*** DBUtil 类的公共查询方法*/
public static List<Employee> executeQuery(String sql, Object[] params) {Connection conn = null;PreparedStatement pstmt = null;ResultSet rs = null;List<Employee> employeeList = new ArrayList<>();try {conn = DBUtil.getConnection();pstmt = conn.prepareStatement(sql);for (int i = 0; i < params.length; i++) {pstmt.setObject(i + 1, params[i]);}rs = pstmt.executeQuery();while (rs.next()) {Employee emp = new Employee();// 获取每一列的值,并通过 setter 方法赋给 emp 对象 !!!!!!修改的地方int empno = rs.getInt("empno");emp.setEmpno(empno);String ename = rs.getString("ename");emp.setEname(ename);String job = rs.getString("job");emp.setJob(job);// 。。。。。。。employeeList.add(emp);}} catch (SQLException throwables) {throwables.printStackTrace();} finally {DBUtil.closeAll(rs, pstmt, conn);}return employeeList;
}/*** Dao层使用公共查询方法之后的 查询所有*/
public List<Employee> findAll() {String sql = "SELECT * FROM emp";Object[] params = {};return DBUtil.executeQuery(sql, params);
}/*** Dao层使用公共查询方法之后的 查询单个员工*/
public Employee findById(int empno1) {String sql = "SELECT * FROM emp WHERE empno = ?";Object[] params = {empno1};List<Employee> employeeList = DBUtil.executeQuery(sql, params);Employee emp = null;if (employeeList.size() > 0) {// 只取第一个即可emp = employeeList.get(0);}return emp;
}
问题:此时又可以看到,每次查询到数据时,都需要将数据一一进行对应(此时是所有的数据),赋值给实体类对应的字段,而且此时 DButil 的查询,只能查询【员工表】的【所有的】数据
7.3 提取后的结果
- 使用反射解决只能创建 Employee 对象的问题(要根据运行时获取的信息来动态的创建对象)
- 使用泛型来解决返回的数据只能是员工列表的问题
- 使用 ResultSetMetaData 来解决可以查询其他表,以及某些字段的问题
public static <T> List<T> executeQuery(String sql, Object[] params, Class clazz) {Connection conn = null;PreparedStatement pstmt = null;ResultSet rs = null;List<T> list = new ArrayList<>();try {conn = DBUtil.getConnection();pstmt = conn.prepareStatement(sql);for (int i = 0; i < params.length; i++) {pstmt.setObject(i + 1, params[i]);}rs = pstmt.executeQuery();// 获取结果集的 ResultSetMetaData 对象ResultSetMetaData rsmd = rs.getMetaData();int count = rsmd.getColumnCount();while (rs.next()) {T entity = (T) clazz.newInstance();for (int i = 0; i < count; i++) {// 获取当前列的名称String columnName = rsmd.getColumnName(i + 1);// 获取当前列的值Object value = rs.getObject(columnName);// 获取 setter 方法名:通过 getColumnName() 方法获取数据库字段名,去对应实体类的setter方法String methodName = "set" + columnName.substring(0, 1).toUpperCase()+ columnName.substring(1).toLowerCase();// 获取参数的类型:通过 getColumnClassName() 方法获取数据库字段对应的Java类型的全限定类名Class parameterType = Class.forName(rsmd.getColumnClassName(i + 1));// 将当前列的值,赋给 entity 对象,调用 setter 方法Method setterMethod = clazz.getMethod(methodName, parameterType);setterMethod.invoke(entity, value);}list.add(emp);}} catch (Exception throwables) {throwables.printStackTrace();} finally {DBUtil.closeAll(rs, pstmt, conn);}return list;
}
【JavaLearn】#(19)反射、Class类、使用反射创建对象-操作属性-执行方法、泛型与反射、反射案例相关推荐
- 【JavaLearn】(19)反射、Class类、使用反射创建对象-操作属性-执行方法、泛型与反射、反射案例
目录 1. 反射 1.1 反射引入 1.2 反射的入口-Class类 2. 认识 Class 类 3. 使用反射创建对象 4. 使用反射操作属性 5. 使用反射执行方法 6. 使用反射操作泛型 6.2 ...
- 二十六. Python基础(26)--类的内置特殊属性和方法
二十六. Python基础(26)--类的内置特殊属性和方法 ● 知识框架 ● 类的内置方法/魔法方法案例1: 单例设计模式 # 类的魔法方法 # 案例1: 单例设计模式 class Teacher: ...
- 《JAVA练习题目8》 编写一个类Person,包含的属性和方法如下: 属性:字符串类型的名字name,字符类型的性别gender,和整型的序号number
题目内容: 编写一个类Person,包含的属性和方法如下: 属性:字符串类型的名字name,字符类型的性别gender,和整型的序号number:方法:showMe,以"name-gende ...
- 反射获取类_新人也能看懂?如何使用 Java 反射?反射的用法及案例
· 简介 Java Reflection,称为 Java 反射,是Java基础部分的一个比较难的点.Reflection(反射)是被视为动态语言的关键,通过反射机制,我们可以在运行时(runtime) ...
- 给Python的类和对象动态增加属性和方法
通常我们会将编程语言分为静态和动态.静态语言的变量是在内存中的有类型的且不可变化的,除非强制转换它的类型:动态语言的变量是指向内存中的标签或者名称,其类型在代码运行过程中会根据实际的值而定.Pytho ...
- 遍历一个类中的每一个属性、方法、公共字段
//获取对象类型 Type t = obj.GetType(); //获取类的属性 PropertyInfo[] propertys = t.GetPr ...
- python 类的特殊成员(属性和方法)
在 Python 类中有些方法名.属性名的前后都添加了双下画线,这种方法.属性通常都属于 Python 的特殊方法和特殊属性,开发者可以通过重写这些方法或直接调用这些方法来实现特殊的功能. 最常见的特 ...
- 新建银行账户类,实现基本的属性和方法。
新建银行账户类(代码中类名为person),类中有账户ID.账户姓名.账户密码.余额.银行名称等属性,类中有存钱.取钱.改密码.查询等方法.使用字符界面实现菜单程序,通过接收不同的参数值来执行不同的操 ...
- 网页上有错误(类不能支持 Automation 操作)解决方法
应该JS代码错误,打开"运行"(Win+R键),依次重新注册以下文件: regsvr32 msscript.ocx regsvr32 dispex.dll regsvr32 vbs ...
最新文章
- SQL基础--过滤和排序
- asp.net的dropDownlist只显示第一个字
- 遍历Map的几种方式以及性能小结
- jquery-懒加载技术(简称lazyload)
- java 复制剪贴板_java_swing复制粘贴、剪贴板
- ubuntu_常用命令_01
- RabbitMQ交换机简介
- 如何处理CloudFoundry应用部署时遇到的254错误
- pydebugger
- 什么是Google Play保护以及如何确保Android安全?
- msSql 利用 xp_cmdshell 删除 创建目录
- 连接mysql数据库_解决Navicat连接MySQL数据库报错问题
- word List 15
- oracle数据库扩容方案_ORACLE数据库扩容
- 消息中间件学习总结(21)——RocketMQ 消息丢失场景分析及如何解决!
- Java list.remove( )方法需要注意的地方
- 鸿鹄论坛oracle资料,鸿鹄论坛_HCNA-Storage (H13-611)题库 v4.0.pdf
- 【学习笔记】Unreal(虚幻)4引擎入门(四)
- 关于SO、SOP、SOIC封装(宽体、中体、窄体)的详解
- 资源分配问题(动态规划)
热门文章
- Mts视频丢失损坏的恢复修复
- 饥荒联机服务器未响应,《饥荒》联机版常见问题及解决方法一览
- 数字图书是计算机技术,计算机技术在数字图书馆的运用
- Winform控件之菜单控件,工具栏控件和状态栏控件
- 在Excel中打开XML文件
- 平板第三方电容笔怎么样?apple pencil一代平替笔推荐
- python middleware模块_详解利用django中间件django.middleware.csrf.CsrfViewMiddleware防止csrf攻击...
- SpringBoot fastjson自定义注解时间转时间戳
- JAVA的infinite_Java Float类isInfinite()方法与示例
- 计算机零配件的增值税率,​电脑耗材增值税税率是多少