- 是什么:什么是反射?
- 为什么:为什么会有反射?
- 怎么办:如何使用反射
- 在哪里使用?
什么是反射?
- 正常情况下:先有类,然后有对象
import java.util.Date;//先有类
public class ReflectTest1 {
public static void main(String[] args) {
Date date = new Date();//后有对象
System.out.println(date);
}
}
- 反射中的反意思是: 通过对象来找类
import java.util.Date;
public class ReflectTest2 {
public static void main(String[] args) {
// 对象
Date date = new Date();
// 找类
System.out.println(date.getClass());
}
}
为什么会有反射?
反射其实是一种语言特性,设计反射的目的是为了溯源
:找到源头类,然后使用源头类。
大部分情况都可以通过 new
一个对象 来使用类以及类上方法,但是有些情况下无法使用 new 来创建对象,因为类是再运行时动态创建的,压根就不存在,此时就是反射上场了。
当然我们先用简单的例子来讲解。
正常使用一个类的方式是: 首先先有这个类,然后通过new ClassName()
来创建这个类的对象,然后就能够借助这个对象调用这个类上面的方法。
代码演示:实现一个唱歌的功能类
package com.ifdom.reflection;
/**
* 1. 先有 Song 这个歌曲类
**/
class Song {
public void sing() {
System.out.println("我正在唱歌");
}
}
/**
* 2.然后通过 `new Song()` 创建出对象 song,借助 song 对象来调用类上面的方法 sing()
**/
public class Reflection1 {
public static void main(String[] args) {
Song song = new Song();
song.sing();
}
}
而反射的使用方式有所不同,需要理解:正常使用一个类,等于我们已经有了工具箱,并且知道工具箱上有哪些方法(工具)了,可以直接使用。
反射是需要先找到,先找到,先找到:先把工具箱(类)找到,把工具箱中的工具(构造方法,属性,方法)找到,找到之后才能使用。
如何使用反射?- 01 溯源
反射首先要溯源(寻找对象是由哪个类产生),你只有找到了源头,歌曲类,锤子类,镰刀类,工具箱类…找到了源头,才能调用源头类的属性和方法,否则调用空气呢?
反射溯源有3种方法:
- 1.通过对象上的
anyObject.getClass()
方法
package com.ifdom.reflection;
/**
* Reflection
**/
public class Reflection1 {
public static void main(String[] args) {
Song song = new Song();
Class<?> oneClass = song.getClass();
// 结果:class com.ifdom.reflection.Song
System.out.println(oneClass);
}
}
class Song {
public void sing() {
System.out.println("我正在唱歌");
}
}
- 2.通过每个类都有的属性
AnyClass.class
package com.ifdom.reflection;
class Song {
public void sing() {
System.out.println("我正在唱歌");
}
}
/**
* Reflection
**/
public class Reflection1 {
public static void main(String[] args) {
Song song = new Song();
Class<Song> oneClass = Song.class;
// 结果:class com.ifdom.reflection.Song
System.out.println(oneClass);
}
}
- 3.通过最底层的Class类提供的
forName()
方法每个类都有的属性Class.forName("AnyClassName")
package com.ifdom.reflection;
class Song {
public void sing() {
System.out.println("我正在唱歌");
}
}
/**
* Reflection
**/
public class Reflection1 {
public static void main(String[] args) throws ClassNotFoundException {
Song song = new Song();
Class<?> oneClass = Class.forName("com.ifdom.reflection.Song");
System.out.println(oneClass);
}
}
如果你不瞎,那么你应该发现了第三种方法要求必须抛出一个异常。
这是为什么?因为我们是在溯源,溯源,溯源,我们对要找的那个类不甚了解,所以有可能失误,导致要寻找的那个类并不存在
所以会抛出ClassNotFoundException
没有找到类异常细心的同学应该发现了,在上面三种溯源方法里有一点差异:
- 第一种是在已经有 初始化 了一个对象 song 的前提下去溯源。
- 第二种是已知类名称为Song,通过类名称去溯源
- 而第三种方法,是在我们只知道 类的包路径 的情况下去溯源。
此时,思考一下,什么时候使用哪种方法去溯源?
别犹豫,你可以相信自己的思考结果。
如何使用反射?- 02 生成实例
现在我们已经知道如何溯源,接下来该如何通过反射溯源后的对象来使用 源头类的 属性和方法呢?
- 调用溯源后的类方法
newInstance()
生成实例对象;
由于第一和第二种溯源方法已经有 song 对象了,可以直接调用对象上的方法
song.sing()
,没必要再经过反射。这里使用第三种溯源方法演示
package com.ifdom.reflection;
class Song {
private String title;
public void sing() {
System.out.println("我正在唱歌" + this.title);
}
}
/**
* Reflection
**/
public class Reflection1 {
public static void main(String[] args) throws ClassNotFoundException, InstantiationException, IllegalAccessException {
Class<?> oneClass = Class.forName("com.ifdom.reflection.Song");
System.out.println(oneClass);
Object instance = oneClass.newInstance();
Song song = (Song) instance;
song.sing();
}
}
通过上面的演示会发现,通过反射来创建实例过于麻烦,写了3行代码,通过new反而只需要一行代码,但是这种方式有一个好处,避免了使用new,而 new 是代码耦合的元凶。
比如:在工厂模式中,现在想知道一首歌曲作者的年龄
package com.ifdom.reflection;
interface Author {
void age();
}
class Song implements Author {
@Override
public void age() {
System.out.println("我的年龄18岁");
}
}
class Company {
public static Author getInstance(String className) {
if ("Song" == className) {
return new Song();
}
return null;
}
}
/**
* Reflection
**/
public class Reflection1 {
public static void main(String[] args) throws ClassNotFoundException, InstantiationException, IllegalAccessException {
Author song = Company.getInstance("Song");
song.age();
}
}
如果 现在公司又有了舞蹈,想知道这个舞蹈作者的年龄.那么不得不修改 Company.getInstance
方法中的内容
package com.ifdom.reflection;
interface Author {
void age();
}
class Song implements Author {
@Override
public void age() {
System.out.println("我的年龄18岁");
}
}
class Dance implements Author {
@Override
public void age() {
System.out.println("我的年龄88岁");
}
}
class Company {
public static Author getInstance(String className) {
if ("Song" == className) {
return new Song();
} else if ("Dance" == className) {
return new Dance();
}
return null;
}
}
/**
* Reflection
**/
public class Reflection1 {
public static void main(String[] args) throws ClassNotFoundException, InstantiationException, IllegalAccessException {
Author song = Company.getInstance("Song");
song.age();
Author dance = Company.getInstance("Dance");
dance.age();
}
}
而使用反射则可以解耦,让你无需改动 Company.getInstance,却依然可以添加额外功能
package com.ifdom.reflection;
interface Author {
void age();
}
class Song implements Author {
@Override
public void age() {
System.out.println("我的年龄18岁");
}
}
class Dance implements Author {
@Override
public void age() {
System.out.println("我的年龄88岁");
}
}
class Company {
public static Author getInstance(String className) throws InstantiationException, IllegalAccessException, ClassNotFoundException {
Author instance = null;
try {
Class<?> oneClass = Class.forName(className);
instance = (Author) oneClass.newInstance();
return instance;
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
}
/**
* Reflection
**/
public class Reflection1 {
public static void main(String[] args) throws ClassNotFoundException, InstantiationException, IllegalAccessException {
Author song = Company.getInstance("com.ifdom.reflection.Song");
song.age();
Author dance = Company.getInstance("com.ifdom.reflection.Dance");
dance.age();
}
}
如何使用反射?- 03 调用构造方法
在上面使用 newInstance()
创建实例时,Song 使用的都是无参构造函数
。
如果一个类只有有参构造函数,又或者有多个有参构造函数,构造函数有一个参数,有2个参数,有3个参数…
那么,想要调用 newInstance() 创建实例,就需要先借助 Class类提供的构造方法,主要有2种
- 获取指定构造函数
public Constructor<?> getConstructor() throws NoSuchMethodException,SecurityException;
- 获取所有构造函数
public Constructor<?>[] getConstructors() throws SecurityException;
package com.ifdom.reflection;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
class Song {
private String title;
private String city;
public Song(String title) {
this.title = title;
}
public Song(String title, String city) {
this.title = title;
this.city = city;
}
public void sing() {
System.out.println("我正在唱 " + this.city + this.title);
}
}
/**
* Reflection
**/
public class Reflection1 {
public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {
Class<?> oneClass = Class.forName("com.ifdom.reflection.Song");
// 获取一个参数的构造函数
Constructor<?> constructor = oneClass.getConstructor(String.class);
Song song = (Song) constructor.newInstance("孤勇者");
song.sing();
// 获取两个参数的构造函数
Constructor<?> constructor1 = oneClass.getConstructor(String.class, String.class);
Song song1 = (Song) constructor1.newInstance("下一站天后", "港台");
song1.sing();
// 获取所有构造函数
Constructor<?>[] constructorAll = oneClass.getConstructors();
Arrays.stream(constructorAll).forEach(System.out::println);
}
}
如何使用反射?-04 调用方法
在上面演示了如何通过反射获取源头类,如何通过反射获取源头类的构造函数,接下来讲解如何通过反射获取源头类上的方法。
在Class类里面提供有以下取得类中Method()的操作:
- 取得一个类中的全部方法:
public Method[] getMethods() throws SecurityException;
- 取得类中指定方法:
public Method getMethod(String name,Class<?> ... ParameterTypes) throws NoSuchMethodException,SecurityException;
package com.ifdom.reflection;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
class Song {
private String title;
private String city;
public void setTitle(String title) { this.title = title;}
public String getTitle() { return title;}
public void setCity(String city) {this.city = city;}
public String getCity() {return city;}
public void setTitleAndCity(String title, String city) {
this.title = title;
this.city = city;
}
@Override
public String toString() {
return "Song{" +
"title='" + title + '\'' +
", city='" + city + '\'' +
'}';
}
}
/**
* @Author ifredom
* @Date 2022/6/29
* @ClassName Reflection1
* @Version 1.0.0
* @Description 描述
**/
public class Reflection1 {
public static void main(String[] args) throws ClassNotFoundException, InstantiationException, IllegalAccessException, NoSuchMethodException, InvocationTargetException {
Class<?> oneClass = Class.forName("com.ifdom.reflection.Song");
Object instance = oneClass.newInstance();
// 获取一个方法并调用该方法
Method setTitleMethod = oneClass.getMethod("setTitle", String.class);
setTitleMethod.invoke(instance, "洋葱");
// 获取一个方法并调用该方法
Method getTitleMethod = oneClass.getMethod("getTitle");
// 打印结果:洋葱
System.out.println(getTitleMethod.invoke(instance));
// 获取一个方法并调用该方法
Method setCityMethod = oneClass.getMethod("setCity", String.class);
setCityMethod.invoke(instance, "大陆");
// 获取所有
Method[] methods = oneClass.getMethods();
for (Method method : methods) {
System.out.println("迭代:" + method);
}
Song song = (Song) instance;
// 打印结果:大陆
System.out.println(song.getCity());
System.out.println(song);
}
public static String generatorMethodName(String str) {
return str.substring(0, 1).toUpperCase() + str.substring(1);
}
}
如何使用反射?- 05 调用属性
到了这里,其实已经可以预料到接下来讲解什么。
一个类上能有什么东西呢?无非就是构造方法,普通方法,属性(或者叫成员,字段)
,当然他们都有修饰符 public, private, static 可以进行修饰。
但是在大类别上,只剩下一个属性
还没有讲解。
所以,使用反射如何获取属性?
Class类中提供了2个API方法:
- 取得全部成员:
public Filed[] getDeclaredFileds() throws SecurityException;
- 取得指定成员:
public Filed getDeclaredFiled(String name) throws NoSuchMethodException,SecurityException;
package com.ifdom.reflection;
import java.lang.reflect.Field;
class Song {
private String title;
@Override
public String toString() {
return "Song{" +
"title='" + title + '\'' +
'}';
}
}
/**
* @Author ifredom
* @Date 2022/6/29
* @ClassName Reflection1
* @Version 1.0.0
* @Description 描述
**/
public class Reflection1 {
public static void main(String[] args) throws ClassNotFoundException, InstantiationException, IllegalAccessException, NoSuchFieldException {
Class<?> oneClass = Class.forName("com.ifdom.reflection.Song");
Object instance = oneClass.newInstance();
// 获取一个方法并调用该方法
Field field = oneClass.getDeclaredField("title");
// 值为 true 时,则表示取消 Java 语言访问检查(private关键字不在生效)
field.setAccessible(true);
field.set(instance, "她");
System.out.println(field.get(instance));
// 获取所有
Field[] declaredFields = oneClass.getDeclaredFields();
Arrays.stream(declaredFields).forEach(System.out::println);
Song song = (Song) instance;
System.out.println(song);
}
}
反射在 Spring 中的应用
spring 框架内部已经提前提供了一个工具类:ReflectionUtils
,通过这个类上提供的静态方法invokeMethod
,与我们上面反射提供的获取method的API是一模一样的效果。
- ReflectionUtils:ReflectionUtils.invokeMethod(method, obj, args);
另外Spring框架,还提供了一个获取bean的方法 ApplicationContext.getbean(className)
,其功能与上面的通过反射获取实例是一模一样的效果。稍微服了一些需要手动去继承这个类 ApplicationContextAware
ApplicationContext.getbean(className)
≈≈Class.forName(className).newInstance()
package com.ifdom.reflection;
import com.ifdom.reflection.entity.User;
import com.ifdom.reflection.utils.SpringContextUtils;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
@SpringBootTest
class ReggieApplicationTests {
@Test
void contextLoads() {
User user = SpringContextUtils.getBean(User.class);
}
}
package com.ifdom.reflection
lection;
import org.jetbrains.annotations.NotNull;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Component;
/**
* @Author ifredom
**/
@Component
public class SpringContextUtils implements ApplicationContextAware {
private static ApplicationContext applicationContext = null;
@Override
public void setApplicationContext(@NotNull ApplicationContext applicationContext) throws BeansException {
if (SpringContextUtils.applicationContext == null) {
SpringContextUtils.applicationContext = applicationContext;
}
}
/**
* @apiNote 获取applicationContext
*/
public static ApplicationContext getApplicationContext() {
return applicationContext;
}
/**
* @apiNote 通过name获取 Bean.
*/
public static Object getBean(String name) {
return getApplicationContext().getBean(name);
}
/**
* @apiNote 通过class获取Bean.
*/
public static <T> T getBean(Class<T> clazz) {
return getApplicationContext().getBean(clazz);
}
/**
* @apiNote 通过name, 以及Clazz返回指定的Bean
*/
public static <T> T getBean(String name, Class<T> clazz) {
return getApplicationContext().getBean(name, clazz);
}
}
拓展:反射reflection提供的其他API
以下内容已经不用阅读,大部分人从入行到入土也用不上,仅仅对骨灰级玩家有用。
- 获取修饰符
- 获取注解
package com.ifdom.reflection;
import java.lang.annotation.Annotation;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
@DemoAnnotation(key = "author", value = "ifredom")
class Song {
private String title;
public String author;
public static void test() {
System.out.println("test");
}
}
/**
* 自定义注解:这个注解有2个属性
*
* @author ifredom
*/
@Retention(RetentionPolicy.RUNTIME)
@interface DemoAnnotation {
public String key();
public String value();
}
/**
* @Author ifredom
* @Date 2022/6/29
* @ClassName Reflection1
* @Version 1.0.0
* @Description 描述
**/
public class Reflection1 {
public static void main(String[] args) throws ClassNotFoundException, InstantiationException, IllegalAccessException, NoSuchFieldException, NoSuchMethodException {
Class<?> oneClass = Class.forName("com.ifdom.reflection.Song");
// 获取属性上的修饰符类型
Field titleField = oneClass.getDeclaredField("title");
int modifiers = titleField.getModifiers();
System.out.println(modifiers);
// 获取属性字段名
Field authorField = oneClass.getDeclaredField("author");
String fieldName = authorField.getName();
System.out.println(fieldName);
// 获取方法上的修饰符类型
Method testMethod = oneClass.getMethod("test");
int modifiers2 = testMethod.getModifiers();
// 获取方法的注解
Annotation testMethodAnnotation1 = testMethod.getAnnotation(DemoAnnotation.class);
System.out.println(testMethodAnnotation1);
// 获取类的注解
Annotation testMethodAnnotation2 = oneClass.getAnnotation(DemoAnnotation.class);
System.out.println(testMethodAnnotation2);
}
}
修饰符属性对照表
- PUBLIC: 1
- PRIVATE: 2
- PROTECTED: 4
- STATIC: 8
- FINAL: 16
- SYNCHRONIZED: 32
- VOLATILE: 64
- TRANSIENT: 128
- NATIVE: 256
- INTERFACE: 512
- ABSTRACT: 1024
- STRICT: 2048
多个修饰符,getModifiers()获取的值是他们和。 例如:public static final 三个修饰的 就是3 个的加和 为 25
扩展阅读
- Spring ReflectionUtils
- What is reflection and why is it useful?
- baeldung: java-reflection
- Spring 中的反射与反射的原理
------ 如果文章对你有用,感谢 >>>点赞 | 收藏 <<<