有关JavaSe基础的反射知识总结

反射这门技术在说之前首先来介绍一下动态语言和静态语言

动态语言:在服务器运行的期间可以改变其结构的语言,在运行时代码可以根据某些条件来改变自身的结构,我们目前学习到的有JS和JQuery类库

静态语言:C、C++、Java这些在运行时结构不可改变的语言,就称之为静态语言

而反射(Reflection)就被视为动态语言的关键,反射机制可以让程序在执行期间借助反射的API来获取类的内部信息,直接可以操作对象的内部属性和方法

一般使用对象:调用构造器——>>>new实例化对象

反射:通过实例化对象——>>>getClass()方法——>>>得到完整的包类名称

反射机制和类的封装机制不冲突,类的封装机制会不让你调用私有方法,让你调用公有方法,因为他会在Public方法里面写好逻辑和生成私有的信息返回给你,而反射机制只是说明了你可以用反射机制直接获取类的私有属性和私有方法。

在使用反射之前,需要学习Class的一些相关知识,没有Class的执行对象就无法进行后续的工作。

类的加载过程:javac.exe编译java文件,生成一个或多个字节码文件。java.exe解释运行字节码文件,把class文件加载到内存的时候就叫类的加载。加载到内存中的类称之为运行时类

就作为一个class的实例。比如:Class class1 = Person.class(Person是我自定义的一个类),换句话说,一个Class对象就对应一个运行时类

不仅仅是类可以作为Class的一个实例,外部类,内部类、接口、数组、枚举、注解、基本数据类型、void都可以作为Class类的一个实例化对象,比如(int .class)甚至Class本身也可以。

数组只要维度一样,得到的Class实例化对象也就相同,维度不同就不一样。

以下是有关类的加载的详细过程(了解就好,打字就太累了QAQ)

 

 

 

 

 

类加载器的作用就是把类的字节码文件装载进内存的。引导类加载器无法获取,引导类加载器用于加载类的核心类库,比如String.

扩展类加载器适用于jar包(打包的class字节码文件)、系统类加载器用于自定义类的加载到内存。

总结:Class类用来加载字节码文件,然后链接、最后初始化。而类加载器只是先把字节码文件加载到内存之中。

获取Class类对象(对应运行时类)的方法有:1、类名.class    2、类的实例化对象.getClass()   3、Class.forName("自定义类的相对路径") 4、当前文件类名.class.getClassLoader().loadClass("自定义类的相对路径")

clazz.newInstance()可以返回一个运行时类的对象(比如Person类,使用这个方法的前提是运行时类及其父类要有午餐构造方法,new一个对象的方法只有通过构造器来实现,所以newInstance方法也是间接用到了无参构造器)

 反射具有动态性,在这里举个例子,当我们写一个web项目的时候,客户端或者浏览器端输入一个信息,我们一开始不知道要创建什么对象,如果用new的方式去创建对象就显得很繁琐

在这里用反射的话,通过Class.forName(自定义类的相对路径).newInstance()就可以返回一个我们想要的对象,自定义路径只需要我们输入即可。

-------------------------------------------------------------------------以下为利用反射的特性和Class对象来获取运行时类的方法属性和构造器等等信息的方式-------------------------------------------------------

Class clazz = Person.class; Field  [] fields = clazz.getFields();//获取当前运行时类及其父类的public访问权限的属性。

clazz.getDeclaredFields()//获取当前运行时类声明的所有属性,公有私有都可以、不包含父类的所有属性

通过Field对象获取运行时类的属性信息(修饰符、变量类型、变量名)

 

Class<Student> clazz = Student.class;
Field[] fields = clazz.getDeclaredFields();
for(Field f :fields){
//权限修饰符
int m1 = f.getModifiers();
System.out.print(m1+"---->>>");
System.out.print(Modifier.toString(m1)+"-------------");
//数据类型
Class<?> type = f.getType();
System.out.print(type.getName()+"--------------");
//变量名
String name = f.getName();
System.out.println(name);

 

}

通过Filed对象的getMethods()方法获取当前运行时类及其所有父类中声明为public的方法

通过getDeclaredMethods()方法只可获取当前运行时类的所有方法,不包括父类的方法。构造器同理,这里就不写了

-------------------------获取注解、方法的返回值类型、函数名、形参名、接口、父类、泛型以及所在包的位置自行查询API即可,这里不过多赘述---------------------------

其次是如何操作运行时类的属性(设置属性值、获取属性值),具体实现如下:

Class clazz = Person.class;//获取Class类对象

Person p = (Person)clazz.newInstance();//获取实例化对象,前面clazz用泛型的话,这一步就不用强转了。。。

Field name = clazz.getDeclaredField("name");//name是Person类的属性变量名

name.setAccessible(true);//不管公有私有,保证当前属性是可访问的,这一点一定不能少

name.set(p,"Tom")//设置p对象的name变量值为Tom ;name.get(p)//通过字段类对象得到这个实例化对象的name属性值 默认是Object类型

附加说明:如果需要操作的属性是static修饰的类型。则需要将name.set(p,"Tom")修改为name.set(运行时类.class,"true");//第二个是可变形参,看运行时类的参数来决定。

接下来是如何操作运行时类的指定方法

 Class clazz = Person.class; 

Person p1 = clazz.newInstance();    Method method = clazz.getDeclaredMethod("函数名",形参类型.class...);

method.setAccessible(true); method.invoke(p1,"形参的值");invoke()方法本身带有返回值。Object obj1 = method.invoke(p1,"形参的值“);

注意:若为static方法,则和属性值同理,method.invoke(运行时类.class,"形参的值"); 

最后是如何调用指定的构造器:具体实现如下:

Class clazz = Person.class;          Constructor constructor = clazz.getDeclaredConstructor(String.class)//构造器的形参

constructor.setAccessible(true); Person per = (Person) constructor .newInstance("Tom")//相当于new一个对象

这里特别提醒:构造器的操作,我们一般不用。因为我们可以用clazz.newInstance();调用无参构造器这一通用的方法,而操作构造器只适用于解决某一个问题,对于以后的开发和使用框架来说有诸多不便之处。况且我们可以通过属性操作来set相关的值,比构造器要好用。不过也不是不能用,学了也不亏嘛。。。

这期内容就到这里,打字打的我手都疼,我们下期再见!!

 

 

posted @ 2022-12-06 14:23  -她的梦-  阅读(15)  评论(0编辑  收藏  举报