Java:反射学习的整理

mac2026-10-02  0

1.前言

个人对于反射的学习总结以及经验,在认真学习反射之前,一直不能理解为什么要用反射,也不知道反射主要运用点在哪里,但是听说反射很重要。

反射是什么? 反射的使用是一种动态编程,主要是可以在运行中创建对象,调用方法、可以获取类中的一切。反射可以干什么? (1)代理模式 代理模式被用在Spring的AOP中 代理模式在日志实现中例如log4j (2)获取类中的全部信息(变量、方法这些,还要就是注解,自定义注解)。 (3)有一句话说的好,不存在完美的框架,虽然反射会破坏封装性,但是也可以让你修改原本封装好的私有变量,或者私有方法。 (4)跳过编译过程

注:代码段都是用了junit进行单员测试写的,需要获得源码可以去我的github获取 在reflex分支 我的github地址

2反射的学习

2.1 概念

在面向对象的世界里,万事万物皆对象。(java语言中,静态的成员、普通数据类型除外) 类是不是对象呢?类是(哪个类的对象呢?)谁的对象呢? 类是对象,类是java.lang.Class类的实例对象

2.2 这个对象到底如何表示

/** * java是面向对象编程的语言,所以全部都是对象 * 类是不是对象?是谁的对象? * 类是对象,类是java.lang.Class类的实例对象 *任何一个类都是Class的实例对象,这个实例对象有三种表示方式 */ /** * java是面向对象编程的语言,所以全部都是对象 * 类是不是对象?是谁的对象? * 类是对象,类是java.lang.Class类的实例对象 *任何一个类都是Class的实例对象,这个实例对象有三种表示方式 */ @Test public void test2() throws ClassNotFoundException { /** * 说明每一个类中都有一个class静态变量 */ Class s = Student.class; /** * 每一个对象中可以调用getClass()方法获取本对象Class */ Student a =new Student(); Class s1 = a.getClass(); /** * 知道类的路径可以使用Class的静态方法来获取类 */ Class s2 = Class.forName("com.imooc.reflex.Student"); /** * 三种方法获取的类的类对象都是一样的 */ System.out.println(s1 == s2); System.out.println(s2 == s); }

2.3 跳过编译过程

首先泛型的概念大家都知道、但是集合类中泛型在底层实现的时候存放的都是Object类型,基本所有的基本类型或者自己创建的对象都是Object的子类,所以我一个ArraysList 也可以存放String的变量,只要在编译阶段使用反射跳过泛型,不然直接调用add方法,编译会过不去,就会报错。

/** * 泛型只是为了防止编译过程中传入不符合自己定义泛型的对象(输入错误) * 还说明编译以后的泛型是去泛型化的,泛型只在编译阶段有效,反射会绕过编译 * 底层存储还是用Object类型存储的,下面就来证明这个。 * 这个例子就是往ArrayList<Interge>泛型中插入字符串 */ @Test public void test3() throws Exception { ArrayList<Integer> arr1 = new ArrayList<Integer>(); arr1.add(1); Class c = arr1.getClass(); Method m = c.getMethod("add", Object.class); m.invoke(arr1,"字符串"); for(Object i : arr1){ System.out.println(i); } }

结果如下图: 可见ArraysList里面保存了字符串。

2.5获取基本的数据类型

/** * 反射还能获取类中所有属性、方法、注解、构造方法、私有属性..... */ @Test public void test4() throws Exception { /** * 获取属性,这样只能获取public 属性 */ Class c = Person.class; Field[] f = c.getFields(); for(Field i : f){ System.out.println(i.getName()); } /** * 获取本类所有私有的属性 */ Field[] t = c.getDeclaredFields(); for(Field i:t){ System.out.println(i.getName()); } }

2.5获取类中的所有方法

@Test public void test4() throws Exception { /** *获取类中的所有方法并且使用 */ Person t1 = (Person) c.newInstance(); Method[] m =c.getMethods(); for(Method i : m){ if(i.getName() .equals("setEmail") ){ i.invoke(t1,"1160827860@qq.com"); }else if(i.getName().equals("setName")){ i.invoke(t1,"lzy"); }else if(i.getName().equals("setAge")){ i.invoke(t1,21); } } Method p = c.getMethod("print"); p.invoke(t1); }

2.6获取构造方法,并且使用

@Test public void test4() throws Exception { /** * 获取构造方法,并且使用 */ Constructor construct = c.getConstructor(String.class,int.class,String.class); t1 = (Person)construct.newInstance("sz",25,"a1160827860@163.com"); p.invoke(t1); }

2.7其他需要注意的问题

2.7.1编译、运行中加载类

编译时刻加载类是静态加载类、运行时刻加载类是动态加载类

2.7.2其他的关键字

void关键字都存在类类型

2.7.3为什么要使用反射

就像底下这个代码一样: 比如我们完成了Peson类,但是noExist类不存在,如果采用new 创建一个对象的方式,就无法运行,如果使用反射,就可以运行。 1.在项目中如果要先对已经完成的代码,进行测试就可以采用动态加载的方式。 2.例如企业中更新软件,不是删除原来的代码,而是采用动态加载的方式

/** * 静态加载在编译的时候加载,例如本例下面,如果没有Student * 就会报错,但是我有Person类,我输入Person也不能过编译 * 动态加载就可以避免这种情况(也就是使用反射的方法) * 静态加载引发的问题 */ @Test public void test(){ String chose = "Student"; if(chose.equals("Person")){ Person p = new Person(); p.toString(); }else if(chose.equals("Student")){ Student t = new Student(); t.toString(); } // else{ // noExist no = new noExist(); // } } /** * 使用动态加载,来解决上面这个问题 * 虽然没有noExist类但是可以允许使用其他分支 * 而第一个在编译阶段都过不去 */ @Test public void test1() throws Exception { String chose ="Person"; if(chose.equals("Person")){ Person p = new Person(); System.out.println(p.toString()); }else if("noExist".equals(chose)){ Class c = Class.forName("com.imooc.reflex.noExist"); Object e = c.newInstance(); e.toString(); } }

最新回复(0)