当前位置: 首页 > 后端技术 > Java

你真的了解反射吗?

时间:2023-04-01 18:30:22 Java

什么是反射1.刚开始学习反射的时候,我惊呆了。这东西真是“抽象妈妈打开了抽象之门——抽象就是家”。为什么在创建objects类对象的时候需要先获取它?这不是多余的吗?我直接new不是更方便吗?程序运行时得到的类的属性和方法是什么?通常,程序编译错误,然后修改代码。为什么要考虑程序运行时的状态?我平时开发用不着,学了这个东西有什么用呢?后来学习了注解、spring、SpringMVC等技术,发现反射无处不在。2、JVM加载类我们编写的java程序需要在JVM中运行,所以要学习反射,首先要了解JVM加载类的过程。1、我们编写的.java文件称为源代码。2、我们在一个类中写了一个main方法,然后点击IDEA的运行按钮。JVM运行时,会触发jdk的javac指令,将源代码编译成.class文件,也称为字节码文件。3、JVM的类加载器(可以理解为工具)通过类的全限定名获取类的二进制字节流,然后将类文件加载到JVM的方法区中。4.当类加载器加载一个.class文件到方法区时,会在堆中生成一个唯一的Class对象。这个Class包含了这个类的成员变量、构造方法和成员方法。5、这个Class对象会创建一个该类对应的对象实例。所以表面上你创建了一个对象,但实际上JVM运行程序的时候,真正帮你创建对象的是类的Class对象。也就是说,反射其实就是JVM在运行程序时,将你创建的所有类都封装成一个唯一的Class对象。此Class对象包含属性、构造函数和成员方法。拿到Class对象后,也可以获得这三个东西。反射得到属性(Class)后,可以得到对象的属性名、属性类别、属性值,还可以为属性设置值。反射得到构造函数(Class)后,就可以创建对象了。反射得到成员方法(Class)后就可以执行方法了。3、反射的概念JAVA的反射机制就是在程序运行状态下,对于任何一个类,都可以知道这个类的所有属性和方法;对于任何对象,您都可以调用它的任何方法和属性;这种动态获取信息和动态调用对象方法的功能称为java语言的反射机制。了解了JVM加载类的过程,相信大家应该对反射的概念有了更深的理解。反射:JVM运行程序-->.java文件-->.class文件-->类对象-->创建对象实例并操作实例的属性和方法下面说一下相关类和常用方法在反思中。2.Class对象获取Class对象,先创建一个User类:publicclassUser{privateStringname="认识你";publicStringsex="男";publicUser(){}publicUser(Stringname,Stringsex){this.name=name;this.sex=sex;}publicvoideat(){System.out.println("Peoplewanttoeat!");}privatevoidrun(){System.out.println("PeoplewanttoRunning!");}publicStringgetName(){returnname;}publicvoidsetName(Stringname){this.name=name;}publicStringgetSex(){returnsex;}publicvoidsetSex(Stringsex){this.sex=sex;}}复制代码获取Class对象的三种方式:1.Class.forName("fullclassname")全类名:包名+类名ClassuserClass=Class.forName("com.xxl.model.User");复制代码2.类名.classClassuserClass=User.class;复制代码3.Object.getClass()Useruser=newUser();类userClass=user.getClass();复制代码虽然有三种获取Class对象的两种方式,但是我们一般使用上面的第一种方式。拿到Class对象后,我们就可以操作与之相关的方法了。三、获取类名1、获取完整的类名:包名+类名getName()ClassuserClass=Class.forName("com.xxl.model.User");Stringname=userClass.getName();System.out.println(名字);复制代码并打印结果:2.获取简单类名:不包括包名System.out.println(simpleName);复制代码打印结果:Attribute4.1获取Attribute1.获取所有公共属性:publicmodificationgetFields()ClassuserClass=Class.forName("com.xxl.model.User");Field[]fields=userClass.getFields();for(Fieldfield:fields){System.out.println(field);}复制代码打印结果:2.获取单个公共属性getField("propertyname")ClassuserClass=Class.forName("com.xxl.model.User");Field字段=userClass.getField("sex");System.out.println(field);复制代码打印结果:3.获取所有属性:public+privategetDeclaredFields()ClassuserClass=Class.forName("com.xxl.model.User");Field[]fields=userClass.getDeclaredFields();for(Fieldfield:fields){System.out.println(field);}复制代码,打印结果:4.获取单个属性:publicorprivategetDeclaredField("AttributeName")ClassuserClass=Class.forName("com.xxl.model.User");FieldnameField=userClass.getDeclaredField("name");FieldsexField=userClass.getDeclaredField("sex");System.out.println(nameField);System.out.println(sexField);将代码复制到打印结果:4.2操作属性1.获取属性名getName()ClassuserClass=Class.forName("com.xxl.model.User");FieldnameField=userClass.getDeclaredField("name");System.out。println(nameField.getName());复制代码打印结果:2.获取属性类型getType()ClassuserClass=Class.forName("com.xxl.model.User");FieldnameField=userClass.getDeclaredField("name");System.out.println(nameField.getType());复制代码打印结果:3.获取属性值get(object)ClassuserClass=Class.forName("com.xxl.model.User");FieldnameField=userClass.getDeclaredField("sex");Useruser=newUser();System.out.println(nameField.get(user));复制代码打印结果:注意:private的值property不能直接通过反射获取,但是可以修改访问入口获取私有property的值设置允许访问私有属性:field.setAccessible(true);复制代码示例:ClassuserClass=Class.forName("com.xxl.model.User");FieldnameField=userClass.getDeclaredField("name");nameField.setAccessible(true);用户user=newUser();System.out.println(nameField.get(user));复制代码打印方法:4.设置属性值set(object,"attributevalue")ClassuserClass=Class.forName("com.xxl.model.User");FieldnameField=userClass.getDeclaredField("name");nameField.setAccessible(true);Useruser=newUser();nameField.set(user,"张无忌");System.out.println(nameField.get(user));复制代码并打印结果:构造函数1.获取所有公共构造函数getConstructors()ClassuserClass=Class.forName("com.xxl.model.User");Constructor[]constructors=userClass.getConstructors();for(Constructorconstructor:constructors){System.out.println(constructor);}复制代码打印结果:2.获取与参数类型匹配的构造器getConstructor(参数类型)类userClass=Class.forName("com.xxl.model.User");构造函数constructor=userClass.getConstructor(String.class,String.class);System.out.println(构造函数);复制代码打印结果:成员方法6.1获取成员方法1.获取所有公共方法{System.out.println(method);}复制打印结果的代码:我们发现打印结果除了自定义的public方法外,还有一个继承自Object类的public方法2.获取一个公共方法getMethod("方法名",参数类型)ClassuserClass=Class.forName("com.xxl.model.User");Methodmethod=userClass.getMethod("setName",String.class);System.out.println(method);复制代码打印结果:3.获取所有方法:public+privategetDeclaredMethods()ClassuserClass=Class.forName("com.xxl.model.User");Method[]declaredMethods=userClass.getDeclaredMethods();for(Methodmethod:declaredMethods){System.out.println(method);}复制代码并打印结果:4.获取方法:publicorprivategetDeclaredMethod("方法名",参数类型)类userClass=Class.forName("com.xxl.model.User");方法method=userClass.getDeclaredMethod("run");System.out.println(方法);复制代码打印结果:6.2执行成员方法invoke(object,"Methodparameter")ClassuserClass=Class.forName("com.xxl.model.User");Methodmethod=userClass.getDeclaredMethod("eat");Useruser=newUser();method.invoke(user);复制代码打印结果:注意:私有成员方法不能通过反射直接执行,但可以设置允许访问。设置允许执行私有方法:method.setAccessible(true);复制代码7.注解1.判断一个类或方法时,一个注解isAnnotationPresent(注解名.class)isAnnotationPresent(注解名.class)复制代码例如:ClassuserClass=Class.forName("com.xxl.model.User");if(userClass.isAnnotationPresent(Component.class)){组件注解=(Component)userClass.getAnnotation(Component.class);字符串值=annotation.value();系统输出。println(value);};复制代码2.获取注解getAnnotation(annotationname.class)复制代码例如:ClassuserClass=Class.forName("com.xxl.model.User");//获取注解Annotationontheclassannotation1=userClass.getAnnotation(Component.class);Methodmethod=userClass.getMethod("eat");//获取方法上的注解Annotationannotation2=userClass.getAnnotation(Component.class);复制代码8.创建类实例1.通过Class实例化对象Class.newInstance()ClassuserClass=Class.forName("com.xxl.model.User");Useruser=(User)userClass.newInstance();System.out.println("姓名:"+user.getName()+"性别:"+user.getSex());复制代码并打印结果:2.通过构造函数constructor.newInstance(参数值)实例化对象ClassuserClass=Class.forName("com.xxl.model.User");Constructorconstructor=userClass.getConstructor(String.class,String.class);Useruser=(User)constructor.newInstance("李世清","女");System.out.println("姓名:"+user.getName()+"性别:"+user.getSex());复制代码,打印结果:ReflectionCase有一天,技术总监对张三说:“张三,听说你最近学了反射,那你给一个对象设计一个工厂类给我看看。”张三心想:哎,马上就要过年了,领导又要给我涨工资了,这次我可得好好表现了。”5分钟后,张三提交了代码:User)userClass.newInstance();}catch(ClassNotFoundExceptione){e.printStackTrace();}catch(IllegalAccessExceptione){e.printStackTrace();}catch(InstantiationExceptione){e.printStackTrace();}返回用户;}publicstaticUserServicegetUserService(){UserServiceuserService=null;try{ClassuserClass=Class.forName("com.xxl.service.impl.UserServiceImpl");userService=(UserService)userClass.newInstance();}catch(ClassNotFoundExceptione){e.printStackTrace();}catch(IllegalAccessExceptione){e.printStackTrace();}catch(InstantiationExceptione){e.printStackTrace();}returnuserService;,对张三说:“有两个你的工厂类有问题。》1、代码冗余度很高,如果有10000个类,你要写10000个静态方法吗?2、代码耦合度太高。如果存放这些类的包路径发生变化,用forName()获取Class对象会不会有问题?得自己手动改代码,一个一个改,然后编译,打包,部署。.你不觉得麻烦吗?《发散思路,想想看,能不能设计一个静态类,通过传参创建带反射的对象,传参应该减少与工厂类的耦合度,顺便提醒一下,可以参考JDBC获取数据库连接参数的方法。”张三义一听:“不愧是导演,你开窍了!等我10分钟。”》10分钟后,张三再次提交代码:object.propertiesuser=com.xxl.model.UseruserService=com.xxl.service.impl.UserServiceImpl复制代码ObjectFactorypublicclassObjectFactory{privatestaticPropertiesobjectProperty=newProperties();//静态方法在类初始化时执行,只执行一次}catch(IOExceptione){e.printStackTrace();}}publicstaticObjectgetObject(Stringkey){Objectobject=null;try{ClassobjectClass=Class.forName(objectProperty.getProperty(key));object=objectClass。newInstance();}catch(Exceptione){e.printStackTrace();}returnobject;}}复制代码测试方法:@TestvoidtestObject(){Useruser=(User)ObjectFactory.getObject("user");UserServiceuserService=(UserService)ObjectFactory.getObject("userService");System.out.println(用户);System.out.println(userService);}复制代码执行结果:主任看完连连点头,笑着对张三说:“用properties文件存储类的全限定名,降低了耦合度代码的使用反射通过传参创建对象在一定程度上减少了代码的冗余,这次可以改三:“......好导演。”10.作用反射我们或多或少听说过在设计框架的时候会用到反射,比如Spring的IOC就是用工厂模式和反射来创建对象,BeanUtils底层也是用反射来拷贝属性的。所以反射无处不在。虽然我们几乎不怎么用反射在我们日常的开发中,一定要了解反射的原理,因为它可以帮助我们理解框架设计的原理。