栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

Java 注解和反射入门

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

Java 注解和反射入门

Java 注解和反射入门
  • 前言
  • 一、Annotation
    • 1、内置注解
    • 2、元注解
  • 二、反射
    • 1、Java反射机制概述
    • 2、理解Class类并获取Class实例
    • 3、类的加载与ClassLoader
    • 4、利用反射做事
    • 5、反射操作泛型
    • 6、反射操作注解
  • 总结
  • 参考文献

前言

Java 反射和注解是以后框架的核心之一,了解Java注解和反射有助于后面框架的学习。

一、Annotation

1 作用)给类、包、方法、变量等加上注解,就是对它们的一种解释或是一种备注信息,这个备注信息不想注释一样只能给程序员看,调用它们的其它程序也能读懂上面的注解,从而了解使用它需要注意的约束和信息。编译时,编译器也能读懂一些注释。
2 使用)@+注解名+(参数名)(可选)
3 使用地方)包、类、变量、方法等。

1、内置注解

1)Override,重写超类的方法,只能用在方法上。

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.SOURCE)
public @interface Override {
}
	
    @Override
    public String toString() {
        return super.toString();
    }

2)Deprecated,不建议使用的方法,通常该方法存在危险或者已经过时,有了新的方法可替代。

@documented
@Retention(RetentionPolicy.RUNTIME)
@Target(value={CONSTRUCTOR, FIELD, LOCAL_VARIABLE, METHOD, PACKAGE, MODULE, PARAMETER, TYPE})
public @interface Deprecated {
    String since() default "";
    boolean forRemoval() default false;
}
	public void show() {
        System.out.println("suggestion to not used.");
    }

3)SuppressWarnings,镇压一些警告,什么类型的警告需要我们传入参数。

@Target({TYPE, FIELD, METHOD, PARAMETER, CONSTRUCTOR, LOCAL_VARIABLE, MODULE})
@Retention(RetentionPolicy.SOURCE)
public @interface SuppressWarnings {
    String[] value();
}
    @SuppressWarnings(value={"all","unchecked","deprecation"})
    public void warning() {
        System.out.println("suppressing all warnings");
    }
2、元注解

含义)最基本的注解,注解对象为所有注解,4大meta-annotation。
1)@Target,表示该注解的注解目标可选择的地方。

@documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)
public @interface Target {
    ElementType[] value();
}

2)@Retention,表示该注解的生命周期,Runtime>Class>Source

@documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)
public @interface Retention {
    
    RetentionPolicy value();
}

3)@documented,生成javaDoc时是否包含此注解

@documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)
public @interface documented {
}

4)@Inherited,子类可继承父类中的注解

@documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)
public @interface Inherited {
}

5)自定义注解

//Target,表示该注解的注解目标可选择的地方
@Target({ElementType.TYPE, ElementType.CONSTRUCTOR, ElementType.FIELD, ElementType.METHOD, ElementType.PARAMETER})
//Retention,表示该注解的生命周期,Runtime>Class>Source
@Retention(RetentionPolicy.RUNTIME)
//documented,生成javaDoc时是否包含此注解
@documented
//Inherited,子类可继承父类中的注解
@Inherited
@interface MyAnnotation1 {
    //参数定义:返回值 + 参数名 + () + default (default 可选)
    String[] value() default {"annotation"};

    int age() default 18;

    long id() default -1l;
}
二、反射

反射机制让Java成为一门“准动态语言”,即具有一定的灵活性,通过反射机制获得类似的动态语言的动态特性。
动态语言:即可以在运行的时候根据某些条件改变自身结构,如PHP、Javascript、Object-C等。
静态语言:运行时程序结构不可变,如Java、C、C++。

1、Java反射机制概述

允许程序在执行时借助Java Reflection API获取任何类的内部信息,并能直接操作任意对象中的属性和方法。
加载完类到堆内存的方法区之后,就会对每个类产生唯一的一个Class类型的对象,该对象包含了完整的类结构信息,通过这个唯一的Class类型对象来看到完整的类结构信息,称其为反射。
正常方式:引入类 -> new 一个对象 -> 得到实例化的对象
反射方式:实例化对象 -> 获取该类唯一的Class类型对象 -> 得到完整的类结构信息

反射机制提供给我们的功能
1)运行时判断任意一个对象所属的类
2)运行时构造任意一个类的对象。
3)运行时判断任意一个类所具有的成员变量和方法。
4)运行时获取泛型信息
5)运行时调用任意一个类所具有的成员变量和方法。
6)运行时处理注解
7)动态代理

2、理解Class类并获取Class实例

Object中有一个获取Class类对象的方法定义public final native Class getClass();
1)Class本身也是一个类
2)Class对象只能由系统来建立
3)一个加载的类在JVM中只有唯一的Class类型对象
4)一个Class对象对应加载到JVM中的.class文件
5)每个类对象都会记录是那个Class对象将其实例化
6)通过Class对象可以得到对应类被加载的所有结构信息
7)Class类型实例是Reflection的核心,反射是通过Class类型对象来反射的。

Class类常用方法

获取Class类型对象的方法

	public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException {
        //方式一:通过类名获取
        Class c1 = Class.forName("com.xhu.java.annotation.User");
        //方式二:通过对象获取
        Class c2 = new User().getClass();
        //方式三:最安全,通过类的class属性获取
        Class c3 = User.class;
        //方式四:基本内置类型的包装类都由TYPE属性可获取
        Class c4 = Integer.TYPE;
        //方式五:通过ClassLoader
        System.out.println(c2.getDeclaredField("name").toString());
    }

哪些可以获取Class类型对象?

		Class c1 = Object.class;//对象类型
        Class c2 = Comparable.class;//接口类型
        Class c3 = String[].class;//一维数组
        Class c4 = int[][].class;//二维数组
        Class c5 = ElementType.class;//枚举类型
        Class c6 = Override.class;//注释类型
        Class c7 = int.class;//基本内置类型
        Class c8 = void.class;//空类型
        Class c9 = Class.class;//Class类型
3、类的加载与ClassLoader

1)Java内存分析
栈)每个线程一个方法栈:存放基本数据类型变量、对象引用变量,不同线程不相互共享。
堆)存放new出的对象,栈中的对象引用存的就是堆中实际对象的地址。线程共享。
方法区)分出来的一块堆,存放类结构信息和静态变量及常量池。线程共享。

2)类的加载过程
如果程序需要某个类时,但是该类还未被加载到内存,系统就会加载类(Load)、类的链接(link)、类的初始化(Initialize)

加载)将类的字节码文件加载到内存,并将类信息和静态数据转化成方法区的运行时数据结构,再给这个类在堆中生成一个唯一的Class对象。
链接)将Java的二进制代码合并到JVM的运行状态之中。
A 验证)验证类信息是否符合JVM规范,保证安全。
B 分配内存)为静态变量分配方法区的堆内存,并赋值为默认值,注意不是初始化值。
C 解析)把常量池中的符号引用替换成地址引用(该常量在常量池的地址)
初始化)调用类构造器,对类变量初始化。把静态代码块的语句和静态变量赋值语句合并到clinit()中(按顺序合并),进行初始化。
注1:初始化该类时,如果父类未初始化,则先初始化父类。
注2:JVM保证clinit()在多线程环境中被正确的加锁和同步。

什么时候会发生类的初始化?
类的主动引用,一定会发生初始化)
1)JVM启动,初始化main方法的所用到的类。
2)new一个类的对象,类加载有一个生命周期。
3)调用类的静态成员和静态方法(常量除外)
4)使用reflect包的方法对类进行反射调用
5)当初始化一个类,如果其父类没有被初始化,则先初始化父类
类的被动引用,不会发生初始化)
1)当访问一个静态域时,只有真正声明这个静态域的类会被初始化,如子类调用父类的静态方法,则只初始化父类
2)通过数组定义类引用,不会初始化此类。(new 数组只是开辟了一个空间,数组类会被初始化。)
3)引用常量不会触发初始化,常量在链接阶段就存入调用类的常量池中了。

类加载器
作用)将类的字节码文件加载到内存,并将类信息和静态数据转化成方法区的运行时数据结构,再给这个类在堆中生成一个唯一的Class对象,作为通过反射操作类信息的唯一入口。
缓存)一旦一个类被加载到类加载器中,将会维持加载一段时间,等生命周期结束时,将会回收这些Class对象。
JVM类加载器)


Bootstap ClassLoader 由c++编写,Java无法读取到,读到为null,它会去加载jdk核心包ar.jar包。

package com.xhu.java.annotation;

public class TestClassLoader {
    public static void main(String[] args) {
        //最上层系统类加载器
        ClassLoader sysClassLoader = ClassLoader.getSystemClassLoader();
        System.out.println(sysClassLoader);
        //中间层扩展加载器,现在的平台加载器
        ClassLoader extClassLoader = sysClassLoader.getParent();
        System.out.println(extClassLoader);
        //最下层根加载器,C++编写,Java读不到,为null
        ClassLoader rootClassLoader = extClassLoader.getParent();
        System.out.println(rootClassLoader);
        //本类的类加载器,应该是系统类加载器
        ClassLoader thisClassLoader = TestClassLoader.class.getClassLoader();
        System.out.println(thisClassLoader);
        //JDK的类是那个加载器加载的,应该是根加载器去加载
        ClassLoader jdkClassLoader = Object.class.getClassLoader();
        System.out.println(jdkClassLoader);
        //系统类加载器可以加载的路径
        System.out.println(System.getProperty("java.class.path"));
        //双亲委派机制,加载一个包时,会向上查找是否有相同的包,来保证安全性。
        
    }
}

4、利用反射做事
package com.xhu.java.annotation;

import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Arrays;

public class TestClass {
    public static void main(String[] args) throws NoSuchFieldException, NoSuchMethodException, IllegalAccessException, InstantiationException, InvocationTargetException {
        Class c1 = User.class;
        //获取类的名字:包名+类名
        System.out.println(c1.getName());
        //获取类的简单名字:类名
        System.out.println(c1.getSimpleName());
        //获取类的属性:获取public
        Field[] fields = c1.getFields();
        for (Field field : fields)
            System.out.println(field);
        //获取类的属性:所有属性
        Field[] allF = c1.getDeclaredFields();
        for (Field field : allF) {
            System.out.println(field);
        }
        //根据名字来获取指定属性
        System.out.println(c1.getDeclaredField("name"));
        //获取类的方法
        System.out.println(Arrays.toString(c1.getMethods()));//获取本类和父类的public方法
        System.out.println(Arrays.toString(c1.getDeclaredMethods()));//获取本类的所有方法
        //获取指定方法
        System.out.println(c1.getDeclaredMethod("privateF",String.class));//由于重载,不传参数可能会报错
        //获取指定构造器
        System.out.println(Arrays.toString(c1.getDeclaredConstructors()));
        //获取指定构造器
        System.out.println(c1.getDeclaredConstructor(String.class, String.class, int.class, int.class));
        //创建对象
        @Deprecated
        User u = (User)c1.newInstance();//类中必须有一个无参构造器且访问权限足够。
        System.out.println(u);
        //通过参数构造
        System.out.println(c1.getDeclaredConstructor(String.class, String.class, int.class, int.class).newInstance("name", "id", 18, 1));
        //通过反射调用方法,private 不能调用
        Method method = c1.getDeclaredMethod("privateF", String.class);
        method.invoke(u,"调用方法");
        //设置属性,私有属性不能操作
        Field name = c1.getDeclaredField("name");
        name.set(u,"new name");
        System.out.println(name.get(u));
        //关闭权限,访问private
        Field age = c1.getDeclaredField("age");
        age.setAccessible(true);
        age.set(u,100);
        System.out.println(age.getInt(u));
        System.out.println();

    }
}

//POJO 类
class User extends Object {
    public String name;
    protected String id;
    private int age;
    int sex;

    public User() {
    }

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

    public int getSex() {
        return sex;
    }

    public void setSex(int sex) {
        this.sex = sex;
    }

    public String getName() {
        return name;
    }

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

    public String getId() {
        return id;
    }

    public void setId(String id) {
        this.id = id;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    @Override
    public String toString() {
        return "User{" +
                "name='" + name + ''' +
                ", id='" + id + ''' +
                ", age=" + age +
                ", sex=" + sex +
                '}';
    }

    public void privateF(String s) {
    }
}

性能比较

package com.xhu.java.annotation;

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

public class TestPower {
    //普通new的方式
    public static void test01() {
        long start = System.currentTimeMillis();
        User u = new User();
        for (int i = 0; i < 1000000000; i++) {
            u.getName();
        }
        long end = System.currentTimeMillis();
        System.out.println((end - start)+"ms");
    }

    //反射的方式
    public static void test02() throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
        long start = System.currentTimeMillis();
        User u = new User();
        Class c1 = User.class;
        Method method = c1.getDeclaredMethod("getName", null);
        for (int i = 0; i < 1000000000; i++) {
            method.invoke(u, null);
        }
        long end = System.currentTimeMillis();
        System.out.println((end - start)+"ms");
    }

    //反射,但关闭权限
    public static void test03() throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
        long start = System.currentTimeMillis();
        User u = new User();
        Class c1 = User.class;
        Method method = c1.getDeclaredMethod("getName", null);
        method.setAccessible(true);
        for (int i = 0; i < 1000000000; i++) {
            method.invoke(u, null);
        }
        long end = System.currentTimeMillis();
        System.out.println((end - start)+"ms");
    }

    public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException {
        test01();
        test02();
        test03();
    }
}

5、反射操作泛型

Java采用泛型擦除机制来引入泛型,只是给javac使用的,确保数据的安全性和免去强制转换类型,一旦编译完成,所有泛型有关的类型全部擦除变为Object。
为了通过反射操作泛型,Java新增ParameterizedType、GenericArrayType、TypeVariable、WildcardType.
ParameterizedType,参数化类型,如Collection.
GenericArrayType,表示一种元素类型是参数化类型或者类型变量的数组类型
TypeVariable,是各种类型变量的公共父接口
WildcardType,代表一种通配符类型表达式

package com.xhu.java.annotation;

import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.Arrays;
import java.util.List;
import java.util.Map;

//测试泛型
public class TestGer {
    public static void test01(Map m, List n) {
        System.out.println("test01");
    }

    public static Map test02() {
        System.out.println("test02");
        return null;
    }

    public static void main(String[] args) throws NoSuchMethodException {
        Method method = TestGer.class.getMethod("test01", Map.class, List.class);
        //获取参数泛型
        Type[] types = method.getGenericParameterTypes();
        for (Type type : types) {
            if (type instanceof ParameterizedType) {
                Type[] ps = ((ParameterizedType) type).getActualTypeArguments();
                for (Type p : ps) {
                    System.out.println(p);
                }
            }
        }
        //获取返回值泛型
        Method m2 = TestGer.class.getMethod("test02", null);
        Type t2 = m2.getGenericReturnType();
        if (t2 instanceof ParameterizedType) {
            Type[] tp2 = ((ParameterizedType) t2).getActualTypeArguments();
            for (Type p : tp2) {
                System.out.println(p);
            }
        }
    }
}

6、反射操作注解
package com.xhu.java.annotation;

//利用反射操作注解
public class TestReflectAnno {
    public static void main(String[] args) throws NoSuchFieldException {
        Class c1 = User.class;
        //通过反射获取注解
        ORM a = (ORM)c1.getAnnotation(ORM.class);
        System.out.println(a.value());
        //获取field注解
        java.lang.reflect.Field field = c1.getDeclaredField("name");
        Field as = (Field)field.getAnnotation(Field.class);
        System.out.println(as.columnName());
        System.out.println(as.type());
        System.out.println(as.len());
    }
}

//POJO 类
@ORM("DB_User")
class User extends Object {
    @Field(columnName = "name", type = "varchar", len = 10)
    public String name;
    @Field(columnName = "id", type = "varchar", len = 10)
    protected String id;
    @Field(columnName = "age", type = "int", len = 3)
    private int age;
    @Field(columnName = "sex", type = "int", len = 1)
    int sex;

    public User() {
    }

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

    public int getSex() {
        return sex;
    }

    public void setSex(int sex) {
        this.sex = sex;
    }

    public String getName() {
        return name;
    }

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

    public String getId() {
        return id;
    }

    public void setId(String id) {
        this.id = id;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    @Override
    public String toString() {
        return "User{" +
                "name='" + name + ''' +
                ", id='" + id + ''' +
                ", age=" + age +
                ", sex=" + sex +
                '}';
    }

    public void privateF(String s) {
        System.out.println(s);
    }
}

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@interface ORM {
    String value();
}

@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@interface Field {
    String columnName();

    String type();

    int len();
}


注:通过操作注解来拼接SQL。Mybatis的核心之一就是反射操作注解。

总结

1)反射、反射操作

参考文献

[1] [JDK 1.12]
[2] 注解和反射 狂神

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/571284.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号