JDK 5开始,Java增加了注解,这些标记可以在编译、类加载、运行时被读取,并执行相应的处理
标准注解
元注解
@Targe取值是一个ElementType类型数组
ElementType.TYPE:能修饰类接口或枚举
ElementType.FIELD:能修饰成员变量
ElementType.METHOD:能修饰方法
ElementType.PARAMETER:能修饰参数
ElementType.CONSTRUCTOR:能修饰构造方法
ElementType.LOCAL_AVRIABLE:能修饰局部变量
ElementType.ANNOTATION_TYPE:能修饰注解
ElementType.PACKAGE:能修饰包
ElementType.TYPE_PARAMETER:类型参数声明
ElementType.TYPE_USE:使用类型
@Retention注解有3中类型
RetentionPolicy.SOURCE:源码级注解,注解信息只会保留在 .java 源码中,编译后注解被丢弃不回保留在 .class文件中
RetentionPolicy.CLASS:编译时注解,当运行Java程序时,JVM也会保留该注解信息,可以通过反射获取该注解信息
RetentionPolicy.RUNTIME:运行时注解,运行Java程序时,JVM会会保留该注解信息,可通过反射获取该注解信息
反射机制允许程序在执行期间借助Reflection API获得任何类的内部信息,并直接操作任意对象的内部属性及方法
引入需要的“包类”名称------>通过new 实例化--------> 取得实例化对象
实例化对象-------->getClass()方法-------->得到完整的“包类”名称
java.lang.Class 表示一个类
java.lang.reflect.Method 表示一个类的方法
java.lang.reflect.Field 表示一个类的成员变量
java.lang.reflect.Constructor 表示一个类的构造器
class 本身也是一个类,class对象只能由系统建立对象,一个加载的类在JVM中只会有一个Class实例,一个Class对象对应的是一个加载到JVM中的一个.class文件,每个类的实例都会记得自己是由那个Class实例所生成的,通过class可以完整地得到一个类中的所有被加载的结构,Class 类是 Reflection的根源,针对任何想动态加载、运行的类,唯有先获得相应的Class对象
获得Class对象方法
1.对象.getClass();
2.Class.forName(“对象类全名”); //可能抛出ClassNotFoundException错误
3.通过 类名.class 方法获得
4.基本内置类型的包装类有一个Type属性(Integer.TYPE;)
堆:可以存放new对象和数组,可以被所有的线程共享,不会存放别的对象引用
栈:存放基本变量类型(包含这个基本类型的具体数值),引用对象的变量(存放这个引用在堆里面的具体地址)
方法区(特殊的堆):包含了所有的class和static变量,可以被所有的线程共享
类的加载分为三步
1.加载:将class文件读入内存,并将这些静态数据转换成方法区的运行时数据,然后创建一个代表这个类的java.lang.Class对象,此过程由类的加载器完成
2.链接:将二进制数据合并到JVM的运行状态环境中
1.验证:确保加载的类信息符合JVM规范,没有安茜方面问题
2.准备:正式为类变量(static)分配内存并设置初始值,这些内存都将在方法区中进行分配
3.解析:虚拟机常量池的符号引用(常量名)替换为直接引用(地址)的过程
3.初始化:JVM负责对类进行初始化
1.执行类构造器()方法,类构造器()方法是由编译期间自动和静态代码块中的语句合并产生(类构造器是构造类信息,不是构造该类对象的构造器)
2.当初始化一个类的时候,若发现父类没有初始化,则先触发其父类的初始化
3.虚拟机会保证一个类的()在多线程环境中被正确枷锁和同步
public class test {
public static void main(String[] args) throws ClassNotFoundException {
A a = new A();
System.out.println(a.m);
}
}
class A{
static {
System.out.println("A类静态代码块");
m = 300;
}
public A() { System.out.println("A类构造器"); }
public static int m = 100;
}
可见static 修饰的成员变量在初始化之前就已经声明过了,如果m不是静态,则静态代码块中,都合并到()方法中并执行,所以static修饰 的成员变量不需要new出来的对象调用(已经初始化过了)
类的主动引用(一定会发生初始化)
1.虚拟机启动先初始化main方法所在的类
2.new一个类对象
3.调用类的静态成员(除了final常量)和静态方法
4.当初始化一个类,如果其父类没有初始化,会先初始化父类
类的被动引用(不会发生类的初始化)
1.当访问一个静态域时,只有真正声明这个域的类才会被初始化,如当通过子类引用父类的静态变量,不会导致子类的初始化
public class test {
static { System.out.println("main方法所在类初始化"); }
public static void main(String[] args) throws ClassNotFoundException {
System.out.println(Children.A);
}
}
class Parent{
static { System.out.println("Parent初始化"); }
public static int A = 1;
}
class Children extends Parent{
static { System.out.println("Children初始化"); }
public static int B = 2;
}
可以看到并没有初始化Children类
2.通过数组定义类引用,不会出发此类的初始化
Children[] array = new Children[5]
数组只是分配了空间,并不会加载类
3.引用常量不会出发此类的初始化(常量在连接阶段就存入调用类的常量池中了在这里插入代码片
)
//同样在main方法中调用上面代码中Children类的常量B,Children和Parent两个类都不会初始化
System.out.println(Children.B)
将class文件字节码内容加载到内存中,并将这些静态数据转换成方法区的运行时数据结构,然后再堆中生成一个代表这个类的java.lang.Class对象,作为方法区中类数据的访问入口
引导类加载器(Bootstap Classloader):用C++编写,是JVM自带的类加载器,负责Java平台核心库,用来装在核心类库,该加载器无法直接获取
扩展类加载器(Extension Classloader):负责jre/lib/ext目录下的jar包或 -D java.ext.dirs 指定的目录下的jar包装入工作库
用户类加载器(System Classloader):负责java -classpath 或 -D java.class.path 所指的目录下的类与jar包装入工作库,是最常用的类加载器
public static void main(String[] args) {
//获得系统类加载器
ClassLoader systemClassLoader = ClassLoader.getSystemClassLoader();
System.out.println(systemClassLoader);
//获得系统类加载器的父类加载器->扩展加类载器
ClassLoader extensionClassloader = systemClassLoader.getParent();
System.out.println(extensionClassloader);
//获得扩展类加载器的父类->引导类加载器(C/C++)
ClassLoader bootstapClassloader = extensionClassloader.getParent();
System.out.println(bootstapClassloader);
System.out.println("==============================");
//获得用户自定义类是那个类加载器加载的
ClassLoader myClassLoader = Class.forName("com.zwt.learning.User").getClassLoader();
System.out.println(myClassLoader);
//JDK内置类是由那个类加载器加载的
ClassLoader objClassLoader = Class.forName("java.lang.Object").getClassLoader();
System.out.println(objClassLoader);
}
由于引导类加载器是由C或C++编写,java无法直接获取所以获取了一个null
类缓存:标准的JavaSE类加载器可以按照需求查找类,一旦某个类被加载到类加载器中,他将维持加载(缓存)一段时间,不过JVM垃圾回收机制可以回收这些Class对象
=====================================
public @interface ZwtNote {
String name() default "zwt";
int age() default 22;
}
与接口类似,使用 @interface 关键字 default 关键字为其指定默认值
@ZwtNote(name = "111")
public class aaa {
@ZwtNote
public void fighting(){
}
}
使用 @Retention 来设定注解的保留策略,SOURCE 如果没有处理注解的工具,那注解就是普通的注释,不同的注解有不同的注解处理器,通常针对运行时注解采用反射机制处理,针对编译时注解采用AbstractProcessor来处理 通过反射获取运行时的完整结构 Method、Fied、Constructor对象都有setAccessible()方法,setAccessible()方法是启用和禁用访问安全检查的开关,设置为true关闭Java语言的安全访问检查,关闭后提高反射的效率,使得原本无法访问的私有成员也可以访问 init:被注解处理工具调用 注册注解处理器 先建立一个module,一定要是Java library,android library使用的是 Open JDK,Open JDK中没有AbstractProcessor 类 建立一个实体类,用于存储获得注解的一些相关信息 注册注解处理器 (注意注册注解处理器) 最后在项目中导入刚刚编写的 zBing 的依赖注解处理器
运行时注解
Field(字段)、Method(方法)、Constructor(构造器)、Superclass(父类)、Interface(接口)、Annotation(注解) public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException, NoSuchMethodException, InvocationTargetException, IllegalAccessException, InstantiationException {
//获得用户自定义类是那个泪加载器加载的
User user = new User(1, "zwt", 18);
Class<? extends User> myClass = user.getClass();
System.out.println(myClass.getName()); //获得包名+类名
System.out.println(myClass.getSimpleName()); //获得类名
//获得类的属性
System.out.println("===============================================");
Field[] fields = myClass.getFields(); //只能获得public属性,由于User类中无public定义属性,所以打印为空
fields = myClass.getDeclaredFields(); //能获得全部属性
for (Field field : fields) {
System.out.println(field);
}
Field field = myClass.getDeclaredField("name");//获得指定的属性,由于User.name 是private修饰,使用getField("name")会编译报错
System.out.println(user.getName());
field.setAccessible(true);//由于name是私有属性,要想访问修改需要禁用访问安全检查
field.set(user,"zzz"); //设置属性,第一个参数传递一个实体对象
System.out.println(user.getName());
//获得类的方法
System.out.println("===================================================");
Method[] methods = myClass.getMethods(); //获得本类及其父类所有public方法
for (Method method : methods) {
System.out.println("getMethods:::"+method);
}
System.out.println("");
methods = myClass.getDeclaredMethods(); //获得本类的所有方法,不会获取继承父类的方法
for (Method method : methods) {
System.out.println("getDeclaredMethods:::"+method);
}
Method setName = myClass.getDeclaredMethod("setName", String.class);//方法没有参数第二个传null即可
System.out.println("获得指定方法:::"+setName);
System.out.println(user.getName());
setName.invoke(user,"lll"); //调用方法,第一个参数传递一个实体对象
System.out.println(user.getName());
//获得指定构造器
System.out.println("==============================================");
Constructor<?>[] constructors = myClass.getConstructors(); //获得本类的public构造器
for (Constructor<?> constructor : constructors) {
System.out.println(constructor);
}
System.out.println("");
constructors = myClass.getDeclaredConstructors(); //获得本类的所有构造器
for (Constructor<?> constructor : constructors) {
System.out.println(constructor);
}
Constructor<? extends User> constructor = myClass.getConstructor(int.class, String.class, int.class);
System.out.println("获得指定构造:::"+constructor);
User user1 = constructor.newInstance(2, "yyy", 18);//通过制定的构造器构造对象
System.out.println(user1);
//获得注解
System.out.println("==============================================");
Annotation[] annotations = myClass.getAnnotations();
for (Annotation annotation : annotations) {
System.out.println(annotation);
}
Table annotation = (Table) myClass.getAnnotation(Table.class);//获得指定的注解
System.out.println(annotation.value());
Field annotation1 = name.getAnnotation(Field.class);//这里的name是上面user类中name属性的反射
System.out.println(annotation1.columnName());
System.out.println(annotation1.type());
System.out.println(annotation1.length());
}
@Table("db_student")
class User{
@Field(columnName = "student_id", type = "int", length = 10)
private int id;
@Field(columnName = "student_name", type = "varchar", length = 10)
private String name;
@Field(columnName = "student_age", type = "int", length = 3)
private int age;
public User(int id, String name, int age) {
this.id = id;
this.name = name;
this.age = age;
}
public User(){}
//省略getter setter方法
@Override
public String toString() {
return "User{" +
"id=" + id +
", name='" + name + '\'' +
", age=" + age +
'}';
}
}
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@interface Table{
String value();
}
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@interface Field{
String columnName();
String type();
int length();
}
编译时注解
public class ClassProcessor extends AbstractProcessor {
@Override
public synchronized void init(ProcessingEnvironment processingEnv) {
super.init(processingEnv);
}
@Override
public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
//TODO
return false;
}
@Override
public Set<String> getSupportedAnnotationTypes() {
Set<String> annotataions = new LinkedHashSet<String>();
annotataions.add(zwtBindView.class.getCanonicalName())
return annotataions;
}
@Override
public SourceVersion getSupportedSourceVersion() {
return super.getSupportedSourceVersion();
}
}
process:每个处理器的主方法,在这写扫描、评估和处理注解的代码,以及生成Java文件
getSupportedAnnotationTypes:必须指定的方法,指定这个注解处理器是注册给那个注解的,返回是一个字符串集合,包含本处理器想要处理的注解类型的合法全称
getSupportedSourceVersion:用来指定使用的Java版本,通常返回SourceVersion.latestSupported() Java 7以后可以使用注解来代替getSupportedAnnotationTypes 和 getSupportedSourceVersion 方法,但考虑兼容性,低版本的无法使用这种方式,不建议采用这种注解的方法@SupportedSourceVersion(SourceVersion.RELEASE_8)
@SupportedAnnotationTypes("com.zwt.zwtBindView.zwtBindView")
public class ClassProcessor extends AbstractProcessor {
@Override
public synchronized void init(ProcessingEnvironment processingEnv) {
super.init(processingEnv);
}
@Override
public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
return false;
}
}
首先在新建是库的 main 目录下建立 resources 资源文件夹,再在 resource 文件夹中建立 META-INF/services 目录,最后在 META-INF/services 文件夹中创建 javax.annotation.processing.Processor 文件,在这个文件中将刚刚写的 ClassProcessor 路径配置其中
当然嫌麻烦的话可以使用 @AutoService 注解,就不必手动建立这个文档,但要导入依赖
手写一个简单的BindView
定义注解@Target(ElementType.FIELD)
@Retention(RetentionPolicy.CLASS)
public @interface zwtBindView {
int value();
}
public class FieldViewBinding {
private int id; //需要绑定的id
private TypeMirror fieldType; //需要绑定组件的类型
private String fieldName; //需要绑定组件的名称
public FieldViewBinding(int id, TypeMirror fieldType, String fieldName) {
this.id = id;
this.fieldType = fieldType;
this.fieldName = fieldName;
}
//省略getter 和 setter 方法
}
public class ClassProcessor extends AbstractProcessor {
private Messager messager;
private Elements elements;
private Filer filer;
@Override
public synchronized void init(ProcessingEnvironment processingEnvironment) {
super.init(processingEnvironment);
elements = processingEnvironment.getElementUtils();
filer = processingEnvironment.getFiler();
messager = processingEnvironment.getMessager();
}
@Override
public boolean process(Set<? extends TypeElement> set, RoundEnvironment roundEnvironment) {
messager.printMessage(Diagnostic.Kind.NOTE, "zwt");
// 获得所有的使用@zwtBindview所修饰的字段,并存储在一个哈希表中
Map<TypeElement, List<FieldViewBinding>> targetMap = getTargeMap(roundEnvironment);
// 根据创建map创建一个java文件
createJavaFile(targetMap);
return true;
}
@Override
public Set<String> getSupportedAnnotationTypes() {
Set<String> annotation = new LinkedHashSet<String>();
annotation.add(zwtBindView.class.getCanonicalName());
return annotation;
}
@Override
public SourceVersion getSupportedSourceVersion() {
return SourceVersion.latestSupported();
}
private Map<TypeElement, List<FieldViewBinding>> getTargeMap(RoundEnvironment roundEnvironment){
Map<TypeElement, List<FieldViewBinding>> targetMap = new HashMap<>();
// 获取所有使用@zwtBindview所修饰的字段
Set<? extends Element> annotatedElements = roundEnvironment.getElementsAnnotatedWith(zwtBindView.class);
for (Element element : annotatedElements) {
//获取元素注解的值
int bindId = element.getAnnotation(zwtBindView.class).value();
//获得元素的类型
TypeMirror fieldType = element.asType();
//获得元素的名称
String fieldName = element.getSimpleName().toString();
//获取element所在的类名称
TypeElement typeElement = (TypeElement)element.getEnclosingElement();
List<FieldViewBinding> list = targetMap.get(typeElement);
if (list == null){
list = new ArrayList<>();
targetMap.put(typeElement, list);
}
list.add(new FieldViewBinding(bindId, fieldType, fieldName));
}
return targetMap;
}
private void createJavaFile(Map<TypeElement, List<FieldViewBinding>> bindMap){
for (Map.Entry<TypeElement, List<FieldViewBinding>> map: bindMap.entrySet()) {
TypeElement typeElement = map.getKey();
List<FieldViewBinding> list = map.getValue();
if (list == null || list.size() == 0){
continue;
}
String packageName = elements.getPackageOf(typeElement).getQualifiedName().toString();
String className = typeElement.getQualifiedName().toString();
String newClassName = className.substring(packageName.length()+1) + "_ViewBinding";
StringBuffer builder = new StringBuffer();
builder.append("package ").append(packageName).append(";\n\n")
.append("public class ").append(newClassName).append(" {\n\n")
.append("\tpublic static void bind("+className+" activity) {\n");
for (FieldViewBinding fieldViewBinding : list) {
builder.append(String.format("\t\tactivity.%s = (%s) activity.findViewById(%d);\n", fieldViewBinding.getFieldName(), fieldViewBinding.getFieldType(), fieldViewBinding.getId()));
}
builder.append("\t}\n}");
try { // write the file
JavaFileObject source = filer.createSourceFile(packageName+"."+newClassName);
Writer writer = source.openWriter();
writer.write(builder.toString());
writer.flush();
writer.close();
} catch (IOException e) {
// Note: calling e.printStackTrace() will print IO errors
// that occur from the file already existing after its first run, this is normal
}
}
}
}
注册后在编译期间就会运行到process方法从而生成一个java文件,但这个文件没有执行,并且这个文件在编译前是不存在的所以无法直接调用,需要使用反射的机制在编译完成后动态的调用public class zwtBind {
public static void bindView(Object activity){
try {
Class bindClass = Class.forName(activity.getClass().getName() + "_ViewBinding");
Method bind = bindClass.getDeclaredMethod("bind", activity.getClass());
bind.invoke(null,activity);
} catch (ClassNotFoundException e) {
e.printStackTrace();
}catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
}
}
注意引入注解处理器要使用annotationProcessor,在编译时,会自动识别META-INF里的类名,找到类进行注解处理器的执行,否则注解处理器不会执行public class MainActivity extends AppCompatActivity {
@zwtBindView(R.id.hello_text)
TextView helloText;
@zwtBindView(R.id.hello_button)
Button helloButton;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
zwtBind.bindView(this); //执行生成的绑定组件的代码
helloButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
helloText.setText("I am fine");
}
});
}
}