0.4-注解和反射

注解

Annotation

  • 内置注解
    1. @Override
    2. @Deprecated
    3. @SuppressWarnings

元注解

  • 作用:负责注解其他注解
    1. @Target 描述注解使用范围
    2. @Retention 表示在什么级别保存该注解(SOURCE < CLASS < RUNTIME)
    3. @Documented 说明该注解将被包含在javadoc中
    4. @Inherited 说明子类可以继承父类中的该注解

自定义注解

  • 使用@interface自定义注解时,自动继承java.lang.annotation.Annotation接口

    @Target({ElementType.Type,ElementType.METHOD})
    @Retention(Retention.RUNTIME)
    @interface MyAnnotation{
        //注解的参数: 参数类型+参数名+();
        String name() default "";
    }
    
    //只有一个参数时,参数名设为value,使用时可以直接传值
    

反射

Reflection,反射机制允许程序在执行期间借助于Reflection API 取得任何类的内部信息,并能直接操作任意对象的内部属性及方法。

加载完类之后,在堆内存区中就产生一个Class类型的对象(一个类只有一个Class对象),这个对象包含了完整的类的结构信息。

  • 反射机制提供的功能
    • 在运行时判断任意一个对象所属的类
    • 在运行时构造任意一个类的对象
    • 在运行时判断任意一个类所具有的成员变量和方法
    • 在运行时获取泛型信息
    • 在运行时调用任意一个对象的成员变量和方法
    • 在运行时处理注解
    • 生成动态代理
  • 反射相关API
    • java.lang.Class 代表一个类
    • java.lang.reflect.Method 代表类的方法
    • java.lang.reflect.Field 代表类的成员变量
    • java.lang.reflect.Constructor 代表类的构造器

获取Class类的实例

  1. 已知具体的类,通过类的class属性获取,该方法最安全可靠,程序性能最高

    Class clazz = Person.class;
    
  2. 已知某个类的实例,调用该实例 getClass()方法

  3. 已知一个类的全类名,且该类在类路径下,通过Class.forName(String fullName)获取,可能抛出ClassNotFoundException

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

  5. ClassLoader

有Class对象的类型

  1. class:外部类,成员(成员内部类,静态内部类),局部内部类,匿名内部类
  2. interface
  3. 数组
  4. enum:枚举类型
  5. annotation: 注解@interface
  6. primitive type:基本数据类型
  7. void

类加载

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

类初始化

  • 类的主动引用(一定会发生类的初始化)
    • 当虚拟机启动,先初始化main方法所在类
    • new一个类的对象
    • 调用类的静态成员(除了final常量)和静态方法
    • 使用java.lang.reflect包的方法对类进行反射调用
    • 当初始化一个类,如果父类没有初始化,则先初始化父类
  • 类的被动引用(不会发生类的初始化)
    • 当访问一个静态域时,只有真正声明这个域的类才会被初始化。如,当通过子类引用父类的静态变量,不会导致子类初始化
    • 通过数组定义类引用,不会触发此类初始化
    • 引用常量不会触发此类初始化(常量在链接阶段就存入调用类的常量池中)
public class Test {
    static{
        System.out.println("main");
    }
    public static void main(String[] args) throws ClassNotFoundException {
        //1.主动引用
        /**B b = new B();*/
        /**输出
         * main
         * A
         * B
         */

        //反射
        /**Class.forName("cqu.ma.classLoad.B");*/
        /**输出
         * main
         * A
         * B
         */

        //2.不会产生类的引用的方法
        /**System.out.println(B.m); //子类调用父类静态域和方法,不会导致子类初始化*/
        /**输出
         * main
         * A
         * 6
         */

        /**B[] bs = new B[4];*/
        /**输出
         *main
         */

        /**System.out.println(B.S);*/
        /**输出
         *main
         * 4
         */
    }

}

class A{
    static{
        System.out.println("A");
        m = 3;

    }
    static int m = 6;
}
class B extends A{
    static {
        System.out.println("B");
    }
    static int c = 8;
    static final int S = 4;
}

类加载器

  • 类加载作用
    将class文件字节码内容加载到内存,并将这些静态数据转换成方法区的运行时数据结构,然后在堆中生成一个代表这个类的java.lang.Class对象,作为方法区中类数据的访问入口
  • 类缓存
    标准的javaSE类加载器可以按要求查找类,但一旦某个类被加载到类加载器中,它将维持加载(缓存)一段时间,不过JVM垃圾回收机制可以回收这些Class对象
  • 类加载器:
    • 引导类加载器:C++编写,JVM自带的类加载器,负责java平台核心库,用来装载核心类库,该加载器无法直接获取
    • 扩展加载器:负责jre/lib/ext目录下的jar包或 -D java.ext.dirs指定目录下的jar包装入工作库
    • 系统类加载器:负责java -classpath 或 -D java.class.path所指目录下的类与jar包装入工作,是最常用的加载器

动态创建对象执行方法

import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

public class Test2 {
    public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException, NoSuchFieldException {
        Class clazz = Class.forName("cqu.ma.reflection.User");

        //new 对象
        Constructor c = clazz.getConstructor(String.class,int.class);
        c.setAccessible(true);

        User u = (User) c.newInstance("X",18);

        //调用方法
        Method m = clazz.getMethod("getName",null);
        Method m2 = clazz.getMethod("setName",String.class);

        System.out.println(m.invoke(u, null));
        m2.invoke(u,"Y");
        System.out.println(m.invoke(u, null));

        //直接访问成员变量
        Field f = clazz.getDeclaredField("name"); //name字段是私有的需要 getDeclaredField()获取
        f.setAccessible(true); //访问私有字段,需设置
        System.out.println(f.get(u));

    }
}

获取泛型信息

  • java采用泛型擦除机制来引入泛型,java中的泛型仅仅是给编译器javac使用,确保数据的安全性和免去强制类型转换问题,但是,一旦编译完成,所有和泛型有关的类型全部擦除
  • 为了通过反射操作这些类型,java新增 ParameterizedType, GenericArrayType, TypeVariable 和 WildcardType 几种类型来代表不能被归一到Class类中的类型但又和原始类型齐名的类型
    • ParameterizedType :表示一种参数化类型,如 Collection<String>
    • GenericArrayType :表示一种元素类型是参数化类型或者类型变量的数组类型
    • TypeVariable :是各种类型变量的公共父接口
    • WildcardType :代表一种通配符类型表达式
import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.List;
import java.util.Map;

public class Test3 {
    public void t0(Map<String,User> map, List<User> list){
        System.out.println("t0");
    }
    public Map<String,User> t1(){
        System.out.println("t1");
        return null;
    }
    public static void main(String[] args) throws NoSuchMethodException {
        Method m = Test3.class.getMethod("t0",Map.class,List.class);

        Type[] genericParameterTypes = m.getGenericParameterTypes();
        for(Type genericParameterType: genericParameterTypes){
            System.out.println("#"+genericParameterType);
            if(genericParameterType instanceof ParameterizedType){
                Type[] actualTypeArguments = ((ParameterizedType) genericParameterType).getActualTypeArguments();
                for(Type actualTypeArgument: actualTypeArguments){
                    System.out.println(actualTypeArgument);
                }
            }
        }

        Method m2 = Test3.class.getMethod("t1",null);
        Type genericReturnType = m2.getGenericReturnType();
        if(genericReturnType instanceof ParameterizedType){
            Type[] actualTypeArguments = ((ParameterizedType)genericReturnType).getActualTypeArguments();
            for(Type actualTypeArgument:actualTypeArguments){
                System.out.println(actualTypeArgument);
            }
        }
    }
}

获取注解信息

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

public class Test4 {
    public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException {
        Class c = Class.forName("cqu.ma.reflection.Student");

        //获取注解
        Annotation[] ans = c.getAnnotations();
        for(Annotation a:ans){
            System.out.println(a);
        }
        //获取注解值
        Annotation an1 = c.getAnnotation(Table.class);
        String value = ((Table) an1).value();
        System.out.println(value);

        //
        Field f = c.getDeclaredField("name");
        Fields an2 = f.getAnnotation(Fields.class);
        System.out.println(an2.columnName());
        System.out.println(an2.type());
        System.out.println(an2.length());
    }
}

@Table("db_student")
class Student{
    @Fields(columnName = "db_id",type = "int",length = 10)
    private int id;
    @Fields(columnName = "db_name",type = "varchar",length = 3)
    private String name;
    @Fields(columnName = "db_age",type = "int",length = 10)
    private int age;

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

/**
 * 类注解
 */
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@interface Table{
    String value();
}
/**
 * 属性注解
 */
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@interface Fields{
    String columnName();
    String type();
    int length();
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值