文章目录
1、从使用场景来看需要反射理由
Java程序在运行的时候会用到很多类的实例对象,会出现两种类型:
- 编译时类型
- 运行时类型
Object obj = new String("Hello") ;
在上面的语句中编译时类型时java.lang.Object
类型,运行时的类型时java.lang.String
类型。
如果我们的程序需要使用到该对象运行时类型的方法,有两种做法:
- 如果在编译和运行的时候完全知道类型的具体信息,可以直接使用
instanceof
运算符来判断,然后进行强制类型转换
package learn.demo.reflect;
import org.junit.Test;
public class ReflectTest {
@Test
public void testIsInstanceOf(){
Object obj = new String("Hello") ;
if(obj instanceof String){
// 将obj对象强制转化为String对象,之后可以使用String对象相应的方法
String str = (String)obj;
System.out.println(str);
}
}
}
- 如果在编译的时候无法预知对象和类可能属于那些类型,成刷只能使用运行时的信息来发现类的真实信息,这里就必须反射。
2、获取Class对象:
对于每一实例对象,都会有一个对应的Class对象,通过该Class对象就可以访问到JVM中的这个类。
Java中获取Class对象的方式:
- 使用
Class.forName(全限定类名)
的静态方法,(全限定类名:完整的包名+类名) - 调用
某个类的class
属性来获取该对应的Class对象,例如String.class
将会返回String类对应的Class对象 - 调用
实例对象的getClass()
方法,该方法时属于java.lang.Object类的一个方法,所以任何实例对象都可以调用。
实例:
/**
* 获取String类的class对象来
* */
@Test
public void testGetClass() throws ClassNotFoundException {
// 方式1:这种方式可能抛出ClassNotFoundException异常,也就找不到对应的情况
Class clazz1 = Class.forName("java.lang.String");
// 方式2:
Class clazz2 = String.class;
//方式3:
String str = "hello";
Class clazz3 = str.getClass();
}
这三种方式相比之下,第二种方式有两个优势:
- 代码更安全,在编译阶段就可以检查需要的Class对象是否存在
- 程序性能更高,无需调用方法,性能更好
3、从Class中获取到需要的信息
Class提供大量的实例方法老获取Class对象对应的类的详细信息,
用于获取Class类对应所包含的构造器:
// 返回Class对象对应类的public构造器
public Constructor<T> getConstructor(Class<?>... parameterTypes)
// 返回Class对象对应类的所有public构造器
public Constructor<?>[] getConstructors()
// 返回Class对象对应类的构造器,这里可以public,也可以是private
public Constructor<T> getDeclaredConstructor(Class<?>... parameterTypes)
// 返回Class对象对应类的所有构造器,这里可以public,也可以是private
public Constructor<?>[] getDeclaredConstructors()
用于获取Class对应的类所包含的方法(Method):
// 返回Class对象对应类的public方法
public Method getMethod(String name,Class<?>... parameterTypes)
// 返回Class对象对应类的所有public方法
public Method[] getMethods()
// 返回Class对象对应类的方法,这里可以public,也可以是private
public Method getDeclaredMethod(Sring name,Class<?>... parameterTypes)
// 返回Class对象对应类的所有方法,这里可以public,也可以是private
public Method[] getDeclaredMethods()
用于获取Class对应的类所包含的属性(Field):
public Field getField(String name)
// 返回Class对象对应类的所有public属性
public Field[] getFields()
// 返回Class对象对应类的属性,这里可以public,也可以是private
public Field getDeclaredField(String name)
// 返回Class对象对应类的所有属性,这里可以public,也可以是private
public Field[] getDeclaredFields()
用于获取Class类上的所包含的注释:
// 视图获取该Class对象所表示类上的指定类型的注释,如果不存在该类的注释,返回null
<A extends Annotation> A getAnnotation(Class<A> annotationClass)
// 返回此元素上存在的所有注释
Annotation[] getAnnotations()
// 获取直接此元素上的所有注解
Annotation[] getDeclaredAnnotations()
4、使用反射生成并操作对象
我们可以使用Class对象获取到对应类的方法
(Method
对象表示)、构造器
(Constructor
对象表示)、属性
(Field
对象表示),这三个类都在java.lang.reflect包下,这样我们就可以用于创建该类对应的对象和操作对象(修改属性值、调用方法)
4.1、通过反射来创建对象的方式:(常见的是使用第一种来创建对象)
- 使用
Class
对象的newInstance()
来创建Class对象对应类的实例,这里需要目标类中必须有默认构造器,调用newInstance()实际就是利用默认构造器来创建该类的实例。 - 获取Class对象指定的
Constructor
对象,在调用Constructor
对象的newInstance()
来创建相应的实例对象
下面是通过代码来创建对象的实例:
package learn.demo.reflect;
import org.junit.Test;
import java.lang.reflect.Constructor;
public class ReflectTest {
// 测试通过反射获取实例的方法
@Test
public void testNesInstance(){
Object strInstance = newInstanceFromClass("java.lang.String");
Object dataInstance = newInstanceFromConstructor("java.util.Date");
System.out.println("strInstance:" + strInstance.getClass().getName());
System.out.println("dataInstance:" + dataInstance.getClass().getName());
}
/**
* @param className 类的全限定名
* @return 返回该类的一个实例对象
* */
public static Object newInstanceFromClass(String className){
System.out.println("通过Class类的newInstance方法来创建对象");
Object instance = null;
try {
Class<?> clazz = Class.forName(className );
instance = clazz.newInstance();
}catch (ClassNotFoundException e){
e.printStackTrace();
}finally {
return instance;
}
}
/**
* @param className 类的全限定名
* @return 返回该类的一个实例对象
* */
public static Object newInstanceFromConstructor(String className){
System.out.println("通过Constructor类的newInstance方法来创建对象");
Object instance = null;
try {
Class<?> clazz = Class.forName(className );
Constructor<?> declaredConstructor = clazz.getDeclaredConstructor();
declaredConstructor.setAccessible(true); // 设置允许
instance = declaredConstructor.newInstance();
}catch (ClassNotFoundException e){
e.printStackTrace();
}finally {
return instance;
}
}
}
4.2、获取和设置对象的属性值:
设置对象的属性值:
- 获取:
- 设置:
示例
这里先看一下String
的声明:发现有一个char[] value
的属性值
这里通过反射的形式来修改StringBuilder实例对象的value
import org.junit.Test;
import java.lang.reflect.Field;
public class ReflectTest {
// 通过反射来修改属性值
@Test
public void testSetFieldValue() throws Exception {
String str = new String("World");
Class<?> clazz = String.class;
Field value = clazz.getDeclaredField("value");
value.setAccessible(true);
// 这里存在一个问题,就是如果数组类型的话返回的是一个地址
System.out.println("返回的是一个[C+引用地址:"+ value.get(str));
char[] valueArr = (char[])value.get(str);
System.out.println("修改前的value是:"+ String.valueOf(valueArr));
value.set(str,"Hello".toCharArray());
System.out.println("修改后的value是:"+str);
}
}
备注:在就Java中String 和 char[] 之间的转换的方法使用:
- String转为char[] 的方法 : public char[]
toCharArray()
- char[] 转为String的方法:public static String
valueOf(char data[])
执行结果:
测试了对于数组类型的Class类型:
@Test
public void testArrayType(){
int[] intArray = {1,2,3,4};
char[] charArray = "char".toCharArray();
String[] strArray = {"test","array","type"};
System.out.println("intArray的Class类型:"+intArray.getClass());
System.out.println("intArray:"+intArray);
System.out.println("charArray的Class类型:"+charArray.getClass());
System.out.println("charArray:"+charArray);
System.out.println("strArray的Class类型:"+strArray.getClass());
System.out.println("strArray:"+strArray);
}
执行结果:
4.3、通过反射调用实例对象的方法
获取某个类的对应的Class对象
后,就可以通过Class对象的getMethod()
等方法来获取该类上的Method实例对象
,然后使用Method对象的invoke()
方法,方法签名如下:
Object invoke(Object object,Object... args)
下面通过反射的方式来调用String类的indexOf()方法
import org.junit.Test;
import java.lang.reflect.Method;
public class ReflectTest {
// 通过反射的方式来调用String类的indexOf()方法
@Test
public void testInvoke() throws Exception {
String str = "Hello";
Class<?> strClass = str.getClass();
Method indexOf = strClass.getMethod("indexOf", String.class);
Object result = indexOf.invoke(str, "o");
System.out.println("执行indexOf方法的返回结果:"+result);
}
}
5、使用反射生成JDK动态代理
Java提供了Proxy
类和InvocationHandler
接口:
- public class
Proxy
implements java.io.Serializable - public interface
InvocationHandler
使用Proxy 和InvocationHandle生成JDK动态代理类或者动态代理对象
Proxy 类提供的方法:
public static Class<?> getProxyClass(ClassLoader loader, Class<?>... interfaces)
public static Object newProxyInstance(ClassLoader loader,Class<?>[] interfaces,InvocationHandler h)
定义一个Work接口
:
package learn.demo.reflect;
public interface Work {
void work();
}
定义一个Work的实现类Worker
:
package learn.demo.reflect;
public class Worker implements Work{
@Override
public void work() {
System.out.println("打工人干的比牛都多");
}
}
InvocationHandler
接口的实现类:
package learn.demo.reflect;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
public class MyInvocationHandler implements InvocationHandler {
private Object target;
public MyInvocationHandler() {
}
public MyInvocationHandler(Object target) {
this.target = target;
}
public Object getTarget() {
return target;
}
public void setTarget(Object target) {
this.target = target;
}
// 实现invoke()方法
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("起的比鸡都早");
Object result = method.invoke(target, args);
System.out.println("睡的比狗的晚");
return result;
}
}
使用动态代理
package l earn.demo.reflect;
import org.junit.Test;
import java.lang.reflect.*;
import learn.demo.reflect.Worker;
public class ReflectTest {
@Test
public void testJDKProxy(){
Work target = new Worker();
MyInvocationHandler handler = new MyInvocationHandler();
handler.setTarget(target);
// 这里强制转化的类必须是接口类型的,如果是Work类型的话就会出现类型转换错误
Work instance = (Work)Proxy.newProxyInstance(target.getClass().getClassLoader(),
target.getClass().getInterfaces(), handler);
instance.work();
}
执行结果:
注意点:这里强制转化的类型必须是接口类型的,如果是Work类型的话就会出现类型转换错误