计算机系统应用教程网站

网站首页 > 技术文章 正文

Java反射机制的使用

btikc 2024-09-22 01:10:20 技术文章 23 ℃ 0 评论

JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性;这种动态获取的信息以及动态调用对象的方法的功能称为java语言的反射机制。—百度百科

动态:运行期间动态绑定执行规则。

静态:编译以后就已经确定的执行过程。

一、动态获取类的信息

动态获取类的方法信息

Foo类(package:demo):

 //编译以后是 demo.Foo.class
 //类的全名是 demo.Foo
 class Foo{
     public int test(){
         return 5;
     }
     public double test1(){
         return 5d;
     }
 }

动态获取Foo类的方法:

 @Test
 public void testClassForName(){
   /*
    * 动态的加载类信息到方法区
    * 并且返回对应的Class对象!
    * Class 对象可以访问类的全部信息!
    * 
    * 将className对应的类文件,从磁盘中加载
    * 内存方法区,返回这个类的信息
    */
     String className = "demo.Foo";
     try {
         Class<?> cls = Class.forName(className);
         Method[] all = cls.getDeclaredMethods();
         for (Method method : all) {
             System.out.println(method.getName());
         }
     } catch (ClassNotFoundException e) {
         e.printStackTrace();
     }
 }
 
 //输出:
 // test
 // test1

动态获取类的属性信息

Eoo类:

 class Eoo{
     int id;
     String name;
     double salary;
     String meno;
     
     public Eoo(){}
     
     public Eoo(int id, String name, double salary, String meno) {
         super();
         this.id = id;
         this.name = name;
         this.salary = salary;
         this.meno = meno;
     }
     // get,set略
 }

动态获取Eoo类的属性:

 @Test
 public void testField()throws Exception {
   /*
    * 动态获取一个类的全部属性信息
    * 1 动态加载一个类到方法区
    * 2 动态获取类的属性信息
    */
     String className = "demo.Eoo";
     // 动态加载类
     Class<?> cls = Class.forName(className);
     // 动态获取类声明的属性信息
     Field[] all = cls.getDeclaredFields();
     for (Field field : all) {
         // getName 获取属性的名字
         System.out.print(field.getName() + " ");
     }
 }
 
 // id name salary meno

动态获取类的构造器信息

 @Test
 public void testCon() throws Exception{
   /* 
    * 1 动态加载类
    */
     String className = "demo.Eoo";
     Class<?> cls = Class.forName(className);
     
     Constructor[] all = cls.getDeclaredConstructors();
     for (Constructor c : all) {
         System.out.print(c.getName());
         //获取构造器的参数类型列表
         // Parameter 参数  Type类型
         // Class[] 代表所有参数的类型列表
         Class[] types = c.getParameterTypes();
         System.out.println(Arrays.toString(types));
     }
 }
 
 // demo.Eoo[]
 // demo.Eoo[int, class java.lang.String, double, class java.lang.String]

二、动态创建对象

调用无参构造器创建对象

如果没有无参数构造器,将发生异常!Class 提供了方法 newInstance()。

 @Test
 public void testNewInstance() throws Exception{
   /*
    * 动态调用无参数构造器创建对象
    * 1 动态加载类
    * 2 利用class 的方法 newInstance 执行
    * 无参数构造器常见对象
    * 注意:类必须有无参数,否则出异常 
    */
     String className = "java.util.Date";
     Class<?> cls = Class.forName(className);
     // cls.newInstance()调用无参数构造器创建对象
     Object obj = cls.newInstance();
     System.out.println(obj);
     
     //静态的创建对象!编译已经就固定了!
     Date date = new Date();
 }
 
 // Fri Sep 16 20:04:55 CST 2020

调用有参构造器创建对象

如果没有对应有参数构造器!将发生异常!参数传递错误、将发生异常!

 /**
  * 调用 className 类名对应的类的有参数构造器,paramTypes 代表对应构造器的参数列表
  * className + paramTypes 共同决定调用哪个构造器!执行构造器还需要具体的参数params
  */
 public Object create(String className,Class[] paramTypes,Object[] params)
     throws Exception{
     // 动态加载类
     // 动态获取指定参数类型的构造器
     // 执行这个构造器,传递 params 参数。
     Class<?> cls = Class.forName(className);
     //getDeclaredConstructor 在类信息中查找
     //给定参数类型的构造器信息
     Constructor c = cls.getDeclaredConstructor(paramTypes);
     //执行构造器 c.newInstance() 方法,创建对象
     //返回值就是这个构造器创建的对象
     Object obj = c.newInstance(params);
     return obj;
 }
 
 @Test
 public void testCreate() throws Exception {
     String className = "java.util.Date";
     //类型列表==Class类型的数组
     Class[] paramTypes = {long.class};
     //实际参数列表
     Object[] params={-1000L * 60 * 60 * 24 * 365};
     Object obj = create(className, paramTypes, params);
     System.out.println(obj);
     //思考:如何动态调用 new String("Hello");
     className = "java.lang.String";
     /*
      * {} 只能拥有声明变量时候直接初始化
      * 不能用于赋值语句!
      * 赋值语句可以使用 new Object[]{"Hello"} 
      */
     paramTypes = new Class[]{String.class};
     params = new Object[]{"Hello"};
     obj = create(className, paramTypes, params);
     System.out.println(obj);//Hello
     
     //思考:如何动态调用 new String(byte[],"utf-8");
     obj = create("java.lang.String", new Class[]{byte[].class, String.class}, 
         new Object[]{new byte[]{65,66,67,68}, "UTF-8"});
     System.out.println(obj);
 }
 
 // Wed Jan 01 08:00:00 CST 1969
 // Hello
 // ABCD

三、动态获取类的属性值

实现过程(如何利用反射API实现动态属性访问):

1、找到对象的类型信息(方法区)

2、在信息中找属性信息(Field)

3、在对象上获取属性的值!

类Goo:

 public class Goo {
     public int id;
     public String name;
    
     public Goo() {}
     
     public Goo(int id, String name) {
         super();
         this.id = id;
         this.name = name;
     }
 }

动态获取属性的值:

  /**
  * 获取obj对象的 fieldName 对应属性的值
  * @param obj
  * @param fieldName
  * @return 属性值
  */
 public Object get(Object obj, String fieldName)throws Exception{
     //1 获取类信息
     /* 
      * Java 中对象的getClass()方法可以获取 对象的类型信息!
      * Java 中 有3种方法可以获取Class信息
      *  1. 类名.class 获取类信息(静态)
      *  2. Class.forName("类名") 获取类信息 动态 
      *  3. obj.getClass() 获取类信息。运行期间,通过当前对象获取类信息
      */
     Class<?> cls = obj.getClass();
     //找到属性:
     /* 
      * getDeclaredField 按照属性名在cls中查找
      * 类信息。 当属性没有找到时候,抛出异常!
      */
     Field field = cls.getDeclaredField(fieldName);
     //在对象上获取属性的值!
     /*
      * get方法:在一个对象上获取属性的值,对象上没有对应的属性,抛出异常
      */
     Object value = field.get(obj);
     return value;
 }  
  
 @Test
 public void testGetField()throws Exception {
     /*
      * 动态获取对象的属性
      */
     Goo goo = new Goo(5, "Tom");
     Object v1 = get(goo, "id");
     Object v2 = get(goo, "name");
     System.out.println(v1+" "+v2);
 }
 
 // 5 Tom

四、动态调用类的方法

 @Test
 public void testinvoke()throws Exception{
     List<String> list=new ArrayList<String>();
     list.add("tom");
     list.add("jack");
     // 动态获取类信息
     Class<?> cls = list.getClass();
     // 通过方法名和参数类型找到对应的方法
     Method method = cls.getDeclaredMethod("remove", new Class[]{int.class});
     // 调用方法,传递对象和具体参数
     Object value=method.invoke(list,new Object[]{0});
     System.out.println(value); //tom
 }

五、反射的意义

常见的框架的底层都是使用反射实现的!如:Spring、MyBatis、Struts2、Hibernate …

现有application.xml文件:

 <?xml version="1.0" encoding="UTF-8"?>
 <beans>
     <bean id="date" class="java.util.Date"></bean>
     <bean id="list" class="java.util.ArrayList"></bean>
 </beans>

模拟Spring框架的getBean()方法:

 public class ApplicationContext {
     private HashMap<String, Object> map= new HashMap<>();   
     public ApplicationContext(String xml) {
         SAXReader reader = new SAXReader();
         try{
             //读取xml
             InputStream in = this.getClass().getClassLoader().getResourceAsStream(xml);
             Document doc = reader.read(in);
             //解析XML内容 获取全部的<bean>
             List<Element> beans = doc.getRootElement().elements();
             for (Element e : beans) {
                 //e 是每个 <bean> 元素
                 String id = e.attributeValue("id");
                 String className = e.attributeValue("class");
                 //利用反射创建对象
                 Class cls = Class.forName(className);
                 Object obj = cls.newInstance();
                 //对象缓存到 map中
                 map.put(id, obj);
             }
             
         }catch(Exception e){
             e.printStackTrace();
             throw new RuntimeException(e);
         }
     }
     public Object getBean(String id){
         return map.get(id);
     }
     public static void main(String[] args) {
         String conf = "application.xml";
         ApplicationContext ac = new ApplicationContext(conf);
         Object o = ac.getBean("date");
         System.out.println(o); 
     }
 }
 
 // Fri Sep 16 21:30:07 CST 2020

Tags:

本文暂时没有评论,来添加一个吧(●'◡'●)

欢迎 发表评论:

最近发表
标签列表