注解
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对象的三种方式
- 通过类名获取 —>类名.class
- 通过对象获取 ---->对象名.getClass()
- 通过全类名获取 ------>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 + '}'; } }