Java注解与反射

Java注解与反射

注解(Annotation)

注解是JDK5.0引入的一个技术。

作用

  • 不是程序本身,可以对程序做出解释(这点功能与注释相同)
  • 可以被其他程序读取(可以作为功能加入程序中)

格式

  • 注解以”@注释名“再代码中存在,还可以添加一些参数值。

    @SuppressWarnings(value=”unchecked“)

内置注解

  1. @Override:重写
  2. @Deprecated:表示不鼓励程序员使用
  3. @SuppressWainings:用来抑制警告。就是在编译器中编码的时候不会爆出警告

自定义注解

  • 使用 @interface 定义类后该类就是注解了。
// TYPE:类; METHOD:方法; FIELD:属性
@Target(value="ElementType.METHOD")
@Retention(value="RetentionPolicy.RUNTIME")
public @interface MyAnnotation{
    // 这是注解的参数(),如果没有默认值就必须赋值,否则注解报错
    String name();
    String sex() default "xxx";
    int age() default 0;
    String[] schools() defaule {"xxx", "xxx"};
}

元注解

作用:用来注解其他注解的注解。用来对其他Annotation类型做说明。

  • @Target:描述了注解的使用范围

    @Target(value="ElementType.METHOD")
    // 或者可以这样@Target([ElementType.METHOD, ElementType.TYPE])表示多个作用域
    // ElementType是枚举类型,其中属性有类、方法等。这里METHOD就是指这个注解只能用在方法上 
    public @interface MyAnnotation{
        
    }
    
  • @Retention:表示什么级别保存该注释信息,用于描述注解的声明信息

    @Retention(value="RetentionPolicy.RUNTIME")
    // RetentionPolicy是枚举类型,RUNTIME指运行时注解有效。
    public @interface MyAnnotation{
        
    }
    
  • @Documentd:表示是否将我们的注解生成在JAVAdoc中

    @Documentd
    // 表示是否将我们的注解生成在JAVAdoc中
    public @interface MyAnnotation{
        
    }
    
  • @Inherited:表示子类是否继承父类注解

    @Inherited
    // 表示子类是否继承父类注解
    public @interface MyAnnotation{
        
    }
    

反射

使java具有动态性的一个技术!使用反射后可以使Java编程准动态语言。

动态语言:

  • 动态语言是一类可以在运行时,改变其结构的语言。就是在运行时代码可以根据某些条件改变自身结构。
  • 主要的动态语言有:PHP、JavaScript、Python

静态语言:

  • 运行时结构不可改变:例如Java、c++、c

反射的定义

反射是Java被视为动态语言的关键,反射机制允许程序在执行期间借助于Reflection API获取任何类的内部信息,并能直接操作任意对象的内部属性及方法!

Class c = Class.forName("java.lang.String");
// Class类是用来管理Reflection API的
// Object类拥有getClass方法用来获取类的结构,所以所有类都能获取类的结构
Object obj = new Object();
Class objClass = obj.getClass();

加载完类后, 在堆内存就产生了一个Class类型的对象(一个类只能有一个Class对象),该对象包含类的完整结构信息。 我们可以通过这个类看到类的结构,这个对象就像以面镜子可以看到类的结构,所以我们叫反射。

Class类

  • Class本身也是一个类
  • Class对象只能由系统建立对象
  • 一个加载的类在JVM中只会有一个Class实例
  • 一个Class对象对应的是一个加载到JVM中的一个.class文件
  • 每个类的实例都会记得自己是由那个Class实例所生成的
  • 通过Class可以完整的得到一个类中的所有被加载的结构
  • Class类是Reflection的根源,针对任何你想动态加载、运行的类,唯有先获得相应的Class对象。

Class类常用方法:

方法名功能说明
static Class ClassforName(String name)返回指定类名name的Class对象
Object newInstance()调用缺省构造方法,返回Class对象的一个实例
getName()返回Class对象多表示的实体的名称
Class getSuperClass()返回当前Class对象的父类Class对象
Class[] getinterfaces()获取当前Class对象的接口
CassLoader getClassLoader()返回该类的类加载器
Constructor[] getConstructors()返回一个包含某些Constructor对象的数组
Method getMethod(String name, Cass…T)返回一个Method对象,此对象的形参类型为paramType
Field[] getDeclaredFields()返回Field对象的一个数组

获取Class对象:

  1. 已知具体类,通过class属性获得

    Class c = Person.class;
    
  2. 已知某个类的实例,通过getClass方法获得

    Person person = new Person();
    Class c = person.getCalss();
    
  3. 已知一个类的全类名,通过Cass.forName获得

    Class c = Class.forName("com.qiu,Person");
    

类加载内存分析

在这里插入图片描述

  • 加载:将class文件字节码加载到内存中,并将这些静态数据转换成方法区的运行是数据结构(Class对象存放在方法区),然后生成一个代表这个类的java.lang.Class对象
  • 链接:将Java类的二进制代码合并到JVM的运行状态中
    • 验证:确保加载的类的信息符合JVM规范
    • 准备:正式为类变量分配内存并设置类变量默认初始值的阶段,这些内存都将在方法区中进行分配
    • 装入:JVM常量池中的符号引用替换为直接地址的过程
  • 初始化:
    • 将所有静态代码块与所有类变量的赋值动作合并为一个类构造器< clinit >(),并执行
    • 当初始化一个类的时候,如果发现其父类还没有初始化,则先触发其父类的初始化
    • 虚拟机会保证一个类的< clinit >()方法在对线程环境中被正确加锁和同步
public class Person{
    static {
        System.out.println("静态代码块")
        m = 300;
    }
    
    static int m = 100;
    
    public Person(){
        System.out.println("无参构造")
    }
}
public class Test{
    public static void main(String[] args){
        Person a = new Person();
        Systen.out.println(Person.m);
    }
}
结果:
静态代码块
无参构造
100

在这里插入图片描述

类加载器

类加载器作用:将class文件字节码内容加载到内存中,并将这些静态数据转换成方法区的运行时数据结构,然后在堆内存中生成一个代表这个类的java.lang.Class对象,作为方法区中类数据的访问入口。

类缓存:标准javaSE类加载器可以按照要求查找相应类,但一旦某个类被加载到类加载器中,它将维持加载一段时间。不过JVM垃圾回收机制可以回收这些Class对象。

类加载器需要加载路径,将指定路径下的包、class文件加载到内存中。加载后程序才能运行。

所以有一个双亲委派机制,保证根加载器加载路径中的包不会被覆盖。例如你要写一个java.lang.String类
那么这个类是不能被类加载器加载的(从系统加载器向父级查找是否有这个包,有则不被加载),不被加载就不会运行。

类加载器分为:

  • 引导类加载器:用C++编写的,是JVM自带的类加载器,负责Java平台核心库,用来装载核心类库。该加载器无法直接获取。
  • 扩展类加载器:负责JRE/lib/ext目录下的jar包或-D java.ext.dirs指定目录下的jar包装入工作库
  • 系统类加载器:负责java -classpath或-D java.class.path所致的目录下的类与jar包装入工作,是最常见的加载器

三者关系为:引导类加载器(根加载器)==----是父类---->扩展类加载器----是父类---->==系统类加载器

ClassLoader systemClassLoader = ClassLoader.getClassLoader();
ClassLoader extClassLoader = systemClassLoader.getParent();
ClassLoader rootClassLoader = extClassLoader.getParent();

获取类对象的类加载器:

ClassLoader loader = Class.forName("com.qiu.pojo.Student").getClassLoader();

获取类的信息

Class c = Class.forName("com.qiu.pojo.Student");
  1. 获取类的名字:

    // 全类名
    String name = c.getName();
    // 类名
    String simpleName = c.getSimpleName();
    
  2. 获取属性:

    // 获取所有public属性
    Field[] fields = c.getFields();
    
    // 获取所有属性
    Field[] fields = c.getDeclaredFields();
    
    // 获取指定public属性
    Field field = c.getField("name");
    
    // 获取指定属性
    Field field = c.getDeclaredField("name");
    
    //-----------------------------------------------------
    
    // 获取方法以及父类及以上方法
    Method[] methods = c.getMethods();
    
    // 获取本类的方法
    Method[] method = c.getDeclaredMethods();
    
    // 获取指定方法("方法名", 参数值类)
    Method getName = c.getMethod("getName", null);
    Method setName = c.getMethod("setName", String.class);
    
    //-----------------------------------------------------
    
    //获取public构造器
    Constructor[] constructors = c.tgetConstructors();
    
    //获取所有构造器
    Constructor[] constructors = c.tgetDeclareConstructors();
    
    //获取指定构造器
    Constructor declaredConstructor = c.getDeclaredConstructor(String.class, int.class, int.class);
    

动态创建对象(通过反射操作对象)

通过类对象创建对象
Class c = Class.forName("com.qiu.pojo.Student");
Student s = (Student)c.newInstance();

这样创建对象调用的是无参构造

  • 类必须有一个无参构造器
  • 类的构造器的访问权限需要足够
通过构造器创建对象
Class c = Class.forName("com.qiu.pojo.Student");
Constructor constructor = c.getDeclaredConstructor(String.class);
Student s = (Student)constructor.newInstance("hahaha")

通过反射获取方法, 再进行调用

直接使用对象的方法不好, 需要通过反射获取方法, 然后调用

Class c = Class.forName("com.qiu.pojo.Student");
Student s = (Student)c.newInstance();
Method setName = c.getMethod("setName", String.class);
// 调用(对象, 参数), 对象表示是哪个对象调用的
setName.invoke(s, "jajaja");
通过反射获取属性
Class c = Class.forName("com.qiu.pojo.Student");
Student s = (Student)c.newInstance();
Field name = c.getDeclaredField("name");

// 取消安全检测   只有对自由属性赋值/获取的时候需要这个,因为私有属性是不可访问的
name.setAccessilble(true);

// 对对象s的私有属性进行赋值
name.set(s, "qiqiqi");
// 获取对象s的name属性
String name_string = (String)name.get(s)

Method, Field, Constructor对象都有一个setAccessible方法用于启动/解除安全检测!

通过反射操作注解
@Target(ElementType.TYPE)
@Retention(RetenrionPolicy.RUNTIME)
@interface ClassAnnotation{
    String value();
}
@ClassAnnotation(value="MyClass")
class Student{
    private String name;
}
Class c = Class.forName(com.qiu.reflection.ClassAnnotation);
// 获取类的所有指定注解
Annotation[] annotation = c.getAnnotation();

// 获取指定注解
ClassAnnotation annotation = (ClassAnnotation)c.getAnnotation(ClassAnnotation.class);
String value = annotation.value();
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值