简单了解java中的反射、注解、动态代理相关知识

类加载器

类加载器的作用

加载器是java运行时环境的一部分,负责加载字节码文件,即将磁盘上的某个class文件读取到内存并生成Class的对象

类加载器的分类

启动类加载器

用于加载系统类库**<JAVA_HOME>\bin**目录下的calss

扩展类加载器

用于加载扩展类库**<JAVA_HOME>\lib\ext**目录下的class

应用程序类加载器

用于加载我们自定义的加载器

获取加载类的方式

来自Class类型获取类加载器的方式

public ClassLoader getClassLoader()//返回类的加载器

示例:

public class TestDemo1 {
    @Test
    public void testClassLoader(){
        //获取TestDemo1的Class对象
        Class<TestDemo1> demo = TestDemo1.class;

        //获取AppClassLoader类加载器对象
        ClassLoader cl1 = demo.getClassLoader();
        System.out.println(cl1);

        //获取AppClassLoader类加载器的父类扩展类加载器ExtClassLoader
        ClassLoader cl2 = cl1.getParent();
        System.out.println(cl2);

        //获取ExtClassLoader类加载器的父类启动类加载器Bootstrap
        ClassLoader cl3 = cl2.getParent();//null
        System.out.println(cl3);
    }
}

双亲委派机制

工作机制

某个“类加载器”收到类加载的请求,它首先不会尝试自己取加载这个类,而是把请求交给父级类加载

因此,所有的类加载的请求最终都会被传送到顶层的“启动类加载器”中。

如果父级类加载器无法加载这个类,然后子级类加载器再去加载。

使用类加载器加载配置文件的方式

示例步骤:

src下新建一个user.properties文件,并输入:

userName=TG
age=21

创建properties类的集合对象p

使用当前类TestDemo2获取Class对象并调用Class类中的getClassLoader函数

使用类加载器对象调用ClassLoader类中的**InputStream getResourceAsStream(String name)**返回读取指定资源的输入流

public class TestDemo2 {
    @Test
    public void testGetResourceAsStream() throws IOException {
        //获取类加载器对象
        Class<TestDemo2> cla = TestDemo2.class;
        ClassLoader classLoader = cla.getClassLoader();

        //使用类加载器中的方法,获取src目录下的配置文件
        InputStream is = classLoader.getResourceAsStream("user.properties");

        //创建Properties对象
        Properties prop = new Properties();
        prop.load(is);

        System.out.println(prop);
    }
}

反射

概述

是针对Class对象进行操作,是一种类的解剖技术(.class文件中有:构造方法、成员方法、成员变量),反射就可以获取.class文件中的构造方法、成员方法和成员变量。获取到这些东西有啥用嘞?当我们获取到构造方法时,我们可以创建对象,当我们获取到成员变量时,我们可以进行取值和赋值,当我们获取到成员方法时,我们可以进行方法调用执行。上述这种在运行过程中动态的获取类的信息以及调用类中成分的能力称为java语言的反射机制

反射的关键

反射的第一步都是先得到编译后的Class类对象,然后就可以得到Class的全部成分。

反射获取类对象

  1. 当方法区中创建了.class文件的Class对象后,就可以使用类名.class
Class cls = 类名.class
  1. Class cls = Class.forName(“类的全限定名”)
Class cls = Class.forName("com.mysql.cj.jdbc.Driver");//编写代码时不要求Driver必须存在
  1. 在创建具体对象后,用对象名获取Class对象
Class cls = 对象名.getClass();

示例:

public class TestDemo3 {
    //类名.class
    @Test
    public void classTest() {
        System.out.println(User.class);
    }

    //Class.forName:读取配置文件
    @Test
    public void forNameTest() throws IOException, ClassNotFoundException {
        //读取配置文件
        Class cla = TestDemo3.class;
        ClassLoader classLoader = cla.getClassLoader();
        InputStream ras = classLoader.getResourceAsStream("user.properties");
        Properties prop = new Properties();
        prop.load(ras);


        String className = prop.getProperty("className");

        Class cls = Class.forName(className);

        System.out.println(cls);
    }

    //对象名.getClass()
    @Test
    public void getClassTest() {
        demo(new User());
    }

    public void demo(User user) {
        Class cls = user.getClass();
        System.out.println(cls);
    }
}

使用反射技术获取构造器对象并使用

Class类中用于获取构造器的方法

方法说明
Constructor<?>[] getConstructors()返回所有构造对象的数组
Constructor<?>[] getDeclaredConstructors()返回所有构造器数组,存在就能拿到
Constructor< T> getConstructor(Class<?>… parameterTypes)返回单个构造器对象(只能拿到public的)
Constructor< T> getDeclaredConstructor(Class<?>… parameterTypes)返回单个构造器存在就能拿到

示例:

/**
 * 利用反射获取构造器,并为对象属性赋值
 */
public class ReflectDemo1 {
    @Test
    public void demo() throws NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {
        //获取Class对象
        Class<User> userClass = User.class;

        //获取构造器(要是你想获取所有(包括私有)的构造器可以使用getDeclaredConstructor)
        //获取public无参构造
        Constructor<User> constructor = userClass.getConstructor();
        //获取public有参构造
        Constructor<User> constructor1 = userClass.getConstructor(String.class, String.class);

        //利用构造器创建对象
        User user = constructor.newInstance();//无参构造方法中无需传递参数
        //利用有参构造创建对象
        User user1 = constructor1.newInstance("糖魅", "8849");

        //为对象属性赋值
        user.setName("糖锅");
        user.setPassword("8848");

        System.out.println(user);
        System.out.println(user1);
    }

    @Test
    public void demo1() {
        //获取Class对象
        Class<User> userClass = User.class;

        //获取构造器(若是你想获取所有(包括私有)的构造器你可以使用getDeclaredConstructors)
        Constructor<?>[] constructors = userClass.getConstructors();

        //查看获取到的构造器
        for (Constructor<?> cons : constructors) { //可以获取所有public构造器
            System.out.println(cons);
        }
    }
}

Constructor类中用于创建对象的方法

方法说明
T newInstance(Object… initargs)根据指定构造器创建对象
public void setAccessible(boolean flag)设置为true表示取消访问检查,进行暴力反射

注意:这里解释一下第二个方法

继承中,父类的私有内容是可以被继承的,但是由于java语言有权限检查过滤,所以才导致私有内容子类无法访问;Class对象中存储的private构造器,我们虽然可以得到私有构造器,但是我们无法通过一般手段来用它创建对象,而反射技术的强大之处在于可以暴力破解权限限制(setAccessible方法),可以设置本次访问时暂时取消权限检查。

示例:

@Test
public void demo2() throws NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {
        /*
         反射技术:获取私有构造器(如果有私有属性也是一样的可以获取)
        */
        //获取Class类
        Class<User> userClass = User.class;

        //获取私有构造器
        Constructor<User> c = userClass.getDeclaredConstructor(String.class);
        c.setAccessible(true);//取消权限检查

        //用私有构造器创建对象
        User user = c.newInstance("糖解");

        System.out.println(user);
    }

使用反射技术获取成员方法变量

Class类中用于获取成员方法的方法

方法说明
Method[] getMethods()返回所有成员方法对象的数组(只能拿public)
Method[] getDeclaredMethods()返回所有成员方法对象的数组,只要存在就能拿到
Method getMethod(String name, Class< ?>… parameterType)返回单个成员方法对象,只能拿public
Method getDeclaredMethod(String name, Class< ?>… parameterType)返回单个成员方法,只要存在就能拿到

Method类中用于除法执行的方法

方法说明
Object invoke(Object obj, Object… args)参数一:用于obj对象调用该方法;参数二:调用方法的传递参数;返回值:方法的返回值(如果没有就不写

示例:

Demo类

public class Demo {
    int id;

    public Demo() {
    }

    public Demo(int id) {
        this.id = id;
    }

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    @Override
    public String toString() {
        return "Demo{" +
                "id=" + id +
                '}';
    }

    public String haha() {
        return "hahahaha";
    }
}

测试方法

public class MethodDemo {
    @Test
    public void getMethodTest() throws NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {
        //获取Class对象
        Class<Demo> demoClass = Demo.class;

        //获取对象中的方法
        Method haha = demoClass.getMethod("haha");
        Constructor<Demo> cons = demoClass.getConstructor();

        //调用方法
        Demo demo = cons.newInstance();
        System.out.println(haha.invoke(demo));
    }
}

使用反射技术获取成员变量

Class类中用于获取成员变量的方法

方法说明
Field[] getFields()返回所有(public)成员变量对象数组
Field[] getDeclaredFields ()返回所有成员变量对象的数组
Field[] getField(String name)返回单个成员变量(public)
Field[] getDeclaredField ()返回单个成员变量对象存在就能获得

Field类中用于取值赋值的方法

方法说明
void set(Object obj, Object value)赋值
Object get(Object obj)获取值

注解

单独使用没有意义,通常配合反射技术使用

概述

java注解又称java标注,时jdk5引入的一种注释机制,java语言中类、构造方法、方法、成员变量、参数等都可以被注解进行标注

作用

对java中类、方法、成员变量做标记,然后进行特殊处理,至于到底作何处理由业务需求决定,比如:JUnit框架中,标记了注解@Test的方法就可以当作测试方法执行,而没有标记的就不能作为测试方法执行。

自定义注解

格式
public @interface 注解名称 {
    public 属性类型 属性名() default 默认值;
}

上述注解中,属性类型可以是:八种基本数据类型、String、Class、注解类型、枚举类型

特殊属性

value属性,如果只有一个value属性的情况下,使用value属性的时候可以省略value名称不写,但是如果有多个属性,且多个属性没有默认值,那么value名称是不能省略的。

自定义注解书写示例:

自定义注解书写示例:

public @interface Student {
    public String value();
    public String[] teacherName();
    public String schoolName() default "TG大学";
}

元注解

概述

就是修饰自定义注解的注解

常见的元注解有两个

@Target:约束自定义注解只能在哪些地方使用,其中可以使用的值定义在ElementType枚举类中,常用值如下:

  • TYPE:类、接口
  • FIELD:成员变量
  • METHOD:成员方法
  • PARAMELER:方法参数
  • CONSTRUCTOR:构造器
  • LOCAL_VARIABLE:局部变量

示例:

@Target(ElementType.TYPE)//限定当前注解只能在类上使用
public @interface Student {
    
}

@Retention:声明注解生命周期,其可以使用RetentionPolicy枚举类中,常用值如下:

  • SOURCE:注解之作用在源码阶段,生成的字节码文件中不存在
  • CLASS:注解作用在源码阶段,字节码文件阶段,运行阶段不存在,默认值
  • RUNTIME:注解作用在源码阶段,字节码文件阶段,运行阶段(开发常用 )

注解解析

概述

注解的操作中经常要进行解析,注解的解析就是判断是否存在注解,存在注解就解析出内容。

相关接口

AnnotatedElement:该接口定义了与注解解析相关的方法

方法说明
Annotation[] getDeclaredAnnotations()获得当前对象上使用的所有注解,返回注解数组
T getDeclaredAnnotation(Class< T> annotationClass)根据注解类型获得对应注解对象
boolean isAnnotationPresent(Class < Annotation> annotationClass)判断当前对象是否使用了指定注解,如果使用了则返回true,否则false

示例:获取注解中的value并为使用该注解的对象成员变量赋值为value

自定义一个注解SetStringValue

@Target(ElementType.FIELD)//限定该注解就只能在成员变量上使用
@Retention(RetentionPolicy.RUNTIME)//该注解生命周期为源码、字节码、运行阶段
public @interface SetStringValue {
    public String value();
}

自定义了一个Person类,其中name属性使用@SetStringValue注解

public class Person {
    @SetStringValue("糖锅")
    String name;

    public Person() {
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}

定义测试类TestPerson

public class TestPerson {
    @Test
    public void testPerson() throws NoSuchFieldException, NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {
        //获取Class对象
        Class<Person> cls = Person.class;

        //根据Class对象获取成员变量对象和构造器对象
        Field field = cls.getDeclaredField("name");
        Constructor<Person> cons = cls.getDeclaredConstructor();
        
        //如果对象是私有的,用setAccessible(true)方法消除权限判断

        //创建Person对象
        Person person = cons.newInstance();

        //根据成员变量对象判断是否使用@SetStringValue注解
        if (field.isAnnotationPresent(SetStringValue.class)) {
            //如果使用了该注解,则对该注解进行解析
            SetStringValue anno = field.getDeclaredAnnotation(SetStringValue.class);

            //根据获取的注解对象,获取注解中的value值
            String value = anno.value();

            //赋值
            field.set(person, value);
        }
        System.out.println(person.getName());
    }
}

测试运行结果:

糖锅

动态代理

概述

动态代理是被代理者没有能力或者不愿意取完成某件事情,需要找个人代替自己去完成这件事情,动态代理就是用来对业务功能进行代理的

步骤

必须有接口,实现类要实现接口(代理通常是基于接口实现的)

创建一个实现类对象,该对象为业务对象,紧接着为业务对象做一个代理对象

实现

代理对象 = Proxy.newProxyInstance(类加载器, 父接口, 处理器);

参数解释:

  • 类加载器:动态加载.class文件
  • 父接口:代理类和被代理类需要拥有共同的父接口
  • 处理器:代理对象拦截了方法后,对方法进行前增强,后增强,是由处理器来书写逻辑
代理对象 = Proxy.newProxyInstance(.class.getClassLoader(),//类加载器
    被代理类.class.getInterfaces(),//父接口
    new InvocationHandler(){
        public Object invoke(Object 代理对象,Method 被拦截的方法对象,Object[] 方法中的实参){
            //业务逻辑
        }
    }
)
  • 36
    点赞
  • 23
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值