反射和注解

一.反射

1.概念
反射:指我们的程序可以通过获取Class对象,去"反向"的加载"一个类",并创建一个对象,以及调用它的属性、方法。
作用:解开类和类之间的"耦合关系",利于程序的开发、后期维护!!!
2.类加载器
1.类的加载:

1).当我们的程序第一次使用某个类(创建对象、访问它的静态成员…)(除常量)时,
JVM会到硬盘上读取这个类的class文件。
2).Java对于任何在程序运行过程中使用过的类,都会将它们的类信息存储到"方法区",
都会为其创建一个、而且只创建一个Class对象。
例如:Student类、System类、Scanner类、Random类、ArrayList类…
这就是为什么我们之前学习"静态同步方法",它的锁对象是本类的Class对象。
public synchronized static void show(){
}
3).当JVM加载我们的class文件时,也是用一个"类"去加载,这种类在JVM中被称为:类加载器:

2.Java中有三种类加载器:

​ 1).启动类加载器(Bootstrap ClassLoader):用于加载系统类库<JAVA_HOME>\bin目录下的class,例如:rt.jar。(例如:System类、Random类、ArrayList类)
2).扩展类加载器(Extension ClassLoader):用于加载扩展类库<JAVA_HOME>\lib\ext目录下的class。
3).应用程序类加载器(Application ClassLoader):用于加载我们自定义的加载器(例如:Student类、Phone类…)
类JVM加载一个类,使用"双亲委托模型":
当我们要加载一个类:
例如:Student类–>JVM–>应用程序类加载器–>委托给"扩展类加载器"–>委托给"启动类加载器",发现不能加载 应用程序类加载器 <–“扩展类加载器”<–

3.获取Class的对象三种方式,反射的前提
1).Object类:getClass()【适合获取类库的类的Class2).任何数据类型(包括基本数据类型):数据类型.class【适合获取类库的类的Class3).Class的静态方法forName(String 全类名)Class.forName(String 全类名)【常用-适合获取我们自定义类的Class对象】
注意:上述三种方式,都会先判断是否有Class对象,如果没有,会读取class文件,然后在方法区中创建它的Class对象。
4.反射获取构造方法(,通过Class对象获取),创建对象
1).批量获取:[了解]
    1).public Constructor[] getConstructors():获取所有的"公有构造方法"
            会将每个公有构造方法封装为一个Constructor对象,多个Constructor对象封装到一个数组中返回。
    2).public Constructor[] getDeclaredConstructors():获取所有的构造方法,包括:公有、受保护、默认、私有。
2).获取单个:[掌握]
    3).public Constructor getConstructor(Class ... parameterTypes):获取某个"公有构造方法"
    4).public Constructor getDeclaredConstructor(Class ... parameterTypes):获取任何的构造方法。

    调用这个类的构造方法:
    Constructor(代表一个构造方法)-->newInstance(实参)
    如果调用此构造方法,没有"权限(访问权限修饰符的限制)",要先设置:暴力访问
    如果有访问权限,也可以设置"暴力访问"
    Constructor-->setAccessible(true)
    tip: 
        如果类的无参构造是公有的,可以直接使用  class对象.newInstance
5.反射获取属性(,通过Class对象获取)
1).批量获取:[了解]
    1).public Field[] getFields():获取所有的"公有成员属性"
            会将每个公有成员属性封装为一个Field对象,多个Field对象封装到一个数组中返回。
    2).public Field[] getDeclaredFields():获取所有的成员属性,包括:公有、受保护、默认、私有。

2).获取单个:[掌握]
    3).public Field getField(String fieldName):通过"属性名",获取某个"公有成员属性"
    4).public Field getDeclaredField(String fieldName):通过属性名,获取任何访问权限的成员属性。

    注意:
    1).要为属性赋值,必须先创建此类对象;
    2).赋值:Field-->set(Object targetObj,Object value):
            如果没有访问权限,设置暴力访问
            Field-->setAccessible(true)

    3).获取值:Field-->get(Object targetObj)
6.反射获取属性(,通过Class对象获取)
1).批量获取:[了解]
    1).public Method[] getMethods():获取所有的"公有成员方法"
            会将每个公有成员方法封装为一个Method对象,多个Method对象封装到一个数组中返回。
    2).public Method[] getDeclaredMethods():获取所有的成员方法,包括:公有、受保护、默认、私有。


2).获取单个:[掌握]
    3).public Method getMethod(String methodName,Class... params):通过"方法名,参数的Class",获取某个"公有成员方法"
    4).public Method getDeclaredMethod(String methodName,Class ... params):通过方法名、参数Class,获取任何访问权限的成员方法。

    调用方法:Method类的-->invoke(Object targetObj,Object ... params)
    如果没有访问权限,需要设置暴力访问:
            Method类的-->setAccessible(true)
7.反射可以跳过集合的泛型检测.泛型擦除机制
//泛型在编译的时候都会确定为Object类
public class Practice {
    public static void main(String[] args) throws Exception {
        List<String> list = new ArrayList<>();
        list.add("冰冰");
        list.add("秋雅");
        list.add("冬梅");
        Class<? extends List> clazz = list.getClass();
        Method addMethod = clazz.getMethod("add", Object.class);
        //这里通过反射调用方法,添加了Student类型
        addMethod.invoke(list,new Student("赵敏",23));
        System.out.println(list);
    }
}

二.Annotation 注解

1.使用方法
从JDK1.5开始,Java提出了"注解",
    例如:@Override(重写方法时)  //Class 生命周期 编译期
         @FunctionalInterface(定义函数式接口) // 生命周期 Class
         @Test(JUnit工具)   //生命周期 Runtime
注解的作用:写在"源码"中,告诉"注解解析器",下面的代码怎样编译和运行。

注解的应用包含:注解 + 注解解析器(程序)

A).注解:
    1).我们可以自己定义注解
        public @interface MyTest {
        }
    2).使用注解:
        @MyTest
        public class Student {
            @MyTest
            private String name;

            @MyTest
            public void show(){
                System.out.println("呵呵");
            }
        }
        注意:此时该注解可以使用,但没有作用

    3).元注解:Java类库中定义好的注解,用在"注解的定义"上,用于对注解进行"约束"的(即用于定义注解的注解):
        1).@Target:约束新定义的注解的"使用位置"

            例如:@Target(ElementType.METHOD)可以约束新注解只能用在:方法上

            ElementType的常用值:
                  1).TYPE:类,接口
                  2).FIELD:成员变量
                  3).METHOD, 成员方法
                  4).PARAMETER, 方法参数
                  5).CONSTRUCTOR, 构造方法
                  6).LOCAL_VARIABLE, 局部变量

        2).@Retention:约束新定义的注解的"生命周期"
            例如:@Retention(RetentionPolicy.SOURCE)可以约束注解的生命周期:源码

            RetentionPolicy的取值:
                  1).SOURCE:在源码中。不会编译到class文件中。作用:给编译器中的"注解解析器"看的。例如:@Override
                  2).CLASS:在源码中、class文件。但运行时,不会被加载到运行时内存。
                  3).RUNTIME:在源码中、class文件中,运行时内存。例如:@Test
B).注解解析器
        使用反射方式,创建使用了注解的类的对象
        反射后,获取类的组成部分,
        调用isAnnotationPresent方法,判断是否使用了注定注解.class


C).注解的属性:刚才使用元注解,都是带属性:
            @Target(ElementType.METHOD)
            其中的ElementType.METHOD就是属性

   属性的作用:可以”更加细致"的对新定义的注解进行"约束"
   注解中定义属性的格式:
    public @interface MyTest {
        //属性的格式:数据类型名  属性名() [default 值];
        int index() default 0;  //属性定义后,使用时必须赋值,否则报错
        //定义默认值后,在使用注解的类中可以不赋值使用默认值,或者显示的重新赋值
    }

   属性的获取:
        可以反射获取加入了注解的部分,并调用getAnnotation(注解类.class)方法获取注解对象
        注解对象.属性(),即可获得注解属性值
   注意:
   1).其中的数据类型,只能是:八种数据数据类型、StringClass、注解类型、枚举类型、上述几种类型的"数组"类型。【背下来】
   2).如果一个注解中:有一个属性名叫:value,而且其他属性都有默认值,而且在使用这个注解时,只需要设置value属性,可以省略:"value = ",直接写值。
2.自定义注解
需求:
    做一个"注解解析器",模拟使用JUnit的@Test注解时:右键-->Run "xxxx.show()"功能菜单。
    1.自定义一个注解,并约束为定义在方法上,且生命周期为运行时
    2.自定义类,Student,在某个方法上加入自定义注解
    3.定义注解解析器,解析带注解的方法,并执行
    4.1获取使用了注解的类的所有方法,使用方法名称进行对方法的排序
    4.2在注解中定义int类型的index属性,使用反射的方式获取使用了注解的类的所有方法,使用注解的index属性对方法进行排序

注解

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface MyAnno {
    int index();
}

使用了注解的类

public class Student {
    private String name;
    private int age;

    public Student() {
    }

    public Student(String name, int age) {
        this.name = name;
        this.age = age;
    }

    public String getName() {
        return name;
    }

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

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }
    @MyAnno(index = 3)
    public void eat(){
        System.out.println("eat");
    }
    @MyAnno(index = 19)
    public void sleep(){
        System.out.println("sleep");
    }
    @MyAnno(index = 9)
    public void love(){
        System.out.println("love");
    }
    public void fen(){
        System.out.println("分");
    }
}

注解解析类

public class Demo {
    public static void main(String[] args) throws Exception {
        List<Method> list = new ArrayList<>();
        Class<Student> clazz = Student.class;
        Method[] methods = clazz.getDeclaredMethods();
        for (Method method : methods) {
            if(method.isAnnotationPresent(MyAnno.class)){
                method.invoke(stu);
                list.add(method);
            }
        }
        //4.1获取使用了注解的类的所有方法,使用方法名称进行对方法的排序
        Collections.sort(list, new Comparator<Method>() {
            @Override
            public int compare(Method o1, Method o2) {
                return o1.getName().compareTo(o2.getName());
            }
        });
        System.out.println("-------方法名称进行对方法的排序----------");
        for (Method method : list) {
            System.out.println(method);
        }
        //4.2在注解中定义int类型的index属性,使用反射的方式获取使用了注解的类的所有方法,使用注解的index属性对方法进行排序
        System.out.println("-------根据index的排序----------");
        Collections.sort(list, new Comparator<Method>() {
            @Override
            public int compare(Method o1, Method o2) {
                return o1.getAnnotation(MyAnno.class).index()-o2.getAnnotation(MyAnno.class).index();
            }
        });
        for (Method method : list) {
            System.out.println(method+" index="+method.getAnnotation(MyAnno.class).index());
        }
    }
}

三.代理模式

1.什么是"设计模式":

​ 模式:解决某种问题的一些固定的方式、流程。例如:盈利模式、工作模式、学习模式…
Java的设计模式:指利用Java的一些语法特征(继承、封装、多态…)针对某些问题,提出的一种固定的设计方式。

2.Java中已定义的设计模式有很多种(20多种,扩展的有40-50多种),有专门的书介绍Java的设计模式。
3.代理模式:

解决的问题:在不更改原类代码的前提下,为原类的方法提供"功能增强"

​ “代理模式"就类似于现实中的"代理”。

代理增强,装饰者模式

4.java中动代理API
动态代理:指程序可以在运行时"动态的"为某个"被代理类"产生一个"动态代理对象",并可以进行方法增强。
注意:JDK提供的动态代理是基于"接口"的——要求所有"被代理的类"必须实现同一个接口,从而具有相同的功能

动态代理类:Proxy
    产生代理方法 public static Object newProxyInstance(ClassLoader loader,
                                                        Class<?>[] interfaces,
                                                        InvocationHandler h)
    参数:loader  类加载器,代理类的"类加载器",与"被代理类"的相同
    参数:interfaces  代理类的"父接口",与"被代理类"相同
    参数:h  执行代理控制对象

    InvocationHandler接口内方法:
            public Object invoke(Object proxy, Method method, Object[] args)
           方法的作用:当代理调用任何方法,都会被该拦截,执行此方法
                             可以根据逻辑判断是否执行被代理者的原方法,
                             并且可以增强方法,即在原方法执行前后,执行其他逻辑。
            参数说明:
                 proxy;产生的代理对象[不使用]
                 method:被代理执行的方法对象
                 args:被代理执行的方法的参数
                 return 代理对象	
5.代理模式案例

需求

定义接口IStar#sing#dance
定义类Cxk,实现IStar接口
    sing方法中打印真唱,很难听的唱
    dance方法中打印,花式篮球玩法,篮球玩的好
Demo中:
    创建Cxk对象
    创建Cxk对象的代理对象
        代理执行逻辑:
            如果是sing方法,就代唱,唱出激昂高亢的真男人歌声
            如果是dance方法,就让被代理者自己跳即可
    使用代理对象执行sing方法与dance方法,完成代理调用。

被代理类

public class Cxk implements DaiLi{
    public void sing(){
        System.out.println("真唱,很难听的唱");
    }
    public void dance(){
        System.out.println("花式篮球玩法,篮球玩的好");
    }

    @Override
    public int yanDianYing(String name) {
        System.out.println("经典再现 "+name);
        return 9;
    }
}

被代理类实现接口

public interface IStar {
    void sing();
    void dance();
}

代理测试类

public class Demo {
    public static void main(String[] args) {
       Cxk cxk = new Cxk();
       //Proxy.newProxyInstance
        //第一个参数  类加载器
        ClassLoader classLoader = Cxk.class.getClassLoader();
        //第二个参数   实现的所有接口数组
        Class<?>[] interfaces = Cxk.class.getInterfaces();
        IStar proxy = (IStar) Proxy.newProxyInstance(classLoader, interfaces, new InvocationHandler() {
            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                if("sing".equals(method.getName())){
                    System.out.println("代唱,唱的真不戳");
                }
                if("dance".equals(method.getName())){
                    method.invoke("唱,跳,rap"+cxk);
                }
                return null;
            }
        });
        proxy.sing();
        proxy.dance();
    }
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
Java反射(Reflection)是指在运行时动态地获取类的信息,包括类的属性、方法、构造器等,并可以使用这些信息来创建对象、调用方法、获取或修改属性值等。Java反射Java语言的一个特性,它使得我们可以在运行时通过获取类的信息来操作类。 Java注解(Annotation)是在Java程序中的代码中加入的一些元数据,用于对程序代码进行说明、标记和补充。Java注解可以用于对类、方法、参数、字段等进行标记和描述,使得程序开发和维护变得更加方便和简单。 Java反射注解的原理分别如下: Java反射的原理:Java反射基于Java虚拟机在运行时对类的动态加载、链接、初始化等机制。在运行时,Java虚拟机可以通过类的全限定名获取类的字节码文件,并通过类加载器将字节码文件加载到内存中。加载完成后,Java虚拟机会根据字节码文件创建一个Class对象,该对象包含了类的所有信息,包括类的属性、方法、构造器等。通过Class对象Java反射可以获取类的信息,并使用这些信息来进行对象的创建、方法的调用、属性获取或修改等操作。 Java注解的原理:Java注解是通过在代码中添加注解来标记和描述程序代码的信息。注解本身并不会对程序的运行产生任何影响,但可以通过反射机制获取注解信息,从而实现对程序的控制和调用。在编译时,Java编译器会将注解信息保存到类文件中,通过反射机制可以在运行时获取这些注解信息,从而实现对程序的控制和操作。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值