【Java_Senior】八、反射

反射

框架 = 注解 + 反射 + 设计模式

概述

定义

反射是允许程序在执行期间获取类的内部信息并操作类内部的属性和方法的特殊机制。
静态语言:Java、C、C++;
动态语言:Object-c、C#、Python、PHP等

类在加载完成后,堆的方法区中会生成一个class对象,反射就是透过这个class对象看到类的完整信息的机制。

反射可以在运行时调用类、也可以调用类的私有构造器、属性、方法。
注:由于在实际运行过程中,类是动态的,用户要使用什么样的功能,就要创建什么样的类,则就需要反射来创建类。

一般在开发过程中,还是优先使用new来创建对象。

私有的属性与方法构造器只是一种使用建议

java.lang.Class的理解

定义

程序在经过javac.exe命令进行编译之后,会生成多个字节码文件(.class结尾)。
接着我们使用java.exe命令对某个字节码文件进行解释运行。就相当于把该字节码文件写入内存中。该过程叫做类的加载。加载到内存中的类,我们称之为运行时类(需要反射机制进行操作),此时的运行时类,我们就称作Class的一个实例。

其中不仅是运行时类可以作为Class的实例对象,class、类、接口、枚举类、基本数据类型、一维、二维数组、注解、void都可以作为Class类的实例对象。

举例实践

四种获取Class实例的方法


    @Test
    public void ReflectionTest1() throws ClassNotFoundException {
        //方法一:调用运行时类的.class()方法来获取Class的实例。
        //使用泛型,要求只能返回Person类型的类
        //返回一个Person样子的Class类的实例,我们可以在此之上通过Class类的实例对Person类进行操作
        Class<Person> clazz1 =  Person.class;

        //方法二:通过运行时类的对象来获取Class类的实例
        Person p1 = new Person();
        Class clazz2 = p1.getClass();

        //方法三:调用Class静态方法:forName(String classPath)参数为希望获取的类的全路径
        Class clazz3 = Class.forName("Reflection.Person");
        System.out.println(clazz3);

        //方法四:使用类的加载器:ClassLoader
        ClassLoader classLoader = ReflectionTest.class.getClassLoader();
        Class clazz4 = classLoader.loadClass("Reflection.Person");
    }

类的加载

概述

Class加载的过程:
类加载(将类加载到内存中,生成Class对象)->类的连接(将类的内容赋默认值)->类的初始化(将类中的变量赋初值(自己写的值))
例如静态代码块和定义赋初值,按照代码顺序决定变量最终的值。

加载器的分类:
引导类加载器(加载String等核心类库,无法被用户获取)->扩展类加载器(负责导入lib文件夹下的jar包)->系统类加载器(负责加载自定义类)

举例实践

一、读取配置文件的两种方法

public class ClassLoaderTest {
    @Test
    public void ClassLoaderTestT1() throws IOException {
        Properties pros = new Properties();
        //读取配置文件的方式一:
        //FileInputStream的默认路径在module下
//        FileInputStream fis = new FileInputStream("jdbc.properties");
//        pros.load(fis);

        //读取配置文件的方式二:
        //获取ClassLoaderTest大类的ClassLoader
        //classLoader.getResourceAsStrea()的默认路径在src下
        ClassLoader classLoader =  ClassLoaderTest.class.getClassLoader();
        InputStream is = classLoader.getResourceAsStream("jdbc.properties");
        pros.load(is);

        String user = pros.getProperty("user");
        String password = pros.getProperty("password");
        System.out.println("user = " + user + "\npassword = " + password);
    }
}

反射的应用

通过反射创建运行时类的对象

//使用Class的这个方法调用空参构造器实例化类的对象
.newInstance();
实例
    public void NewInstanceT1() throws InstantiationException, IllegalAccessException {
        //若不加<>泛型则后面需要强转
        Class<Person> clazz =  Person.class;
        //newInstance()方法默认调用空参构造器实例化对应类的对象
        Person p1 =  clazz.newInstance();
        System.out.println(p1);
    }

程序运行时才创建实例的理解

创建一个随机数012,使其对应不不同的数时,创建不同的实例化对象。

    @Test
    public void NewInstanceT2() {
        String classPath = "";
        for (int i = 0; i < 10; i++) {
            int num = new Random().nextInt(3);
            switch (num) {
                case 0:
                    classPath = "java.util.Date";
                    break;
                case 1:
                    classPath = "java.lang.Object";
                    break;
                case 2:
                    classPath = "Reflection.Person";
                    break;
            }
            try {
                Object obj = this.getNewInstance(classPath);
                System.out.println(obj);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }

    //使用.newInstance()构建一个方法,传入一个类绝对路径,返回对应的类的实例
    public Object getNewInstance(String classPath) throws Exception {
        Class clazz = Class.forName(classPath);
        return clazz.newInstance();
    }
//结果:
Fri Jan 06 14:06:29 CST 2023
Fri Jan 06 14:06:29 CST 2023
Reflection.Person@61e717c2
Reflection.Person@66cd51c3
Fri Jan 06 14:06:29 CST 2023
java.lang.Object@4dcbadb4
Fri Jan 06 14:06:29 CST 2023
Reflection.Person@4e515669
Fri Jan 06 14:06:29 CST 2023
Reflection.Person@17d10166

获取运行时类的完整结构

提供一个详细的类

父类:

public class Creature<T> implements Serializable {
    private char gender;
    public double weight;

    public void breath(){
        System.out.println("生物呼吸");
    }

    public void eat(){
        System.out.println("生物吃");
    }

}

接口:

public interface MyInterface {
    //抽象方法
    void Info();
}

注解:

//注解所必须添加的两个元注解
@Target({TYPE, FIELD, METHOD, PARAMETER, CONSTRUCTOR, LOCAL_VARIABLE})
@Retention(RetentionPolicy.RUNTIME)
public @interface MyAnnotation {
    String value() default "hello";
}

核心类:

//尝试修改注解中属性的内容
@MyAnnotation(value = "hi")
public class Person extends Creature<String> implements Comparable<String>, MyInterface{
    private String name;
    int age;
    public int id;

    public Person(){}

    @MyAnnotation(value = "abc")
    private Person(String name){
        this.name = name;
    }

    Person(String name, int age){
        this.name = name;
        this.age = age;
    }

    @MyAnnotation
    private String show(String nation){
        System.out.println("我的国籍是:" + nation);
        return nation;
    }

    public String display(String interest){
        return interest;
    }

    @Override
    public void Info() {
        System.out.println("人人人");
    }

    @Override
    public int compareTo(String o) {
        return 0;
    }
}

获取运行时类的属性以及属性的属性

获取运行时类的属性

    @Test
    public void Test1(){
        Class clazz = Person.class;

        //方式一:.getField()方法
        //其会获取运行时类和其父类中所有声明为public的方法
        Field[] fields = clazz.getFields();
        for(Field f : fields) {
            System.out.println(f);
        }
        System.out.println();

        //通过.getDeclaredFields()方法获取到的是当前运行时类的所有权限的属性(没有父类)
        Field[] declaredFields = clazz.getDeclaredFields();
        for(Field df : declaredFields){
            System.out.println(df);
        }
    }
//结果为:
public int ReflectionT1.Person.id
public double ReflectionT1.Creature.weight

private java.lang.String ReflectionT1.Person.name
int ReflectionT1.Person.age
public int ReflectionT1.Person.id

获取运行时类属性的权限、类型、变量名。

    @Test
    public void Test2(){
        Class clazz = Person.class;
        Field[] declaredFields = clazz.getDeclaredFields();
        for(Field df : declaredFields){
            //通过.getModifiers()方法获取对应属性的权限。
            //其返回的是int类型,需要再进行翻译才能转换成我们熟悉的权限修饰符
            int modifier = df.getModifiers();
            //调用Modifier.toString(modifier)来进行翻译
            System.out.println(Modifier.toString(modifier) + '\t');

            //获取属性的类型
            Class type = df.getType();
            System.out.println(type.getName() + '\t');

            //获取变量名
            String Sname = df.getName();
            System.out.println(Sname);
            System.out.println();
        }
    }
//输出结果:
private	
java.lang.String	
name

	
int	
age

public	
int	
id


Process finished with exit code 0

获取运行时类的方法

.getMethods()方法获取本类及父类中所有的方法,只能返回public
.getDeclaredMethods()获取本类中声明的所有方法(包括private),不包括父类。

    @Test
    public void Test(){
        Class clazz = Person.class;
        //.getMethods()方法获取本类及父类中所有的方法,只能返回public
        Method[] methods = clazz.getMethods();
        for(Method m : methods){
            System.out.println(m);
        }

        System.out.println("\t");

        //.getDeclaredMethods()获取本类中声明的所有方法(包括private),不包括父类。
        Method[] declaredMethods = clazz.getDeclaredMethods();
        for(Method dm : declaredMethods){
            System.out.println(dm);
        }
    }
    
//结果
//两个compareTo是因为泛型的覆盖
public int ReflectionT1.Person.compareTo(java.lang.String)
public int ReflectionT1.Person.compareTo(java.lang.Object)
public void ReflectionT1.Person.Info()
public java.lang.String ReflectionT1.Person.display(java.lang.String)
public void ReflectionT1.Creature.breath()
public void ReflectionT1.Creature.eat()
//下面是Object下的方法
public final void java.lang.Object.wait() throws java.lang.InterruptedException
public final void java.lang.Object.wait(long,int) throws java.lang.InterruptedException
public final native void java.lang.Object.wait(long) throws java.lang.InterruptedException
public boolean java.lang.Object.equals(java.lang.Object)
public java.lang.String java.lang.Object.toString()
public native int java.lang.Object.hashCode()
public final native java.lang.Class java.lang.Object.getClass()
public final native void java.lang.Object.notify()
public final native void java.lang.Object.notifyAll()
	
public int ReflectionT1.Person.compareTo(java.lang.String)
public int ReflectionT1.Person.compareTo(java.lang.Object)
public void ReflectionT1.Person.Info()
private java.lang.String ReflectionT1.Person.show(java.lang.String)
public java.lang.String ReflectionT1.Person.display(java.lang.String)

Process finished with exit code 0

获取运行时类的方法的属性

  1. 获取方法的注解:.getAnnotations();
    只能获取RUNTIME的注解
  2. 获取权限修饰符:.getModifiers()其返回int,需要Modifier.toString()来转换
  3. 获取权限修饰符:.getModifiers()其返回int,需要Modifier.toString()来转换
  4. 获取返回值类型:.getReturnType(),其返回class类型的对象,调用其.getName()方法获取名称
  5. 获取返回值类型:.getReturnType(),其返回class类型的对象,调用其.getName()方法获取名称
  6. 获取形参列表.getParameterTypes()
  7. 获取异常.getExceptionTypes()
    @Test
    public void Test1(){
        Class clazz = Person.class;
        Method[] declaredMethods = clazz.getDeclaredMethods();
        for(Method m : declaredMethods){
            //获取方法的注解:.getAnnotations();
            //只能获取RUNTIME的注解
            Annotation[] annos = m.getAnnotations();
            //由于一个方法的注解可能有多个,故需要循环遍历
            for(Annotation a : annos){
                System.out.println(a);
            }

            //获取权限修饰符:.getModifiers()其返回int,需要Modifier.toString()来转换
            System.out.print(Modifier.toString(m.getModifiers()) + "\t");

            //获取返回值类型:.getReturnType(),其返回class类型的对象,调用其.getName()方法获取名称
            System.out.print(m.getReturnType().getName() + "\t");
            System.out.print("(");
            //获取形参列表.getParameterTypes()
            Class[] parameterTypes = m.getParameterTypes();
            if (!(parameterTypes == null && parameterTypes.length == 0)) {
                for (int i = 0; i < parameterTypes.length; i++) {
                    if (i == parameterTypes.length - 1) {
                        System.out.print(parameterTypes[i].getName() + ", " + "args_" + i);
                        break;
                    }
                    System.out.print(parameterTypes[i].getName() + ", " + "args_" + i + ",");
                }
            }
            System.out.println(")" + "\t");

            //获取异常.getExceptionTypes()
            Class[] exceptionTypes = m.getExceptionTypes();
            if(exceptionTypes.length > 0){
                System.out.print("throws ");
                for(int i = 0; i < exceptionTypes.length; i++){
                    if(i == exceptionTypes.length - 1){
                        System.out.println(exceptionTypes[i].getName());
                    }
                    System.out.print(exceptionTypes[i].getName() + ", ");
                }
            }
        }
    }
//运行结果
public	int	(java.lang.String, args_0)	
public volatile	int	(java.lang.Object, args_0)	
@ReflectionT1.MyAnnotation(value=hello)
private	java.lang.String	(java.lang.String, args_0)	
public	java.lang.String	(java.lang.String, args_0,int, args_1)	
throw java.lang.NullPointerException, java.lang.ClassCastException
java.lang.ClassCastException, public	void	()	

获取运行时类的构造器

获取运行时类的构造器意义不大,略过。

获取运行时类的父类

  1. 获取运行时类的父类:.getSuperclass()
  2. 获取带泛型的运行时类的父类.getGenerricSuperclass(),Type是一个被Class实现的接口
  3. 获取带泛型的类的父类的泛型:见代码
    @Test
    public void Test(){
        Class clazz = Person.class;
        //获取运行时类的父类:.getSuperclass()
        Class superclass = clazz.getSuperclass();
        System.out.println(superclass);

        //获取带泛型的运行时类的父类,Type是一个被Class实现的接口
        Type genericSuperclass = clazz.getGenericSuperclass();
        System.out.println(genericSuperclass);

        //获取带泛型的类的父类的泛型
        //强制转换一个ParameterizedType的类型,以获取class
        ParameterizedType paramType = (ParameterizedType)genericSuperclass;
        //将泛型获取到数组中
        Type[] actualTypeArguments = paramType.getActualTypeArguments();
        //通过将Type类型的强制转换成Class类型的以使用.getName()方法
        System.out.println(((Class)actualTypeArguments[0]).getName());
    }

获取运行时类的接口、包、注解等

  1. 获取运行时类的接口:.getInterfaces()
    @Test
    public void Test1(){
        Class clazz = Person.class;

        //获取运行时类的接口:.getInterfaces()
        Class[] Interfaces = clazz.getInterfaces();
        for(Class c : Interfaces){
            System.out.println(c);
        }
    }
  1. 获取运行时类的包:.getPackage(),返回Package类型的对象
    @Test
    public void Test2(){
        Class clazz = Person.class;
        //获取运行时类的包
        Package pack = clazz.getPackage();
        System.out.println(pack);
    }
  1. 获取运行时类的注解:.getAnnotations(),返回的是Annotation类型的数组(Annotation[])
    @Test
    public void Test3(){
        Class clazz = Person.class;
        //获取注解
        Annotation[] annos = clazz.getAnnotations();
        for(Annotation a : annos){
            System.out.println(a);
        }
    }

调用运行时类的属性、方法

调用运行时类的属性

获取并操作运行时类的属性的方法:
获取运行时类->实例化一个运行时类的具体对象->使用get方法获取具体的对象->使用Field.set(Object, T)来修改变量->使用Field.get(Object)来获取

  1. 获取运行时类的属性的方法一.getField(“Field”)此种方法只能获取public权限的属性值。
    @Test
    public void Test() throws Exception {
        Class clazz = Person.class;

        //调用.newInstance()方法创建一个实例化对象
        Person p = (Person) clazz.newInstance();

        //通过.getField获取制定属性
        //注意:通过.getField()方法只能获取public权限的属性
        Field id = clazz.getField("id");

        //用属性名调用.set()方法可以修改制定对象的某个属性的参数。
        id.set(p, 1024);

        //获取p对象的id属性,并保存下来(需要强转,或不强转可以理解为自动拆箱)
        int pId = (int)id.get(p);
        System.out.println(pId);
    }
  1. 获取运行时类的属性的方法二:.getDeclaredField(“Field”)此方法可以获取并操作private修饰的属性,但在操作的过程中必须将Field.setAccessible()设置为true
    @Test
    public void Test1() throws Exception {
        Class clazz = Person.class;
        Person p = (Person)clazz.newInstance();

        //使用.getDeclaredField("Field")创建对象
        Field name = clazz.getDeclaredField("name");

        //操作非public修饰的对象时,必须将setAccessible()设置为true
        name.setAccessible(true);

        name.set(p, "张亮");
        String name1 = (String) name.get(p);
        System.out.println(name1);
    }
调用运行时类的方法的方法

与调用运行时类的属性的方式基本相同,区别在于调用.invoke()调用方法时的参数以及调用静态方法时的不同,具体见代码

    @Test
    public void Test2() throws Exception {
        Class clazz = Person.class;
        Person p = (Person) clazz.newInstance();
        //参数1:方法名      参数2:方法要传入的形参类型
        Method show = clazz.getDeclaredMethod("show", String.class);

        show.setAccessible(true);
        //Method类型调用.invoke(Object, args)实现方法的调用
        //参数1:实例化的对象    参数2:要传入的形参列表
        //.invoke()的返回值就是想要调用的方法的返回值
        Object returnValue = show.invoke(p, "CHN");
        System.out.println(returnValue);

        //调用静态方法的方式:
        Method showDesc = clazz.getDeclaredMethod("showDesc");
        showDesc.setAccessible(true);
        //若没有参数则直接填写Person.class,或填写null,若有参数在后面跟上
        //void类型的返回值也可以强行接,接到就是null
        Object returnVal = showDesc.invoke(Person.class);
        System.out.println(returnVal);
    }
//输出结果:
我的国籍是:CHN
CHN
这是一个静态方法
null
调用运行时类的constructor
    @Test
    public void Test3() throws Exception {
        Class clazz = Person.class;

        //获取构造器,并生成Constructor类型的构造器
        Constructor constructor = clazz.getDeclaredConstructor(String.class);

        constructor.setAccessible(true);

        //调用构造器的newInstance()来创建实例化对象,其中是参数
        Person p = (Person) constructor.newInstance("JAVA");
        System.out.println(p);
    }
Person{weight=0.0, name='JAVA', age=0, id=0}

反射的具体应用:动态代理

概述

通过代理类调用具体的对象的方法,且是在程序运行的过程中根据需要动态的创建代理类的对象。

动态代理的经典使用场景:
调试
远程方法调用

实例

代理模式中:
必定有一个接口,其中封装被代理类要实现的方法。
代理类和被代理类都实现这个接口,代理类中定义一个接口类型的变量,存储被代理类多态生成的对象,通过这个变量实现在代理类中调用被代理类方法的效果。

静态代理
//静态代理中必定有接口
interface ClothFactory{
    void produceCloth();
}

//代理类
class ProxyClothFactory implements ClothFactory{

    //在代理类中封装被代理类的对象,待稍后调用
    private ClothFactory factory;

    public ProxyClothFactory(ClothFactory factory){
        this.factory = factory;
    }

    @Override
    public void produceCloth() {
        System.out.println("代理工厂做一些准备工作");

        //在代理类中封装被代理类的通用方法
        factory.produceCloth();

        System.out.println("代理工厂做一些后续收尾工作");
    }
}

//被代理类
class NikeClothFactory implements ClothFactory{

    @Override
    public void produceCloth() {
        System.out.println("Nike工厂生产一批运动服");
    }
}

public class StaticProxyTest {
    public static void main(String[] args) {
        //创建被代理类的对象,多态
        ClothFactory nike = new NikeClothFactory();
        //创建代理类的对象
        ClothFactory proxyClothFactory = new ProxyClothFactory(nike);

        //被代理类的方法由代理类使用并替代调用
        proxyClothFactory.produceCloth();
    }
}
代理工厂做一些准备工作
Nike工厂生产一批运动服
代理工厂做一些后续收尾工作
动态代理

代理类动态生成,只需要定义被代理类,代理类由反射动态生成(ClassLoader)。

package ProxyTest;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

/**
 * @ClassName ProxyTest
 * @Description TODO
 * @Author @BaiNan
 * @Date 2023/1/7 21:18
 * @Version 1.0
 */
//动态代理
    //需要代理的方法
interface Human{
    //接口中定义的抽象方法
    String getBelief();
    //一个需要形参的抽象方法
    void eat(String food);
}

//被代理类
class SuperMan implements Human{

    @Override
    public String getBelief() {
        return "I Believe I Can Fly";
    }

    @Override
    public void eat(String food) {
        System.out.println("我喜欢吃" + food);
    }
}

//通过ProxyTest创建一个代理类对象
class ProxyFactory {
    //静态方法,通过类的直接调用,传入被代理类的对象,动态创建一个代理类对象,此方法的返回值就是代理类对象
    public static Object getProxyInstance(Object obj) {
        MyInvocationHandler handler = new MyInvocationHandler();

        handler.bind(obj);
        //调用.newProxyInstance()方法来创建一个代理对象,通过此代理对象来调用被代理对象的方法
        //参数1:ClassLoader,被代理类的,用形参obj反射之后调用
        //参数2:接口,同样通过obj反射之后调用
        //参数3:Handler,用来直接调用方法
        return Proxy.newProxyInstance(obj.getClass().getClassLoader(), obj.getClass().getInterfaces(), handler);
    }
}

class MyInvocationHandler implements InvocationHandler {
    //创建Object对象用来保存被代理类的实例化对象,但此obj必须被多态的定义为具体的被代理类对象
    private Object obj;

    //定义一个方法,用来接收被代理类的实例
    public void bind(Object obj) {
        this.obj = obj;
    }

    //调用代理类的方法时,会自动转到被代理类的下面这个invoke()方法
    //将被代理类要执行的方法声明在下面的invoke()中
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        //调用方法!
        Object returnValue = method.invoke(obj, args);
        //将方法的返回值对应为.invoke()方法的返回值
        return returnValue;
    }
}

public class ProxyTest{
    public static void main(String[] args) {
        //创建被代理类实例
        SuperMan sp = new SuperMan();
        //动态生成代理类
        Human proxyInstance = (Human)ProxyFactory.getProxyInstance(sp);
        //调用方法
        proxyInstance.getBelief();
        proxyInstance.eat("蜀留香火锅");

        System.out.println("*******************************");
        //创建Nike的代理实行
        NikeClothFactory nikeClothFactory = new NikeClothFactory();
        ClothFactory proxyClothFactory = (ClothFactory) ProxyFactory.getProxyInstance(nikeClothFactory);
        proxyClothFactory.produceCloth();
    }
}
我喜欢吃蜀留香火锅
*******************************
Nike工厂生产一批运动服
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值