- 是什么:什么是反射?
- 为什么:为什么会有反射?
- 怎么办:如何使用反射
- 在哪里使用?
- 正常情况下:先有类,然后有对象
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;
class Song {
public void sing() {
System.out.println("我正在唱歌");
}
}
public class Reflection1 {
public static void main(String[] args) {
Song song = new Song();
song.sing();
}
}
而反射的使用方式有所不同,需要理解:正常使用一个类,等于我们已经有了工具箱,并且知道工具箱上有哪些方法(工具)了,可以直接使用。
反射是需要先找到,先找到,先找到:先把工具箱(类)找到,把工具箱中的工具(构造方法,属性,方法)找到,找到之后才能使用。
如何使用反射?- 01 溯源反射首先要溯源(寻找对象是由哪个类产生),你只有找到了源头,歌曲类,锤子类,镰刀类,工具箱类…找到了源头,才能调用源头类的属性和方法,否则调用空气呢?
反射溯源有3种方法:
- 1.通过对象上的 anyObject.getClass() 方法
package com.ifdom.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("我正在唱歌");
}
}
public class Reflection1 {
public static void main(String[] args) {
Song song = new Song();
Class 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("我正在唱歌");
}
}
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);
}
}
如何使用反射?- 02 生成实例如果你不瞎,那么你应该发现了第三种方法要求必须抛出一个异常。
这是为什么?因为我们是在溯源,溯源,溯源,我们对要找的那个类不甚了解,所以有可能失误,导致要寻找的那个类并不存在
所以会抛出 ClassNotFoundException 没有找到类异常细心的同学应该发现了,在上面三种溯源方法里有一点差异:
第一种和第二种都是在已经有对象 song 的前提下去溯源。
而第三种方法,是在我们只知道类的包路径的情况下去溯源。此时,思考一下,什么时候使用哪个方法去溯源?
别犹豫,你可以相信自己的思考结果。
现在我们已经知道如何溯源,接下来该如何通过反射溯源后的对象来使用 源头类的 属性和方法呢?
- 调用溯源后的类方法 newInstance()生成实例对象;
由于第一和第二种溯源方法已经有 song 对象了,可以直接调用对象上的方法 song.sing(),没必要再经过反射。这里使用第三种溯源方法演示
package com.ifdom.reflection;
class Song {
private String title;
public void sing() {
System.out.println("我正在唱歌" + this.title);
}
}
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();
}
}
通过上面的演示会发现,通过反射来创建实例过于麻烦了,但是这种方式有一个好处,避免了使用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;
}
}
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;
}
}
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();
}
}
而使用反射则可以解耦
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;
}
}
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);
}
}
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 + ''' +
'}';
}
}
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 + ''' +
'}';
}
}
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 中的应用
- ReflectionUtils.invokeMethod(method, obj, args); // ReflectionUtils 是 spring 中内置的工具类
- Spring: ApplicationContext.getbean(className) ≈≈ Java: Class.forName(className).newInstance().
以下内容已经不用阅读,大部分人从入行到入土也用不上,仅仅对骨灰级玩家有用。
- 获取修饰符
- 获取注解
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");
}
}
@Retention(RetentionPolicy.RUNTIME)
@interface DemoAnnotation {
public String key();
public String value();
}
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 中的反射与反射的原理
------ 如果文章对你有用,感谢 >>>点赞 | 收藏 <<<



