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();
}
}
而反射的使用方式有所不同,需要理解:正常使用一个类,等于我们已经有了工具箱,并且知道工具箱上有哪些方法(工具)了,可以直接使用。
反射是需要先找到,先找到,先找到:先把工具箱(类)找到,把工具箱中的工具(构造方法,属性,方法)找到,找到之后才能使用。
反射首先要溯源(寻找对象是由哪个类产生),你只有找到了源头,歌曲类,锤子类,镰刀类,工具箱类…找到了源头,才能调用源头类的属性和方法,否则调用空气呢?
反射溯源有3种方法:
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("我正在唱歌");
}
}
AnyClass.classpackage 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);
}
}
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 的前提下去溯源。
而第三种方法,是在我们只知道类的包路径的情况下去溯源。此时,思考一下,什么时候使用哪个方法去溯源?
别犹豫,你可以相信自己的思考结果。
现在我们已经知道如何溯源,接下来该如何通过反射溯源后的对象来使用 源头类的 属性和方法呢?
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();
}
}
通过上面的演示会发现,通过反射来创建实例过于麻烦了,但是这种方式有一个好处,避免了使用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();
}
}
而使用反射则可以解耦
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();
}
}
在上面使用 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);
}
}
在上面演示了如何通过反射获取源头类,如何通过反射获取源头类的构造函数,接下来讲解如何通过反射获取源头类上的方法。
在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);
}
}
到了这里,其实已经可以预料到接下来讲解什么。
一个类上能有什么东西呢?无非就是构造方法,普通方法,属性(或者叫成员),当然他们都有修饰符 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);
}
}
以下内容已经不用阅读,大部分人从入行到入土也用不上,仅仅对骨灰级玩家有用。
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);
}
}
修饰符属性对照表
多个修饰符,getModifiers()获取的值是他们和。 例如:public static final 三个修饰的 就是3 个的加和 为 25
------ 如果文章对你有用,感谢 >>>点赞 | 收藏 <<<