编程技术是改变世界的力量。
本站
当前位置:网站首页 > 后端语言 > 正文

Java反射详解 java反射的三种方法

gowuye 2024-04-04 11:57 8 浏览 0 评论

反射

反向探知,在程序运行是动态的获取类的相关属性 这种动态获取类的内容以及动态调用对象的方法和获取属性的机制,叫做java反射机制;

反射的优缺点

优点
增加了程序的灵活性,避免的固有逻辑写死到程序中 代码简介,提高程序的复用性

缺点
相比于直接调用,反射有比较大的性能消耗
内部暴露和安全隐患 (因为反射可以操作private成员变量和调用private成员方法)

反射的基本操作

获取类对象的4种方式

// 调用forName方法得到一个对象,这也是最容易想到的方式
Class<?> object = Class.forName("com.ibli.javaBase.reflection.User");

// 通过实例对象调用getClass方法
Teacher teacher = new Teacher();
Class<?> objectT = teacher.getClass();

// 通过类加载器的方式
Class<?> loader = ClassLoader.getSystemClassLoader().loadClass("com.ibli.javaBase.reflection.User");

//通过一个类.class
Class<?> tt = Teacher.class;
复制代码

基本信息操作

类修饰符PUBLICPRIVATEPROTECTEDSTATICFINALSYNCHRONIZEDVOLATILETRANSIENTNATIVEINTERFACEABSTRACTmodifiers12481632641282565121024

// 类的修饰符 具体的值可以参考JDK API文档中的定义 返回值是int类型  public:1
System.err.println(tt.getModifiers());
// 包名
System.err.println(tt.getPackage());
// 类的名称
System.err.println(tt.getName());
// 父类
System.err.println(tt.getSuperclass());
// 类加载器
System.err.println(tt.getClassLoader());
// 简称
System.err.println(tt.getSimpleName());
// 类实现的所有的接口
System.err.println(tt.getInterfaces().length);
// 所有的注解类型
System.err.println(tt.getAnnotations().length);
复制代码

执行结果:

1
package com.ibli.javaBase.reflection
com.ibli.javaBase.reflection.Teacher
class java.lang.Object
sun.misc.Launcher$AppClassLoader@18b4aac2
Teacher
0
0
复制代码

查看类的变量

// User extend Person(aa,bb)
Class<User> obj = User.class;
User user = obj.newInstance();
// 能够拿到类的所有的变量
Field[] fields = obj.getDeclaredFields();
for (Field field : fields){
    System.out.println(field.getModifiers() + " " + field.getName());
}
System.out.println("    ");

// 只能够拿到类的public的变量
Field[] fields1 = obj.getFields();
for (Field field : fields1){
    System.out.println(field.getModifiers() + " " + field.getName());
}
System.out.println("     ");
复制代码

执行结果:

2 age
2 name
1 sex
10 height
    
1 sex
1 aa
1 bb
复制代码

结论:

  • getDeclaredFields
    (1)getDeclaredFields能够获取本类的所有成员变量,无论是public还是private;
    (2)但是不能获取父类的任何属性;
    (3)可以获取static类型的属性;
  • getFields
    (1)只能够获取本类的public属性;
    (2)能够获取父类的public属性;
    (3)可以获取static类型的属性;

修改属性

// 设置Person中的变量aa
Field aaField = obj.getField("aa");
aaField.setInt(user,111);
System.err.println(user.getAa());

// 设置User私有成员变量
Field ageField = obj.getDeclaredField("age");
// 设置访问权限
ageField.setAccessible(true);
ageField.set(user,333);
System.err.println(user.getAge());
复制代码

执行结果:

111
333
复制代码

查看方法

Class<User> obj = User.class;
User user = obj.newInstance();

// 可以获取父类的方法
Method[] methods = obj.getMethods();
for (Method method : methods) {
    System.out.println(method.getModifiers() + "  " + method.getName());
}
System.err.println(" -----  ");

// 获取本类中的所有方法
Method[] methods1 = obj.getDeclaredMethods();
for (Method method : methods1) {
    System.out.println(method.getModifiers() + "  " + method.getName());
}
System.err.println(" 。。。。。。 ");
// 执行结果就不展示了
复制代码

结论:

  • getDeclaredMethods
    (1)可以获取本类中的所有方法; (2)可以获取本类的静态方法
  • getMethods
    (1)可以获取本类中的所有==公有==方法;
    (2)可以获取父类中的所有==公有==方法;
    (3)可以获取本类和父类的公有静态方法;

调用方法

// 访问私有方法
Method sleep = obj.getDeclaredMethod("sleep");
sleep.setAccessible(true);
sleep.invoke(user);

// 如果是静态方法,invoke第一个参数传null即可
Method say = obj.getDeclaredMethod("say",String.class);
say.setAccessible(true);
say.invoke(null,"hello java");
复制代码

执行结果:

Im sleeping!
say hello java
复制代码

构造器的使用

Class<User> obj = User.class;
// 查询共有的构造器
Constructor<?>[] constructors = obj.getConstructors();
for (Constructor<?> constructor : constructors){
    System.out.println(constructor.getModifiers() + "   " + constructor.getName());
}

// 可以获取私有的构造器
Constructor<?>[] constructors1 = obj.getDeclaredConstructors();
for (Constructor<?> constructor : constructors1){
    System.err.println(constructor.getModifiers() + "   " + constructor.getName());
}
复制代码

执行结果:

1   com.ibli.javaBase.reflection.User
1   com.ibli.javaBase.reflection.User

1   com.ibli.javaBase.reflection.User
2   com.ibli.javaBase.reflection.User
1   com.ibli.javaBase.reflection.User
复制代码

结论:

  • getConstructors
    (1)获得本类所有的公有构造器
  • getDeclaredConstructors
    (1)获得本类所有的构造器(public&private)

实例化对象

// 使用newInstance创建对象 调用无参构造器
User user = obj.newInstance();
// 获取构造器来实例化对象
Constructor<User> constructor = obj.getDeclaredConstructor(Integer.class, String.class);
constructor.setAccessible(true);
User temp = constructor.newInstance(22, "java");
System.err.println(temp.getAge() + " " + temp.getName());
复制代码

执行结果:

22 java

反射性能为什么差

可以从两方面考虑,第一个是反射生成Class对象时性能差,第二是通过反射调用对象方式是的性能差;

(1) 调用forName 本地方法
(2)每次newInstance 都会进行一次安全检查
(3)在默认情况下,方法的反射调用为委派实现,委派给本地实现来进行方法调用。在调用超过 15次之后,委派实现便会将委派对象切换至动态实现。这个动态实现的字节码是自动生成的,它将直接使用 invoke 指令来调用目标方法。

方法的反射调用会带来不少性能开销,原因主要有三个:

  • 变长参数方法导致的Object数组
  • 基本类型的自动装箱、拆箱 (参考资料2)
  • 还有最重要的方法内联。

参考资料
(1)反射为什么慢
(2)关于装箱拆箱为什么会影响效率
(3)jvm之方法内联优化

使用反射注意点

  • 在获取Field,method,construtor的时候,应尽量避免使用getDelcaredXXX(),应该传进参数获取指定的字段,方法和构造器;
  • 使用缓存机制缓存反射操作相关元数据的原因是因为反射操作相关元数据的实时获取是比较耗时的

相关推荐

爱上开源之golang入门至实战第四章-切片(Slice)

前言Go数组的长度不可改变,在特定场景中这样的集合就不太适用,Go中提供了一种灵活,功能强悍的内置类型切片("动态数组"),与数组相比切片的长度是不固定的,可以追加元素,在追加时可...

Go语言入门必知教程-切片

切片是一种灵活的和可扩展的数据结构,用于实现和管理数据集。切片由多个元素组成,所有元素都是相同类型的。切片是动态数组的一部分,可以根据需要进行增长和收缩。与数组一样,切片也可以索引。切片具有容量和长度...

Go语言基础-切片

切片是什么?切片是Go语言的一种数据结构。和数组相似,不过切片可以在它的结尾增加更多的元素。这样可变长度在实际编程中更为有用。声明切片切片的声明和数组也很相似,只是声明切片时不需要指定大小。例:va...

5分钟掌握GO中切片的基本使用方法

最近Golang越来越火,不少小伙伴都纷纷开始学习Golang,但对于原先为C++或者JAVA的同学,用习惯了数据、list、vector等,会对Go的切片slice不习惯,下面整理出go中slice...

揭秘 Go 切片(Slice)的秘密

当向切片添加新参数时,底层数组会发生什么变化?它会扩展以容纳更多元素吗?在这篇文章中,我们将深入探讨切片的内部工作原理,以及如何利用这些知识来进行更好的内存管理和性能优化。具体而言,我们将探索Go...

【Go语言slice详解】深入掌握Go语言中的slice类型及常用操作!

Go语言中的slice(切片)是一种非常方便的数据结构,可以动态地增加或减少其元素数量,且可以访问底层数组的任意一个子序列。本文将对Go语言中的slice进行详细的讲解。Slice的定义在Go语言中,...

掌握GO中的Slice,这就够了

最近Golang越来越火,不少小伙伴都纷纷开始学习Golang,但对于原先为C++或者JAVA的同学,用习惯了数据、list、vector等,会对Go的切片slice不习惯,下面整理出go中slice...

golang2021面向对象(26)Go语言类型内嵌和结构体内嵌

结构体可以包含一个或多个匿名(或内嵌)字段,即这些字段没有显式的名字,只有字段的类型是必须的,此时类型也就是字段的名字。匿名字段本身可以是一个结构体类型,即结构体可以包含内嵌结构体。?可以粗略地将这个...

2022-11-13:以下go语言代码中,如何获取结构体列表以及结构体内

2022-11-13:以下go语言代码中,如何获取结构体列表以及结构体内的指针方法列表?以下代码应该返回{"S1":["M1","M2"],"S...

Go语言文件和目录操作

文件和目录操作概述一、文件和目录操作概述在计算机中,文件和目录是存储数据的重要方式。在Go语言中,我们可以使用os和io/ioutil包提供的函数和结构体来进行文件和目录操作。本文将详细介绍Go语言中...

跟我一起学习go语言(五)golang中结构体的初始化方法

1、自定义一个结构体typeVertexstruct{X,Yfloat64}2、初始化方法-指针:rect1:=new(Vertex)rect2:=&Vertex...

Go复合数据类型:结构体

一种通用的、对实体对象进行聚合抽象的能力,在Go中,提供这种聚合抽象能力的类型是结构体类型,也就是struct。自定义一个新类型在Go中,我们自定义一个新类型一般有两种方法。第一种是类型定义...

Go语言基础:方法

导读在阅读本文章前,假定你具备如下能力:?已掌握结构体1.方法1.1方法的概念在理解程序中方法的概念时,我们先看看现实中的一些情况,这样相对比较好理解一些。在农村的朋友可能会知道,在医疗落后的情况...

为什么 Go 语言 struct 要使用 tags

在Go语言中,struct是一种常见的数据类型,它可以用来表示复杂的数据结构。在struct中,我们可以定义多个字段,每个字段可以有不同的类型和名称。除了这些基本信息之外,Go还提供了s...

一文带你掌握掌握 Golang结构体与方法

1.Golang结构体的概念及定义结构体是Golang中一种复合类型,它是由一组具有相同或不同类型的数据字段组成的数据结构。结构体是一种用户自定义类型,它可以被用来封装多个字段,从而实现数据的...

取消回复欢迎 发表评论: