Java 筑基之注解&反射

本文介绍了Java注解和反射的基础知识,包括如何声明和定义注解,使用@Target和@Retention元注解,以及注解在方法、字段和参数上的应用。接着详细讲解了反射的使用,如获取Class对象,反射调用构造器、函数、获取成员变量等,并通过注解实现Activity资源id和Intent数据的自动注入实例。
摘要由CSDN通过智能技术生成

注解

Java 注解(Annotation)又称 Java 标注,是 JDK5.0 引入的一种注释机制。 注解是元数据的一种形式,提供有关 于程序但不属于程序本身的数据。注解对它们注解的代码的操作没有直接影响。

声明一个注解类型

Java中所有的注解,默认实现 Annotation 接口:

package java.lang.annotation;
public interface Annotation {
    boolean equals(Object obj);
    int hashCode();
    String toString();
    Class<? extends Annotation> annotationType();
}

与声明一个"Class"不同的是,注解的声明使用 @interface 关键字。一个注解的声明如下:

public @interface LHW{
}
定义注解
首先要使用@Target和@Retention两个元注解

[^]: 元注解表示用于注解上的注解,称之为 meta- annotation(元注解)

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

    ElementType[] value();
}
@Target 定义 ElementType Java 元素类型
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
}
  • ElementType.ANNOTATION_TYPE 可以应用于注解类型。
  • ElementType.CONSTRUCTOR 可以应用于构造函数。
  • ElementType.FIELD 可以应用于字段或属性。
  • ElementType.LOCAL_VARIABLE 可以应用于局部变量。
  • ElementType.METHOD 可以应用于方法级注解。
  • ElementType.PACKAGE 可以应用于包声明。
  • ElementType.PARAMETER 可以应用于方法的参数。
  • ElementType.TYPE 可以应用于类的任何元素。
应用于方法级注解 ElementType.METHOD
    @Target({ElementType.METHOD})//注解用于方法上
    @Retention(RetentionPolicy.SOURCE)//注解保留源码
    public @interface LHW{
        int value(); //无默认值
        boolean sex() default false;//有默认值
    }
    @LHW(value =1,sex = true)
    private void test(){


    }
应用于字段或属性ElementType.FIELD
    @Target(ElementType.FIELD)
    @Retention(RetentionPolicy.SOURCE)
    public @interface Filed{
        int value() default 20;
        String name() default "low";
    }

    @Filed(name = "Tom")//在都定义有默认值的情况下,可以只传单个参数
    private String happy;

    @Filed(19)//如果只存在value元素需要传值的情况,则可以省略:元素名=,定义的不是value名,则不能省略
    private int index;
应用于方法的参数 ElementType.PARAMETER
 private static final int SUNDAY=0;
 private static final int MONDAY=1;
  
    @IntDef(value = {SUNDAY,MONDAY})//限制输入类型
    @Target(ElementType.PARAMETER)
    @Retention(RetentionPolicy.SOURCE)
    public @interface Parameter{
        int value() default 20;
        String name() default "Tom";
    }

    private void showDay(@Parameter int day){//Error must be one of:MainActivity.SUNDAY or MainActivity.MONDAY

    }
@Retention 标记注解的存储方式 RetentionPolicy
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)
public @interface Retention {
    /**
     * Returns the retention policy.
     * @return the retention policy
     */
    RetentionPolicy value();
}
public enum RetentionPolicy {
    /**
     * Annotations are to be discarded by the compiler.
     */
    SOURCE,
  
    CLASS,

    RUNTIME
}
  • RetentionPolicy.SOURCE - 标记的注解仅保留在源级别中,并被编译器忽略,多用于IDE语法检查和APT场景使用,编译后的class会被丢弃。
  • RetentionPolicy.CLASS - 标记的注解在编译时由编译器保留在class中,但 Java 虚拟机(JVM)会忽略,多用于字节码操作,热修复和插装等场景。
  • RetentionPolicy.RUNTIME - 标记的注解由 JVM 保留,因此运行时环境可以使用它,多用于反射

[^]: @Retention 三个值中 SOURCE < CLASS < RUNTIME,即CLASS包含了SOURCE,RUNTIME包含SOURCE、 CLASS。

反射

反射就是在运行状态中,获取一个类的所有属性和方法,能够调用它的对象的属性和方法,并且能够改变其属性,主要功能如下:

  • 在运行时构造任意一个类的对象
  • 在运行时获取或者修改任意一个类所具有的成员属性和方法

反射获取Class对象

获取Class对象的三种方式

  1. 通过类名获取 —>类名.class
  2. 通过对象获取 ---->对象名.getClass()
  3. 通过全类名获取 ------>Class.forName(String className) classLoader.loadClass(String className)
常用反射方法
  • 判断是否为某个类的实例isInstance(Object obj)
  • 判断是否是某个类的类型isAssignableFrom(Class<?> cls)
  • 设置函数可访问权限(包含私有)setAccessible(true)
获取构造器函数
  • Constructor getConstructor(Class[] params) – 获得使用特殊的参数类型的public构造函数(包括父类)
  • Constructor[] getConstructors() – 获得类的所有公共构造函数
  • Constructor getDeclaredConstructor(Class[] params) – 获得使用特定参数类型的构造函数(包括私有)
  • Constructor[] getDeclaredConstructors() -- 获得类的所有构造函数(与接入级别无关)
通过构造函数创建实例

通过调用函数 newInstance 实现

public T newInstance(Object ... initargs)

先通过Class对象获取指定的Constructor对象,再调用Constructor对象的newInstance()方法来创建实例。这 种方法可以用指定的构造器构造类的实例。

//获取String所对应的Class对象
Class<?> str= String.class; 
//获取String类带一个String参数的构造器
Constructor constructor = str.getConstructor(String.class);
//根据构造器创建实例
Object obj = constructor.newInstance("This's ok");
System.out.println(obj);
反射通过Array.newInstance()创建数组
public static Object newInstance(Class<?> componentType, int length);
 Field[] fields = clazz.getDeclaredFields();
        for (Field field : fields) {
      
     //获得单个数据类型
      Class<?> type = field.getType().getComponentType();
      ArrayList list = (ArrayList) Array.newInstance(type, 10);
        }
获取类的成员变量信息
  • Field getField(String name) – 获得命名的公共字段
  • Field[] getFields() – 获得类的所有公共字段
  • Field getDeclaredField(String name) – 获得类声明的命名的字段
  • Field[] getDeclaredFields() – 获得类声明的所有字段
调用函数
  • Method getMethod(String name, Class[] params) – 使用特定的参数类型,获得命名的公共方法

  • Method[] getMethods() – 获得类的所有公共方法

  • Method getDeclaredMethod(String name, Class[] params) – 使用特写的参数类型,获得类声明的命名的方法

  • Method[] getDeclaredMethods() – 获得类声明的所有方法

获取方法后使用invoke() 实现调用

public Object invoke(Object obj, Object... args)
获取泛型的真实类型

当我们对一个泛型类进行反射时,需要的到泛型中的真实数据类型,此时需要通 过 Type 体系来完成。 Type 接口包含了一个实现类(Class)和四个实现接口:

  • TypeVariable 泛型类型变量。可以获得泛型上下限等信息;

  • ParameterizedType 具体的泛型类型,可以获得元数据中泛型签名类型(泛型真实类型)

  • GenericArrayType 当需要描述的类型是泛型类的数组时,比如List[],Map[],此接口会作为Type的实现。

  • WildcardType 通配符泛型,获得上下限信息;

  public class TestType < K extends Comparable & Parcelable,V>{
        K key;
        V value;
        List<String> list;
        Map<Integer,String>[] map;
        Map<Integer,String>[] maps;
        private List<? extends Number> up; // 上限
        private List<? super String> down; //下限
    }
  private void testMain() {
        try {
            Field key = TestType.class.getDeclaredField("key");
            Field value = TestType.class.getDeclaredField("value");
            // TypeVariable 泛型类型变量,可以获取泛型上下限等信息
            TypeVariable keyGenericType = (TypeVariable) key.getGenericType();
            TypeVariable valueGenericType = (TypeVariable) value.getGenericType();

            Log.d("LHW","keyGenericType="+keyGenericType.getName());// keyGenericType=K
            Log.d("LHW","valueGenericType="+valueGenericType.getName());//valueGenericType=V
            // getGenericDeclaration 方法
            System.out.println(keyGenericType.getGenericDeclaration());
            System.out.println(valueGenericType.getGenericDeclaration());
            System.out.println("Key 的上界:");
            for (Type type : keyGenericType.getBounds()) {
                System.out.println(type);
            }
            System.out.println("Value 的上界:");
            for (Type type : valueGenericType.getBounds()) {
                System.out.println(type);
            }
//            System.out: class com.lhw.test.MainActivity$TestType
//            System.out: class com.lhw.test.MainActivity$TestType
//            System.out: Key 的上界:
//            System.out: interface java.lang.Comparable
//            System.out: interface android.os.Parcelable
//            System.out: Value 的上界:
//            System.out: class java.lang.Object

            //ParameterizedType 具体的泛型类型,可以获得元数据中泛型签名类型(泛型真实类型)
            Field map = TestType.class.getDeclaredField("map");
            Field list = TestType.class.getDeclaredField("list");
            System.out.println(map.getGenericType());
            System.out.println(list.getGenericType());
            // I/System.out: java.util.Map<java.lang.Integer, java.lang.String>
            // I/System.out: java.util.List<java.lang.String>
            ParameterizedType listType = (ParameterizedType) list.getGenericType();

            Log.d("LHW","getRawType:"+listType.getRawType());

            for (Type type : listType.getActualTypeArguments()) {
                Log.d("LHW","getActualTypeArguments="+type);
            }

//             D/LHW: getRawType:interface java.util.List
//             D/LHW: getActualTypeArguments=class java.lang.String
            // GenericArrayType  当需要描述的类型是泛型类的数组时
            Field maps = TestType.class.getDeclaredField("maps");
            GenericArrayType mapsType= (GenericArrayType) maps.getGenericType();
            System.out.println(mapsType.getGenericComponentType());

            //WildcardType 通配符泛型,获得上下限信息
            Field up = TestType.class.getDeclaredField("up");
            Field down = TestType.class.getDeclaredField("down");

            //获取真实类型
            ParameterizedType upType= (ParameterizedType) up.getGenericType();
            ParameterizedType downType= (ParameterizedType) down.getGenericType();
            //获取通配符信息
            WildcardType upWild = (WildcardType) upType.getActualTypeArguments()[0];
            WildcardType downWild = (WildcardType) downType.getActualTypeArguments()[0];
            //输出通配符信息
            System.out.println(upWild);//? extends java.lang.Number
            System.out.println(downWild);//? super java.lang.String

            System.out.println(upWild.getUpperBounds()[0]);//class java.lang.Number
            System.out.println(downWild.getLowerBounds()[0]);//class java.lang.String

        }catch (Exception e){
            e.printStackTrace();
        }

    }

注解反射实例

实现Activity的资源id依赖注入
  • 首先定义注解InjectView.java

    @Target(ElementType.FIELD)
    @Retention(RetentionPolicy.RUNTIME)
    public @interface InjectView {
        @IdRes
        int value();
    }
    
    
  • 定义注解工具类InjectUtils 调用 injectView

    //第一种实现
    public static void injectView(Activity activity){
            Class<? extends Activity> clazz = activity.getClass();
            //获取此类的所有成员
            Field[] fields = clazz.getDeclaredFields();
            for (Field field : fields) {
                //判断是否包含InjectView 注解
                if (field.isAnnotationPresent(InjectView.class)){
                    //获取注解
                    InjectView injectView = field.getAnnotation(InjectView.class);
                    //获取注解属性值 id
                    int value = injectView.value();
                    View view = activity.findViewById(value);
                    //设置访问权限 private
                    field.setAccessible(true);
                    try {
                        //反射赋值
                        field.set(activity,view);
                    } catch (IllegalAccessException e) {
                        e.printStackTrace();
                    }
                }
    
            }
        }
    //第二种实现
       public static void injectViews(Activity activity){
            Class<? extends Activity> clazz = activity.getClass();
            Field[] fields = clazz.getDeclaredFields();
    
            for (Field field : fields) {
                if (field.isAnnotationPresent(InjectView.class)){
                    InjectView injectView = field.getAnnotation(InjectView.class);
                    int id = injectView.value();
                    try {
                        Method viewById = clazz.getMethod("findViewById",int.class);
                        viewById.setAccessible(true);
                        Object view = viewById.invoke(activity, id);
                        field.set(activity,view);
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                }
    
    
            }
        }
    
  • MainActivity 调用

public class MainActivity extends AppCompatActivity {

    @InjectView(R.id.tv_info)
    public TextView tv_info;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        InjectUtils.injectView(this);

        tv_info.setText("注解注入成功!!!");

        tv_info.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                goLogin();
            }
        });

  
    }
Intent数据的自动注入
  • 定义InjectIntent注解

    @Target({ElementType.FIELD})
    @Retention(RetentionPolicy.RUNTIME)
    public @interface InjectIntent {
         String value() default "";
    
    }
    
  • 定义函数注解反射注入

      public static void injectIntent(Activity activity){
            Class<? extends Activity> clazz = activity.getClass();
            Intent intent = activity.getIntent();
            Bundle extras = intent.getExtras();//获取intent 传递过来的值
            if (extras==null){
                return;
            }
            Field[] fields = clazz.getDeclaredFields();
            for (Field field : fields) {
                if (field.isAnnotationPresent(InjectIntent.class)){
                    InjectIntent injectIntent = field.getAnnotation(InjectIntent.class);
                    //获取putExtras的key值,注解value为空时,使用filed 的getName作为key
                    String key = TextUtils.isEmpty(injectIntent.value())?field.getName():injectIntent.value();
                    if (extras.containsKey(key)){
                        Object data = extras.get(key);
                        //error got android.os.Parcelable[] 不能直接设置Parcelable 数据类型
                        //获得单个数据类型
                        Class<?> type = field.getType().getComponentType();
                        if (field.getType().isArray() && Parcelable.class.isAssignableFrom(type)){
                            Object[] datas= (Object[]) data;
                            //创建对应类型的数组并由objs拷贝
                            Object[] objects = Arrays.copyOf(datas,datas.length, (Class<? extends Object[]>) field.getType());
                            data=objects;
                        }
                        //设置访问权限 private
                        field.setAccessible(true);
                        try {
                            //反射赋值
                            field.set(activity,data);
                        } catch (IllegalAccessException e) {
                            e.printStackTrace();
                        }
                    }
    
                }
            }
        }
    
  • 定义Parcelable和Serializable数据类型

    public class PersonParcelable implements Parcelable {
        public String name;
        public PersonParcelable(String name){
            this.name=name;
        }
        protected PersonParcelable(Parcel in) {
            name = in.readString();
        }
    
    
        @Override
        public void writeToParcel(Parcel dest, int flags) {
            dest.writeString(name);
        }
    
        @Override
        public int describeContents() {
            return 0;
        }
    
        public static final Creator<PersonParcelable> CREATOR = new Creator<PersonParcelable>() {
            @Override
            public PersonParcelable createFromParcel(Parcel in) {
                return new PersonParcelable(in);
            }
    
            @Override
            public PersonParcelable[] newArray(int size) {
                return new PersonParcelable[size];
            }
        };
    
        @Override
        public String toString() {
            return "PersonParcelable{" +
                    "name='" + name + '\'' +
                    '}';
        }
    }
    

public class PersonSerializable implements Serializable {
    private String name;
    private int age;

   public PersonSerializable(String name,int age){
        this.name=name;
        this.age=age;
    }
    public String getName() {
        return name;
    }

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

    public int getAge() {
        return age;
    }

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

    @Override
    public String toString() {
        return "PersonSerializable{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}
  • Activity实现Intent注入传参
public void goLogin() {
        ArrayList<PersonParcelable> personParcelableList = new ArrayList<>();
        personParcelableList.add(new PersonParcelable("Cattary"));
        Intent intent=new Intent(MainActivity.this,LoginActivity.class)
                .putExtra("name","Tom")
                .putExtra("age","18")
                .putExtra("numbers", new int[]{1, 2, 3, 4, 5, 6})
                .putExtra("personSerializable", new PersonSerializable("Person",25))
                .putExtra("personParcelables", new PersonParcelable[]{new PersonParcelable("Janny")})
                .putExtra("persons",new String[]{"a","b","g"})
                .putParcelableArrayListExtra("personParcelableList", personParcelableList);
                ;

        startActivity(intent);
    }
  • LoginActivity 注入获取参数

    public class LoginActivity extends AppCompatActivity {
    
        @InjectIntent
        public String name;
        @InjectIntent("age")
        public String age;
        @InjectIntent
        int[] numbers;
    
        @InjectIntent("personSerializable")
        PersonSerializable personSerializable;
    
        @InjectIntent("personParcelables")
        PersonParcelable[] personParcelables;
    
        @InjectIntent("personParcelableList")
        List<PersonParcelable> personParcelableList;
    
        @InjectIntent("persons")
        String[] strsList;
    
        private TextView tv_login;
    
        @Override
        protected void onCreate(@Nullable Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_login);
            InjectUtils.injectIntent(this);
            tv_login = findViewById(R.id.tv_login);
            tv_login.setOnClickListener(new View.OnClickListener() {
               @Override
               public void onClick(View v) {
                   tv_login.setText("LoginActivity{" +
                           "name='" + name + '\'' +
                           ", age='" + age + '\'' +
                           ", array=" + Arrays.toString(numbers) +
                           ", personSerializable=" + personSerializable +
                           ", personParcelables=" + Arrays.toString(personParcelables) +
                           ", personParcelableList=" + personParcelableList +
                           ", strsList=" + Arrays.toString(strsList) +
                           ", tv_login=" + tv_login +
                           '}');
               }
           });
    
            System.out.println(toString());
        }
    
        @Override
        public String toString() {
            return "LoginActivity{" +
                    "name='" + name + '\'' +
                    ", age='" + age + '\'' +
                    ", array=" + Arrays.toString(numbers) +
                    ", personSerializable=" + personSerializable +
                    ", personParcelables=" + Arrays.toString(personParcelables) +
                    ", personParcelableList=" + personParcelableList +
                    ", strsList=" + Arrays.toString(strsList) +
                    ", tv_login=" + tv_login +
                    '}';
        }
    }
    
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值