IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> Java知识库 -> Java中的反射机制 -> 正文阅读

[Java知识库]Java中的反射机制

?🤨🤨🤨终于终于,把反射这一篇博客补过来了,初学时有点晕,现在,重新认真整理整理

1.反射的概念

1.1 定义:

Java程序运行期间,对于任意一个类都能得到该类的所有属性和方法,既然能得到,当然也可以修改部分信息;这种动态获取信息以及动态调用对象方法的功能称为java语言的反射(reflection)机制。

说点人话,反射反射就是倒着反过来,正常来说,对于一个类,我们是定义者,定义类的属性和方法,而反射就是倒过来,在运行期间直接拿到该类的所有属性和方法。

再底层一点来说,反射机制的核心类就是Class类,我们知道, 我们每编写一个类,就会有一个.java文件,使用javac编译后就得到了被编译后的.class文件,而每一个.class都是Class类的对象

1.2 用途

首先, 反射机制是所有第三方框架的基础,反射最重要的用途就是开发各种通用框架,比如在spring中,我们将所有的类Bean交给spring容器管理;

其次,在实际开发中,经常会遇到某个属性或方法是私有的,这时候,就可以利用反射机制通过反射来获取私有变量和方法

还有,我们知道 对象类型有运行时类型和编译时类型,我们常常要在运行时才能知道其真实类型,比如,Person p = new Student() ,这句代码中p在编译时类型为Person,运行时类型为Student,通过反射我们可以清楚的知道运行时到底是哪个类

编译时类型&运行时类型:

//        编译时类型&运行时类型
        Person p1 = new Person();
        System.out.println(p1.getClass());
        Person p2 = new Student();
        System.out.println(p2.getClass());

由结果就可以看到p1,p2的运行时类型到底是什么:

??

2.反射的四个核心类(Class,Constructor,Method, Field)

  • Class类——反射操作的核心,代表类的实体,应用反射的第一步就是获取Class类的对象?
  • Constructor类——与构造方法有关
  • Method类——与类中的方法有关
  • Field类——与类中属性相关

其中,Class类是核心第一步,Method类最为常用

额外说一句:所有和反射相关的包都在import java.lang.reflect 包下面

在介绍这四个类之前,我们先来做一些准备工作,创建两个类,Person类和Student类,其中,Student类是Person类的子类

Person类:

/**Person父类
 * @author sunny
 * @date 2022/04/14 17:45
 **/
public class Person {
//    三个成员变量,分别不同的权限
    public String name;
    int phone;
    private int age;

//    无参构造
    public Person() {
        name = "无参";
        phone = 0;
        age = 0;
        System.out.println("调用了父类无参构造方法");
    }

//    有参构造
    public Person(String name, int phone, int age) {
        this.name = name;
        this.phone = phone;
        this.age = age;
        System.out.println("调用了父类有参构造方法");
    }

    private void test() {
        System.out.println("调用了父类的test方法,private权限");
    }

    public void testPerson() {
        System.out.println("调用了父类的testPerson方法,public权限");
    }
}

Student类:

/**Student类
 * @author sunny
 * @date 2022/04/14 17:54
 **/
public class Student extends Person {
    public String studentName;
    int studentPhone;
    private int studentAge;

    public Student() {
        System.out.println("调用了子类的无参构造");
    }

    public Student(String studentName, int studentPhone, int studentAge) {
        this.studentName = studentName;
        this.studentPhone = studentPhone;
        this.studentAge = studentAge;
        System.out.println("调用了子类的有参构造方法");
    }

    private void fun() {
        System.out.println("调用了子类的fun方法,private权限");
    }

    public int getAge(int num) {
        System.out.println("调用了子类的getAge方法,public权限");
        return this.studentAge + num;
    }

    @Override
    public String toString() {
        return "Student{" +
                "studentName='" + studentName + '\'' +
                ", studentPhone=" + studentPhone +
                ", studentAge=" + studentAge +
                '}';
    }
}

2.1 Class类

正如上文写道,每一个类都对应一个Class类的对象(对象就是编译后的.class文件),所以,要应用反射机制获取一个类的所有属性和方法第一步就是获取其对应得Class类对象

🤨获取方式有三种!!!

  • 直接使用 类名称.class 来获取,注意只适合在编译期就明确要操作的Class,用法如下:
    //        方法一:直接类名获取
            Class cls1 = Student.class;
  • 通过该类的任意实例化对象调用 getClass()方法?,用法如下:
    //        方法二:类对象的 getClass() 方法
            Class cls2 = new Student().getClass();
  • ?使用 Class类提供的:Class.forName("类的全路径名"),全路径名就是 包名.类名
  • ?注意此方法是静态方法,用的最多, 但可能抛出 ClassNotFoundException 异常
    //        方法三:Class.forName("类的全路径名")
            try {
                Class cls3 = Class.forName("practice_class.reflect.Student");
            }catch(ClassNotFoundException e){
                e.printStackTrace();
            }

无论哪种方式创建,指向的都是同一个对象:

//        无论哪种方式获取的,都指向同一个对象
        System.out.println(cls1 == cls2);

可以看到,输出结果为 true?

?

🤨通过反射创建类的实例:

直接使用newInstance方法

//        通过反射创建类的实例
//        先创建Class类对象cls
        Class<Student> cls = Student.class;
//        调用newInstance方法,默认调无参构造
        Student stu = cls.newInstance();

?由运行结果可以看到,默认调用的是无参构造函数

那么问题来啦,当无参构造不存在或者无参构造不可见,就会报错,或者说我们需要用该类的其他构造方法创建类的实例

那么这时候,就需要用到Constructor类了,下面我们来看Constructor类

2.2 Constructor类

先看主要方法:

具体怎么用,什么意思呢,让我们一个一个来分析一哈:?

(1)getConstructors()

        Class<Student> cls = Student.class;
        Constructor[] con = cls.getConstructors();
        System.out.println(Arrays.toString(con));

当我们修改其中一个构造方法为private时:

    public Student() {
        System.out.println("调用了子类的无参构造");
    }

    private Student(String studentName, int studentPhone, int studentAge) {
        this.studentName = studentName;
        this.studentPhone = studentPhone;
        this.studentAge = studentAge;
        System.out.println("调用了子类的有参构造方法");
    }

输出结果只剩下了共有的构造方法?

?

可以得到:getConstructors() ——只能获取当前类(不包括父类)的所有public构造方法

(2)getDeclaredConstructors()

不修改两个构造方法得访问权限,保持一个public,一个private,运行下面这段程序:

        Class<Student> cls = Student.class;
//        cls.getDeclaredConstructors()
        Constructor[] declared = cls.getDeclaredConstructors();
        System.out.println(Arrays.toString(declared));

可以得到:getDeclaredConstructors()——可以取到当前类(也不包括父类)的所有构造方法(包括私有方法)

(3)getDeclaredConstructor(参数类型.class…)

前两个都是获取构造方法的数组,当我们如果想要拿到某一个指定的构造方法时,用下面这种方法:

       Class<Student> cls = Student.class;
//        拿到指定的有参构造
        Constructor c = cls.getDeclaredConstructor(String.class,int.class,int.class);
//        用该构造方法实例化一个对象
//        如果该构造方法是私有的,我们要先用下面这句破坏封装
        c.setAccessible(true);
        Student s = (Student) c.newInstance("笨笨",123,333);
        System.out.println(s);

可以得到:要得到指定的某个构造方法,可以用getDeclaredConstructor(参数类型.class…)方法,小括号内就是指定构造方法的类型.class;

再用此构造方法创建对象时,如果此构造方法是私有的,要先破坏其封装性,怎么破坏呢,传入setAccessable方法的参数为true;

【注意】破坏封装性,仅对当前JVM进程中的这个对象生效,出了这个进程或是别的对象是不起作用的

构造方法可见,直接使用newInstance方法传参即可,注意强制类型转换

2.3 Method类

Method类对应类中方法的反射类,其实反射中的套路都差不多,这里,与上文的构造方法类似

ps:可以看到,后缀+s的就是获取到一定范围内的方法,返回Method数组,不加s的是获取某个指定的方法

(1)?getMethods()方法——能拿到当前类及其父类的所有public方法

(2)getDelaredMethods()方法,仅能拿到当前类的所有方法(包含private方法)

这里不再赘述分析,直接上代码看两个方法的结果验证结论即可:

/**Method类的学习
 * @author sunny
 * @date 2022/04/15 08:22
 **/
public class Method_Test {
    public static void main(String[] args) throws Exception{
//        第一步一定是获取Class对象
        Class<Student> cls = Student.class;
//        复习一下刚学的,用指定的构造方法创建实例
        Constructor c = cls.getDeclaredConstructor(String.class,int.class,int.class);
        c.setAccessible(true);
        Student stu = (Student) c.newInstance("笨笨",6,8);
//        学习使用Method类
        Method[] m1 = cls.getMethods();
        System.out.println(Arrays.toString(m1));
        Method[] m2 = cls.getDeclaredMethods();
        System.out.println(Arrays.toString(m2));
    }
}

打印输出:?

唔……?getMethods()方法方法能打印当前类及其父类的所有Public方法,有点多,截图不完整

getDelaredMethods()方法能拿到当前类的所有方法,包含private方法,fun方法是私有的也能拿到

来个容易混淆的点:

如果我现在想拿到父类的一个private方法怎么办呢?

只能先拿到父类的Class对象,再调用getDeclaredMethods方法

【ps:】因为?getMethods()方法仅能拿到当前类及其父类的所有public方法,也不存在什么去破坏封装性拿到父类私有方法的可能,所以只能用父类的Class对象去解决

(3)重点来看下拿到指定的方法:

🤔🤔🤔首先来看成员方法的使用:

  • 得到Class对象
    Class<Student> cls = Student.class;
  • 得到指定的Method方法
    Method method = cls.getDeclaredMethod("方法名称",方法参数类型.class);
  • 如果此方法为private权限,仍然要先破坏封装
    method.setAccessible(true);
  • 创建类的实例对象
    //        复习一下刚学的,用指定的构造方法创建实例
            Constructor c = cls.getDeclaredConstructor(String.class,int.class,int.class);
            c.setAccessible(true);
            Student stu = (Student) c.newInstance("笨笨",6,8);
  • 执行该方法

    method.invoke(对象名,方法参数);

代码示例如下:

import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.util.Arrays;

/**Method类的学习
 * @author sunny
 * @date 2022/04/15 08:22
 **/
public class Method_Test {
    public static void main(String[] args) throws Exception{
//        第一步一定是获取Class对象
        Class<Student> cls = Student.class;
//        复习一下刚学的,用指定的构造方法创建实例
        Constructor c = cls.getDeclaredConstructor(String.class,int.class,int.class);
        c.setAccessible(true);
        Student stu = (Student) c.newInstance("笨笨",6,8);

//        学习使用Method类
//        因为fun方法是无参的,所以这里只需传入方法名称即可
        Method method = cls.getDeclaredMethod("fun");
//        fun方法是私有的,先破坏封装
        method.setAccessible(true);
//        执行该方法
        method.invoke(stu);

//        再看一个有参的方法调用,传入方法名称和参数类型.class
        Method m = cls.getDeclaredMethod("getAge",int.class);
        m.invoke(stu,100);
    }
}

看输出结果:


??🤔🤔🤔然后就是静态方法

静态方法与成员方法只两点不同

因为是静态所以无需创建对象实例;

因为无对象实例,所以invoke方法执行时,第一个参数为null,invole(null,方法参数)

import java.lang.reflect.Method;

/**
 * @author sunny
 * @date 2022/04/15 09:08
 **/
public class StaticMethod_Test {
    public static void main(String[] args) throws Exception{
//        获得Class对象
        Class<Student> cls = Student.class;
//        得到指定方法
        Method m = cls.getDeclaredMethod("staticTest",int.class);
//        执行该方法,第一个参数为null
        m.invoke(null,111);
    }
}

打印结果:

?最后来个小结:

(1)获取Class对象

(2)

(3)如果是成员方法,先产生类实例,再invoke(对象名,传入的方法参数)

? ? ? ? ?如果是静态方法,直接invoke(null,传入的方法参数)

(4)当然如果方法权限是不可见的,先破坏封装

2.4 Field类?

Field类是描述类的属性的反射类

跟Method方法大差不差,一个获取方法,一个获取属性而已

该表描述不大准确,Field同Method一样:?

(1)?getFields()方法——能拿到当前类及其父类的所有public属性

?(2)getDelaredFields()方法,仅能拿到当前类的所有属性(包含private属性)

(3)getField("属性名称")——能拿到当前类及其父类的所有public属性中指定的属性

?(4)getDelaredField("属性名称"),能拿到当前类的所有属性中的指定属性

🤔🤔🤔既然能拿到属性,那必然也是能修改属性值得呀~

import java.lang.reflect.Field;

/**
 * @author sunny
 * @date 2022/04/15 09:20
 **/
public class Field_Test {
    public static void main(String[] args) throws Exception{
        Class cls = Class.forName("practice_class.reflect.Student");
        Field field = cls.getDeclaredField("studentAge");
        field.setAccessible(true);
//        创建对象实例
        Student stu = (Student) cls.newInstance();
//       设置属性值为99
        field.set(stu,99);
        System.out.println(stu);
    }
}

?看结果:

yes,修改成功啦!

终于反射总结完了,套路真真都一样,基本反射原理及用法就是这样得啦!

具体反射在第三方框架里得应用我们框架学习中再来品味😊😊😊😊😊?

  Java知识库 最新文章
计算距离春节还有多长时间
系统开发系列 之WebService(spring框架+ma
springBoot+Cache(自定义有效时间配置)
SpringBoot整合mybatis实现增删改查、分页查
spring教程
SpringBoot+Vue实现美食交流网站的设计与实
虚拟机内存结构以及虚拟机中销毁和新建对象
SpringMVC---原理
小李同学: Java如何按多个字段分组
打印票据--java
上一篇文章      下一篇文章      查看所有文章
加:2022-04-22 18:22:34  更:2022-04-22 18:22:56 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/24 4:36:58-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码