栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

java 反射机制二:创建运行时类的对象、调用运行时类的指定方法/属性/构造器

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

java 反射机制二:创建运行时类的对象、调用运行时类的指定方法/属性/构造器

前文回顾

java 反射机制一:获取Class 类实例、理解ClassLoader读取配置文件操作


文章目录
  • 前文回顾
  • 前言
  • 一、创建运行时类的对象
  • 二、获取运行时类的完整结构
    • 1.实现的全部接口
    • 2.所继承的父类
    • 3.全部的构造器
    • 4.全部的方法
    • 5.全部的Field
    • 6.Annotation相关
    • 7.泛型相关
    • 8.类所在的包
  • 三、调用运行时类的指定结构
    • 1.调用指定方法
    • 2.调用指定属性
    • 3.调用指定构造器
  • 四、反射的应用:动态代理
    • 1.动态代理步骤
    • 2.动态代理与AOP


前言

上期我们对 java 的反射机制有了一定的了解,主要是能够掌握 Class 类实例的获取,同时要能够对 ClassLoader 要有一些理解,尤其是读取配置文件的操作要能够掌握。详细的信息可以点击上方的链接查看”☝️


一、创建运行时类的对象

有了Class对象,能做什么?
❤️ 创建类的对象: 调用Class对象的 newInstance() 方法

  • 要 求:
    1)类必须有一个无参数的构造器。
    2)类的构造器的访问权限需要足够。

难道没有无参的构造器就不能创建对象了吗?
不是!只要在操作的时候明确的调用类中的构造器, 并将参数传递进去之后,才可以实例化操作。

  • 步骤如下:
    1)通过Class类的 getDeclaredConstructor(Class … parameterTypes) 取得本类的指定形参类型的构造器。
    2)向构造器的形参中传递一个对象数组进去,里面包含了构造器中所需的各个参数。
    3)通过Constructor实例化对象。
public class NewInstanceTest {
    @Test
    public void test1() throws InstantiationException, IllegalAccessException {
        Class clazz = Person.class;
        
        Person obj = clazz.newInstance();
        System.out.println(obj);  // Person{name='null', age=0}
    }


    // 体会反射的动态性
    @Test
    public void test2() {
        for (int i = 0; i < 100; i++) {

            int num = new Random().nextInt(3);
            String classPath = "";
            switch (num) {
                case 0:
                    classPath = "java.util.Date";
                    break;
                case 1:
                    classPath = "java.lang.Object";
                    break;
                case 2:
                    classPath = "com.deve.java.Person";
                    break;
            }

            try {
                Object obj = getInstance(classPath);
                System.out.println(obj);
            } catch (Exception e) {
                e.printStackTrace();
            }

        }
    }

    
    public Object getInstance(String classPath) throws Exception {
        Class clazz = Class.forName(classPath);
        return clazz.newInstance();
    }
    
}
二、获取运行时类的完整结构 1.实现的全部接口

❤️ public Class[] getInterfaces() 确定此对象所表示的类或接口实现的接口。

public class OtherTest {
    
    @Test
    public void test5() {
        Class clazz = Person.class;
        Class[] interfaces = clazz.getInterfaces();
        for (Class c : interfaces) {
            System.out.println(c);
        }

        // 获取当前运行时类的父类实现的接口
        Class[] interfaces1 = clazz.getSuperclass().getInterfaces();
        for (Class c : interfaces1) {
            System.out.println(c);
        }
    }

}
2.所继承的父类

❤️ public Class getSuperclass() 返回表示此 Class 所表示的实体(类、接口、基本类型)的父类的Class。

public class OtherTest {
	
    @Test
    public void test2() {
        Class clazz = Person.class;
        Class superclass = clazz.getSuperclass();
        System.out.println(superclass);  // class com.atguigu.java1.Creature
    }

    
    @Test
    public void test3() {
        Class clazz = Person.class;
        Type genericSuperclass = clazz.getGenericSuperclass();
        System.out.println(genericSuperclass);  // com.atguigu.java1.Creature
    }

}
3.全部的构造器

❤️ public Constructor[] getConstructors() 返回此 Class 对象所表示的类的所有public构造方法。
❤️ public Constructor[] getDeclaredConstructors() 返回此 Class 对象表示的类声明的所有构造方法。
❤️ Constructor类中:

  • 取得修饰符: public int getModifiers();
  • 取得方法名称: public String getName();
  • 取得参数的类型 public Class[] getParameterTypes();
public class OtherTest {
    @Test
    public void test1() {
        Class clazz = Person.class;
        // getConstructors():获取当前运行时类中声明为public的构造器
        Constructor[] constructors = clazz.getConstructors();
        for (Constructor c : constructors) {
            System.out.println(c);
        }

        System.out.println();

        // getDeclaredConstructors():获取当前运行时类中声明的所有的构造器
        Constructor[] declaredConstructors = clazz.getDeclaredConstructors();
        for (Constructor c : declaredConstructors) {
            System.out.println(c);
        }
    }

}
4.全部的方法

❤️ public Method[] getDeclaredMethods() 返回此Class对象所表示的类或接口的全部方法
❤️ public Method[] getMethods() 返回此Class对象所表示的类或接口的public的方法
❤️ Method类中:

  • public Class getReturnType() 取得全部的返回值
  • public Class[] getParameterTypes() 取得全部的参数
  • public int getModifiers() 取得修饰符
  • public Class[] getExceptionTypes() 取得异常信息
public class MethodTest {
    @Test
    public void test1() {
        Class clazz = Person.class;

        // getMethods(): 获取当前运行时类及其所有父类中声明为public权限的方法。
        Method[] methods = clazz.getMethods();
        for (Method m : methods) {
            System.out.println(m);
        }

        System.out.println();

        // getDeclaredMethods():获取当前运行时类中声明的所有方法。(不包含父类中声明的方法)
        Method[] declaredMethods = clazz.getDeclaredMethods();
        for (Method m : declaredMethods) {
            System.out.println(m);
        }
    }

    
    @Test
    public void test2() {
        Class clazz = Person.class;
        Method[] declaredMethods = clazz.getDeclaredMethods();
        for (Method m : declaredMethods) {
            // 1. 获取方法声明的注解
            Annotation[] annotations = m.getAnnotations();
            for (Annotation a : annotations) {
                System.out.println(a);  // @com.atguigu.java1.MyAnnotation(value=hello)
            }

            // 2. 获取方法的权限修饰符
            System.out.print(Modifier.toString(m.getModifiers()) + "t");

            // 3. 获取方法的返回值类型
            System.out.print(m.getReturnType().getName() + "t");

            // 4. 获取方法的方法名
            System.out.print(m.getName());
            System.out.print("(");

            // 5. 获取方法的形参列表
            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.print(") ");

            // 6. 获取方法抛出的异常
            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.print(exceptionTypes[i].getName());
                        break;
                    }
                    System.out.print(exceptionTypes[i].getName() + ", ");
                }
            }

            System.out.println();
        }

    }

}
5.全部的Field

❤️ public Field[] getFields() 返回此Class对象所表示的类或接口的public的Field。
❤️ public Field[] getDeclaredFields() 返回此Class对象所表示的类或接口的全部Field。
❤️ Field方法中:

  • public int getModifiers() 以整数形式返回此Field的修饰符
  • public Class getType() 得到Field的属性类型
  • public String getName() 返回Field的名称。
public class FieldTest {
    @Test
    public void test1() {
        Class clazz = Person.class;

        // 获取属性结构
        // getFields():获取当前运行时类及其所有父类中声明为public访问权限的属性
        Field[] fields = clazz.getFields();
        for (Field f : fields) {
            System.out.println(f);
        }

        // getDeclaredFields():获取当前运行时类当中声明的所有属性。(不包含父类中声明的属性)
        Field[] fields1 = clazz.getDeclaredFields();
        for (Field f1 : fields1) {
            System.out.println(f1);
        }
    }

    // 权限修饰符  数据类型  变量名
    @Test
    public void test2() {
        Class clazz = Person.class;
        Field[] declaredFields = clazz.getDeclaredFields();
        for (Field f : declaredFields) {
            // 1. 权限修饰符
            int modifiers = f.getModifiers();
            System.out.print(Modifier.toString(modifiers) + "t");

            // 2. 数据类型
            Class type = f.getType();
            System.out.print(type.getName() + "t");

            // 3. 变量名
            String fName = f.getName();
            System.out.println(fName);
        }
    }

}
6.Annotation相关

❤️ get Annotation(Class annotationClass)
❤️ getDeclaredAnnotations()

public class OtherTest {
    
    @Test
    public void test7() {
        Class clazz = Person.class;
        Annotation[] annotations = clazz.getAnnotations();
        for (Annotation annos : annotations) {
            System.out.println(annos);
        }
    }
}
7.泛型相关

❤️ 获取父类泛型类型: Type getGenericSuperclass()
❤️ 泛型类型: ParameterizedType
❤️ 获取实际的泛型类型参数数组: getActualTypeArguments()

public class OtherTest {
    @Test
    public void test4() {
        Class clazz = Person.class;
        Type genericSuperclass = clazz.getGenericSuperclass();
        ParameterizedType paramType = (ParameterizedType) genericSuperclass;
        Type[] actualTypeArguments = paramType.getActualTypeArguments();
//        System.out.println(actualTypeArguments[0].getTypeName());  // java.lang.String
        System.out.println(((Class) actualTypeArguments[0]).getName());  // java.lang.String
    }
}
8.类所在的包

❤️ Package getPackage()

public class OtherTest {
    
    @Test
    public void test6() {
        Class clazz = Person.class;
        Package aPackage = clazz.getPackage();
        System.out.println(aPackage);
    }
}

1.在实际的操作中,取得类的信息的操作代码,并不会经常开发。
2.一定要熟悉java.lang.reflect包的作用,反射机制。
3.如何取得属性、方法、构造器的名称,修饰符等。

三、调用运行时类的指定结构 1.调用指定方法

❤️ 通过反射,调用类中的方法,通过Method类完成。步骤:
1.通过Class类的 getMethod(String name,Class…parameterTypes) 方法取得一个Method对象,并设置此方法操作时所需要的参数类型。
2.之后使用 Object invoke(Object obj, Object[] args) 进行调用,并向方法中传递要设置的obj对象的参数信息。

❤️ Object invoke(Object obj, Object … args)
说明:
1.Object 对应原方法的返回值,若原方法无返回值,此时返回null。
2.若原方法若为静态方法,此时形参Object obj可为null。
3.若原方法形参列表为空,则Object[] args为null。
4.若原方法声明为private,则需要在调用此invoke()方法前,显式调用方法对象的setAccessible(true)方法,将可访问private的方法。

❤️ 关于setAccessible方法的使用

  • Method和Field、Constructor对象都有setAccessible()方法。
  • setAccessible启动和禁用访问安全检查的开关。
  • 参数值为true则指示反射的对象在使用时应该取消Java语言访问检查。
  • 提高反射的效率。 如果代码中必须用反射, 而该句代码需要频繁的被调用, 那么请设置为true。
  • 使得原本无法访问的私有成员也可以访问
  • 参数值为false则指示反射的对象应该实施Java语言访问检查
public class ReflectionTest {
    
    @Test
    public void testMethod() throws InstantiationException, IllegalAccessException, NoSuchMethodException, InvocationTargetException {
        Class clazz = Person.class;
        // 创建运行时类的对象
        Person p = (Person) clazz.newInstance();

        // 1. 获取指定的某个方法  getDeclaredMethod():  参数1:指明获取的方法的名称  参数2:指明获取的方法的形参列表
        Method show = clazz.getDeclaredMethod("show", String.class);

        // 2. 保证当前的方法是可访问的
        show.setAccessible(true);

        // 3. 调用方法的invoke():  参数1:方法的调用者  参数2:给方法形参赋值的实参
        // invoke()的返回值即为对应类中调用的方法的返回值。
        Object returnValue = show.invoke(p, "CHN");
        System.out.println(returnValue);

        System.out.println("===============如何调用静态的方法==================");
//        private static void showDesc()
        Method showDesc = clazz.getDeclaredMethod("showDesc");
        showDesc.setAccessible(true);
        // 如果调用的运行时类中的方法没有返回值,则此invoke()返回null
        Object returnVal = showDesc.invoke(Person.class);
        // 或
//        Object returnVal = showDesc.invoke(null);
        System.out.println(returnVal);   // null
    }

}
2.调用指定属性

❤️ 在反射机制中,可以直接通过Field类操作类中的属性,通过Field类提供的 set() 和 get() 方法就可以完成设置和取得属性内容的操作。

  • public Field getField(String name) 返回此Class对象表示的类或接口的指定的public的Field。
  • public Field getDeclaredField(String name)返回此Class对象表示的类或接口的指定的Field。

❤️ 在Field中:

  • public Object get(Object obj) 取得指定对象obj上此Field的属性内容
  • public void set(Object obj,Object value) 设置指定对象obj上此Field的属性内容
public class ReflectionTest {
    @Test
    public void testField() throws Exception {
        Class clazz = Person.class;
        // 创建运行时类的对象
        Person p = (Person) clazz.newInstance();

        // 获取指定的属性:要求此运行时类中的属性是public的
        // 通常不采用此方法
        Field id = clazz.getField("id");

        
        id.set(p, 1001);

        
        int pId = (int) id.get(p);
        System.out.println(pId);
    }


    
    @Test
    public void testField1() throws InstantiationException, IllegalAccessException, NoSuchFieldException {
        Class clazz = Person.class;
        // 创建运行时类的对象
        Person p = (Person) clazz.newInstance();

        // 1、getDeclaredField(String fieldName):获取运行时类中指定变量名的属性
        Field name = clazz.getDeclaredField("name");

        // 2、保证当前属性是可访问的
        name.setAccessible(true);

        // 3、获取、设置指定对象的属性值
        name.set(p, "Tom");

        // 获取属性的值
        System.out.println(name.get(p));
    }

}
3.调用指定构造器
public class ReflectionTest {
    
    @Test
    public void testConstructor() throws NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {
        Class clazz = Person.class;

        // private Person(String name)
        // 1. 获取指定的构造器  getDeclaredConstructor(): 参数:指明构造器的参数列表
        Constructor declaredConstructor = clazz.getDeclaredConstructor(String.class);

        // 2. 保证此构造器是可访问的
        declaredConstructor.setAccessible(true);

        // 3. 调用此构造器创建运行时类的对象
        Person per = (Person) declaredConstructor.newInstance("Tom");
        System.out.println(per);  // Person{name='Tom', 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) {
        // 创建被代理类的对象
        NikeClothFactory nike = new NikeClothFactory();
        // 创建代理类的对象
        ProxyClothFactory proxyClothFactory = new ProxyClothFactory(nike);
        //
        proxyClothFactory.produceCloth();
    }
}
1.动态代理步骤




2.动态代理与AOP

❤️ 使用Proxy生成一个动态代理时,往往并不会凭空产生一个动态代理,这样没有太大的意义。通常都是为指定的目标对象生成动态代理。
❤️ 这种动态代理在AOP中被称为AOP代理, AOP代理可代替目标对象, AOP代理包含了目标对象的全部方法。但AOP代理中的方法与目标对象的方法存在差异。
❤️ AOP代理里的方法可以在执行目标方法之前、之后插入一些通用处理。


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);
    }
}



class ProxyFactory {
    // 调用此方法,返回一个代理类的对象。解决问题一
    public static Object getProxyInstance(Object obj) {  // obj:被代理类的对象
        MyInvocationHandler handler = new MyInvocationHandler();
        handler.bind(obj);
        return Proxy.newProxyInstance(obj.getClass().getClassLoader(), obj.getClass().getInterfaces(), handler);
    }
}

class MyInvocationHandler implements InvocationHandler {
    private Object obj;  // 需要使用被代理类的对象进行赋值

    public void bind(Object obj) {
        this.obj = obj;
    }

    // 当我们通过代理类的对象,调用方法 A 时,就会自动的调用如下的方法:invoke()
    // 将被代理类要执行的方法 A 的功能就声明在invoke()中
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        // method:即为代理类对象调用的方法,此方法也就作为了被代理类对象要调用的方法
        // obj:被代理类的对象
        Object returnValue = method.invoke(obj, args);
        // 上述方法的返回值就作为当前类中的invoke()的返回值
        return returnValue;
    }
}

public class ProxyTest {
    public static void main(String[] args) {
        SuperMan superMan = new SuperMan();
        // proxyInstance:代理类的对象
        Human proxyInstance = (Human) ProxyFactory.getProxyInstance(superMan);
        // 当通过代理类对象调用方法时,会自动的调用被代理类中同名的方法
        String belief = proxyInstance.getBelief();
        System.out.println(belief);
        proxyInstance.eat("香蕉");

        System.out.println("===================================");

        NikeClothFactory nikeClothFactory = new NikeClothFactory();
        ClothFactory proxyClothFactory = (ClothFactory) ProxyFactory.getProxyInstance(nikeClothFactory);
        proxyClothFactory.produceCloth();
    }
}
转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/861984.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号