JavaSE----注解与反射

1.注解

1.常见内置注解

1.@Override:子类重写父类的方法,在子类的方法上面添加
2.@Deprecated:已过时的方法,类,属性上使用,表示不推荐程序员继续使用
3.@SupressWarning:用来抑制编译时的警告信息,即不让警告信息显示出来,与前两个注释有所不同,你需要添加一个参数才能正确使用,这些参数都是已经定义好了的,我们选择性的使用就好了:

  • @SuppressWarningsl’“ll”)
  • @SuppressWarnings(“unchecked”)
  • @SuppressWarnings(value={“unchecked”,“deprecation”})…

2.常用的元注解

元注解的作用就是负责注解其他注解,即使用在其他注解上的注解, Java定义了4个标准的meta-annotation类型,他们被用来提供对其他annotation类型作说明.这些类型和它们所支持的类在java.lang.annotation包中可以找到(@Target 、@Retention、@Documented 、@Inherited)。
➢@Target :用于描述注解的使用范围(即:被描述的注解可以用在什么地方),可以设置如下作用域:

public enum ElementType {
    /** Class, interface (including annotation type), or enum declaration */
    TYPE,
    /** Field declaration (includes enum constants) */
    FIELD,
    /** Method declaration */
    METHOD,
    /** Formal parameter declaration */
    PARAMETER,
    /** Constructor declaration */
    CONSTRUCTOR,
    /** Local variable declaration */
    LOCAL_VARIABLE,
    /** Annotation type declaration */
    ANNOTATION_TYPE,
    /** Package declaration */
    PACKAGE,
    /**
     * Type parameter declaration
     * @since 1.8
     */
    TYPE_PARAMETER,
    /**
     * Use of a type
     * @since 1.8
     */
    TYPE_USE
}

测试:

//测试元注解
public class Test {
    @MyAnnotation
    public void display(){
        System.out.println("@MyAnnotation只能作用在方法上");
    }
    @MyAnnotation
    private int a;//报错了,因为设置了只能用在方法上
}
//定义一个注解,设置为只能作用在方法上
@Target(ElementType.METHOD)
@interface MyAnnotation{}

➢@Retention :表示需要在什么级别保存该注释信息,用于描述注解的生命周期 (SOURCE < CLASS < RUNTIME)

public enum RetentionPolicy {
    /**
     * Annotations are to be discarded by the compiler.
     */
    SOURCE,

    /**
     * Annotations are to be recorded in the class file by the compiler
     * but need not be retained by the VM at run time.  This is the default
     * behavior.
     */
    CLASS,

    /**
     * Annotations are to be recorded in the class file by the compiler and
     * retained by the VM at run time, so they may be read reflectively.
     * @see java.lang.reflect.AnnotatedElement
     */
    RUNTIME
}

➢@Document:说明该注解将被包含在javadoc中
➢@Inherited: 说明子类可以继承父类中的该注解

3.自定义注解

➢使用@interface自定义注解时,自动继承了java.lang.annotation.Annotation接口
➢分析:

  • @interface用来声明一个注解,格式: public @interface 注解名 { 定义内容 }
  • 其中的每一个方法实际上是声明了一个配置参数.
  • 方法的名称就是参数的名称.
  • 返回值类型就是参数的类型(返回值只能是基本类型,Class , String , enum ).
  • 可以通过default来声明参数的默认值
  • 如果只有一个参数成员, 一般参数名为value
  • 注解元素必须要有值, 我们定义注解元素时,经常使用空字符串,0作为默认值.
public class Test02 {
    
    @MyAnnotation2(name = "dongli",age = 10)
    public void display(){}
    
    @MyAnnotation3("dongli")
    public void display(int a){}
}

@Target({ElementType.TYPE,ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@interface MyAnnotation2{
    /**
     * 定义参数: 参数类型+属性名+() [+default+值],注意这不是方法
     * 如果没有设置默认值,在使用时就必须设置值
     */
    String name() default "";//参数名为name,默认值为""
    int age();
    int id() default -1;
    String[] course() default {"",""};
}

@Target({ElementType.TYPE,ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@interface MyAnnotation3{
    String value();//如果只有一个属性,且名为value,使用时可以直接复制,而不用写”value=***“
}

2.反射

动态语言
➢是一类在运行时可以改变其结构的语言:例如新的函数、对象、甚至代码可以被引进,已有的函数可以被删除或是其他结构上的变化。通俗点说就是在运行时代码可以根据某些条件改变自身结构。
➢主要动态语言: Object-C、 C#、JavaScript、 PHP、 Python等。
静态语言
➢与动态语言相对应的,运行时结构不可变的语言就是静态语言。如Java、 C、C++.
➢Java不是动态语言,但Java可以称之为“准动态语言”。即Java有一定的动态性,我们可以利用反射机制获得类似动态语言的特性。Java的动态性让编程的时候更加灵活!
Reflection (反射)
Reflection (反射)是Java被视为动态语言的关键,反射机制允许程序在执行期借助于Reflection API取得任何类的内部信息,并能直接操作任意对象的内部属性及方法。

Class C = Class forName("java.lang.String")

➢加载完类之后,在堆内存的方法区中就产生了一个Class类型的对象(一个类只有一个Class对象),这个对象就包含了完整的类的结构信息。我们可以通过这个对象看到类的结构。这个对象就像一面镜子,透过这个镜子看到类的结构,所以,我们形象的称之为:反射
正常方式: 引入需要的”包类”名称→ 通过new实例化→ 取得实例化对象
反射方式: 实例化对象 → getClass()方法→ 得到完整的“包类”名称
Class类
所有的对象都继承自Object类,Object类有一个getClass()方法,对象通过getClass()方法可以得到的信息:某个类的属性、方法和构造器、某个类到底实现了哪些接口。对于每个类而言,JRE都为其保留一个不变的Class类型的对象。一个Class对象包含了特定某个结构(class/interface/enum/annotation/primitive type/void/[])的有关信息。
➢Class本身也是一个类
➢Class 对象只能由系统建立对象
➢一个加载的类在JVM中只会有一个Class实例
➢一个Class对象对应的是- - 个加载到JVM中的一个.class文件
➢每个类的实例都会记得自己是由哪个Class实例所生成
➢通过Class可以完整地得到- -个类中的所有被加载的结构
➢Class类 是Reflection的根源,针对任何你想动态加载、运行的类,唯有先获得相应的Class对象
在这里插入图片描述

1.Class类的创建方式

a)若已知具体的类,通过类的class属性获取,该方法最为安全可靠,程序性能最高。

Class clazz = Person.class;

b) 已知某个类的实例,调用该实例的getClass()方法获取Class对象

Class clazz = person.getClass();

c) 已知一个类的全类名,且该类在类路径下,可通过Class类的静态方法forName()获取,可能抛出ClassNotFoundException

Class clazz = Class.forName("demo01.Student");

d)内置基本数据类型可以直接用类名 .Type

Class C4 = Integer.TYPE;

e)还可以利用ClassL oader
示例如下:

package edu.ncu.dong.reflection;
/**
 * 创建Class对象的方式
 */
public class Test {
    public static void main(String[] args) throws NoSuchMethodException, ClassNotFoundException {
        Person person=new Student();
        System.out.println(person.name);
        //方式一
        Class<?> c1=person.getClass();
        System.out.println(c1.hashCode());
        //方式二
        Class<?> c2=Class.forName("edu.ncu.dong.reflection.Student");
        System.out.println(c2.hashCode());
        //方式三
        Class<?> c3=Student.class;
        System.out.println(c3.hashCode());
        //方式四:基本内置类型的包装类都有一个Type属性
		//Class C4 = Integer.TYPE;
		//System.out.println(c4);
    }
}

class Person{
    public String name;
    public Person() {
        this.name = "person";
    }
    public Person(String name) {
        this.name = name;
    }
}
class Student extends Person{
    public Student() {
        super("student");
    }
    public Student(String name) {
        super(name);
    }
}

运行结果为:可以看出方式不同,但是创建的Class对象是同一个
在这里插入图片描述
哪些类型可以拥有Class对象?
➢class: 外部类,成员(成员内部类,静态内部类),局部内部类,匿名内部类。
➢interface: 接口
➢[]:数组
➢enum:枚举
➢annotation: 注解@interface
➢primitive type:基本数据类型
➢void

public class Test2 {
    public static void main(String[] args) {
        Class C1 = Object.class; //类
        Class c2 = Comparable.class; //接口
        Class c3 = String[] .class; //- 维数组
        Class c4 = int[][] .class; //二维数组.
        Class c5 = Override.class; //注解
        Class c6 = ElementType.class; //枚举
        Class c7 = Integer .class; //基本数据类型
        Class c8 = void.class; //void
        Class c9 = Class.class; //class
    }
}

2.了解类加载的流程

加载:将class文件字节码内容加载到内存中,并将这些静态数据转换成方法区的运行时数据结构,然后生成一个代表这个类的
java.lang.Class对象.
链接:将Java类的二进制代码合并到JVM的运行状态之中的过程。
➢验证: 确保加载的类信息符合JVM规范,没有安全方面的问题
➢准备: 正式为类变量(static) 分配内存并设置类变量默认初始值的阶段,这些内存都将在方法区中进行分配。
➢解析: 虚拟机常量池内的符号引用(常量名)替换为直接引用(地址)的过程。
初始化:
➢执行类构造器 ()方法的过程。类构造器< clinit> ()方法是由编译期自动收集类中所有类变量的赋值动作和静态代码块中的语句合并
产生的。(类构造器 是构造类信息的,不是构造该类对象的构造器)。
➢当初始化一个类的时候,如果发现其父类还没有进行初始化,则需要先触发其父类的初始化。
➢虚拟机会保证一个类的< clinit> ()方法在多线程环境中被正确加锁和同步。

3.了解类的初始化的时机

➢类的主动引用(一定会发生类的初始化)
➢当虚拟机启动,先初始化main方法所在的类
➢new一个类的对象
➢调用类的静态成员(除了final常量)和静态方法
➢使用java.lang.reflect包的方 法对类进行反射调用
➢当初始化一个类,如果其父类没有被初始化,则先会初始化它的父类
➢类的被动引用(不会发生类的初始化)
➢当访问一个静态域时,只有真正声明这个域的类才会被初始化。如:当通过子类引用父类的静态变量,不会导致子类初始化
➢通过数组定义类引用,不会触发此类的初始化
➢引用常量不会触发此类的初始化(常量在链接阶段就存入调用类的常量池中了)

4.类加载器

➢类加载的作用:将class文件字节码内容加载到内存中,并将这些静态数据转换成方法区的运行时数据结构,然后在堆中生成一个代表这个类的java.lang.Class对象, 作为方法区中类数据的访问入口。
➢类缓存:标准的JavaSE类加载器可以按要求查找类,但一旦某个类被加载到类加载器中,它将维持加载(缓存)一段时间。不过JVM垃圾回收机制可以回收这些Class对象
在这里插入图片描述
在这里插入图片描述

5.类加载过程的双亲委派机制

当一个XXX.class这样的文件要被加载时。不考虑我们自定义类加载器,首先会在AppClassLoader中检查是否加载过,如果有那就无需再加载了。如果没有,那么会拿到父加载器,然后调用父加载器的loadClass方法。父类中同理也会先检查自己是否已经加载过,如果没有再往上。注意这个类似递归的过程,直到到达Bootstrap classLoader之前,都是在检查是否加载过,并不会选择自己去加载。直到BootstrapClassLoader,已经没有父加载器了,这时候开始考虑自己是否能加载了,如果自己无法加载,会下沉到子加载器去加载,一直到最底层,如果没有任何加载器能加载,就会抛出ClassNotFoundException。
在这里插入图片描述

6.通过反射获取类的结构

public class Test3 {
    public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException {
        Class c1=Class.forName("edu.ncu.dong.reflection.User");
        System.out.println("获取User类所有的public属性");
        for (Field field : c1.getFields()) {
            System.out.println(field);
        }
        System.out.println("获取User类所有的属性");
        for (Field field : c1.getDeclaredFields()) {
            System.out.println(field);
        }
        System.out.println("User类所有的构造器");
        for (Constructor constructor : c1.getDeclaredConstructors()) {
            System.out.println(constructor);
        }
        System.out.println("User类所有的public构造器");
        for (Constructor constructor : c1.getConstructors()) {
            System.out.println(constructor);
        }
        System.out.println("User类指定的构造器");
        System.out.println(c1.getConstructor(String.class,int.class));//参数是构造器的参数列表,这是为了应对重载的情况

    }
}

还有很对方法,都属于Class类的方法,获取了一个对象的Class类,就可以获取一类的信息。

7.通过反射创建实例对象

创建实例:
➢创建类的对象:调用Class对象的newInstance()方法,有如下两个限制:
➢1) 类必须有一个无参数的构造器。
➢2)类的构造器的访问权限需要足够
➢难道没有无参的构造器就不能创建对象了吗?只要在操作的时候明确的调用类中的构造器,并将参数传递进去之后,才可以实例化操作。
步骤如下:
➢1)通过Class类的getDeclaredConstructor(Class…parameterTypes)取得本类的指定形参类型的构造器
➢2)向构造器的形参中传递一个对象数组进去, 里面包含了构造器中所需的各个参数。
➢3)通过Constructor实例化对象

执行指定方法
上面讲了如何获取方法,这里说说怎么执行指定的方法呢?通过反射,调用类中的方法,通过Method类完成:
①获取:通过Class类的getMethod(String name,Class…parameterTypes)方法取得Method对象,并设置此方法操作时所需要的参数类型。
②执行:使用Object invoke(Object obj, Object[] args)方法进行调用(第1步中返回的Method对象中的),并向方法中传递要设置的obj对象
的参数信息。
注意:Object invoke(Object obj, Object[] args)方法
➢Object对应原方法的返回值,若原方法无返回值,此时返回null
➢若原方法若为静态方法,此时形参Object obj可为null
➢若原方法形参列表为空,则Object[] args为null
➢若原方法声明为private,则需要在调用此invoke()方法前,显式调用方法对象的setAccessible(true)方法,将可访问private的方法。
➢Method和Field、 Constructor对象都有setAccessible()方法。
➢setAccessible作用是:启动和禁用访问安全检查的开关。
➢参数值为true则指示反射的对象在使用时应该取消Java语言访问检查。
➢提高反射的效率。如果代码中必须用反射,而该句代码需要频繁的被调用,那么请设置为true。
➢使得原本无法访问的私有成员也可以访问
➢参数值为false则指示反射的对象应该实施Java语言访问检查
在这里插入图片描述
获取泛型信息
➢Java采用泛型擦除的机制来引入泛型,Java中的泛型仅仅是给编译器javac使用的,确保数据的安全性和免去强制类型转换问题,但是, 一旦编译完成,所有和泛型有关的类型全部擦除
➢为了通过反射操作这些类型, Java新增了ParameterizedType , GenericArrayType,TypeVariable和WildcardType几种类型来代表不能被归一到Class类中的类型但是又和原始类型齐名的类型.
➢ParameterizedType :表示一种参数化类型,比如Collection
➢GenericArrayType :表示一种元素类型是参数化类型或者类型变量的数组类型
➢TypeVariable :是各种类型变量的公共父接口
➢WildcardType :代表一种通配符类型表达式

public static void main(String[] args) throws NoSuchMethodException {
        Method method = Test11.class.getMethod("test01",Map.class, List.class) ;//test01参数的结构是:Map<String,User>
        Type[] genericParameterTypes = method.getGenericParameterTypes();//参数类型
        for (Type genericParameterType:genericParameterTypes) {
            System.out.println ("#"+ genericParameterType) ;
            if (genericParameterType instanceof ParameterizedType) {
                Type[] actualTypeArguments = ((ParameterizedType) genericParameterType).getActualTypeArguments();
                for (TypeactualTypeArgument:actualTypeArguments){
                    System.out.println(ac tualTypeArgument);
                }
            }
            method = Test11.class.getMethod( "test02",null) ;//test02返回的结构是:Map<String,User>
            TypegenericReturnType = method.getGenericReturnType();//返回值类型
            if (genericReturnType instanceof ParameterizedType) {
                Type[] actualTypeArguments =((ParameterizedType)genericReturnType).getActualTypeArguments();
                for (Type actualTypeArgument:actualTypeArguments) {
                    System.out.println(actualTypeArgument) ;
                }
            }
        }

反射获取注解信息

package edu.ncu.dong.reflection;

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

/**
 * 测试反射,获取注解
 */
public class TestAnno {
    public static void main(String[] args) {
        //获取Pojo_Stu的注解
        Pojo_Stu pojoStu=new Pojo_Stu();
        Class c=pojoStu.getClass();
        System.out.println("获取Pojo_Stu类的注解:");
        for (Annotation annotation : c.getAnnotations()) {
            System.out.println(annotation);
        }
        System.out.println("获取Pojo_Stu属性的注解:");
        for (Field field : c.getDeclaredFields()) {
            for (Annotation annotation : field.getAnnotations()) {
                System.out.println(annotation);
            }
        }
    }
}
@Table(tableName = "Student")
class Pojo_Stu{
    @Col(colName = "name",type = "String",len = 20)
    private String name;
    @Col(colName = "id",type = "int",len = 10)
    private int id;

    public Pojo_Stu() {
    }

    public String getName() {
        return name;
    }

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

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }
}
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@interface Table{
    String tableName() default "";
}
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@interface Col{
    String colName();
    String type();
    int len() default 0;
}

在这里插入图片描述

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值