java编程学习之反射技术及其应用

Java是一种可以撰写跨平台应用软件的面向对象的程序设计语言。Java 技术具有卓越的通用性、高效性、平台移植性和安全性,广泛应用于PC、数据中心、游戏控制台、科学超级计算机、移动电话和互联网,同时拥有全球最大的开发者专业社群。

给你学习路线:html-css-js-jq-javase-数据库-jsp-servlet-Struts2-hibernate-mybatis-spring4-springmvc-ssh-ssm

翻阅了很多资料想查看一个比较通俗易懂的关于java反射机制的理解,但是想要从概念中去理解一项技术,可能有点困难,所以先将理论型知识贴出来,后面,慢慢来理解。

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

注意:要理解java的反射机制,先要了解以下基本概念:

运行时,编译时,编译型,解释型,类加载器,动态加载类等相关概念。

小编推荐一个学Java的学习裙【 六五零,五五四,六零七 】,无论你是大牛还是小白,是想转行还是想入行都可以来了解一起进步一起学习!裙内有开发工具,很多干货和技术资料分享!

一. Class类的使用

概念理解

在Java中,每个class都有一个相应的Class对象。也就是说,当我们编写一个类,编译完成后,在生成的.class文件中,就会产生一个Class对象,用于表示这个类的类型信息。

获取Class实例的方式

不能直接创建Class的实例对象,因为Class类的构造方法是私有的,只有jvm可以去创建。

(1) 利用对象调用getClass()方法获取该对象的Class实例;

(2) 使用Class类的静态方法forName(),用类的名字获取一个Class实例,源码如下;

123456@CallerSensitivepublic static Class forName(String className) throws ClassNotFoundException { Class caller = Reflection.getCallerClass(); return forName0(className, true, ClassLoader.getClassLoader(caller), caller);}

(3)运用.class的方式获取Class实例,对于基本数据类型的封装类,还可以采用TYPE来获取对应的基本数据类型的Class实例

综上所述,其实我们代码中创建的每一个类都是一个对象,只不过她是Class类的实例对象,这个对象我们称为该类的类类型。并且一个类只可能是Class类的一个实例对象,即获取的类类型是相同的

那么,如何去创建Class的实例呢?

首先,过程要理解,源文件经过编译(javac.exe)以后,得到一个或多个.class文件。.class文件经过运行(java.exe)这步,就需要进行类的加载(通过JVM的类的加载器),记载到内存中的缓存。每一个放入缓存中的.class文件就是一个Class的实例!下面是创建Class实例的三种方法。

实例如下:

12345678910111213141516171819202122232425public class ReflectTest { public void display(){ System.out.println("Hello World!"); } public static void main(String[] args){ ReflectTest reflectTest = new ReflectTest(); Class clazz1 = reflectTest.getClass(); Class clazz2 = ReflectTest.class; Class clazz3 = null; try { clazz3 = Class.forName("com.william.test.ReflectTest"); } catch (ClassNotFoundException e) { e.printStackTrace(); } System.out.println(clazz1==clazz2); System.out.println(clazz1==clazz3); System.out.println(clazz2==clazz3); }}output:truetruetrue

其实,还有一种创建Class实例的方法,是通过类的加载器,如下:

1234567891011121314@Testpublic void LoadClassTest(){ ClassLoader loader = this.getClass().getClassLoader(); Class clazz4 = null; try { clazz4 = loader.loadClass("com.william.test.ReflectTest"); } catch (ClassNotFoundException e) { e.printStackTrace(); } System.out.println(clazz4);}output:class com.william.test.ReflectTest

如何根据类类型去创建类的实例呢?

12345678910111213//通过类的类型创建该类的实例对象ReflectTest reflectTest1 = null;try { reflectTest1 = (ReflectTest) clazz1.newInstance();//需要类有无参的构造方法} catch (InstantiationException e) { e.printStackTrace();} catch (IllegalAccessException e) { e.printStackTrace();}reflectTest.display();output:Hello World!

动态加载类

Class.forName("类的全称")

表示类的类类型,还代表了动态加载类

区分上面所说的编译,运行

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

关于动态加载类的实例代码请参考:http://www.imooc.com/video/3733

java获取方法信息

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107/***打印类的信息,包括类的成员函数,成员变量*/public class ClassUtil { /** * 获取成员函数 * object 该对象所属类的信息 * * @param object */ public static void printMethodMessage(Object object) { //要获取类的信息,首先要获取类的类类型 Class clazz = object.getClass();//传递的是哪个子类的对象, clazz就是该子类的类类型 //获取类的名称 System.out.println("类的名称是:" + clazz.getName()); /** * Method类,方法对象 * 一个成员方法就是一个Method对象 * getMethods()方法获取的是所有的public的函数,包括父类继承而来的 * getDeclaredMethods()获取的是所有该类自己声明的方法,不问访问权限 */ Method[] methods = clazz.getDeclaredMethods(); for (int i = 0; i < methods.length; i++) { //1. 获取注解 Annotation[] ann = methods[i].getAnnotations(); for (Annotation a : ann) { System.out.println(a); } //2. 获取权限修饰符 String str = Modifier.toString(methods[i].getModifiers()); System.out.print(str + " "); //3. 得到方法的返回值类型的类类型 Class returnType = methods[i].getReturnType(); System.out.print(returnType.getName() + " "); //4. 得到方法的名称 System.out.print(methods[i].getName() + "("); //5.获取参数类型-->得到的是参数列表的类型的类类型 Class[] paramTypes = methods[i].getParameterTypes(); //解析数组 for (int j = 0; j < paramTypes.length; j++) { if (j == 1 || j == paramTypes.length - 1) { System.out.print(paramTypes[j].getName() + " args" + j); } else { System.out.print(paramTypes[j].getName() + " args" + j + ","); } } System.out.print(")"); //6.获取异常类型 Class[] exps = methods[i].getExceptionTypes(); if (exps.length != 0) { System.out.print(" throws "); } for (int k = 0; k < exps.length; k++) { System.out.print(exps[k].getName() + " "); } System.out.println(); } } }class MethodTest { private String name; //注解类型在java之注解开发章节讲解过的 @MyAnnotation(wd = WeekDay.MON,id = 4) public int age; static String desc = "这是一个人"; public MethodTest() { } private MethodTest(String name, int age) { this.name = name; this.age = age; } @Deprecated public void print(int a, int b) throws Exception { System.out.println(a + b); } public void print(String a, String b) { System.out.println(a.toUpperCase() + "," + b.toLowerCase()); } @Override public String toString() { return "MethodTest{" + "name='" + name + ''' + ", age=" + age + '}'; }//调用MethodTest methodTest = new MethodTest();ClassUtil.printMethodMessage(methodTest);output:类的名称是:com.william.test.MethodTestpublic java.lang.String toString()@java.lang.Deprecated()public void print(int args0,int args1) throws java.lang.Exceptionpublic void print(java.lang.String args0,java.lang.String args1)

获取方法的信息,主要通过Method类数组来接受getMethods()方法的返回值,然后进行遍历解析。

获取成员变量信息

获取成员变量的信息,同获取方法的信息类似,只不过使用Field类数组来接收getFieldss()方法的返回值,然后进行解析。代码如下:

1234567891011121314151617181920212223242526272829303132333435363738394041/*** 获取成员变量*/public static void printFieldMessage(Object object) { //要获取类的信息,首先要获取类的类类型 Class clazz = object.getClass();//传递的是哪个子类的对象, clazz就是该子类的类类型 //获取类的名称 System.out.println("类的名称是:" + clazz.getName()); /** * 成员变量也是对象 * java.lang.reflect.Field * Field类封装了关于成员变量的操作 * getFields()方法获取的是所有的public的成员变量的信息 * getDeclaredFields获取的是该类自己声明的成员变量的信息 */ Field[] fields = clazz.getDeclaredFields(); for (Field field : fields) { //获取每个属性的权限修饰符 int i = field.getModifiers(); String str = Modifier.toString(i); //得到成员变量的类型的类类型 Class fieldType = field.getType(); String typeName = fieldType.getName(); //得到成员变量的名称 String fieldName = field.getName(); System.out.println(str + " "+ typeName + " " + fieldName); }}//调用String str = "Hello World!";ClassUtil.printFieldMessage(str);output:类的名称是:java.lang.String类的名称是:java.lang.Stringprivate final [C valueprivate int hashprivate static final long serialVersionUID

获取构造函数

12345678910111213141516171819202122232425262728293031323334353637383940414243/*** 获取对象的构造函数的信息** @param object*/public static void printConMessage(Object object) { //要获取类的信息,首先要获取类的类类型 Class clazz = object.getClass();//传递的是哪个子类的对象, clazz就是该子类的类类型 //获取类的名称 System.out.println("类的名称是:" + clazz.getName()); /** * 构造函数也是对象 * java.lang.Constructor中封装了构造函数的信息 * getConstructors获取所有的public的构造函数 * getDeclaredConstructors得到所有的构造函数 */ Constructor[] constructors = clazz.getConstructors(); for (Constructor constructor : constructors) { System.out.print(constructor.getName() + "("); //获取构造函数的参数列表--->得到的是参数列表的类类型 Class[] paramTypes = constructor.getParameterTypes(); for (Class clazz1 : paramTypes) { System.out.print(clazz1.getName() + ","); } System.out.println(")"); }}//调用String str = "Hello World!";ClassUtil.printConMessage(str);output:类的名称是:java.lang.Stringjava.lang.String([B,int,int,)java.lang.String([B,java.nio.charset.Charset,)java.lang.String([B,java.lang.String,)java.lang.String([B,int,int,java.nio.charset.Charset,)java.lang.String([B,int,int,java.lang.String,)java.lang.String(java.lang.StringBuilder,)java.lang.String(java.lang.StringBuffer,)

小编推荐一个学Java的学习裙【 六五零,五五四,六零七 】,无论你是大牛还是小白,是想转行还是想入行都可以来了解一起进步一起学习!裙内有开发工具,很多干货和技术资料分享!

二. 方法的反射

方法的反射

1) 如何获取某个方法

方法的名称和方法的参数里列表才能唯一决定某个方法

2) 方法反射的操作

method.invoke(对象,参数里列表)

示例代码:

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263public class MethodReflect { public static void main(String[] args){ //要获取print(int,int)方法 1. 要获取一个方法就是获取类的信息,获取类的信息首先要获取类的类类型 MethodTest methodTest = new MethodTest(); Class clazz = methodTest.getClass(); try { //Method method = clazz.getMethod("print",new Class[]{int.class,int.class}); Method method = clazz.getMethod("print",int.class,int.class); //以前的方法的方法调用 //methodTest.print(10,20); //方法的反射操作 //方法如果没有返回值返回null,有返回值返回具体的返回值 //method.invoke(methodTest,new Object[]{10,20}); Object o = method.invoke(methodTest,10,20); System.out.println("============================"); //获取方法print(String,String) Method method1 = clazz.getMethod("print",String.class,String.class); //用方法进行反射操作 o = method1.invoke(methodTest,"hello","WORLD!"); } catch (Exception e) { e.printStackTrace(); } }}class MethodTest { private String name; public int age; static String desc = "这是一个人"; public MethodTest() { } private MethodTest(String name, int age) { this.name = name; this.age = age; } public void print(int a, int b) { System.out.println(a + b); } public void print(String a, String b) { System.out.println(a.toUpperCase() + "," + b.toLowerCase()); } @Override public String toString() { return "MethodTest{" + "name='" + name + ''' + ", age=" + age + '}'; }}output:30============================HELLO,world!

三. 成员变量的反射

实例如下:

1234567891011121314151617181920212223242526//成员变量的反射Class clazz1;try { clazz1 = Class.forName("com.william.test.MethodTest"); Object object = clazz1.newInstance(); MethodTest methodTest1 = (MethodTest) object; //调用private属性 Field field1 = clazz1.getDeclaredField("name"); field1.setAccessible(true); field1.set(methodTest1,"william"); System.out.println(methodTest1.toString()); //调用public的属性 Field field2 = clazz1.getField("age"); field2.set(methodTest1,9); System.out.println(methodTest1.toString()); //调用static属性 Field field3 = clazz1.getDeclaredField("desc"); System.out.println(field3.get(MethodTest.desc)); //System.out.println(field3.get(null));} catch (Exception e) { e.printStackTrace();}output:MethodTest{name='william', age=0}MethodTest{name='william', age=9}这是一个人

四. 构造函数的反射

实例如下:

12345678910111213//构造函数的反射Class clazz3 = MethodTest.class;try { Constructor constructor = clazz3.getDeclaredConstructor(String.class,int.class); constructor.setAccessible(true); MethodTest methodTest2 = (MethodTest) constructor.newInstance("will",99); System.out.println(methodTest2.toString());} catch (Exception e) { e.printStackTrace();}output:MethodTest{name='will', age=99}

五. 认识泛型的本质

先来看一个实例:

1234567891011121314151617181920212223242526public class FanXingTest { public static void main(String[] args) { ArrayList list = new ArrayList(); ArrayList list1 = new ArrayList(); list1.add("hello"); //list1.add(20);编译错误 Class c1 = list.getClass(); Class c2 = list1.getClass(); System.out.println(c1 == c2); //反射的操作都是编译之后的操作 try { Method m = c2.getMethod("add",Object.class); m.invoke(list1,20);//绕过编译操作就绕过了泛型 System.out.println(list1.size()); System.out.println(list1); } catch (Exception e) { e.printStackTrace(); } }}output:true2[hello, 20]

通过返回结果,我们可以看到在编译之后集合的泛型是去泛型化的,java中集合类型的泛型,是防止错误输入的,只在编译阶段有效,绕过编译就无效了,所以我们通过方法的反射来操作,可以绕过编译。

六. 反射应用之动态代理

动态代理是指客户通过代理类来调用其他对象的方法,并且是在程序运行时根据需要动态创建目标类的代理对象。

实例代码:

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849//动态代理的使用interface Subject { void action();}//被代理类class RealSubject implements Subject { @Override public void action() { System.out.println("我是被代理类,记得要执行我奥,么么~~~"); }}class MyInvocationHandler implements InvocationHandler { Object object;//实现了接口的被代理类的对象的声明 //①给被代理的对象实例化 ②返回一个代理类的对象 public Object blind(Object object) { this.object = object; return Proxy.newProxyInstance(object.getClass().getClassLoader(), object.getClass().getInterfaces(), this); } //当通过代理类的对象发起对被重写的方法的调用时,都会转化为对如下的invoke方法的调用 @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { //method方法的返回值是returnVal Object returnVal = method.invoke(object,args); return returnVal; }}public class ProxyTest { public static void main(String[] args){ //1. 创建被代理类对象 RealSubject realSubject = new RealSubject(); //2. 创建一个实现了InvocationHandler接口的类的对象 MyInvocationHandler handler = new MyInvocationHandler(); //3. 调用blind()方法,冬天的返回一个同样实现了real所在类实现的接口Subject的代理类的对象。 Object object = handler.blind(realSubject); Subject subject = (Subject) object;//此时sub就是代理类的对象 subject.action();//转到对InvacationHandler接口的实现类的invoke()方法的调用 }}output:我是被代理类,记得要执行我奥,么么~~~

说明,了解动态代理和静态代理的区别,所谓的静态代理,其代理类和目标对象的类在编译期间就确定下来,不利于程序的扩展。即,每一个代理类只能为一个接口服务,也就是说程序开发中会产生很多代理类。

七. 动态代理与AOP

由:

转换为:

实例:

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980interface Human { void info(); void fly();}class SuperMan implements Human { @Override public void info() { System.out.println("我是超人!"); } @Override public void fly() { System.out.println("I believe I can fly!"); }}class HumanUtil { public void method1() { System.out.println("=============方法一============"); } public void method2() { System.out.println("=============方法二============"); }}class MyInvocationHandler implements InvocationHandler { Object object;//被代理类对象的声明 public void setObject(Object object) { this.object = object; } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { HumanUtil h = new HumanUtil(); h.method1(); Object returnVal = method.invoke(object, args); h.method2(); return returnVal; }}//动态的创建一个代理类的对象class MyProxy { public static Object getProxyInstance(Object object) { MyInvocationHandler handler = new MyInvocationHandler(); handler.setObject(object); return Proxy.newProxyInstance(object.getClass().getClassLoader(), object.getClass().getInterfaces(), handler); }}public class TestAOP { public static void main(String[] args){ SuperMan man = new SuperMan();//创建一个被代理类的对象 Human human = (Human) MyProxy.getProxyInstance(man);//返回一个代理类的对象 human.info();//通过代理类的对象嗲用重写的抽象的方法 System.out.println(); human.fly(); }}output:=============方法一============我是超人!=============方法二=========================方法一============I believe I can fly!=============方法二============

AOP代理的方法:

小编推荐一个学Java的学习裙【 六五零,五五四,六零七 】,无论你是大牛还是小白,是想转行还是想入行都可以来了解一起进步一起学习!裙内有开发工具,很多干货和技术资料分享!

相关概念

什么是编译?

答:将原程序翻译成计算机语言,就是二进制代码,在java中是将.java文件也就是源程序翻译成.class的字节码

什么是编译时?

答:将原程序翻译成计算机语言的过程中,将.java翻译为.class文件的过程

什么是运行时?

答:就是在启动这个程序的时候,在java中是,类加载器加载.class文件,并交给jvm处理

什么是编译型语言?

答:将原程序一次性全部转换为二进制代码,然后执行程序

什么是解释型语言?

答:转换一句,执行一句,java是既编译又解释的语言

编译型语言和解释型语言的区别:

答:编译型语言效率高,依赖于编译器,但是跨平台差,解释型的效率低,依赖于解释器,但跨平台强

什么是类加载器?

答:类加载器就是JVM中的类装载器,作用就是将编译好的.class字节码运到检查器进行安全检查的,检查通过后开始解释执行

什么是运行时动态加载类?

答:反射就是可以将一个程序(类)在运行的时候获得该程序(类)的信息的机制,也就是获得在编译期不可能获得的类的信息,因为这些信息是保存在Class对象中的,而这个Class对象是在程序运行时动态加载的

它就是可以在程序运行的时候动态装载类,查看类的信息,生成对象,或操作生成对象。类在运行的时候,可以得到该类的信息,并且 可以动态的修改这些信息,自己能看到自己,跟照镜子一样,class对象是在运行的时候产生的,通过class对象操作类的信息是在运行时进行的,当运行 程序的时候,类加载器会加载真正需要的类,什么是真正需要的呢?就是该类真正起作用,如:有该类的对象实例,或该类调用了静态方法属性等

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,457评论 5 459
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,837评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,696评论 0 319
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,183评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,057评论 4 355
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,105评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,520评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,211评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,482评论 1 290
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,574评论 2 309
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,353评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,213评论 3 312
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,576评论 3 298
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,897评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,174评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,489评论 2 341
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,683评论 2 335

推荐阅读更多精彩内容