欢迎访问 生活随笔!

生活随笔

当前位置: 首页 > 编程语言 > java >内容正文

java

Java资深反射玩家

发布时间:2024/10/14 java 75 豆豆
生活随笔 收集整理的这篇文章主要介绍了 Java资深反射玩家 小编觉得挺不错的,现在分享给大家,帮大家做个参考.

1.反射概述
2.获取Class类对象的三种方法
3.Class类的常用方法
4.反射获取构造方法并使用
5.反射获取类的成员属性并使用
6.反射获取类的成员方法并使用
7.反射的优缺点
8.反射的注意事项

1.反射概述

1.反射定义:

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

2.关于Class类

首先,我要给初学者或者小白定位一下对Class类的理解。常用类有String类、Math类等等,这里的Class也是一个类似于String类、Math类等等的类,和我们随便创建的类的概念是有本质区别的,Class类位于java.lang包下!Class 类的实例表示正在运行的 Java应用程序中的类和接口。也就是jvm中有N多的实例每个类都有该Class对象。(包括基本数据类型) Class 没有公共构造方法。Class对象是在加载类时由 Java 虚拟机以及通过调用类加载器中的defineClass方法自动构造的。也就是这不需要我们自己去处理创建Class对象,JVM已经帮我们创建好了。

3.了解完了反射的概念我们来看看普通类的加载过程:

熟悉一下加载的时候:Class对象的由来是将class文件读入内存,并为之创建一个Class对象。反射的本质理解就是得到Class对象后反向获取Student对象的各种成分信息(成分信息包括成员变量、方法、构造方法、所在包、字段属性等等),下面就以Student对象为例,图解Student类的正常加载过程~


那么我们再看看student加载的三个阶段:

对于这一张图的解释:
我们java类的代码编译后生成了字节码文件,里边包含三个比较重要的部分,分别对应成员变量,构造方法,和成员方法(当然一个字节码文件可不止这些东西,比如还有类名等等,这三个只是比较重要的部分),然后这个字节码文件存储在硬盘上,然后当我们new student()的时候,jvm就会把字节码文件加载到内存中,在内存里有描述我们加载进来的class文件的类对象:Class类对象,我们Class类对象把三个重要部分又分别封装成对象,分别为field,constructor,Method对象(这里注意一个对象只能包含一个成员变量或者一个构造方法成员方法,所以这里要用到数组的形式),然后真正的student对象是通过我们的Class类对象创建出来的

反射才体现出java是如何创建对象的。当java虚拟机(JVM)加载一个class类文件时,就创建这个类的class对象,以后的对象都是由这个class对象创建的,所以同一个类的所有对象的class对象都是一个,比如A a=new A(); A b=new A(); a.class()==b.class()返回true.

2.获取Class类对象的三种方法

1.Class.forName(“包名.类名”):将字节码文件加载进内存(注意这个方法可以加载类),返回Class类对象,当你知道该类的全路径名时,你可以使用该方法获取 Class 类对象。但可能抛出ClassNotFoundException异常
2.类名.class:通过类名的属性class获取,这种方法只适合在编译前就知道操作的 Class。直接通过 类名.class 的方式得到,该方法最为安全可靠,程序性能更高,这说明任何一个类都有一个隐含的静态成员变量 class
3.对象.getClass():这个getClass是在Object里边定义着所以每一个类里边都会有

代码实例:

package untl1; public class student {private int age;private String name;public student(int age,String name){this.age=age;this.name=name;}public void study(){System.out.println("我爱学习");}public static void main(String[] args)throws Exception {Class cl1=Class.forName("untl1.student");System.out.println(cl1);Class cl2=student.class;System.out.println(cl2);student stu=new student(1,"1");Class cl3=stu.getClass();System.out.println(cl3);System.out.println(cl1==cl2);System.out.println(cl1==cl3);} } 运行结果: class untl1.student class untl1.student class untl1.student true true

两个true充分证明了一个类只有一个字节码文件对象

3.Class类的常用方法

(1)常用方法:

方法名作用
getName()一个Class对象描述了一个特定类的属性,Class类中最常用的方法getName以=String 的形式返回此 Class对象所表示的实体(类、接口、数组类、基本类型或 void)名称(简单来说就是获得类的完整路径名)。方法可以简单的理解为:获得字符串参数中指定的类,并初始化该类。
newInstance()为类创建一个实例,但只能调用默认构造器(无参数构造器
getClassLoader()getClassLoader() 方法主要返回该类的类加载器。
getComponentType()getComponentType() 方法主要返回表示数组组件类型的 Class。
getSuperclass()getSuperclass() 返回表示此 Class 所表示的实体(类、接口、基本类型或 void)的超类的 Class。
isArray()isArray() 判定此 Class 对象是否表示一个数组类。

(2)在初始化一个类,生成一个实例的时候,newInstance()方法和new关键字除了一个是方法,一个是关键字外,最主要有什么区别?

newInstance: 弱类型。低效率。只能调用无参构造。使用的时候使用的类必须加载过
new: 强类型。相对高效。能调用任何public构造。使用的时候不需要必须加载过

这里的加载过,博主是这么理解的,当我们使用newInstance()的时候,如果类没加载过那么就不存在这个类的Class类对象文件,既然连这个Class类对象都没有那么怎么调用它的方法来创建对象呢,使用new的时候,我们都知道一个类的实例是必须经过Class类对象文件的,使用new的时候不需要加载类,在new的过程中再加载该类,以这种方法来创建对象就不需要提前加载

4.反射获取构造方法并使用

这里尤其注意看看每一个方法获取的构造方法的访问权限是怎样的

例子:
拿第一个举例子:

package untl1; import java.lang.reflect.Constructor; public class student {private int age;private String name;public student(int age,String name){this.age=age;this.name=name;}public student(){this.name="花花";this.age=100;}public student(int age){this.name="草草";this.age=age;}public void study(){System.out.println("我爱学习");}public String toString(){return "年龄:"+age+"\n"+"名字:"+name;}public static void main(String[] args)throws Exception {Class cla1=student.class;Constructor con1=cla1.getConstructor();System.out.println("空参构造方法为"+con1);student stu1= (student) con1.newInstance();//newInstance是Constructor类里边的方法,和Class的newInstance用法类似System.out.println(stu1);Constructor con2=cla1.getConstructor(int.class,String.class);System.out.println("两个参数的构造方法为"+con2);student stu2= (student) con2.newInstance(33,"卿卿");System.out.println(stu2);} } 运行结果: 空参构造方法为public untl1.student() 年龄:100 名字:花花 两个参数的构造方法为public untl1.student(int,java.lang.String) 年龄:33 名字:卿卿

5.反射获取类的成员属性并使用

这里有两个方法是属性对象里边的方法:

1.void set(Object obj,Object value):为获取的成员变量设置值
2.get(Object obj):获取成员变量的值

具体使用下边例子都有:

package untl1; import java.lang.reflect.Field; public class student {public int age;private String name;public student(int age,String name){this.age=age;this.name=name;}public student(){this.name="花花";this.age=100;}public student(int age){this.name="草草";this.age=age;}public void study(){System.out.println("我爱学习");}public String toString(){return "年龄:"+age+"\n"+"名字:"+name;}public static void main(String[] args)throws Exception {Class cla1=student.class;Field fie1= cla1.getField("age");student stu1=new student();fie1.set(stu1,88);Object value1=fie1.get(stu1);System.out.println(stu1);System.out.println(value1);Field fie2=cla1.getDeclaredField("name");student stu2=new student();fie2.setAccessible(true);fie2.set(stu2,"喵喵");Object value2=fie2.get(stu2);System.out.println(stu2);System.out.println(value2);Field fie3[]= cla1.getDeclaredFields();for (Field a:fie3) {System.out.println(a);}} } 运行结果: 年龄:88 名字:花花 88 年龄:100 名字:喵喵 喵喵 public int untl1.student.age private java.lang.String untl1.student.name

这里有一个fie2.setAccessible(true),这个是暴力反射,我们使用getDeclaredField()虽然可以获得私有的属性,但是不能拿来设置和获得该属性的值,这是由于访问修饰符为private,我们想要设置和获得该属性的值就必须要忽略访问修饰符的安全检查,那么使用setAccessible()方法就能解决,这里参数必须设置为true

6.反射获取类的成员方法并使用


获取的成员方法需要执行可以使用Method里边的执行方法:

Object invoke(Object obj,Object ...args):执行所在对象的指定方法

例子:

package untl1; import java.lang.reflect.Method; public class student {public int age;private String name;public student(int age,String name){this.age=age;this.name=name;}public student(){this.name="花花";this.age=100;}public student(int age){this.name="草草";this.age=age;}public void study(){System.out.println("我爱学习");}public void fly(String str){System.out.println("我要飞得更高");}public String toString(){return "年龄:"+age+"\n"+"名字:"+name;}public static void main(String[] args)throws Exception {Class cla1=student.class;Method me=cla1.getMethod("study");student stu=new student();me.invoke(stu);Method me2=cla1.getMethod("fly",String.class);me2.invoke(stu,"翠花");} } 运行结果: 我爱学习 我要飞得更高

7.反射的优缺点

1.反射优点:

使用反射机制,代码可以在运行时装配,提高了程序的灵活性和扩展性,降低耦合性,提高自适应能力。它允许程序创建和控制任何类的对象,无需硬编码目标类

2.反射缺点:

1.性能问题:使用反射基本上是一种解释操作,JVM无法对这些代码进行优化,因此,反射操作的效率要比那些非反射操作低得多。反射机制主要应用在对灵活性和扩展性要求很高的系统框架上,对性能要求高的程序中不建议使用
2.安全限制:使用反射技术要求程序必须在一个没有安全限制的环境中运行
3.内部暴露:由于反射允许代码执行一些在正常情况下不被允许的操作(比如访问私有的属性和方法),所以使用反射可能会导致意料之外的副作用——代码有功能上的错误,降低可移植性。反射代码破坏了抽象性,因此当平台发生改变的时候,代码的行为就有可能也随着变化。

8.反射的注意事项

1.注意方法中带Declared的不考虑访问权限
2.在Constructor对象里的newInstance()方法,返回值需要强制类型转换,否则不能直接就等于此字节码文件对应的类的引用
3.在反射中传递参数时要在参数后边加上.class才行
4.暴力反射是在三个对象(Constructor,Field,Method)中都能用,只要涉及获取或者修改私有的成员属性
5.Method里边的getMethod方法如果有没参数就直接在参数列表里边以字符串的形式传递方法的名字,如果有参数,不仅要传递方法的名字,还要传递方法的参数
6.Method中invoke方法需要传递一个实例化对象,然后如果获得的方法有参数,海要在invoke里边加上方法的参数

与50位技术专家面对面20年技术见证,附赠技术全景图

总结

以上是生活随笔为你收集整理的Java资深反射玩家的全部内容,希望文章能够帮你解决所遇到的问题。

如果觉得生活随笔网站内容还不错,欢迎将生活随笔推荐给好友。