高级技术【Java】【反射】【注解】【动态代理】

高级特性

1.Junit

Junit单元测试框架:用于对方法进行测试

注意测试用例需要全面。并做断言测试:断言结果是否与预期结果一致。

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

2.反射

反射就是:加载类,并允许以编程的方式解剖类中的各种成分(成员变量、方法、构造器等),是底层框架的通用技术

常见示例:
在这里插入图片描述

  • 获取类的信息,操作他们,获得类中的成员变量、方法、构造器等
  • 1.加载类,获取类的字节码:class对象
  • 2.获取类的构造器:Constructor对象
  • 3.获取类的成员变量: Field对象
  • 4.获取类的成员方法: Method对象

1.获取class对象

1.类名.class
Class c1 = 类名.class
2.调用静态方法:forName
public static Class forName(String package);
3.使用Object提供的方法
Class c3 = 对象.getClass();

package com.reflect.reflectDemo;

import lombok.Data;

public class reflectDemo1 {
    public static void main(String[] args) throws ClassNotFoundException {
        //1.获取类本身: 类名.class
        Class c1 = Animal.class;

        //2.调用static方法:forName
        Class c2 = Class.forName("com.reflect.reflectDemo.Animal");

        //3.使用object提供的getclass方法
        Animal a = new Animal();
        Class c3 = a.getClass();

        System.out.println(c1==c2); //true;
        System.out.println(c2==c3); //true

    }
}

@Data
class Animal {
    private String name;
    private int age;

}

2.获取各种成分

1.获取构造器
2.获取成员变量
3.获取成员方法


package com.reflect.reflectDemo;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.Getter;
import lombok.NoArgsConstructor;
import org.junit.Test;

import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

public class reflectDemo2 {
    public static void main(String[] args) throws Exception {

        Class c1 = Student.class;
        //获取构造器
        Constructor[] cs = c1.getDeclaredConstructors();  //拿到全部构造器
        for (Constructor c : cs) {
            System.out.println(c.getName() + "(" + c.getParameterCount() + ")");
        }

        //获取无参构造器
        Constructor con = c1.getDeclaredConstructor();
        //利用构造器创建对象
        con.setAccessible(true);//私有方法暴力攻破访问权限
        Student s = (Student) con.newInstance();
        System.out.println(s);

        //获取两个参数的构造器
        Constructor con1 = c1.getDeclaredConstructor(String.class, int.class);
        Student s1 = (Student) con1.newInstance("小明", 12); //关系反转,先得到构造器,通过给构造器传入参数从而创建对象
        System.out.println(s1);


        //////////////////////////////////
        //获取成员变量
        Field f1 = c1.getDeclaredField("hobby");
        System.out.println(f1.getName() + "(" + f1.getType().getSimpleName() + ")");
        Field f2 = c1.getDeclaredField("age");
        System.out.println(f2.getName() + "(" + f2.getType().getSimpleName() + ")");

        //获取成员变量的目的依然是取值和赋值
        Student s2 = new Student("小红", 23);
        f1.setAccessible(true);
        f1.set(s2,"写作业");  //赋值
        System.out.println(s2);
        String hobby = (String) f1.get(s2);
        System.out.println(hobby);


        //获取成员方法的目的依然是调用方法
        Student s3 = new Student("小贝", 43);
        Method m1 = c1.getDeclaredMethod("study");
        System.out.println(m1.getName() + "(" + m1.getParameterCount() + ")");
        m1.setAccessible(true);
        Object res1 = m1.invoke(s3);  //唤醒对象s3的study方法执行,相当于s3.study(); -> 小贝学习
        System.out.println(res1); //res1表示方法的返回值

        Method m2 = c1.getDeclaredMethod("study", String.class);
        System.out.println(m2.getName() + "(" + m2.getParameterCount() + ")");
        Object res2 =  m2.invoke(s3, "小黄");
        System.out.println(res2);
    }
}

@Data

class  Student {
    @Getter
    private String name;
    private int age;
    private String hobby;
    public Student(String name, int age) {
        this.name = name;
        this.age = age;
    }

    private Student() {}

    public String study(String name) {
        System.out.println(name + "在学习");
        return "别打扰我学习";
    }

    private void study() {
        System.out.println(this.name + "学习");
    }
}

3.反射的作用

  • 1.得到类的全部成分,然后操作
  • 2.可以破坏封装性
  • 3.可以绕过泛型的约束
    通过获取class文件(运行时),此时的泛型已经被擦除了,不再受泛型的约束。
    再通过反射,获取class里面的方法,通过方法去操作对象,从而到达翻墙(绕过泛型约束)的目的。

反射的含义:之前是通过new创建对象,再通过对象去获取构造器、成员变量、方法。现在可以通过先加载类.class,获取到构造器之后,通过获取的构造器去创建对象。获取成员变量之后,给对象赋属性值

简易框架

  • 实现传入一个对象,将它的所有字段以及对应的值保存到文件中去

package com.reflect.reflectDemo;

import lombok.Data;
import lombok.Getter;

import java.io.*;
import java.lang.reflect.Field;

public class Frame {
    public static void main(String[] args) throws Exception {
        Student1 s = new Student1();
        saveObject(s);
        Animal1 a = new Animal1();
        saveObject(a);
    }

    public static void saveObject(Object obj) throws Exception {
        OutputStream os = new FileOutputStream("object.txt", true);
        PrintStream ps = new PrintStream(os);
        //obj 可能是老师类,学生类
        //只有反射可以知道对象有多少个字段
        //1.获取class对象
        Class c = obj.getClass();
        //2.获取Class对象的所有字段
        Field[] fields = c.getDeclaredFields();
        String simpleName = obj.getClass().getSimpleName();
        ps.println("===============" + simpleName + "=============");
        //遍历字段
        for (Field field : fields) {
            //获取字段的值
            String fieldName = field.getName();
            field.setAccessible(true); //暴力反射
            Object fieldValue = field.get(obj) + "";

            //打印到文件中
            ps.println(fieldName + "=" + fieldValue);
            System.out.println(fieldName + "=" + fieldValue);
        }
    }
}

@Data
class Animal1 {
    private String name;
    private int age;
}

@Data

class  Student1 {
    @Getter
    private String name;
    private int age;
    private String hobby;
    public Student1(String name, int age) {
        this.name = name;
        this.age = age;
    }

    public Student1() {}

    public String study(String name) {
        System.out.println(name + "在学习");
        return "别打扰我学习";
    }

    private void study() {
        System.out.println(this.name + "学习");
    }
}

3.注解

**注解就是Java代码中的特殊标记,比如: @Override、@Test

  • 作用是让其他程序根据注解信息来决定怎么执行程序
  • 注意:注解可以用在类上、构造器、方法、成员变量、参数等位置

1.自定义注解

自己定义注解
在这里插入图片描述

2.注解原理

本质是一个接口,继承了Anotation类
在这里插入图片描述
在这里插入图片描述

3.元注解

注解注解的注解
@Target
@Retention
在这里插入图片描述

@Target:被它标记的注解只能在指定的位置使用,可以使用逗号隔开声明多个位置
在这里插入图片描述
@Retention:约束注解的存活范围,一般为运行时(一直都在),否则没有意义
在这里插入图片描述

4.注解的解析

  • 判断类上、方法上、成员变量上是否存在注解,并把注解的内容解析出来。
    在这里插入图片描述

在这里插入图片描述

5.注解的应用场景

使用注解开发出一个简易的Junit框架

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述


动态代理

形象展示:
在这里插入图片描述
在这里插入图片描述

示例:

package com.itheima.proxyDemo;

import jdk.jfr.DataAmount;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

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

public class proxyDemo {
    public static void main(String[] args) {
        //目标:创建代理对象
        //1.准备一个明星对象:设计明星类
        Start start = new Start("章若楠");
        //2.为明星对象创建代理
        StartServer proxy = ProxyUtil.createProxy(start);
        proxy.sing("罗生门");
        System.out.println(proxy.dance());
    }
}

@Data
@AllArgsConstructor
@NoArgsConstructor
class Start implements StartServer {
    private String name;

    @Override
    public void sing(String name) {
        System.out.println(this.name + "在唱歌,歌名为:" + name);
    }

    @Override
    public String dance() {
        System.out.println(this.name + "在跳舞");
        return "谢谢谢谢";
    }

}

class ProxyUtil {
    //创建一个明星对象的代理对象返回
    public static StartServer createProxy(Start start) {
        /*
        参数一:用于执行哪个类加载器去加载生成的代理类
        参数二:用于指定代理类需要实现的接口:明星类实现的接口,代理类就需要实现
        参数三:用于指定代理类需要如何去代理(代理需要做的事情)
         */
        StartServer proxy = (StartServer) Proxy.newProxyInstance(ProxyUtil.class.getClassLoader(),
                start.getClass().getInterfaces(), new InvocationHandler() {
                @Override
                public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                    String methodName = method.getName();
                    if ("sing".equals(methodName)) {
                        System.out.println("准备话筒,收钱10万!");
                    } else if ("dance".equals(methodName)) {
                        System.out.println("准备跳舞场地,收钱100万!");
                    }
                    //真正干活(把真正的明星对象叫过来正式干活)
                    //调用真正的明星对象来执行被代理的行为
                    Object result = method.invoke(start, args);
                    return result;
                }
        });
        return proxy;
    }
}

interface StartServer {
    void sing(String name);
    String dance();
}

解决实际问题

在这里插入图片描述

  • 可以简化代理的对象的重复操作,代理对象只需要专心处理专门的任务即可
    示例:比如要分析方法的性能,需要对每个方法的执行时间进行统计,在方法内部写性能分析代码会显得重复并且导致业务逻辑不清晰
    那么可以将性能分析任务交给代理来做,使用代理测试每个方法的执行时间。
    在这里插入图片描述

*上述内容均来自黑马程序员B站视频的学习笔记以及截图,仅为学习交流,不作为商业用途,如有侵权,联系删除。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值