17.反射机制

本章目标

  • 反射
  • 相关类及API
  • Properties

本章内容

一、反射

1、概念

反射的概念是由Smith在1982年首次提出的,主要是指程序可以访问、检测和修改它本身状态或行为的一种能力。

我们知道Java程序能够运行,就得让Java类被Java虚拟机(JVM)加载,也就是说我们运行的所有程序都在编译期间已经把所需要的类都加载到了Java虚拟机。

Java的反射机制是在编译时,并不确定哪个类被加载,而是在运行时才加载,这样的特点就是反射

2、功能

在运行时获取一个对象的类信息:访问修饰符、成员、方法、构造及超类的信息。

  • 动态生成对象:反射机制可以在运行时生成对象,这样就可以根据参数的不同,动态的创建不同的类的实例对象。
  • 动态调用方法:通过反射机制可以调用类中的方法,不论这些方法是否是公共的,也不论这些方法的参数个数和类型是什么,反射机制都具有这样的能力。
  • 动态修改属性:利用反射机制可以获取到类中的所有成员变量,并可以对其进行修改

3、反射的应用

  1. 在使用IDE(如Eclipse,IDEA)时,当我们输入一个对象或类并想调用它的属性或方法时,一按点号,编译器就会自动列出它的属性或方法,这里就会用到反射反射最重要的用途就是开发各种通用框架。
  2. 框架(Spring,Mybatis,Hibernate)都是配置化的(比如通过XML文件配置JavaBean,Action之类的),为了保证框架的通用性,它们可能需要根据配置文件加载不同的对象或类,调用不同的方法,这个时候就必须用到反射——运行时动态加载需要加载的对象

4、常用类和API

要使用Java反射机制,就得使用java.lang.Class这个类,它是Java反射机制的起源。

当一个类被加载后,Java虚拟机就会自动产生一个Class对象,通过该对象就能获得该对象的方法、成员及构造等信息

在Java中,主要由以下类实现反射,这些类都位于java.lang.reflect包中:

含义
Method 代表类的方法
Constructor 代表类的构造方法
Field 代表类的成员变量

5、获得Class实例的方式

获得Class实例的有四种方式:

第一种:使用Class类的静态方法forName(),用类的全名获得一个Class实例。

 Class clazz = Class.forName(“java.util.Hashtable”);

第二种:利用对象调用getClass()方法获得对象的Class实例。

 String s = "hello";
 Class clazz = s.getClass();

第三种:通过的类的class属性获得该类的Class实例。

 Class clazz = java.util.Hashtable.class;

第四种:对于基本数据类型的封装类,可以采用Type属性获得对应数据类型的Class实例。

 Class clazz = Integer.Type;

二、相关类及API

1、Class类

1.1、常用方法

方法名 备注
public String getName() 返回完整类名带包名
public String getSimpleName() 返回类名
public Field[] getFields() 返回类中public修饰的属性
public Field[] getDeclaredFields() 返回类中所有的属性
public Field getDeclaredField(String name) 根据属性名name获取指定的属性
public native int getModifiers() 获取属性的修饰符列表,返回的修饰符是一个数字,每个数字是修饰符的代号【一般配合Modifier类的toString(int x)方法使用】
public Method[] getDeclaredMethods() 返回类中所有的实例方法
public Method getDeclaredMethod(String name, Class<?>… parameterTypes) 根据方法名name和方法形参获取指定方法
public Constructor<?>[] getDeclaredConstructors() 返回类中所有的构造方法
public Constructor getDeclaredConstructor(Class<?>… parameterTypes) 根据方法形参获取指定的构造方法
public native Class<? super T> getSuperclass() 返回调用类的父类
public Class<?>[] getInterfaces() 返回调用类实现的接口集合
public T newInstance() 创建对象

1.2、示例

 public class Main {
 
     public static void main(String[] args) {
         try {
             Class<?> clazz = Class.forName("java.util.ArrayList");
             System.out.println(clazz.getName());
         } catch (ClassNotFoundException e) {
             // TODO Auto-generated catch block
             e.printStackTrace();
         }
     }
 
 }

1.3、newInstance()

通过反射实例化对象

 public class MethodTest {
     public static void main(String[] args) throws InstantiationException, IllegalAccessException {
         try {
             Class clazz = Class.forName("java.util.ArrayList");
             //得到对象
             Object object = clazz.newInstance();
             //强转为ArrayList类型
             ArrayList list = (ArrayList)object;
             list.add("hello");
             System.out.println(list.get(0));
         } catch (ClassNotFoundException e) {
             // TODO Auto-generated catch block
             e.printStackTrace();
         }

     }
 
 }

2、Method类

2.1、常用方法

方法名 备注
public String getName() 返回方法名
public int getModifiers() 获取方法的修饰符列表,返回的修饰符是一个数字,每个数字是修饰符的代号【一般配合Modifier类的toString(int x)方法使用】
public Class<?> getReturnType() 以Class类型,返回方法类型【一般配合Class类的getSimpleName()方法使用】
public Class<?>[] getParameterTypes() 返回方法的修饰符列表(一个方法的参数可能会有多个。)【结果集一般配合Class类的getSimpleName()方法使用】
public Object invoke(Object obj, Object… args) 调用方法

2.2、得到所有方法

 public class MethodTest {
     public static void main(String[] args) {
         try {
             Class clazz = Class.forName("java.util.ArrayList");
             Method[] methods = clazz.getDeclaredMethods();
             for (Method method : methods) {
                 System.out.println(method.getName());
             }
         } catch (ClassNotFoundException e) {
             // TODO Auto-generated catch block
             e.printStackTrace();
         }

     }
 
 }

2.3、invoke方法

invoke方法:对带有指定参数的指定对象调用由此 Method 对象表示的基础方法

方法.invoke(对象, 实参);

 public class MethodTest {
     public static void main(String[] args) throws Exception {

         Class clazz = Operator.class;
         //创建对象
         Object object = clazz.newInstance();
         //得到方法
         Method method = clazz.getMethod("add", Integer.class, Integer.class);
         //Method method = clazz.getMethod("add", int.class,int.class);基本数据类型写法
         //调用方法
         Object result = method.invoke(object, 1, 1);
         System.out.println(result);
 
     }
 
 }
 
 class Operator {
     public int add(Integer a, Integer b) {
         return a + b;
     }
 
     public int sub(Integer a, Integer b) {
         return a - b;
     }
 }

3、Field类

3.1、常用方法

方法名 备注
public String getName() 返回属性名
public int getModifiers() 获取属性的修饰符列表,返回的修饰符是一个数字,每个数字是修饰符的代号【一般配合Modifier类的toString(int x)方法使用】
public Class<?> getType() 以Class类型,返回属性类型【一般配合Class类的getSimpleName()方法使用】
public void set(Object obj, Object value) 设置属性值
public Object get(Object obj) 读取属性值
public void setAccessible(true) 允许访问私有属性

3.2、迭代所有属性

注意这里使用的是getDeclaredFields

 public class FieldTest {
     public static void main(String[] args) {
         try {
             Class clazz = Class.forName("java.util.ArrayList");
             Field[] fields = clazz.getDeclaredFields();
             for (Field field : fields) {
                 System.out.println(field.getName());
             }
         } catch (ClassNotFoundException e) {
             // TODO Auto-generated catch block
             e.printStackTrace();
         }

     }
 
 }

3.3、给属性赋值

属性.set(对象, 值);

属性.get(对象);

注意这里使用的是getDeclaredFields

 public static void main(String[] args) throws Exception {
         try {
             //Class<Employee> clazz = Employee.class; 也可以考虑引入泛型,更直观一些
             Class clazz = Class.forName("com.reflect.Employee");
             //创建对象
             Object obj = clazz.newInstance();
             //得到name字段
             Field empField = clazz.getDeclaredField("empId");
             //允许访问私有属性
             empField.setAccessible(true);
             // 给name属性赋值
             empField.set(obj, "1001");
             //输出EmpId的值
             System.out.println(empField.get(obj));
         } catch (ClassNotFoundException e) {
             // TODO Auto-generated catch block
             e.printStackTrace();
         }

     }

4、Constructor类

4.1、常用方法

方法名 备注
public String getName() 返回构造方法名
public int getModifiers() 获取构造方法的修饰符列表,返回的修饰符是一个数字,每个数字是修饰符的代号【一般配合Modifier类的toString(int x)方法使用】
public Class<?>[] getParameterTypes() 返回构造方法的修饰符列表(一个方法的参数可能会有多个。)【结果集一般配合Class类的getSimpleName()方法使用】
public T newInstance(Object … initargs) 创建对象【参数为创建对象的数据】

4.2、示例

     public static void main(String[] args) throws Exception  {
         try {
             Class clazz = Class.forName("com.reflect.Employee");

             //获取无参构造方法
             //Constructor con = clazz.getDeclaredConstructor();
             // 第一步:先获取到这个有参数的构造方法
             Constructor cons= clazz.getDeclaredConstructor(String.class,String.class,float.class);
             // 第二步:调用构造方法创建对象
             Object obj = cons.newInstance("1001","tom",8000f);
             System.out.println(obj);
         } catch (ClassNotFoundException e) {
             // TODO Auto-generated catch block
             e.printStackTrace();
         }

     }

三、Properties

1、简介

Properties 类表示了一个持久的属性集,是一个Map体系集合类,因为其继承于Hashtable,而Hashtable继承于Dictionary类,实现了Map接口,所以Properties也保留了其相关特性。

Properties特点:

  • Properties是Hashtable<Object,Object>的子类;
  • Properties类表示了一个可持久的属性集;
  • Properties可以保存在流中或从流中加载;
  • Properties中每个键和对应的值都是一个字符串(String类型);
  • Properties有一个特殊的作用:专门用来加载xxx.properties配置文件。

2、properties配置文件作用

properties配置主要的作用是通过修改配置文件可以方便地修改代码中的参数,实现不用改class文件即可灵活变更参数。

在Java中,其配置文件常为.properties文件,格式为文本文件,文件的内容的格式是“键=值”的格式,文本注释信息可以用”#“来注释

jdbc.properties文件示例

 db.driver=com.mysql.cj.jdbc.Driver
 db.url=jdbc:mysql://localhost:3306/store?useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai
 db.username=root
 db.password=root

java运行中java文件会变成class文件,之后无法通过反编译找到原样的代码,这样的话,如果java类中某个参数变更,就很难灵活的实现参数修改,这个时候properties 文件就能很灵活的实现配置,减少代码的维护成本和提高开发效率

3、常用方法

它提供了几个主要的方法:

方法 说明
getProperty ( String key) 用指定的键在此属性列表中搜索属性。也就是通过参数 key ,得到 key 所对应的 value
load ( InputStream is) 从输入流中读取属性列表(键和元素对)。通过对指定的文件进行装载来获取该文件中的所有键 - 值对。以供 getProperty ( String key) 来搜索
setProperty ( String key, String value) 调用 Hashtable 的方法 put 。他通过调用基类的put方法来设置 键 - 值对
store ( OutputStream out, String comments) 以适合使用 load 方法加载到 Properties 表中的格式,将此 Properties 表中的属性列表(键和元素对)写入输出流。与 load 方法相反,该方法将键 - 值对写入到指定的文件中去
clear () 清除所有装载的 键 - 值对。该方法在基类中提供

4、Java读取Properties文件

Java读取Properties文件的方法有很多,常见有以下几种方式:

使用方式 文件位置
new BufferedInputStream(new FileInputStream("source.properties")) 项目根目录
Class.getClassLoader().getResourceAsStream("source.properties") src下(ClassPath根)
Class.getResourceAsStream(String path) 此类所在的包下取资源

path不能以’/’开头

aa.properties文件

 age=22
 name=\u5F20\u4E09

读取资源文件中内容

 InputStream  is = Main.class.getClassLoader().getResourceAsStream("aa.properties");
 properties.load(is);
 Object obj = properties.get("age");
 Object name = properties.get("name");

思维导图

image

posted @ 2025-04-09 14:08  icui4cu  阅读(21)  评论(0)    收藏  举报