注解与反射

注解与反射—总结

什么是注解?

  • 不是程序本身,可以对程序作出解释
  • 可以被其他程序读取,例如编译器
  • 注解是以“@注解名”在代码中存在,还可以添加参数值,例如@SuppressWarning(value=”unchecked” )
  • 可以在package、class、method、field等使用,添加额外信息
  • 反射机制编程实现对这些元数据的访问

内置注解

  • @Override 重写: 定义在java.lang.Override中,该注解用于修辞方法,表示一个方法声明重写超类中的另一个方法声明

  • @Deprecated 废弃: 定义在java.lang.Deprecated中,可以用于修饰类、方法、属性、表示不支持程序员使用这种元素,因为有更好的选择。

  • @SupressWarning(“all”)
    定义在java.lang.SuppreWarning中,用来抑制编译时所产生的警告信息。该注解需要添加参数
    • SupressWarning(“unchekced”)
    • SupressWarning({“unchecked”, “deprecation”})

元注解

负责注解其它注解

  1. @Target() 参数: {ElementType.TYPE, ElementType.METHOD} , 用于描述注解的使用范围,例如:类、方法
  2. @Retention() 参数: RetentionPolicy: SOURCE|CLASS|RUNTIME, 表示需要在什么级别保存该注释信息,用于描述注解的生命周期,一般在Runtime中
  3. @Document() 说明该注解将被包含在javadoc中
  4. @Inherited() 说明子类可以继承父类中的该注解

自定义注解

格式: public @interface A{...}

@Retention(RetentionPolicy.RUNTIME) // 元注解
@Target({ElementType.TYPE, ElementType.METHOD}) // 元注解
public @interface A {
    // 每一个方法实际上就是配置了一个参数
    String name() default "";  // name 参数名, String 参数类型 default '' 默认为空
    int age() default 0;
}
public @interface A {
    // 只有一个参数,一般用value命名,传参的时候,不需要写 value = xxx, 直接将xxx传放入
   String value(); 
}

反射概述

反射实际上就是java视为动态语言的关键,借助反射,我们可以在代码执行期间,通过Class类操纵任何类的内部信息,比如内部属性或方法,甚至可以去操作私有属性

但是对性能有影响!

Class类 的概念下面讲!

Java反射机制提供的功能:

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

Class 类

注意Class 的首字母大写,代表一种类!(小写class 有其它作用)

首先呢,本身也是一个对象!由系统创建,在加载器将你写的代码定义的类加载到堆内存中的时候,就创建了这个对象。而它呢,是用来描述你写的代码中类的结构的 (比如:属性,构造器,方法,接口,枚举,注解….),你写的类不论创建(new)多少对象,这些对象都只拥有一个Class 对象!而我们呢,就可以通过对象的反射指向这个Class对象,得到这个Class对象,从而在执行期间去改变或者去获取关于你写的这个类的信息

  • Class类对应一个 JVM中的一个Class实例
  • 一个Class对象对应的是一个加载到JVM中的一个.class文件

获取Class类的一些方法

public class A {...}
A a = new A()
  • Class c1 = A.class;
  • Class c2 = a.getClass();
  • Classs c3 = Class.forName(“xxx.xx.A”);
  • 基础类型的 Class c4 = Integer.TYPE;
  • 通过ClassLoader获得
//方法1 _通过对象获得
Class c1 = person.getClass();
//方法2 _通过forname
Class c2 = Class.forname("com.kuang.reflection.Student");
//方法3 _通过类名.class
Class c3 = Student.class;
//通过父类也能获得
Class c4 = c1.getSuperclass();

哪些类型可以拥有Class对象?

在这里插入图片描述

Java 内存分析

主要分为三个:

  1. 栈内存 : 存放基本变量类型 ,以及对象的指针
  2. 堆内存: 主要存放new的对象和数组,可以被所有线程共享
  3. 方法区: 包含了所有class和static变量的数据结构,可以被所有进程共享

在这里插入图片描述

关于一个类的加载过程

主要分三步:

  1. 加载阶段
    • 这个阶段主要由加载器来工作,也就是ClassLoader, 加载器的概念等下再说
    • 完成的工作呢就是:
      • 1、将class文件的字节码存到内存当中去,变成方法区的运行时数据结构,如上图方法区的样子
      • 2、在堆内存中生成java.lang.Class对象
  2. 链接阶段
    • 完成的工作呢就是:
      • 1、为类变量(static) 分配内存以及赋予上默认初始值!比如 static int a = 0; static String b = “”;这样子。
      • 2、为类的常量值进行赋值 先这么理解吧,也就是说,在这个阶段,常量值是可以使用的!
  3. 初始化阶段
    • 这个阶段调用类构造器,完成的工作,好比上图
      • 1、将类变量进行真正的赋值,也就是你写的代码这个变量到底等于多少!
      • 2、将静态代码块中的语句进行合并!
      • 3、初始一个类的时候,父类没初始化,则先触发父类的初始化

首先来说加载器的概念

类加载器的作用:也就是在上面加载阶段所完成的事

类缓存:一旦类被加载到内存中,则会存在一段时间,然后再被回收!

加载器分为三种:

  1. 系统加载器 : 你写的用户自定义类的加载器,也是我们用的最多的加载器,父加载器是扩展加载器,可以获得引用

    • //    获取系统类加载器
      ClassLoader systemClassLoader = ClassLoader.getSystemClassLoader();
      
  2. 扩展加载器: 存放在 jre/lib/ext目录下jar包里面类的加载器,父加载器就是根加载器,可以获得引用

    • // 获取系统类加载器的父类加载器>>扩展加载器 
      ClassLoader sysParent = systemClassLoader.getParent();
      
  3. 根加载器 : Java的核心类,是通过这个加载器获得,我们并能获取它的引用, 为null

    • //获取扩展类加载器的父类加载器>>根加载器  但是获取的值为null 
      ClassLoader extParent = sysParent.getParent();
      

再来说下初始化阶段的发生阶段

  1. 类的主动引用(一定会发生类的初始化)
    • 虚拟机启动,执行main方法所在的类 ,这个类会到初始化阶段
    • new A() 也会发生初始化
    • 调用类的静态方法和静态属性(除了final 常量)
    • 使用java.lang.reflect包的方法,也就是获取Class的方法,也会将该类进行初始化
    • 初始化子类的时候,父类如果没有初始化,则会先初始化父类
  2. 类的被动引用 (不会发生类的初始化)
    • 子类引用父类的静态变量,子类不会初始化
    • 数组定义分配空间,也不会引起该类的初始化 Son[] son1 = new Son[5]
    • 引用常量也不会触发此类的初始化,因为再类加载的过程的第二阶段链接阶段,常量就已经可以用了

如何在运行时获得类的完整结构

实际上我们就是通过反射,也就是拿到Class类,然后执行Class类的方法,就可以获得运行时你自己写的类的完整的数据结构了,包括:接口、所继承的父类、全部构造器、全部方法、全部属性、注解….

Class类的一些方法:

在这里插入图片描述

package com.ClassLoader.Demo;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
/**
 */
public class Demo2 {
    public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException, NoSuchMethodException {
//        Class c1=Class.forName("com.ClassLoader.Demo.User");
        User user=new User();
        Class c1=user.getClass();
        System.out.println(c1.getSimpleName());
        System.out.println("*************************");
//        获取类的名字 +详细路径
        System.out.println(c1.getName());
//        得到类的名字
        System.out.println(c1.getSimpleName());
//        得到该类的属性
        Field[] fields=c1.getFields();      //能够找到public属性
        fields=c1.getDeclaredFields();      //能够找到全部属性
        for (Field field:fields) {
            System.out.println(field);
        }
        Field name=c1.getDeclaredField("name");
        System.out.println(name);
        System.out.println("*************************");
//        得到类的方法
        Method[] methods=c1.getDeclaredMethods();
        for (Method method : methods) {     //获得奔雷及其父类的所有方法
            System.out.println("getDeclaredMethods:"+method);
        }
        System.out.println("*************************");
        Method[] methods1=c1.getMethods();
        for (Method method : methods1) {        //获得本类的的所有方法
            System.out.println("getMethods:"+method);
        }
        System.out.println("*************************");
//        获得指定的方法
//        重载
        Method getName=c1.getMethod("getName",null);
        Method setName=c1.getMethod("setName", String.class);
        System.out.println(getName);
        System.out.println(setName);
        System.out.println("*************************");
//        获得指定实例对象的构造器
        Constructor[] constructors=c1.getConstructors();//获得public方法
        for (Constructor constructor : constructors) {
            System.out.println(constructor);
        }
        Constructor[] constructors1=c1.getDeclaredConstructors();//获得全部方法
        for (Constructor constructor : constructors1) {
            System.out.println(constructor);
        }
        System.out.println("*************************");
        //获得指定构造器
        Constructor declareConstructor=c1.getDeclaredConstructor(String.class,int.class,double.class);
        System.out.println(declareConstructor);
    }
}

除了获得类的完整结构,得到了Class对象,我们还能干什么?

  1. 创建类的对象,也就是实例,平常我们通过new A()的方式创建的实例,我们现在可以通过反射创建

    1. //  利用 Class类的newInstance()方法,调用了无参构造器创建实例 
       Class aClass=Class.forName("com.ClassLoader.Demo.User");
       User user1=(User) aClass.newInstance();
      
    2. // 非只能用无参构造器创建对象,用有参构造器也可以
      // 先通过Class类的getDeclaredConstructor(对应的参数)方法获得有参构造器
      // 然后调用有参构造器的newInstance()方法,就可以创建实例了
       Constructor constructor=aClass.getDeclaredConstructor(String.class,int.class,double.class);
       User user2=(User) constructor.newInstance("xxx",xx,xx);//参数数量要一致否则出现异常
      
  2. 通过反射,去操作实例的方法和属性

    1. //        通过反射调用普通方法
              User user= (User) aClass.newInstance();
      //        通过反射获取一个方法
              Method setName=aClass.getDeclaredMethod("setName", String.class);
      //        inoke激活,将实例作为第一个参数传入,修改的数据作为第二个参数
              setName.invoke(user,"xxx");
              System.out.println(user.getName());
      
    2. //        通过反射操作属性,不能直接操作私有属性,可以通过关闭程序的安全检测关掉
              User user4=(User) aClass.newInstance();
              Field name=aClass.getDeclaredField("name");
              name.setAccessible(true);       //可通过的意思。private修饰的变量有安全保护机制,
              name.set(user4,"xxx");
              System.out.println(user4.getName());
          }
      

通过反射操作泛型 就省略了!

重点: 通过反射操作注解

package com.AnnotationReflect.Demo;
import java.lang.annotation.*;
import java.lang.reflect.Field;
/**
 * @Description TODO
 * @Author Y
 */
public class Demo1 {
public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException {
    Class c1=Class.forName("com.AnnotationReflect.Demo.Student1");
//    通过反射获得注解
    Annotation[] annotations=c1.getAnnotations();
    for (Annotation annotation : annotations) {
        System.out.println(annotation);
    }
//    获得类的注解value参数的值
    TableY tableY=(TableY)c1.getAnnotation(TableY.class);
    String value=tableY.value();
    System.out.println(value);
//    获得类具体指定属性的注解
    Field field=c1.getDeclaredField("name");
    FiledStudent annotation=field.getAnnotation(FiledStudent.class);
    System.out.println(annotation.columName());
    System.out.println(annotation.type());
    System.out.println(annotation.length());
}
}
@TableY("studentinfo")
class Student1{
    @FiledStudent(columName = "Sno",type="int",length = 10)
    private int id;
    @FiledStudent(columName = "Sname",type="int",length = 10)
    private String  name;
    @FiledStudent(columName = "Sex",type="int",length = 10)
    private String sex;
    public Student1(int id, String name, String sex) {
        this.id = id;
        this.name = name;
        this.sex = sex;
    }
    public int getId() {
        return id;
    }
    public void setId(int id) {
        this.id = id;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public String getSex() {
        return sex;
    }
    public void setSex(String sex) {
        this.sex = sex;
    }
}
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@interface FiledStudent{
    String columName();
    String type();
    int length();
}
//类名的注解
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@interface TableY{
    String value();
}
turn sex;
    }
    public void setSex(String sex) {
        this.sex = sex;
    }
}
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@interface FiledStudent{
    String columName();
    String type();
    int length();
}
//类名的注解
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@interface TableY{
    String value();
}

ISWHL口袋

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

三横同学

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值