Java语法基础:反射reflect

1、从使用场景来看需要反射理由

Java程序在运行的时候会用到很多类的实例对象,会出现两种类型:

  1. 编译时类型
  2. 运行时类型
Object obj = new String("Hello") ;

在上面的语句中编译时类型时java.lang.Object类型,运行时的类型时java.lang.String类型。

如果我们的程序需要使用到该对象运行时类型的方法,有两种做法:

  1. 如果在编译和运行的时候完全知道类型的具体信息,可以直接使用 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);
        }
    }

}
  1. 如果在编译的时候无法预知对象和类可能属于那些类型,成刷只能使用运行时的信息来发现类的真实信息,这里就必须反射。

2、获取Class对象:

对于每一实例对象,都会有一个对应的Class对象,通过该Class对象就可以访问到JVM中的这个类。

Java中获取Class对象的方式:

  1. 使用Class.forName(全限定类名)的静态方法,(全限定类名:完整的包名+类名)
  2. 调用某个类的class属性来获取该对应的Class对象,例如String.class将会返回String类对应的Class对象
  3. 调用实例对象的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、通过反射来创建对象的方式:(常见的是使用第一种来创建对象)

  1. 使用Class对象的newInstance()来创建Class对象对应类的实例,这里需要目标类中必须有默认构造器,调用newInstance()实际就是利用默认构造器来创建该类的实例。
  2. 获取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类型的话就会出现类型转换错误
在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/Hicodden/article/details/110507467