首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >复习知识点(一):Java反射

复习知识点(一):Java反射

原创
作者头像
Java鱼头
发布于 2022-10-11 14:26:41
发布于 2022-10-11 14:26:41
24700
代码可运行
举报
文章被收录于专栏:Java-docJava-doc
运行总次数:0
代码可运行

反射相关的概念

1.什么是反射

反射即反向探知,有点像考古学家根据发掘的物品来探知以前的事情。

Java中的反射指的是程序在运行状态中

  1. 对于给定的一个类(Class)对象,可以获取到这个类(Class)对象的所有的属性和方法
  2. 对于给定的一个对象(new XXXClassName<? extends Object>),都能够调用它的任意一个属性和方法

这种动态获取类的内容以及动态调用对象的方法和属性的机制,就叫做Java的反射机制。

Java反射的优缺点:

优点:

  1. 增加程序的灵活性,避免将固有的逻辑程序写死在代码里面。
  2. 代码简洁,可读性增强,可提高代码的复用率

缺点:

  1. 相较于直接调用在量大的情况下反射性能下降厉害
  2. 内部暴露和安全隐患

2.Class的组成

一个Class实例包含的内容有哪些呢?

我们在程序运行的时候获取到Class类型,我们要根据Class类型来获取相关的内容

二、Class的基本操作

1.怎么获取Class对象

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
package com.bobo.reflection;public class ReflectionDemo01 {
​
​
    /**
     * 获取Class对象的方式
     * @param args
     */
    public static void main(String[] args) throws ClassNotFoundException {
        // 获取Person对应的Class对象
        Class<Person> class1 = Person.class;
        System.out.println(class1);
        Class class2 = Class.forName("com.bobo.reflection.Person");
        System.out.println(class2);
        // 还可以通过 Person对象中的getClass方法获取
        Person p = new Person();
        Class<? extends Person> class3 = p.getClass();
        System.out.println(class3);
        // 系统提供的一些类型获取 Class对象
        Class<String> stringClass = String.class;
        Class<Integer> integerClass = int.class;
        Class<int[]> aClass = int[].class;
        System.out.println(stringClass);
        System.out.println(integerClass);
        System.out.println(aClass);
        Class<double[]> aClass1 = double[].class;
        System.out.println(aClass1);
        // 获取包装类对应的Class对象
        Class<Integer> integerClass1 = Integer.class;
        Class<Integer> type = Integer.TYPE;
        // 我要获取void 没有返回结果的 Class类型
        Class<Void> type1 = Void.TYPE;
        Class<Void> voidClass = Void.class;
        System.out.println(integerClass1);
        System.out.println(type);
        System.out.println(type1);
        System.out.println(voidClass);
​
​
​
    }
}

输出结果:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
class com.bobo.reflection.Person
class com.bobo.reflection.Person
class com.bobo.reflection.Person
class java.lang.String
int
class [I
class [D
class java.lang.Integer
int
void
class java.lang.Void

2.Class对象中的常用方法

2.1 getName

获取当前类型的全类路径名称

2.2 newInstance()

通过无参构造方法获取对应的实例对象,如果无参构造方法被覆盖的话,会抛出NoSuchMethodException: com.bobo.reflection.Student.<init>() 异常

2.3 getSuperclass()

获取当前类型的父类,如果没有显示的继承父类,那么返回的是Object

2.4 getInterfaces()

获取当前类型实现的所有的接口

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
package com.bobo.reflection;import java.util.Arrays;public class ReflectionDemo02 {
    /**
     * 反射中的常用方法
     * @param args
     */
    public static void main(String[] args) throws IllegalAccessException, InstantiationException {
        // 获取一个Student的类对象
        Class<Student> studentClass = Student.class;
        // getName()  完整包名+类名
        System.out.println(studentClass.getName());
        // 通过类型获取一个Student对象 newInstance() 调用的是无参的构造方法
        // 如果没有无参的构造方法那么会抛 NoSuchMethodException: com.bobo.reflection.Student.<init>() 异常
        Student student = studentClass.newInstance();
        System.out.println(student);
        // 获取当前类对象的父类对象
        Class<? super Student> superclass = studentClass.getSuperclass();
        System.out.println(superclass);
        System.out.println(superclass.getSuperclass());
        // 获取当前类型实现的接口
        Class<?>[] interfaces = studentClass.getInterfaces();
        System.out.println(Arrays.toString(interfaces));}
}

输出结果:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
com.bobo.reflection.Student
com.bobo.reflection.Student@4554617c
class com.bobo.reflection.Person
class java.lang.Object
[interface com.bobo.reflection.InterfaceDemo01, interface com.bobo.reflection.InterfaceDemo02]

2.5 获取属性

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
Field   getField(String name)
//返回一个 Field对象,它反映此表示的类或接口的指定公共成员字段 类对象。
Field[] getFields()
//返回包含一个数组 Field对象反射由此表示的类或接口的所有可访问的公共字段 类对象。
Field   getDeclaredField(String name)
//返回一个 Field对象,它反映此表示的类或接口的指定已声明字段 类对象。
Field[] getDeclaredFields()
//返回的数组 Field对象反映此表示的类或接口声明的所有字段 类对象。
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
package com.bobo.reflection;import java.lang.reflect.Field;public class ReflectionDemo03 {
​
​
    /**
     * 反射获取 属性的方法
     * @param args
     */
    public static void main(String[] args) {
        Class<Student> studentClass = Student.class;
        // 获取相关的属性方法 Class中的每一个属性会被封装会一个Field对象
        // getFields() 获取当前类型和父类中的所有的public权限的字段
        Field[] fields = studentClass.getFields();
        for(Field field:fields){
            System.out.println(field.getName());
        }
        System.out.println("---------");
        // getDeclaredFields() 表示的获取当前类中的所有的 属性,包括私有的
        Field[] declaredFields = studentClass.getDeclaredFields();
        for (Field field:declaredFields){
            System.out.println(field.getName());
        }
​
​
    }
}

针对属性的操作:Field的操作

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
package com.bobo.reflection;import java.lang.reflect.Field;public class ReflectionDemo04 {
​
​
    /**
     * 反射获取 属性的方法
     * @param args
     */
    public static void main(String[] args) throws Exception{
        Class<Student> studentClass = Student.class;
        // 获取Student对象
        Student student = studentClass.newInstance();
        student.gender = "男";
        // 通过类型获取gender属性
        Field gender = studentClass.getDeclaredField("gender");
​
        System.out.println(gender.getName());
        // 获取对应的访问权限修饰符 1表示 public 2表示 private 4 表示 protected
        System.out.println(gender.getModifiers());
        // 获取或设置属性的信息
        System.out.println(gender.get(student));
        // 修改属性值
        gender.set(student,"女");
        System.out.println(gender.get(student));
        // 操作私有属性
        Field stuNum = studentClass.getDeclaredField("stuNum");
        // 在反射中是不允许直接操作私有属性的,如果一定要操作必须放开权限
        stuNum.setAccessible(true); // 允许对私有属性的操作
        System.out.println(stuNum.get(student));
        // 修改私有属性的信息
        stuNum.set(student,10001);
        System.out.println(stuNum.get(student));
        // 养成好的习惯,操作属性完成后,把权限关闭
        stuNum.setAccessible(false);}
}

输出结果:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
gender
1
男
女
null
10001

2.6 获取方法

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
Method  getDeclaredMethod(String name,<?>... parameterTypes)
// 返回一个 方法对象,它反映此表示的类或接口的指定声明的方法 类对象。
Method[]    getDeclaredMethods()
// 返回包含一个数组 方法对象反射的类或接口的所有声明的方法,通过此表示 类对象,包括公共,保护,默认(包)访问和私有方法,但不包括继承的方法。
    
Method  getMethod(String name,<?>... parameterTypes)
//返回一个 方法对象,它反映此表示的类或接口的指定公共成员方法 类对象。
Method[]    getMethods()
//返回包含一个数组 方法对象反射由此表示的类或接口的所有公共方法 类对象,包括那些由类或接口和那些从超类和超接口继承的声明。

案例代码

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
package com.bobo.reflection;import java.lang.reflect.Method;public class ReflectionDemo05 {/**
     * 反射 获取相关方法
     * @param args
     */
    public static void main(String[] args) {
        // 获取Student的类对象
        Class<Student> studentClass = Student.class;
        // 获取相关的方法 getMethod获取当前类及父类中的所有的共有的方法
        Method[] methods = studentClass.getMethods();
        for (Method method :methods){
            System.out.println(method.getName());
        }
        System.out.println("----");
        // getDeclaredMethods 获取的是当前类中的所有的方法 包括 private
        Method[] declaredMethods = studentClass.getDeclaredMethods();
        for(Method method:declaredMethods){
            System.out.println(method);
        }}
}

Method的操作

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
package com.bobo.reflection;import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
import java.util.Arrays;public class ReflectionDemo06 {
​
​
    /**
     * Method对象
     * @param args
     */
    public static void main(String[] args) throws Exception{
        Class<Student> studentClass = Student.class;
        //  获取特定的 方法 say
        Method say = studentClass.getDeclaredMethod("say", String.class);
        // 方法的调用  方法属性普通方法,那么我们要通过对象类调用
        Student student = new Student();
        // 第一个参数 要调用哪个对象的say方法  第二个参数 是一个参数列表  调用方法的实际参数
        Object obj = say.invoke(student,"bobo");
        System.out.println(obj);
        // 私有方法的调用
        Method eat = studentClass.getDeclaredMethod("eat");
        // 调用私有的方法,我们需要放开权限
        eat.setAccessible(true);
        Object obj1 = eat.invoke(student);
        System.out.println(obj1);
        // 调用完成后记得收回权限
        eat.setAccessible(false);
        // 获取方法的返回值类型
        System.out.println(say.getReturnType());
        System.out.println(eat.getReturnType());
        // 获取方法显示抛出的异常
        Class<?>[] exceptionTypes = say.getExceptionTypes();
        System.out.println(Arrays.toString(exceptionTypes));
        // 获取方法声明的参数
        Parameter[] parameters = say.getParameters();
        for (Parameter parameter : parameters){
            // Parameter封装的是 属性的对象
            System.out.println(parameter.getName()+ " " +parameter.getModifiers() + " " + parameter.toString() );
        }
    }
}

2.7 获取构造方法

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
Constructor<T>  getConstructor(<?>... parameterTypes)
返回一个 Constructor对象,该对象反映 Constructor对象表示的类的指定的公共 类函数。
Constructor<?>[]    getConstructors()
返回包含一个数组 Constructor对象反射由此表示的类的所有公共构造 类对象。
Constructor<T>  getDeclaredConstructor(<?>... parameterTypes)
返回一个 Constructor对象,该对象反映 Constructor对象表示的类或接口的指定 类函数。
Constructor<?>[]    getDeclaredConstructors()
返回一个反映 Constructor对象表示的类声明的所有 Constructor对象的数组 类 。
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
    /**
     * 反射中的构造方法
     * @param args
     */
    public static void main(String[] args) {
        Class<Student> studentClass = Student.class;
        // 获取对应的构造器 getConstructors 获取的是当前类中的所有的public构造器
        Constructor<?>[] constructors = studentClass.getConstructors();
        for(Constructor constructor:constructors){
            System.out.println(constructor.getName() + " " + Arrays.toString(constructor.getParameters()));
        }
        System.out.println("-----------------");
        // 获取当前类中的所有的 构造方法 包括private 修饰的
        Constructor<?>[] declaredConstructors = studentClass.getDeclaredConstructors();
        for(Constructor constructor:declaredConstructors) {
            System.out.println(constructor.getName()+ " " + Arrays.toString(constructor.getParameters()));
        }}

Constructor操作

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
package com.bobo.reflection;import java.lang.reflect.Constructor;
import java.util.Arrays;public class ReflectionDemo08 {/**
     * 反射 构造器的操作
     * @param args
     */
    public static void main(String[] args) throws Exception {
        Class<Student> studentClass = Student.class;
        // 一个构造器
        Constructor<Student> c = studentClass.getDeclaredConstructor(Integer.class);
        System.out.println(c.getName());
        System.out.println(c.getModifiers());
        System.out.println(c.getParameterCount());
        // 操作private修饰的构造方法 我们同样需要放开权限
        c.setAccessible(true);
        // 通过构造器创建实例对象
        Student s = c.newInstance(1009);
        // 操作完成后同样需要关闭权限
        c.setAccessible(false);
        System.out.println(s);
        // 获取构造方法显示抛出的异常
        Class<?>[] exceptionTypes = c.getExceptionTypes();
        System.out.println(Arrays.toString(exceptionTypes));
​
​
    }
}

2.8 静态属性和静态方法操作

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
    /**
     * 反射 静态属性和静态方法的调用
     * @param args
     */
    public static void main(String[] args) throws Exception {
        Class<Student> studentClass = Student.class;
        // 静态属性的操作
        Field s1 = studentClass.getDeclaredField("s1");
        System.out.println(s1.get("s1"));
        s1.set(null,"hahaha");
        System.out.println(s1.get("s1"));
        // 静态方法的调用
        Method fun1 = studentClass.getDeclaredMethod("fun1");
        fun1.invoke(null);}

三、单例的漏洞

我们之前介绍过单例的使用,保证一个类只有一个实例存在,但是我们将了反射后发现,私有的构造器也能够被获取到,进而可以创建出很多个实例对象,这显然和我们的期望不一致。那么针对这个漏洞,我们应该怎么办呢?

Bug还原

单例代码

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
package com.bobo.reflection;public class SingletonTest {// 声明一个静态的 单例属性
    private static SingletonTest instance;// 私有化构造器
    private SingletonTest(){}// 对外提供一个静态的共有的方法来获取单例对象
    public static SingletonTest getInstance(){
        if(instance == null){
            synchronized (SingletonTest.class){
                if(instance == null){
                    instance = new SingletonTest();
                }
            }
        }
        return instance;
    }
}

测试代码

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
package com.bobo.reflection;import java.lang.reflect.Constructor;public class ReflectionDemo11 {/**
     * 反射 静态代码块的执行
     * @param args
     */
    public static void main(String[] args) throws Exception {
        SingletonTest i1 = SingletonTest.getInstance();
        System.out.println(i1);
        SingletonTest i2 = SingletonTest.getInstance();
        System.out.println(i2);
        // 通过反射的方式来获取对象  获取私有的构造器
        Constructor<SingletonTest> c = SingletonTest.class.getDeclaredConstructor();
        c.setAccessible(true); //放开权限
        SingletonTest i3 = c.newInstance();
        SingletonTest i4 = c.newInstance();
        c.setAccessible(false);
        System.out.println(i3);
        System.out.println(i4);}
}

输出结果

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
com.bobo.reflection.SingletonTest@4554617c // 正常获取
com.bobo.reflection.SingletonTest@4554617c // 正常获取
com.bobo.reflection.SingletonTest@74a14482 // 反射获取
com.bobo.reflection.SingletonTest@1540e19d // 反射获取

我们发现造成这个bug的根本原因是private的构造方法多次执行了,那么我们就只需要在私有构造方法中添加逻辑即可

如果要创建多个就会抛异常,从而不能成功

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
【Java】之反射总结
CSDN话题挑战赛第2期 参赛话题:学习笔记 学习之路,长路漫漫,写学习笔记的过程就是把知识讲给自己听的过程。这个过程中,我们去记录思考的过程,便于日后复习,梳理自己的思路。学习之乐,独乐乐,不如众
天寒雨落
2022/11/20
2570
【Java】之反射总结
详解Java反射机制
Single
2018/01/04
7590
Java 学习笔记(15)——反射
Java中的类文件最终会被编译为.class 文件,也就是Java字节码。这个字节码中会存储Java 类的相关信息。在JVM执行这些代码时首先根据 java 命令中指定的类名找到.class 文件然后使用类加载器将class字节码文件加载到内存,在加载时会调用Class类的classLoader方法,读取.class 文件中保存类的成员变量、方法、构造方法,并将这些内容在需要时创建对应的对象。这个就是java中的反射机制。反射机制提供了由字符串到具体类对象的映射,提高了程序的灵活性,在一些框架中大量使用映射,做到根据用户提供的xml配置文件来动态生成并创建类对象 反射机制最关键的就是从字节码文件中加载类信息并封装为对应的结构。在Java中专门提供了一个 Class 类,用于存储从.class 文件中读取出来的类的信息。 该类的定义和常用方法如下:
Masimaro
2019/08/20
3980
java 反射机制说的透彻一点
很多时候我们会遇到别人问一个问题:你给我讲一下反射,到底是什么东西?怎么实现的?我们能用反射来做什么?它有什么优缺点?下面我们会围绕着这几个问题展开:
秦怀杂货店
2020/11/22
7350
java 反射机制说的透彻一点
Java反射
反射的概念是由 Smith 在 1982 年首次提出的,主要是指程序可以访问、检测和修改它本身状态或行为的一种能力。
阿珍
2025/01/24
1520
Java反射
Java反射机制(简单易懂)
Java程序中许多对象在运行时会出现两种类型:运行时类型(RTTI)和编译时类型,例如Person p = new Student();这句代码中p在编译时类型为Person,运行时类型为Student。程序需要在运行时发现对象和类的真实信息。而通过使用反射程序就能判断出该对象和类属于哪些类。
VIBE
2022/11/18
2390
Java反射机制(简单易懂)
一起学JAVA 反射学习(超详细)
Reflection(反射) 是 Java 程序开发语言的特征之一,它允许运行中的 Java 程序对自身进行检查,或者说“自审”,也有称作“自省”。 反射非常强大,它甚至能直接操作程序的私有属性。我们前面学习都有一个概念,被private封装的资源只能类内部访问,外部是不行的,但这个规定被反射赤裸裸的打破了。 反射就像一面镜子,它可以在运行时获取一个类的所有信息,可以获取到任何定义的信息(包括成员变量,成员方法,构造器等),并且可以操纵类的字段、方法、构造器等部分。
全栈程序员站长
2022/07/01
3720
一起学JAVA 反射学习(超详细)
Java-反射
哈喽!大家好,我是小简。今天开始学习《Java-反射》,此系列是我做的一个 “Java 从 0 到 1 ” 实验,给自己一年左右时间,按照我自己总结的 Java-学习路线,从 0 开始学 Java 知识,并不定期更新所学笔记,期待一年后的蜕变吧!
小简
2023/01/04
4400
Java-反射
原来Java反射这么简单!!!
在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性。也就是通过class文件对象,去使用该文件中的成员变量,构造方法,成员方法。
互联网金融打杂
2022/08/01
2730
原来Java反射这么简单!!!
Java反射:Web学习的灵魂
我们将类分为三部分,成员变量,构造方法,成员方法,代码编译后,变成了字节码文件(.class文件)而万物皆对象,所以在字节码文件中,又将这三部分分别整合成对象,所以我们得出结论:
BWH_Steven
2019/08/09
3530
Java面试手册:反射
Reflection(反射)是被视为动态语言的关键,反射机制允许程序在执行期借助于 Reflection API 取得任何类的内部信息,并能直接操作任意对象的内部属性及方法。
南风
2019/04/22
5600
Java面试手册:反射
Java反射真正的详解,学以致用,实战案例,近万字长文,不读后悔
全文共 7500 字左右,案例均可运行,阅读时间大约需要20分钟左右,如有问题,请留言或发送邮件(nzc_wyh@163.com)。
宁在春
2022/10/31
7530
Java反射真正的详解,学以致用,实战案例,近万字长文,不读后悔
Java反射基础
通过配置文件,在不修改源码的情况下来控制程序,符合设计模式中的开闭原则(ocp)。类似这样的需求在学习框架时特别常见。
堆栈哲学
2022/11/24
2440
Java反射基础
Java反射介绍[通俗易懂]
JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性;这种动态获取的信息以及动态调用对象的方法的功能称为java语言的反射机制。 要想解剖一个类,必须先要获取到该类的字节码文件对象。而解剖使用的就是Class类中的方法.所以先要获取到每一个字节码文件对应的Class类型的对象.
全栈程序员站长
2022/06/29
3500
Java反射介绍[通俗易懂]
Java反射详解
这样的需求在学习框架时特别多,即通过外部文件配置,在不修改源码情况下。来控制程序,也符合设计模式的ocp原则(开闭原则:不修改源码,扩容功能)。
timerring
2023/05/24
3760
Java反射详解
Java:一步步带你深入了解神秘的Java反射机制
在使用Java反射机制时,主要步骤包括: 1. 获取 目标类型的Class对象 2. 通过 Class 对象分别获取Constructor类对象、Method类对象 & Field 类对象 3. 通过 Constructor类对象、Method类对象 & Field类对象分别获取类的构造函数、方法&属性的具体信息,并进行后续操作
Carson.Ho
2019/02/22
5770
Java–反射
Java的反射(reflection)机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意方法和属性,既然能拿到那么,我们就可以修改部分类型信息;这种动态获取信息以及动态调用对象方法的功能称为java语言的反射(reflection)机制。
全栈程序员站长
2022/09/08
6130
Java–反射
Java反射(超详细!)[通俗易懂]
通过java语言中的反射机制可以操作字节码文件(可以读和修改字节码文件。) 通过反射机制可以操作代码片段。(class文件。)
全栈程序员站长
2022/09/02
5070
一文了解Java反射和应用
静态编译:在编译的时候进确定类型,如果绑定对象成功,new 是静态加载类,就编译通过。
Java技术江湖
2019/09/26
4320
一文了解Java反射和应用
Java反射介绍
Java的反射(reflection)机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意方法和属性,既然能拿到那么,我们就可以修改部分类型信息;这种动态获取信息以及动态调用对象方法的功能称为java语言的反射(reflection)机制。
鳄鱼儿
2024/05/21
1910
相关推荐
【Java】之反射总结
更多 >
LV.3
小作坊高级摸鱼攻城狮
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档