反射、注解、动态代理

一、反射

什么是反射

反射就是:以编程的方式获取类中的各种成分(成员变量、方法、构造器等)

反射的特点

使用反射创建对象,无视修饰符,功能更强大灵活, 代码更复杂

反射的作用

可以得到一个类的全部成分然后操作

可以破坏封装性

更重要的用途是:适合做java的开发框架,基本上主流框架都会基于反射设计一些通用技术功能。

为了更好的看懂框架

获取类

三种方式获取到的是同一个Class对象

1、Class c1 = 类名.class

2Object提供的方法: public Class getClass()对象.getClass()

3、调用Class提供方法:public static Class forName(String package)

方式一
Class stu1 = Student.class;

方式二
Student student = new Student();
Class stu2 = student.getClass();

方式三
//类全名  全限定类名  【包名+类名】
Class stu3 = Class.forName("com.demo02_反射.demo01_获取class对象.Student");

System.out.println(stu1 == stu2); //true
System.out.println(stu3 == stu2); //true

获取类的构造器

Class提供了,从类中获取构造器的方法

 

获取到构造器的作用:依然是初始化一个对象返回。

 

如果是非public的构造器,需要打开权限(暴力反射),然后再创建对象

一般获取到类的成员对象都设置暴力反射

public static void main(String[] args) throws Exception {
    Class student = Class.forName("com.demo02_反射.demo02_获取构造器.Student");

    Constructor con1 = student.getDeclaredConstructor(); //获取private无参构造器
    con1.setAccessible(true); //最好设置暴力反射
    Object oj1 = con1.newInstance(); //不设置暴力反射无法用private构造器new对象
    System.out.println(oj1);

    Constructor con2 = student.getConstructor(String.class, int.class); //public满参构造器
    con2.setAccessible(true);
    Student obj2 = (Student) con2.newInstance("张三", 100);
    System.out.println(obj2);
}

获取类的成员变量

Class提供了从类中获取成员变量的方法。

 获取到成员变量的作用:依然是赋值、取值。

public static void main(String[] args) throws Exception {
    Class student = Class.forName("com.demo02_反射.demo03_成员变量.Student");

    Constructor con = student.getDeclaredConstructor();
    con.setAccessible(true);
    Object stu = con.newInstance();

    Field name = student.getDeclaredField("name");
    name.setAccessible(true);
    name.set(stu, "张三");

    Object getName = name.get(stu);  //stu为类的对象, new一个对象也行
    System.out.println(getName);
}

 

获取类的成员方法

Class提供了从类中获取成员方法的API

 获取到成员方法的作用:依然是执行该方法。

 

public static void main(String[] args) throws Exception {
    Class student = Class.forName("com.demo02_反射.demo04_方法.Student");

    Constructor con = student.getDeclaredConstructor();
    con.setAccessible(true);
    Object stu = con.newInstance();

    Method method1 = student.getDeclaredMethod("setName", String.class);
    method1.setAccessible(true);
    method1.invoke(stu, "张三");

    Method method2 = student.getDeclaredMethod("getName");
    method2.setAccessible(true);
    Object invoke1 = method2.invoke(stu);
    System.out.println(invoke1);
}

综合案例

public static void main(String[] args) throws Exception {
        Student s1 = new Student("张三", 23, 6668, "广州");
        Teacher t1 = new Teacher("0001", "Mylo", "男", 234567);
        Dog d1 = new Dog("小哈", "哈士奇", "黑白");
        ArrayList list = new ArrayList();
        Collections.addAll(list, s1, t1, d1);
        // 0.创建流
        PrintWriter pw = new PrintWriter("day14_junit-reflection-proxy-anno\\file\\objectInfo.txt");
        for (Object o : list) {
            // 1.先获取当前对象的class对象
            Class clazz = o.getClass();
            String[] className = clazz.getName().split("\\.");
            pw.println("================" + className[className.length - 1] + "======================");
            // 2.获取所有的字段
            Field[] fields = clazz.getDeclaredFields();
            // 3. 遍历所有的字段,获取对应字段的值出来
            for (Field field : fields) {
                field.setAccessible(true);
                Object value = field.get(o);
                pw.println(field.getName() + "=" + value);
            }
        }
        pw.close();
    }

二、注解【了解】

就是java代码里的特殊标记 例如:@Override @Test

java程序中的类上、构造器上、方法上、成员变量上、参数等都可以用注解进行标记

注解的作用

对Java中类、方法、成员变量做标记,然后进行特殊处理,至于到底做何种处理由业务需求来决定。

例如:JUnit框架中,标记了注解@Test的方法就可以被当成测试方法执行,而没有标记的就不能当成测试方法执行。

自定义注解

public @interface 注解名称 {

   
public 属性类型 属性名() default 默认值 ;

}

public class Demo11 {
   
@MyAnno2(name = "水浒传", price = 9.9)
    public static void main(String[] args) {
    }
}

自定义注解的属性类型

基本数据类型

String

Class

注解

枚举

及以上类型的  一维数组

注解特殊属性value

value属性,如果只有一个value属性的情况下,使用value属性的时候可以省略value名称不写!!

但是如果有多个属性,  且多个属性没有默认值,那么value名称是不能省略的

元注解

修饰注解的注解

元注解有两个:

@Target: 约束自定义注解只能在哪些地方使用,

@Retention:申明注解的生命周期

@Target可使用的值定义在ElementType枚举类中,常用值如下

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

Retention可使用的值定义在RetentionPolicy枚举类中,常用值如下

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

SOURCE(源代码)                          CLASS(字节码)                              RUNTIME(运行时)

xxx.java       ->      javac编译    ->    xxx.class      ->     java运行     ->      程序运行

注解的解析

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

与注解解析相关的接口

Annotation: 注解的顶级接口,注解都Annotation类型的对象

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

 所有的类成分Class, Method , Field , Constructor都实现了AnnotatedElement接口他们都拥有解

析注解的能力

注解解析的步骤

1. 先获取class对象

2. 获取注解所在的成分【方法、字段 、 类..】

3.判断当前成分上是否有指定注解

isAnnotationPresent(注解类型)

4. 获取这个注解

getDeclaredAnnotation(注解类型)

@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface MyAnno {
    String value();
    double price();
    String[] authors();

}
@MyAnno(value = "《母猪的产后护理》", price = 250.0, authors = {"666", "777"})
public class BookStore {

    @MyAnno(value = "《母猪的产后护理2.0》", price = 2500.0, authors = {"666", "777"})
    public void show() {

    }
}
public static void main(String[] args) throws Exception {
    Class<?> book = Class.forName("com.itheima.demo04_注解练习.BookStore");

    if (book.isAnnotationPresent(MyAnno.class)) {
        MyAnno annotation = book.getDeclaredAnnotation(MyAnno.class);
        String value = annotation.value();
        double price = annotation.price();
        String[] authors = annotation.authors();
        System.out.println(value);
        System.out.println(price);
        System.out.println(Arrays.toString(authors));
    }

    Method show = book.getDeclaredMethod("show");
    if (show.isAnnotationPresent(MyAnno.class)) {
        MyAnno method = show.getDeclaredAnnotation(MyAnno.class);
        System.out.println(method.value());
        System.out.println(method.price());
        System.out.println(Arrays.toString(method.authors()));
    }
}

解析注解的技巧

注解在哪个成分上,我们就先拿哪个成分对象。

比如注解作用成员方法,则要获得该成员方法对应的Method对象,再来拿上面的注解

 比如注解作用在类上,则要该类的Class对象,再来拿上面的注解

 比如注解作用在成员变量上,则要获得该成员变量对应的Field对象,再来拿上面的注解

三、动态代理

什么是代理

代理思想就是被代理者没有能力,或者不愿意去完成某件事情,需要找个人(代理)代替自己去完

成这件事。

动态代理的作用

动态代理主要是对被代理对象的行为进行代理对功能进行增强。

动态代理的开发步骤

  1. 必须定义接口,里面定义一些行为,用来约束被代理对象和代理对象都要完成的事情。
  2. 定义一个实现类实现接口,这个实现类的对象代表被代理的对象。
  3. 定义一个测试类,在里面创建被代理对象,然后为其创建一个代理对象返回。(重点)
  4. 代理对象中,需要模拟收首付款,真正触发被代理对象的行为,然后接收尾款操作。
  5. 通过返回的代理对象进行方法的调用,观察动态代理的执行流程。

Java中代理的代表类是:java.lang.reflect.Proxy,它提供了一个静态方法,用于为被代理对象,产

生一个代理对象返回

public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h)

为被代理对象返回一个代理对象。

参数一:类加载器 加载代理类,产生代理对象。,。

参数二:真实业务对象的接口。(被代理的方法交给代理对象)

参数三:代理的核心处理程序。

通过代理对象调用方法,执行流程是什么样的?

先走向代理

代理中可以真正触发被代理对象的方法执行。

回到代理中,由代理负责返回结果给调用者。

public interface kun {
    void sing();
    void dance();
    void rap();

    int getMax(int a, int b);
}
public class kunKun implements kun {
    @Override
    public void sing() {
        System.out.println("giegie在sing");
    }

    @Override
    public void dance() {
        System.out.println("giegie在dance");
    }

    @Override
    public void rap() {
        System.out.println("giegie在rap");
    }

    @Override
    public int getMax(int a, int b) {
        return Math.max(a, b);
    }
}
public class proxyKun {
    public static void main(String[] args) {
        kunKun kunKun = new kunKun(); //被代理对象

        //代理对象 强转
        kun kun = (kun) Proxy.newProxyInstance(
                proxyKun.class.getClassLoader(),  //一般是当前类
                kunKun.getClass().getInterfaces(), //一般是被代理对象
                new InvocationHandler() {
                    @Override
                    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                        /*
                            proxy => 代理对象 --> kun  不能用
                            method => 就是代理对象当前执行的方法
                            args => 执行方法的参数
                         */
                        System.out.println("giegie要表演了,给米");
                        Object result = method.invoke(kunKun, args);
                        System.out.println("giegie辛苦了,给米");
                        return result;
                    }
                });
        kun.sing();
        kun.dance();
        kun.rap();
        int max = kun.getMax(10, 20);
        System.out.println(max);
    }
}

动态代理的优点

可以在不改变方法源码的情况下,实现对方法功能的增强,提高了代码的复用。

简化了编程工作、提高了开发效率,同时提高了软件系统的可扩展性。

可以为被代理对象的所有方法做代理。

非常的灵活,支持任意接口类型的实现类对象做代理。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值