笔记:注解

JDK 5开始,Java增加了注解,这些标记可以在编译、类加载、运行时被读取,并执行相应的处理
标准注解

  • @Override:对覆盖超类中的方法进行标记,若被标记的方法没有实际覆盖超类中的方法,编译器会发出警告
  • @Deprecated:不鼓励使用或已过时的方法添加注释,当其他人员使用这些方法时,编译器会提示信息
  • @SuppressWarnings:选择性取消特定代码中的警告,需要传入参数
  • @SafeVarargs:JDK 7新增,用来声明使用了可变长度参数的方法,其在与泛型类一起使用时不会出现类型安全问题

元注解

  • @Targe:注解所修饰的对象范围
  • @Inherited:表示注解可以继承
  • @Documented:表示 这个注解应该被JavaDoc工具记录
  • @Retention:用来声明注解的保留策略
  • @Repeatable:JDK 8 新增,允许一个注解在同一声明类型(类、属性或方法)上多次使用

@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)

反射机制允许程序在执行期间借助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;)

Java内存分析

堆:可以存放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;
}

笔记:注解_第1张图片
可见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;
}

笔记:注解_第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);
    }

笔记:注解_第3张图片

由于引导类加载器是由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来处理

运行时注解

通过反射获取运行时的完整结构
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();
}

Method、Fied、Constructor对象都有setAccessible()方法,setAccessible()方法是启用和禁用访问安全检查的开关,设置为true关闭Java语言的安全访问检查,关闭后提高反射的效率,使得原本无法访问的私有成员也可以访问

编译时注解

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();
    }
}

init:被注解处理工具调用
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 路径配置其中
笔记:注解_第4张图片
当然嫌麻烦的话可以使用 @AutoService 注解,就不必手动建立这个文档,但要导入依赖
笔记:注解_第5张图片

手写一个简单的BindView

先建立一个module,一定要是Java library,android library使用的是 Open JDK,Open JDK中没有AbstractProcessor 类
笔记:注解_第6张图片
定义注解

@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();
        }
    }
}

最后在项目中导入刚刚编写的 zBing 的依赖
笔记:注解_第7张图片
注意引入注解处理器要使用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");
            }
        });
    }
}

最后在build目录下会生成绑定的代码
笔记:注解_第8张图片
之后就可以打个jar包在其他项目中导入使用了,哈哈
笔记:注解_第9张图片

你可能感兴趣的:(android,android)