学习过好几遍的反射了,但总是过一段又忘了,不知道该怎么使用,特此总结下来,并手撸代码加深印象,感觉这样很快就掌握了。
反射就是Reflection,Java的反射是指程序在运行期可以拿到一个对象的所有信息。
Reflection(反射)是被视为动态语言的关键(本身仍为静态语言,可以说是准动态语言),反射机制允许程序在执行期借助于Reflection API取得任何类的内部信息,并能直接操作任意对象的内部属性及方法。
加载完类之后,在堆内存的方法区中就产生了一个Class类型的对象(一个类只有一个Class对象),这个对象就包含了完整的类的结构信息。我们可以通过这个对象看到类的结构。这个对象就像一面镜子,透过这个镜子看到类的结构,所以,我们形象的称之为:反射。
是一类在运行时可以改变其结构的语言:例如新的函数、对象、甚至代码可以被引进,已有的函数可以被删除或是其他结构上的变化。通俗点说就是在运行时代码可以根据某些条件改变自身结构。主要动态语言:Object-C、C#、JavaScript、PHP、Python、Erlang。
与动态语言相对应的,运行时结构不可变的语言就是静态语言。如Java、C、C++。
Java不是动态语言,但Java可以称之为“准动态语言”。即Java有一定的动态性,我们可以利用反射机制、字节码操作获得类似动态语言的特性。
Java的动态性让编程的时候更加灵活!
class属性
来获取该类对应的Class对象。例如Person.class
将会返回Person类
对应的Class对象
。Class类
的forName()静态方法
。该方法需要传入字符串参数,该字符串参数的值是某个类的全限定类名(必须添加完整包名)。getClass()
方法,该方法是java.lang.Object
类中的一个方法,所以所有Java对象都可以调用该方法,该方法将会返回该对象所属类对应的Class对象。ClassLoader
优势如下:
但如果我们只有一个字符串, 例如“java.lang. String”
,如果需要获取该字符串对应的Class对象,则只能使用第二种方
式了,使用Class的forName方法获取Class对象时,该方法可能抛出一个ClasNotFoundException异常
。
MyAnnotation注解
、Person接口
和Producer类
为通用的,后续还会使用
@Target({TYPE, FIELD, METHOD, PARAMETER, CONSTRUCTOR, LOCAL_VARIABLE})
@Retention(RetentionPolicy.RUNTIME)
public @interface MyAnnotation {
String value() default "Annotation测试";
}
/**
* 测试反射的接口
*/
public interface Person {
//吃饭
void eat(String food);
}
/**
* 功能描述:测试反射的JavaBean
* 生产商类
*
* @author RenShiWei
* Date: 2020/6/19 9:16
**/
@MyAnnotation("反射获取类注解信息")
public class Producer implements Person {
@MyAnnotation("生产商姓名")
private String name;
int age;
public int id;
//生产的商品map
public Map<String,String> productMap=new HashMap<>();
public Producer () {
}
private Producer ( String name, int age, int id ) {
this.name = name;
this.age = age;
this.id = id;
}
//get和set方法
public String getName () {
return name;
}
public void setName ( String name ) {
this.name = name;
}
public int getAge () {
return age;
}
public void setAge ( int age ) {
this.age = age;
}
public int getId () {
return id;
}
public void setId ( int id ) {
this.id = id;
}
//生产产品
@MyAnnotation
public String produce ( String product ) {
return "正在生产产品:" + product;
}
//查看产品
void showProducts () {
System.out.println("正在查看产品");
}
//实现接口的方法
@Override
public void eat ( String food ) {
System.out.println("正在吃:" + food);
}
//内部类
class Inner{}
@Override
public String toString () {
return "Producer{" +
"name='" + name + '\'' +
", age=" + age +
", id=" + id +
'}';
}
}
测试获取Class对象
public static void main ( String[] args ) {
System.out.println("**************测试获取Class的对象******************");
System.out.println("----方式一:调用类的class属性获取----");
Class<Producer> clazz1 = Producer.class;
System.out.println(clazz1);
System.out.println("---方式二:通过Class类的静态方法forName()获取----");
Class<?> clazz2=null;
try {
clazz2 = Class.forName("reflectiontest.Producer");
System.out.println(clazz2);
} catch (ClassNotFoundException e) {
System.out.println("没有通过全限类名找到此类,不能使用反射获取Class对象");
e.printStackTrace();
}
System.out.println("---方式三:调用对象的getClass()方法(此方法为java.lang.Object的方法)获取---");
Producer producer=new Producer();
Class<? extends Producer> clazz3 = producer.getClass();
System.out.println(clazz3);
System.out.println("---方式四:通过类加载器ClassLoader获取---");
ClassLoader classLoader=ReflectionTest.class.getClassLoader();
Class<?> clazz4=null;
try {
clazz4 = classLoader.loadClass("reflectiontest.Producer");
System.out.println(clazz4);
} catch (ClassNotFoundException e) {
System.out.println("没有通过全限类名找到此类,不能使用反射获取Class对象");
e.printStackTrace();
}
System.out.println("对比四种方式获取的Class对象是否是同一个");
System.out.println(clazz1==clazz2);
System.out.println(clazz1==clazz3);
System.out.println(clazz1==clazz4);
}
结果:
**************测试获取Class的对象******************
----方式一:调用类的class属性获取----
class reflectiontest.Producer
---方式二:通过Class类的静态方法forName()获取----
class reflectiontest.Producer
---方式三:调用对象的getClass()方法(此方法为java.lang.Object的方法)获取---
class reflectiontest.Producer
---方式四:通过类加载器ClassLoader获取---
class reflectiontest.Producer
对比四种方式获取的Class对象是否是同一个
true
true
true
构造器 | 描述 |
---|---|
Connstructor getConstructor(Class>… parameterTypes) | 返回此Class对象所表示的类的指定的public构造器 |
Constructor>[] getConstructors() | 返回此Class对象所表示的类的所有public 构造器。 |
Constructor getDeclaredConstructor(Class>… parameterTypes) | 返回此Class对象所表示的类的指定构造器,与构造器的访问级别无关 |
Constructor>[] getDeclaredConstructors() | 返回此Class对象所表示的类的所有构造器,与构造器的访问级别无关 |
方法 | 描述 |
---|---|
Method getMethod(String name, Class>… parameterTypes) | 返回此Class对象所表示的类的指定public方法 |
Method[] getMethods() | 返回此Class对象所表示的类的所有public方法 |
Method getDeclaredMethod(String name, Class>… parameterTypes) | 返回此Class对象所表示的类的指定方法,与方法的访问级别无关 |
Method[] getDeclaredMethods() | 返回此Class对象所表示的类的全部方法,与方法的访问级别无关 |
方法 | 描述 |
---|---|
Field getField(String name) | 返回此Class对象所表示的类的指定的public属性( Field) |
Field[] getFields() | 返回此Class对象所表示的类的所有public属性(Field) |
Field getDeclaredField(String name) | 返回此Class对象所表示的类的指定属性( Field) ,与属性的访问级别无关。 |
Field] getDeclaredFields() | 返回此Class对象所表示的类的全部属性(Field) ,与属性的访间级别无关 |
方法 | 描述 |
---|---|
A getAnnotation(Class annotationClass) | 试图获取该Class对象所表示类上.指定类型的注释;如果该类型的注释不存在则返回null |
Annotation[] getAnnotations() | 返回此元素上存 在的所有注释 |
Annotation[] getDeclaredAnnotations() | 返回直接存在于此元素上的所有注释 |
方法 | 描述 |
---|---|
Class>[ getDeclaredClasses() | 返回该Class对象所对应类里包含的全部内部类 |
方法 | 描述 |
---|---|
Class> getDeclaringClass() | 返回该Class对象所对应类所在的外部类 |
方法 | 描述 |
---|---|
Class>[] getInterfaces() | 返回该Class对象对应类所实现的全部接口 |
int getModifiers() | 返回此类或接口的所有修饰符 修饰符由public. protected、private、final、static、abstract等对应的常量组成 返回的整数应使用Modifier工具类的方法来解码,才可以获取真实的修饰符。 |
Package getPackage() | 获取此类的包 |
String getName() | 以字符串形式返回此Class 对象所表示的类的名称 |
String getSimpleName() | 以字符串形式返回此Class 对象所表示的类的简称 |
Class super T> getSuperclass() | 返回该Class 所表示的类的超类对应的Class对象 |
方法 | 描述 |
---|---|
boolean isAnnotation() | 返回此Class 对象是否表示一个注释类型( 由@interface定义) |
boolean isAnnotationPresent(Class extends Annotation> annotationClass) | 判断此Class对象,上是否使用了Annotation注释修饰 |
boolean isAnonymousClass() | 返回此Class 对象是否是一个匿 名类 |
boolean isArray() | 返回此Class 对象是否表示一个数组类 |
boolean isEnum() | 返回此Class 对象是否表示一个枚举(由enum关键字定义) |
boolean isInterface() | 返回此Class 对象是否表示一一个接口( 使用interface定义) |
boolean isInstance(Object obj) | 判断obj是否是此Class对象的实例,该方法可以完全代替instanceof操作符 |
MyAnnotation注解
、Person接口
和Producer类
还采用上文的。
public static void main ( String[] args ) throws Exception {
Class<Producer> clazz = Producer.class;
System.out.println("************测试获取类的信息****************");
System.out.println("---测试获取Class对象的全部构造器---");
Constructor<?>[] constructors = clazz.getDeclaredConstructors();
for (Constructor<?> c : constructors) {
System.out.println(c);
}
System.out.println("---测试获取Class对象的全部为public的构造器---");
Constructor<?>[] publicConstructors = clazz.getConstructors();
for (Constructor<?> c : publicConstructors) {
System.out.println(c);
}
System.out.println("---测试获取全部方法---");
Method[] declaredMethods = clazz.getDeclaredMethods();
for (Method m : declaredMethods) {
System.out.println(m);
}
System.out.println("---测试获取指定的方法---");
Method method = clazz.getMethod("produce", String.class);
System.out.println(method);
System.out.println("---测试获取类的全部注解---");
Annotation[] annotations = clazz.getAnnotations();
for (Annotation a : annotations) {
System.out.println(a);
}
System.out.println("---测试获取元素的注解---\n" + Arrays.toString(clazz.getAnnotationsByType(MyAnnotation.class)));
System.out.println("---测试获取全部的内部类---");
Class<?>[] declaringClass = clazz.getDeclaredClasses();
for (Class<?> inner : declaringClass) {
System.out.println(inner);
}
System.out.println("---测试使用Class的forName的方法获取内部类---");
System.out.println(Class.forName("reflectiontest.Producer$Inner"));
//还可以获取很多信息,暂略.....
}
结果:
************测试获取类的信息****************
---测试获取Class对象的全部构造器---
public reflectiontest.Producer()
private reflectiontest.Producer(java.lang.String,int,int)
---测试获取Class对象的全部为public的构造器---
public reflectiontest.Producer()
---测试获取全部方法---
public java.lang.String reflectiontest.Producer.getName()
public int reflectiontest.Producer.getId()
public void reflectiontest.Producer.setName(java.lang.String)
public void reflectiontest.Producer.setId(int)
public void reflectiontest.Producer.setAge(int)
public java.lang.String reflectiontest.Producer.produce(java.lang.String)
void reflectiontest.Producer.showProducts()
public int reflectiontest.Producer.getAge()
public void reflectiontest.Producer.eat(java.lang.String)
---测试获取指定的方法---
public java.lang.String reflectiontest.Producer.produce(java.lang.String)
---测试获取类的全部注解---
@reflectiontest.MyAnnotation(value=反射获取类注解信息)
---测试获取元素的注解---
[@reflectiontest.MyAnnotation(value=反射获取类注解信息)]
---测试获取全部的内部类---
class reflectiontest.Producer$Inner
---测试使用Class的forName的方法获取内部类---
class reflectiontest.Producer$Inner
Class对象可以获得该类里的成分包括方法(由Method对象表示)、构造器(由Constructor 对象表示)、Field (由Field 对象表示),这三个类都定义在java.lang.reflect
包下,并实现了java.lang.reflect.Member接口。程序可以通过Method对象来执行对应的方法,通过Constructor对象来调用对应的构造器创建对象,能通过Field对象直接访问并修改对象的属性值。
newInstance()
方法来创建该Class对象对应类的实例,这种方式要求该Class对象的对应类有默认构造器,而执行newlnstance()
方法时实际上是利用默认构造器来创建该类的实例。通过第一种方式来创建对象是比较常见的情形(更加通用)。因为在很多JavaEE框架中都需要根据配置文件信息来创建Java对象,从配置文件读取的只是某个类的字符串类名,程序就需要根据该字符串来创建对应的实例,就必须使用反射。
第二种方式:
1)通过Class类的getDeclaredConstructor(Class … parameterTypes)
取得本类的指定形参类型的构造器
2)向构造器的形参中传递一个对象数组进去,里面包含了构造器中所需的各个参数。
3)通过Constructor实例化对象。
当获得某个类对应的Class对象后,就可以通过该Class对象的getMethods()方法或者getMethod()方法来获取全部方法或指定方法一这两个方法的返回值是Method对象数组,或者Method对象。每个Method对象对应一个方法,获得Method对象后,程序就可通过该Method来调用对应方法。
之后使用Object invoke(Object obj, Object[] args)
进行调用,并向方法中传递要设置的obj对象的参数信息。
说明:
通过Class对象的getFields()或getField(方法可以获取该类所包括的全部Field(属性)或指定Field。通过Field类提供的set()和
get()方法就可以完成设置和取得属性内容的操作。
public Field getField(String name)
返回此Class对象表示的类或接口的指定的public的Field。public Field getDeclaredField(String name)
返回此Class对象表示的类或接口的指定的Field。Field提供了如下两组方法来访问属性:
getXxx(Object obj)
: 获取obj对象该Field的属性值。此处的Xxx对应8个基本类型,如果该属性的类型是引用类型则取消get后面的Xxx。setXxx(Object obj,Xxx val)
:将obj对象的该Field设置成val值。此处的Xxx对应8个基本类型,如果该属性的类型是引用类型则取消set后面的Xxx。使用这两个方法可以随意地访问指定对象的所有属性,包括private访问控制的属性。MyAnnotation注解
、Person接口
和Producer类
还采用上文的。
public static void main ( String[] args ) throws Exception {
System.out.println("*****测试创建运行时类的对象,并通过此对象调用方法和访问属性*****");
System.out.println("---测试创建运行时类的对象---");
//获取Class对象
Class<Producer> clazz = Producer.class;
//获取构造器,这里先使用默认的空参构造器(也可以使用指定的构造器)
Constructor<Producer> constructor = clazz.getConstructor();
//通过调用构造器的newInstance方法创建对象
Producer producer = constructor.newInstance();
System.out.println(producer);
//可以通过这个对象操作数据
System.out.println("------使用这个对象操作类的属性和方法------");
producer.setAge(22);
producer.showProducts();
System.out.println(producer);
System.out.println("---测试通过反射调用方法---");
Method method = clazz.getDeclaredMethod("showProducts");
method.setAccessible(true);
//传入的是创建的运行时类的对象
method.invoke(producer);
System.out.println("---测试通过反射访问属性---");
Field field = clazz.getDeclaredField("name");
field.setAccessible(true);
field.set(producer,"我叫反射");
System.out.println(field.get(producer));
System.out.println(producer);
}
结果:
*****测试创建运行时类的对象,并通过此对象调用方法和访问属性*****
---测试创建运行时类的对象---
Producer{name='null', age=0, id=0}
------使用这个对象操作类的属性和方法------
正在查看产品
Producer{name='null', age=22, id=0}
---测试通过反射调用方法---
正在查看产品
---测试通过反射访问属性---
Producer{name='我叫反射', age=22, id=0}
我叫反射
在java.lang.reflect包下还提供了一个Array类,Array对象可以代表所有的数组。程序可以通过使用Array来动态地创建数组,操作数组元素等。
Array提供了如下几类方法:
static Object newlnstance(Class> componentT ype, int... length)
:创建- -个 具有指定的元素类型、指定维度的新数组。static xxx getXxx(Object array, int index)
:返回array数组中第index个元素。其中xxx是各种基本数据类型,如果数组元素是引用类型,则该方法变为get(Object array, int index)
。static void setXxx(Object array, int index, xXXx val)
:将array数组中第index元素的值设为val。其中xxx是各种基本数据类型,如果数组元素是引用类型,则该方法变成set(Object array,int index, Object val)
。public static void main ( String[] args ) {
System.out.println("*******测试反射操作数组********");
System.out.println("---操作一维数组---");
//创建数组
Object arr = Array.newInstance(String.class, 10);
//赋值
Array.set(arr, 2, "测试反射");
Array.set(arr, 4, "测试反射操作数组");
//获取指定元素的值
System.out.println(Array.get(arr, 2));
System.out.println(Array.get(arr, 4));
System.out.println(Array.get(arr, 6));
//三维数组其实是数组元素为二维数组的特殊一维数组
System.out.println("---操作三维数组---");
Object arr3 = Array.newInstance(String.class, 3, 4, 10);
//
System.out.println("------获取index为2的数组元素,应该是一个二维数组------");
//获取arr3index为2的数组元素,应该是一个二维数组
Object arrObj = Array.get(arr3, 2);
//赋值
Array.set(arrObj,2,new String[]{"反射测试1","反射测试2"});
//获取arrObj数组的第三个元素,应该是一维数组
Object anArr = Array.get(arrObj, 3);
Array.set(anArr,8,"测试一维反射");
String[][][] cast= (String[][][]) arr3;
System.out.println(cast[2][3][8]);
System.out.println(cast[2][2][0]);
System.out.println(cast[2][2][1]);
}
结果:
*******测试反射操作数组********
---操作一维数组---
测试反射
测试反射操作数组
null
---操作三维数组---
------获取index为2的数组元素,应该是一个二维数组------
测试一维反射
反射测试1
反射测试2
在反射中使用泛型,可以避免使用反射的对象需要强制转换类型,从而引起异常。
public static void main ( String[] args ) throws Exception {
System.out.println("*******测试反射获取泛型*******");
System.out.println("---不使用泛型的情况---");
Person producer = (Producer) getInstance(Producer.class);
System.out.println("---使用泛型的情况---");
Person producer2 = getInstance2(Producer.class);
//false,因为一个是Object类型,一个是Producer类型
System.out.println(producer == producer2);
}
/**
* 不使用泛型的情况
*/
public static Object getInstance ( Class clazz ) throws Exception {
return clazz.getConstructor().newInstance();
}
/**
* 使用泛型的情况
*/
public static <T> T getInstance2 ( Class<T> clazz ) throws Exception {
return clazz.getConstructor().newInstance();
}
结果:
*******测试反射获取泛型*******
---不使用泛型的情况---
---使用泛型的情况---
false
通过指定类对应的Class 对象,程序可以获得该类里包括的所有Ficld, 不管该Field 使用private修饰,还是使用public修饰。获得了Field对象后,就可以很容易地获得该Field的数据类型,即使用如下代码即可获得指定Field的类型:
//获取Field对象f的类型
Class<?> a = f.getType() ;
但通过这种方式只对普通类型的Field 有效。但如果该Field 的类型是有泛型限制的类型,如Map
类型,则不能准确得到该Field的泛型参数。
为了获得指定Field的泛型类型,应先使用如下方法来获取指定Field的泛型类型:
//获得Field实例f的泛型类型
Type gType = f.getGenericType() ;
然后将Type对象
强制类型转换为ParameterizedType对象
,ParameterizedType 代表被参数化的类型,也就是增加了泛型限制的类型。ParameterizedType 类提供了两个方法: .
getRawType()
: 返回被泛型限制的类型。getActualTypeArguments()
: 返回泛型参数类型。在producer类
中添加代码:
//生产的商品map
public Map<String,String> productMap=new HashMap<>();
测试:
public static void main ( String[] args ) throws Exception {
System.out.println("***使用反射获取泛型信息****");
Class<Producer> clazz = Producer.class;
Field f = clazz.getDeclaredField("productMap");
Class<?> a = f.getType();
System.out.println("获取参数类型" + a);
Type genericType = f.getGenericType();
System.out.println("获取泛型参数类型"+genericType);
if (genericType instanceof ParameterizedType) {
//将Type强转为ParameterizedType
ParameterizedType parameterizedType = (ParameterizedType) genericType;
//获取原始类型
System.out.println("原始类型:" + parameterizedType.getRawType());
Type[] actualTypeArguments = parameterizedType.getActualTypeArguments();
for(Type t:actualTypeArguments){
System.out.print(t+" ");
}
}
}
结果:
*******测试反射获取泛型*******
---不使用泛型的情况---
---使用泛型的情况---
false
***使用反射获取泛型信息****
获取参数类型interface java.util.Map
获取泛型参数类型java.util.Map<java.lang.String, java.lang.String>
原始类型:interface java.util.Map
class java.lang.String class java.lang.String
新建配置文件Producer.properties
className=reflectiontest.Producer
methodName=showProducts
name=小明
public static void main ( String[] args ) throws Exception {
//加载配置文件
Properties properties = new Properties();
InputStream is = ReflectionTest6.class.getClassLoader().getResourceAsStream("Producer.properties");
assert is != null;
//InputStreamReader将字节流转为字符流(解决乱码问题)
InputStreamReader isr = new InputStreamReader(is, StandardCharsets.UTF_8);
properties.load(isr);
String className = properties.getProperty("className");
String methodName = properties.getProperty("methodName");
String argsType = properties.getProperty("argsType");
String name = properties.getProperty("name");
//创建运行时类的对象
Class<?> clazz = Class.forName(className);
Producer producer = (Producer) clazz.newInstance();
Method method = clazz.getDeclaredMethod(methodName);
method.invoke(producer);
Field field = clazz.getDeclaredField("name");
field.setAccessible(true);
field.set(producer, name);
System.out.println(producer);
isr.close();
}
结果:
正在查看产品
Producer{name='小明', age=0, id=0}