Java 反射机制详解:入门、使用

反射概述


什么是反射

将类的各个组成部分封装为其他对象的过程就叫做 反射,其中 组成部分 指的是我们类的 成员变量(Field)构造方法(Constructor)成员方法(Method)

使用反射的优缺点

  • 优点
  1. 在程序运行过程中可以操作类对象,增加了程序的灵活性;

  2. 解耦,从而提高程序的可扩展性,提高代码的复用率,方便外部调用;

  3. 对于任何一个类,当知道它的类名后,就能够知道这个类的所有属性和方法;而对于任何一个对象,都能够调用它的一个任意方法。

  • 缺点
  1. 性能问题:Java 反射中包含了一些动态类型,JVM 无法对这些动态代码进行优化,因此通过反射来操作的方式要比正常操作效率更低。

  2. 安全问题:使用反射时要求程序必须在一个没有安全限制的环境中运行,如果程序有安全限制,就不能使用反射。

  3. 程序健壮性:反射允许代码执行一些平常不被允许的操作,破坏了程序结构的抽象性,导致平台发生变化时抽象的逻辑结构无法被识别。

Class 对象的获取及使用


获取 Class 对象的方式

  1. Class.forName("全类名")

源代码阶段,它能将字节码文件加载进内存中,然后返回 Class 对象,多用于 配置文件 中,将类名定义在配置文件中,通过读取配置文件来加载类。

  1. 类名.class

类对象阶段,通过类名的 class 属性来获取,多用于 参数的传递

  1. 对象.getClass()

运行时阶段,getClass() 定义在 Object 类中,表明所有类都能使用该方法,多用于 对象的获取字节码 的方式。

我们首先定义一个 Person 类,用于后续反射功能的测试;

package com.cunyu;

/**

  • @author : cunyu

  • @version : 1.0

  • @className : Person

  • @date : 2021/4/7 22:37

  • @description : Person 类

*/

public class Person {

private int age;

private String name;

public long id;

public long grade;

protected float score;

protected int rank;

public Person(int age, String name, long id, long grade, float score, int rank) {

this.age = age;

this.name = name;

this.id = id;

this.grade = grade;

this.score = score;

this.rank = rank;

}

public Person() {

}

public int getAge() {

return age;

}

public void setAge(int age) {

this.age = age;

}

public String getName() {

return name;

}

public void setName(String name) {

this.name = name;

}

public long getId() {

return id;

}

public void setId(long id) {

this.id = id;

}

public long getGrade() {

return grade;

}

public void setGrade(long grade) {

this.grade = grade;

}

public float getScore() {

return score;

}

public void setScore(float score) {

this.score = score;

}

public int getRank() {

return rank;

}

public void setRank(int rank) {

this.rank = rank;

}

@Override

public String toString() {

final StringBuffer sb = new StringBuffer(“Person{”);

sb.append(“age=”).append(age);

sb.append(“, name='”).append(name).append(‘’');

sb.append(“, id=”).append(id);

sb.append(“, grade=”).append(grade);

sb.append(“, score=”).append(score);

sb.append(“, rank=”).append(rank);

sb.append(‘}’);

return sb.toString();

}

}

定义好 Person 类之后,我们尝试用 3 种不同的方式来获取 Class 对象,并比较它们是否相同。

package com.cunyu;

/**

  • @author : cunyu

  • @version : 1.0

  • @className : Demo1

  • @date : 2021/4/7 23:29

  • @description : Class 对象的获取

*/

public class Demo1 {

public static void main(String[] args) throws ClassNotFoundException {

// 第一种方式,Class.forName(“全类名”)

Class class1 = Class.forName(“com.cunyu.Person”);

System.out.println(class1);

// 第二种方式,类名.class

Class class2 = Person.class;

System.out.println(class2);

// 第三种方式,对象.getName()

Person person = new Person();

Class class3 = person.getClass();

System.out.println(class3);

// 比较三个对象是否相同

System.out.println(class1 == class2);

System.out.println(class1 == class3);

}

}

对比结果

上述代码中,会发现最后输出的比较结果返回的是两个 true,说明通过上述三种方式获取的 Class 对象都是同一个,同一个字节码文件(*.class)在一次运行过程中只会被加载一次

Class 对象的使用

获取成员变量

| 方法 | 说明 |

| :-- | :-- |

| Field[] getFields() | 返回包含一个数组 Field对象反射由此表示的类或接口的所有可访问的公共字段类对象 |

| Field getField(String name) | 返回一个 Field对象,它反映此表示的类或接口的指定公共成员字段类对象 |

| Field[] getDeclaredFields() | 返回的数组 Field对象反映此表示的类或接口声明的所有字段类对象 |

| Field getDeclaredField(String name) | 返回一个 Field对象,它反映此表示的类或接口的指定已声明字段类对象 |

  • Field[] getFields()

package com.cunyu;

import java.lang.reflect.Field;

/**

  • @author : cunyu

  • @version : 1.0

  • @className : Demo2

  • @date : 2021/4/7 23:39

  • @description : Class 对象的使用

*/

public class Demo2 {

public static void main(String[] args) throws ClassNotFoundException {

Class class1 = Class.forName(“com.cunyu.Person”);

Field[] fields = class1.getFields();

for (Field field : fields) {

System.out.println(field);

}

}

}

回顾下我们的 Person 类,可以发现 idgrade 成员变量都是被 public 所修饰的,说明该方法是用于获取类中所有被 public 所修饰的成员变量(包括父类)。

  • Field getField(String name)

package com.cunyu;

import java.lang.reflect.Field;

/**

  • @author : cunyu

  • @version : 1.0

  • @className : Demo2

  • @date : 2021/4/7 23:39

  • @description : Class 对象的使用

*/

public class Demo2 {

public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException {

Class class1 = Class.forName(“com.cunyu.Person”);

Field field1 = class1.getField(“id”);

System.out.println(field1);

Field field2 = class1.getField(“age”);

System.out.println(field2);

Field field3 = class1.getField(“rank”);

System.out.println(field3);

}

}

从上面的结果分析可知,该方法只能用于获取类中指定名称的 public 所修饰的成员变量,对于 protectedprivate 所修饰的成员变量,该方法是无法获取的(包括父类)。而获取或设置成员变量值时,可以通过 get/set 方法来操作,具体操作方法如下。

// 假设我们获取到的 Field 为上面的 id,获取和设置 id 的值就可以通过如下操作来进行

// 1. 获取

Field idField = personClass.getField(“id”);

Person person = new Person();

Object idValue = idField.get(person);

System.out.println(“id:” + idValue);

// 2. 设置

idField.set(person, “1312120”);

System.out.println(“person:” + person);

  • Field[] getDeclaredFields()

package com.cunyu;

import java.lang.reflect.Field;

/**

  • @author : cunyu

  • @version : 1.0

  • @className : Demo2

  • @date : 2021/4/7 23:39

  • @description : Class 对象的使用

*/

public class Demo2 {

public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException {

Class class1 = Class.forName(“com.cunyu.Person”);

Field[] fields = class1.getDeclaredFields();

for (Field field : fields) {

System.out.println(field);

}

}

}

观察上面的结果可知,该方法可用于获取所有的成员变量,不用考虑修饰符的限制(不包括父类)。

  • Field getDeclaredField(String name)

package com.cunyu;

import java.lang.reflect.Field;

/**

  • @author : cunyu

  • @version : 1.0

  • @className : Demo2

  • @date : 2021/4/7 23:39

  • @description : Class 对象的使用

*/

public class Demo2 {

public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException {

Class class1 = Class.forName(“com.cunyu.Person”);

Field field1 = class1.getDeclaredField(“id”);

System.out.println(field1);

Field field3 = class1.getDeclaredField(“rank”);

System.out.println(field3);

Field field2 = class1.getDeclaredField(“age”);

System.out.println(field2);

}

}

观察上面的结果可知,该方法可用于获取指定的成员变量,不用考虑成员变量修饰符的限制(不包括父类)。但是在利用 setget 方法来获取和设置 privateprotected 修饰的成员变量时,需要利用 setAccessible() 来忽略访问全新啊修饰符的安全检查,否则程序将会报错。

获取构造方法

| 方法 | 说明 |

| :-- | :-- |

| Constructor<?>[] getConstructors() | 返回包含一个数组 Constructor对象反射由此表示的类的所有公共构造类对象 |

| Constructor<T> getConstructor(类<?>... parameterTypes) | 返回一个 Constructor 对象,该对象反映 Constructor对象表示的类的指定的公共类函数 |

| Constructor<?>[] getDeclaredConstructors() | 返回一个反映 Constructor 对象表示的类声明的所有 Constructor 对象的数组类 |

| Constructor<T> getDeclaredConstructor(类<?>... parameterTypes) | 返回一个 Constructor 对象,该对象反映 Constructor 对象表示的类或接口的指定类函数 |

package com.cunyu;

自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

深知大多数Java工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则几千的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年Java开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。img

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Java开发知识点,真正体系化!

由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!

如果你觉得这些内容对你有帮助,可以扫码获取!!(备注Java获取)

img

Ending

Tip:由于文章篇幅有限制,下面还有20个关于MySQL的问题,我都复盘整理成一份pdf文档了,后面的内容我就把剩下的问题的目录展示给大家看一下

如果觉得有帮助不妨【转发+点赞+关注】支持我,后续会为大家带来更多的技术类文章以及学习类文章!(阿里对MySQL底层实现以及索引实现问的很多)

吃透后这份pdf,你同样可以跟面试官侃侃而谈MySQL。其实像阿里p7岗位的需求也没那么难(但也不简单),扎实的Java基础+无短板知识面+对某几个开源技术有深度学习+阅读过源码+算法刷题,这一套下来p7岗差不多没什么问题,还是希望大家都能拿到高薪offer吧。

《一线大厂Java面试题解析+核心总结学习笔记+最新讲解视频+实战项目源码》点击传送门即可获取!
比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!**

如果你觉得这些内容对你有帮助,可以扫码获取!!(备注Java获取)

img

Ending

Tip:由于文章篇幅有限制,下面还有20个关于MySQL的问题,我都复盘整理成一份pdf文档了,后面的内容我就把剩下的问题的目录展示给大家看一下

如果觉得有帮助不妨【转发+点赞+关注】支持我,后续会为大家带来更多的技术类文章以及学习类文章!(阿里对MySQL底层实现以及索引实现问的很多)

[外链图片转存中…(img-9KbzKnK4-1712109684335)]

[外链图片转存中…(img-pTU325My-1712109684335)]

吃透后这份pdf,你同样可以跟面试官侃侃而谈MySQL。其实像阿里p7岗位的需求也没那么难(但也不简单),扎实的Java基础+无短板知识面+对某几个开源技术有深度学习+阅读过源码+算法刷题,这一套下来p7岗差不多没什么问题,还是希望大家都能拿到高薪offer吧。

《一线大厂Java面试题解析+核心总结学习笔记+最新讲解视频+实战项目源码》点击传送门即可获取!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值