我是靠谱客的博主 纯情月饼,最近开发中收集的这篇文章主要介绍Java高级--反射Java反射机制概述理解Class类并获取Class实例类的加载与ClassLoader的理解创建运行时类的对象获取运行时类的完整结构调用运行时类的指定结构反射的应用:动态代理,觉得挺不错的,现在分享给大家,希望可以做个参考。

概述

反射

  • Java反射机制概述
    • Java反射机制提供的功能
    • 反射相关的主要API
    • 反射和封装性的疑问
  • 理解Class类并`获取Class实例`
    • 关于java.lang.Class类的理解:
    • 获取Class实例的方式
    • 哪些类型可以有Class对象?
  • 类的加载与ClassLoader的理解
    • 读取配置文件的两种方式(掌握)
  • `创建运行时类的对象`
    • newInstance()方法
      • 实例体会反射的动态性
  • 获取运行时类的完整结构
    • 获取运行时类中的属性
    • 获取运行时类中的方法
    • 获取运行时类的构造器
    • 获取运行时类的父类及父类的泛型(掌握)
    • 获取运行时类的接口、所在包、注解
  • `调用运行时类的指定结构`
    • 操作运行时类中的指定的属性(掌握)
    • 操作运行时类中的指定的方法(掌握)
    • 操作运行时类中的指定的构造器
  • 反射的应用:动态代理
    • 动态代理原理
    • 静态代理
    • 动态代理使用场合
    • 动态代理相比于静态代理的优点
    • 示例
      • 静态代理
      • 动态代理
    • 动态代理与AOP(Aspect Orient Programming)

Java反射机制概述

Reflection(反射)是被视为动态语言的关键,反射机制允许程序在执行期借助于Reflection API取得任何类的内部信息,并能直接操作任意对象的内部属性及方法

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

Java反射机制提供的功能

  • 在运行时判断任意一个对象所属的类
  • 在运行时构造任意一个类的对象
  • 在运行时判断任意一个类所具有的成员变量和方法
  • 在运行时获取泛型信息
  • 在运行时调用任意一个对象的成员变量和方法
  • 在运行时处理注解
  • 生成动态代理

反射相关的主要API

  • java.lang.Class:代表一个类
  • java.lang.reflect.Method:代表类的方法
  • java.lang.reflect.Field:代表类的成员变量
  • java.lang.reflect.Constructor:代表类的构造器

反射和封装性的疑问

  • 1、通过直接new的方式或反射的方式都可以调用公共的结构,开发中到底用哪个?
    • 建议直接new的方式
    • 在需要用到反射的特征(动态性)使就用反射。在java代码运行起来后,在不确定需要用到哪个方法的对象时,就需要用到反射。如:我们在一个项目里,java代码已经在服务器里运行了,但并不知道用户会使用哪个功能,可能会注册,可能会登录…,就可以通过url中的后缀来判断需要用到哪个方法,利用反射机制,servlet动态的new出该方法的对象去执行相应的操作。
  • 2、反射机制与面向对象的封装性是不是有矛盾?如何看待两个技术?
    • 不矛盾
    • 封装性,对于公共的可以调,私有的不能掉,但是在公共的方法里可能在内部已经自己调用过了,所以不需要外部去调用。
    • 反射,当公共的结构不满足需求时,就可以通过反射机制去调用
    • 封装性,是建议只用get,set方法去修改公共的,建议怎么调;反射,是能不能调

理解Class类并获取Class实例

关于java.lang.Class类的理解:

  • 1、类的加载过程:程序经过javac.exe命令以后,会生成一个或多个字节码文件(.class结尾),接着我们使用java.exe命令对某个字节码文件进行解释运行。相当于将某个字节码文件加载到内存中。此过程就称为类的加载。加载到内存中的类,我们就称为运行时类,此运行时类,就作为Class的一个实例。类就可以作为Class类的一个对象。eg:Class clazz = Person.class
  • 2、换句话,Class的实例就对应着一个运行时类
  • 3、加载到内存中的运行时类,会缓存一定的时间。在此时间之内,我们可以通过不同的方式来获取此运行时类

获取Class实例的方式

  • 1、调用运行时类的属性:class
Class clazz1 = Person.class;//Class<Person> clazz1 = Person.class;可以加上泛型,后面用对象clazz1就不用强转了
System.out.println(clazz1);
  • 2、通过运行时类的对象,调用getClass()
        Person p1 = new Person();
        Class clazz2 = p1.getClass();
        System.out.println(clazz2);
  • 3、调用Class的静态方法:forName(String classPath)(使用频率更高)
        Class clazz3 = Class.forName("com.llc.java.Person");
        System.out.println(clazz3);
                //任何类都可以作为Class的实例
        Class clazz4 = Class.forName("java.lang.String");
        System.out.println(clazz4);
  • 4、使用类的加载器:ClassLoader(了解)
        ClassLoader classLoader = ReflectionTest.class.getClassLoader();
        Class clazz5 = classLoader.loadClass("com.llc.java.Person");
        System.out.println(clazz5);

哪些类型可以有Class对象?

  • 1)class:
    外部类,成员(成员内部类,静态内部类),局部内部类,匿名内部类
  • 2)interface:接口
  • 3)[]:数组
  • 4)enum:枚举
  • 5)annotation:注解@interface
  • 6)primitive type:基本数据类型
  • 7)void

类的加载与ClassLoader的理解

	@Test
    public void test1(){
        //对于自定义类,使用系统类加载器进行加载
        ClassLoader classLoader = ClassLoaderTest.class.getClassLoader();
        System.out.println(classLoader);

        //调用系统类加载器的父类getParent():获取扩展类加载器
        ClassLoader classLoader1 = classLoader.getParent();
        System.out.println(classLoader1);

        //调用扩展类加载器的父类getParent():无法获取引导类加载器
        //引导类加载器主要负责加载Java的核心类库,无法加载自定义类的
        ClassLoader classLoader2 = classLoader1.getParent();
        System.out.println(classLoader2);//null

		ClassLoader classLoader3 = String.class.getClassLoader();
        System.out.println(classLoader3);//null
    }

读取配置文件的两种方式(掌握)

    @Test
    public void test2() throws Exception {
        Properties pros = new Properties();
        //此时的文件默认路径在当前的module下
        //读取配置文件的方式一:
//        FileInputStream fis = new FileInputStream("jdbc.properties");
//        FileInputStream fis = new FileInputStream("src\jdbc1.properties");
//        pros.load(fis);

        //读取配置文件的方式二:
        //配置文件默认识别为:当前module的src下
        ClassLoader classLoader = ClassLoaderTest.class.getClassLoader();
        InputStream is = classLoader.getResourceAsStream("jdbc1.properties");
        pros.load(is);

        String userName = pros.getProperty("username");
        String password = pros.getProperty("password");
        System.out.println("姓名:"+userName+",密码:"+password);
    }

创建运行时类的对象

newInstance()方法

调用此方法,创建对应的运行时类的对象,内部调用了运行时类的空参构造器
要求:

  • 1、运行时类必须提供空参的构造器
  • 2、空参的构造器的访问权限得够。通常,设置为public

通过在javabean中要求提供一个public的空参构造器。原因:

  • 1、便于通过反射,创建运行时类的对象
  • 2、便于子类继承此运行时类时,默认调用super()时,保证父类有此构造器
	@Test
    public void test1() throws InstantiationException, IllegalAccessException {
        Class clazz = Person.class;
        Object obj = (Object) clazz.newInstance();
        System.out.println(obj);
     }

实例体会反射的动态性

    @Test
    public void test2(){
        for(int i = 0;i < 100;i++){
            int num = new Random().nextInt(3);//0,1,2
            String classPath = "";
            switch (num){
                case 0:
                    classPath = "java.util.Date";
                    break;
                case 1:
                    classPath = "java.lang.Object";
                    break;
                case 2:
                    classPath = "com.llc.java.Person";
                    break;
            }
            try {
//                Object instance = getInstance(classPath);
                NewInstanceTest nit = new NewInstanceTest();
                Object instance =(Object) nit.getInstance(classPath);
                System.out.println(instance);
            } catch (Exception e) {
                throw new RuntimeException(e);
            }
        }
    }

    /*
    创建要给指定类的对象:
    classPath:指定类的全类名
     */
    public Object getInstance(String classPath) throws Exception{
        Class clazz = Class.forName(classPath);
        return clazz.newInstance();
    }

获取运行时类的完整结构

获取运行时类中的属性

  • getFields():获取当前运行时类及其父类中所有声明为public访问权限的属性
  • getDeclaredFields():获取当前运行时类中声明的所有属性(不包含父类中声明的属性)
@Test
    public void test1(){
        Class clazz = Person.class;

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

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

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

获取运行时类的权限修饰符、数据类型、变量名

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

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

            //3、变量名
            String name = field.getName();
            System.out.println(name);
        }
    }

获取运行时类中的方法

  • getMethods():获取当前运行时类及其所有父类中声明public权限的方法
  • getDeclaredMethods():获取当前运行时类中声明的所有方法(不包含父类中声明的方法)
@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);
        }
    }

获取注解、权限修饰符、返回值类型、方法名(参数类型1 形参1,…) throws XxxException()

 /*
    @Xxxx
    权限修饰符 返回值类型 方法名(参数类型1 形参名1,...) throws XxxException()
     */
    @Test
    public void test2(){
        Class clazz1 = Person.class;
        Method[] declaredMethods = clazz1.getDeclaredMethods();
        for (Method m : declaredMethods) {
            //1、获取方法声明的注解
            Annotation[] annos = m.getAnnotations();
            for (Annotation a : annos) {
                System.out.println(a);
            }

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

获取运行时类的构造器

  • getConstructors():获取当前运行时类中声明为public的构造器
  • getDeclaredConstructors():获取当前运行时类中声明的所有的构造器
@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 d : declaredConstructors){
            System.out.println(d);
        }
    }

获取运行时类的父类及父类的泛型(掌握)

 //获取运行时类的父类
    @Test
    public void test2(){
        Class clazz = Person.class;

        Class superclass = clazz.getSuperclass();
        System.out.println(superclass);
    }

    //获取运行时类的带泛型的父类
    @Test
    public void test3(){
        Class clazz = Person.class;

        Type genericSuperclass = clazz.getGenericSuperclass();
        System.out.println(genericSuperclass);
    }

    //获取运行时类的带泛型的父类的泛型
    @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());
    }

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

/*
    获取运行时类实现的接口
     */
    @Test
    public void test5(){
        Class clazz = Person.class;

        Class[] interfaces = clazz.getInterfaces();
        for(Class c : interfaces){
            System.out.println(c);
        }

        System.out.println();

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

    /*
    获取运行时类所在的包
     */
    @Test
    public void test6(){
        Class clazz = Person.class;

        Package aPackage = clazz.getPackage();
        System.out.println(aPackage);
    }

    /*
    获取运行时类声明的注解
     */
    @Test
    public void test7(){
        Class clazz = Person.class;

        Annotation[] annotations = clazz.getAnnotations();
        for(Annotation a : annotations){
            System.out.println(a);
        }

    }

调用运行时类的指定结构

操作运行时类中的指定的属性(掌握)

步骤:

  • 1、getDeclaredField(String fieldName):获取运行时类中指定变量名的属性
  • 2、保证当前属性时可访问的
  • 3、获取、设置指定对象的此属性值
@Test
    public void testField1() throws Exception{
        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,"Jack");

        System.out.println(name.get(p));
    }

操作运行时类中的指定的方法(掌握)

步骤:

  • 1、获取指定的方法
  • 2、保证当前的方法是可访问的
  • 3、调用invoke():参数1:方法的调用者 参数2:给方法形参赋值的实参
    invoke()的返回值即为对应类中调用的方法的返回值
@Test
    public void testMethod() throws  Exception{
        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("********如何调用静态方法************");

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

操作运行时类中的指定的构造器

步骤:

  • 1、获取指定的构造器
  • 2、保证此构造器是可访问的
  • 3、调用此构造器创建运行时类的对象
/*
    如何调用运行时类中指定的构造器
     */
    @Test
    public void testConstructor() throws  Exception{
        Class clazz = Person.class;
        //1、获取指定的构造器
        Constructor declaredConstructor = clazz.getDeclaredConstructor(String.class);
        //2、保证此构造器是可访问的
        declaredConstructor.setAccessible(true);

        //3、调用此构造器创建运行时类的对象
        Person per = (Person) declaredConstructor.newInstance("Tom");
        System.out.println(per);
    }

一般情况下,没有参数值,调用构造器new对象都是用以下的方式:

Object obj = (Object) clazz.newInstance();
        System.out.println(obj);

反射的应用:动态代理

动态代理原理

使用一个代理将对象包装起来,然后用该代理对象取代原始对象。任何对原始对象的调用都要通过代理。代理对象决定是否以及何时将方法调用转到原始对象上。

静态代理

代理类和目标对象的类都是在编译期间确定下来,不利于程序的扩展。同时,每一个代理类只能为一个接口服务,这样一来程序开发中必然产生过多的代理。最好可以通过一个代理类完成全部的代理功能

动态代理使用场合

  • 调试
  • 远程方法调用

动态代理相比于静态代理的优点

抽象角色中(接口)声明的所有方法都被转移到调用处理器一个集中的方法中处理,这样,我们可以更加灵活和统一的处理众多的方法。换句话说,就是一个代理类完成全部的被代理的功能

示例

静态代理

/**
 * 静态代理举例
 *
 * 特点:代理类和被代理类在编译期间,就确定下来了
 * @author LawrenceLan
 * @create 2022--10--03--15:52
 */
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();
    }
}

动态代理

/**

  • 动态代理的示例
  • @author LawrenceLan
  • @create 2022–10–03–16:02
    */
    //代理类
    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("*********************************");

    //new一个被代理类的对象
    NikeClothFactory nikeClothFactory = new NikeClothFactory();
    //根据被代理类的对象动态的造一个代理类的对象
    ClothFactory proxyClothFactory = (ClothFactory) ProxyFactory.getProxyInstance(nikeClothFactory);
    //通过代理类的对象调用被代理类中的方法
    proxyClothFactory.produceCloth();
}

}

动态代理与AOP(Aspect Orient Programming)

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

最后

以上就是纯情月饼为你收集整理的Java高级--反射Java反射机制概述理解Class类并获取Class实例类的加载与ClassLoader的理解创建运行时类的对象获取运行时类的完整结构调用运行时类的指定结构反射的应用:动态代理的全部内容,希望文章能够帮你解决Java高级--反射Java反射机制概述理解Class类并获取Class实例类的加载与ClassLoader的理解创建运行时类的对象获取运行时类的完整结构调用运行时类的指定结构反射的应用:动态代理所遇到的程序开发问题。

如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(66)

评论列表共有 0 条评论

立即
投稿
返回
顶部