上次面试官问我注解,怎么自定义注解?我告诉他我不会
就这样等我再去查校招进度的时候
俺发奋图强,通过一天的学习,终于明白注解。
- 自定义注解的使用!
- 通过注解我们能干什么?
- 注解究竟有什么好处?
话不多说,直奔主题!
自定义注解的创建
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface AutoWired {
//自动注入注解
Class className();//获取class对象
String name();
int age();
}
乍一看您可能就有三个问题
1.@Retention
和@Target
是啥呀?
答:这两个也是注解,是为了注解自定义注解的注解(中文八级hahahaha)
我们称注解自定义注解的注解为元注解,你可以理解为注解是一个标签,而元注解是给这些标签打标签的。
2.元注解有什么用呀?
答:只有两个比较重要和常用的元注解,就是上面两个,分别介绍一下他们的作用
@Target:指明我们的自定义注解可以修饰的范围,如类,方法,变量
@Retention:指明自定义注解存在的范围,可以指定为SOURCE,CLASS,RUNTIME;
SOURCE表明注解只在.java文件中存在,CLASS表明注解在.class文件中存在,而RUNTIME表示在运行时,会把自定义注解加载到虚拟机中。
3.className(),name(),age()
是啥呀
答:可以理解为属性,并且可以定义默认值。有了这些属性,我们在使用注解时可以赋值。
@AutoWired(className = Person.class,name = "Yang",age=20)
public static Person person1;
@AutoWired(className = Person.class,name = "Zhang",age=20)
public static Person person2;
相比大家已经明白了基本使用了(估计有人想打死我)
通过注解我们能干什么?
1.通过注解我们能够减少重复性的代码,让代码的可读性更好。
2.现在很多框架中都有自定义的注解,比如在Spring中有自动注入,也是通过注解实现的,在Android中,为了避免重复的findviewById
可以使用自定义注解,那么下面我们来演示如何在Java中通过注解实现自动注入,通过注解实现findviewById
的自动完成。
实现自动注入
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
//自定义注解,三个属性,class对象
public @interface AutoWired {
//自动注入注解
Class className();//获取class对象
String name();//姓名
int age();//年龄
}
import java.lang.reflect.Field;
//我们知道静态代码块属于类,所以他会被先执行,我们在静态代码块中通过反射获取了Student类中定义的所有的成员变量(两个Person),然后分别给他们赋值,值也是从注解中获取的age和name.
public class Student{
static{
Field[] fields = Student.class.getDeclaredFields();
for (int i=0;i<fields.length;i++){
AutoWired auto = fields[i].getAnnotation(AutoWired.class);
Class cl = auto.className();//获取class对象,根据class对象给Person初始化
String name = auto.name();
int age = auto.age();
try {
Field field = fields[i];
field.setAccessible(true);//暴力访问,取消age的私有权限。让对象可以访问
Object obj = cl.newInstance();
Person p = new Person(name,age);
field.set(obj, p);//设置对象中,私有变道量的值
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
}
@AutoWired(className = Person.class,name = "Yang",age=20)
public static Person person1;
@AutoWired(className = Person.class,name = "Zhang",age=20)
public static Person person2;
public static void main(String[] args) {
//从main方法中看,我们没有任何new,就能够获取到对象的属性,这和自动注入类似,
System.out.println(person1.getName()+"------"+person1.getAge()+"\n");
System.out.println(person2.getName()+"------"+person2.getAge()+"\n");
//自动注入
}
}
实现不实用findviewById就指定绑定ID的功能
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface FindView {
int resId();//我们只要输入id,就能帮我们自动的绑定
}
import androidx.appcompat.app.AppCompatActivity;
import android.app.Activity;
import android.os.Bundle;
import android.view.View;
import android.widget.TextView;
import com.example.annimation.FindView;
import java.lang.reflect.Field;
public class FIndViewActivity extends AppCompatActivity {
@FindView(resId = R.id.tv_name1)
TextView tv_name1;
@FindView(resId = R.id.tv_name2)
TextView tv_name2;
@FindView(resId = R.id.tv_name3)
TextView tv_name3;
@FindView(resId = R.id.tv_name4)
TextView tv_name4;
@FindView(resId = R.id.tv_name5)
TextView tv_name5;
@FindView(resId = R.id.tv_name6)
TextView tv_name6;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_find_view);
//onCreate中我们通过一个函数解决findviewbyId的问题
bindView(this);
}
public static void bindView(Activity activity) {
Class classObj = activity.getClass();
Field [] fields = classObj.getDeclaredFields();
for (int i = 0; i < fields.length; i++) {
Field field = fields[i];
if (field.isAnnotationPresent(FindView.class)) {
FindView findView = field.getAnnotation(FindView.class);
int resId = findView.resId();
View view = activity.findViewById(resId);
field.setAccessible(true);//私有类型必须要设置
Class<?> targetType = field.getType();//成员变量的类型,如TextView
Class<?> viewType = view.getClass();//获取包名
if (!targetType.isAssignableFrom(viewType)) {
continue;
}
try {
field.set(activity, view);//通过set设置
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
}
}
}
大功告成,现在你对注解是不是又多了一点了解呢?如果大家还像了解注解的细节,请查阅其他资料。