JavaLearn#(19)反射Class类使用反射创建对象-操作属性-执行方法泛型与反射反射案例

Posted LRcoding

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了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();   // 获取对象的class

Method 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就新增了 ParameterizedTypeTypeVariableGenericArrayTypeWildcardType 几种类型来操作泛型的反射

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("em

以上是关于JavaLearn#(19)反射Class类使用反射创建对象-操作属性-执行方法泛型与反射反射案例的主要内容,如果未能解决你的问题,请参考以下文章

反射, getClass(), 和something.class以及类型类(转)

反射机制--总结

java反射-基础语法

深入浅出-JVM(75):class 装载

Java反序列化漏洞——反射与反序列化基础

JavaLearn#(20)注解元注解模拟MyBatis注解JDK新特性数据库建模UML建模