Java语法-反射、注解(及APT)、泛型、动态代理

反射

获取Class
//法一,对象.getClass,不适用于int等基础类型
Class clazz = new Person().getClass
//法二,.Class
Class clazz = Person.Class
//法三,包名
try {
    Class clazz = Class.forName("com.android.test.Person");
} catch (ClassNotFoundException e) {
    e.printStackTrace();
}
Class内容清单获取

获取Name

/**
 * 获取类型限定类名,例如:
 * 引用类Person => com.android.text.Person
 * 内部类 => com.frank.test.Outter$Inner
 * 基础类型int => int 
 * 数组string[][] => [[Ljava.lang.String
 **/

clazz.getName();
/**
 * 获取类型名,例如:
 * 引用类Person => Person
 * 内部类 => Inner
 * 匿名内部类 => 空
 * 基础类型int => int 
 * 数组string[][] => String[][]
 **/
Class.getSimpleName();
/**
 * 获取对象的官方名字,也是全限定类名,例如:
 * 引用类Person => com.android.text.Person
 * 内部类 => com.frank.test.Outter.Inner
 * 匿名内部类 => null
 * 局部类 => null
 * 基础类型int => int 
 * 数组string[][] => Ljava.lang.String[][]
 **/
 Class.getCanonicalName();

获取修饰符
四大类:限制作用域(public等)、abstract、static、注解

类修饰符是通过一个int来记录的,Person.class.getModifiers()即可获得 如1025之类的数字,它就记录着该类的修饰符,可通过Modifier来解读Modifier.toString(TestModifier.class.getModifiers())即可打印出该类的修饰符

获取Class的Field(成员属性)、Method(方法)、Constructor(构造器)

获取Field

//获取所有的属性,但不包括从父类继承下来的属性
Field[] getDeclaredFields()

//获取自身的所有的 public 属性,包括从父类继承下来的。
Field[] getFields()
//获取Method

Method getDeclaredMethod(String name, Class<?>... parameterTypes)

Method getMethod(String name, Class<?>... parameterTypes)

Method[] getDeclaredMethods()

Method getMethod(String name, Class<?>... parameterTypes) 
//获取Constructor,因为Constructor无法从父类继承,所以无法获取父类的Constructor

Constructor<T> getDeclaredConstructor(Class<?>... parameterTypes)

Constructor<T> getConstructor(Class<?>... parameterTypes)

Constructor<?>[] getDeclaredConstructors()

Constructor<?>[] getConstructors()

Filed Method Contructor 的 操控

Field

//获取变量名 mPerson
getName()
//获取详细类型名 java.util.List<com.android.test.Person>
Type getGenericType()
//获取类型名 java.util.List
Class<?> getType
//修饰符获取,与Class的雷同
int getModifiers()
//实例化一个对象
Person person = (Person)Person.class.newInstance();

Person clazz = Person.class
Field name = clazz.getField("name");
//如果访问的Field是private的则还需要如下设置
name.setAccessible(true);
//内容获取,Field提供了各种get方法
name.getString(person);
//内容赋值,Field提供了各种set方法
name.setString(person,"huangshunbo");

Method

//获取方法名
getName()
//获取修饰符
getModifiers()
//获取所有参数
Parameter[] getParameters()
//获取所有的参数类型
Class<?>[] getParameterTypes()
//获取所有的参数类型,包括泛型
Type[] getGenericParameterTypes()
//获取返回值类型
Class<?> getReturnType()
//获取返回值类型包括泛型
Type getGenericReturnType()
//获取异常类型
Class<?>[] getExceptionTypes()
Type[] getGenericxceptionTypes()
//执行方法
invoke(Object obj,Object... args)
Parameter操作
//获取参数名字
getName
//获取参数类型
Class<?> getType()
//获取参数的修饰符
int getModifiers()

Constructor

//创建实例Class.newInstance()和Constructor.newInstance()

//Class.newInstance() 只能调用无参的构造方法,而 Constructor.newInstance() 则可以调用任意的构造方法。
//Class.newInstance() 通过构造方法直接抛出异常,而 Constructor.newInstance() 会把抛出来的异常包装到 InvocationTargetException 里面去,这个和 Method 行为一致。
//Class.newInstance() 要求构造方法能够被访问,而 Constructor.newInstance() 却能够访问 private 修饰的构造器。

Class clz = Person.class;
Person mP1 = (Person) clz.newInstance();

Constructor con = clz.getConstructor(String.class);
Person mP2= (Person) con.newInstance("huangshunbo");

反射与数组

本质上数组也是一个Class , 以上方法均可用
Class提供判断是否数组

isArray()

动态创建数组

//相当于new int[2][3]
Array.newInstance(int.class,2,3)

反射与枚举

枚举本质也是一个Class,拥有以上方法
Class 提供判断是不是枚举

isEnum()

获取所有的枚举常量

getEnumConstants()

反射常见异常

这里写图片描述

Java反射基础知识

Java:一步步带你深入了解神秘的Java反射机制


注解

元注解有五种@Retention、@Documented、@Target、@Inherited、@Repeatable;用于解释用户的自定义标签

Retention 表示注解的存活时间

public enum RetentionPolicy { 
//注解只在源码阶段保留,编译器进行编译时丢弃,如@Override
SOURCE, 
//注解保留到编译期,不会加载到JVM中,这个阶段可以让编译器帮助我们去动态生成代码
CLASS, 
//注解保留到JVM运行时,会被加载到JVM中,这个可以结合反射使用
RUNTIME 
} 

Documented 将注解中的元素包含到Javadoc中

Target 指定了注解运用的地方

public enum ElementType { 
//可以给一个类型进行注解,如类、接口、枚举
TYPE, 
//可以给属性进行注解
FIELD, 
//可以给方法进行注解
METHOD, 
//方法参数
PARAMETER, 
//可以给构造方法进行注解
CONSTRUCTOR, 
//可以给局部变量进行注解
LOCAL_VARIABLE, 
//可以给一个注解进行注解
ANNOTATION_TYPE, 
//可以给一个包进行注解
PACKAGE, 
//可以给一个方法内的参数进行注解
PARAMETER
} 

Inherited 如果一个超类被 @Inherited 注解过的注解进行注解的话,那么如果它的子类没有被任何注解应用的话,那么这个子类就继承了超类的注解

Repeatable 注解可多次重复使用在一个Target上

@Person(ability="fly")
@Person(ability="speed")
public class SuperMan{}
使用案例

设置注解的属性:注解没有方法,只有属性

//定义了TestAnnotation这个注解
//是对类型(类、接口等)进行注解的
//存活时间是保留到运行期
//拥有两个属性id和msg,id默认是-1
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface TestAnnotation{
    int id() default -1;
    String msg();
}

注解的提取(利用反射)

@TestAnnotation()
public class Test {

    public static void main(String[] args) {

        boolean hasAnnotation = Test.class.isAnnotationPresent(TestAnnotation.class);
        //判断是否有TestAnnotation注解
        if ( hasAnnotation ) {
            TestAnnotation testAnnotation = Test.class.getAnnotation(TestAnnotation.class);
            System.out.println("id:"+testAnnotation.id());
            System.out.println("msg:"+testAnnotation.msg());
            //一般需要根据id和msg的值对Test类做一些操作,这部分其实也需要通过反射来做,此处省略
        }

    }

}

使用

@TestAnnotation(id=1,msg="hello annotation")
public class Test{}

Java预置(自带的)注解

@Deprecated 标记过时的元素
@Override 复写
@SuppressWarnings 阻止警告
@SafeVarargs 参数安全类型注解。它的目的是提醒开发者不要用参数做一些不安全的操作,它的存在会阻止编译器产生 unchecked 这样的警告
@FunctionalInterface 函数式接口注解,用于接口。函数式接口可以很容易转换为 Lambda 表达式

APT (Annotation Processing Tool)

APT(Annotation Processing Tool)即注解处理器,是一种处理注解的工具,确切的说它是javac的一个工具,它用来在编译时扫描和处理注解。注解处理器以Java代码(或者编译过的字节码)作为输入,生成.java文件作为输出。 简单来说就是在编译期,通过注解生成.java文件。
PS:如果在运行期再利用【注解+反射】来处理代码虽然带来了便利但会影响运行性能,而APT可以在编译期就进行处理,所以不影响应用的运行性能。

BindView的实现

创建注解类

@Retention(RetentionPolicy.CLASS)
@Target(ElementType.FIELD)
public @interface BindView {
    int value();
}

添加注解处理器依赖

dependencies {
    implementation 'com.google.auto.service:auto-service:1.0-rc2' 
    implementation project(':apt-annotation')
}

编写注解处理类:获取使用类的注解,并根据注解生成一个java文件,该文件命名为 XXX_ViewBinding.java 里面提供了一个bind方法,用来绑定view

@AutoService(Processor.class)
public class BindViewProcessor extends AbstractProcessor {
    private Messager mMessager;
    private Elements mElementUtils;
    private Map<String, ClassCreatorProxy> mProxyMap = new HashMap<>();
    @Override
    public synchronized void init(ProcessingEnvironment processingEnv) {
        super.init(processingEnv);
        mMessager = processingEnv.getMessager();
        mElementUtils = processingEnv.getElementUtils();
    }
    @Override
    public Set<String> getSupportedAnnotationTypes() {
        HashSet<String> supportTypes = new LinkedHashSet<>();
        supportTypes.add(BindView.class.getCanonicalName());
        return supportTypes;
    }
    @Override
    public SourceVersion getSupportedSourceVersion() {
        return SourceVersion.latestSupported();
    }
    @Override
    public boolean process(Set<? extends TypeElement> set, RoundEnvironment roundEnvironment) {
        mMessager.printMessage(Diagnostic.Kind.NOTE, "processing...");
        mProxyMap.clear();
        //得到所有的注解
        Set<? extends Element> elements = roundEnvironment.getElementsAnnotatedWith(BindView.class);
        for (Element element : elements) {
            VariableElement variableElement = (VariableElement) element;
            TypeElement classElement = (TypeElement) variableElement.getEnclosingElement();
            String fullClassName = classElement.getQualifiedName().toString();
            ClassCreatorProxy proxy = mProxyMap.get(fullClassName);
            if (proxy == null) {
                proxy = new ClassCreatorProxy(mElementUtils, classElement);
                mProxyMap.put(fullClassName, proxy);
            }
            BindView bindAnnotation = variableElement.getAnnotation(BindView.class);
            int id = bindAnnotation.value();
            proxy.putElement(id, variableElement);
        }
        //通过遍历mProxyMap,创建java文件
        for (String key : mProxyMap.keySet()) {
            ClassCreatorProxy proxyInfo = mProxyMap.get(key);
            try {
                mMessager.printMessage(Diagnostic.Kind.NOTE, " --> create " + proxyInfo.getProxyClassFullName());
                JavaFileObject jfo = processingEnv.getFiler().createSourceFile(proxyInfo.getProxyClassFullName(), proxyInfo.getTypeElement());
                Writer writer = jfo.openWriter();
                writer.write(proxyInfo.generateJavaCode());
                writer.flush();
                writer.close();
            } catch (IOException e) {
                mMessager.printMessage(Diagnostic.Kind.NOTE, " --> create " + proxyInfo.getProxyClassFullName() + "error");
            }
        }
        mMessager.printMessage(Diagnostic.Kind.NOTE, "process finish ...");
        return true;
    }
}

init:初始化。可以得到ProcessingEnviroment,ProcessingEnviroment提供很多有用的工具类Elements, Types 和 Filer
getSupportedAnnotationTypes:指定这个注解处理器是注册给哪个注解的,这里说明是注解BindView
getSupportedSourceVersion:指定使用的Java版本,通常这里返回SourceVersion.latestSupported()
process:可以在这里写扫描、评估和处理注解的代码,生成Java文件(process中的代码下面详细说明)

ClassCreatorProxy 类

public class ClassCreatorProxy {
    private String mBindingClassName;
    private String mPackageName;
    private TypeElement mTypeElement;
    private Map<Integer, VariableElement> mVariableElementMap = new HashMap<>();
    public ClassCreatorProxy(Elements elementUtils, TypeElement classElement) {
        this.mTypeElement = classElement;
        PackageElement packageElement = elementUtils.getPackageOf(mTypeElement);
        String packageName = packageElement.getQualifiedName().toString();
        String className = mTypeElement.getSimpleName().toString();
        this.mPackageName = packageName;
        this.mBindingClassName = className + "_ViewBinding";
    }
    public void putElement(int id, VariableElement element) {
        mVariableElementMap.put(id, element);
    }
    /**
     * 创建Java代码
     * @return
     */
    public String generateJavaCode() {
        StringBuilder builder = new StringBuilder();
        builder.append("package ").append(mPackageName).append(";nn");
        builder.append("import com.example.gavin.apt_library.*;n");
        builder.append('n');
        builder.append("public class ").append(mBindingClassName);
        builder.append(" {n");
        generateMethods(builder);
        builder.append('n');
        builder.append("}n");
        return builder.toString();
    }
    /**
     * 加入Method
     * @param builder
     */
    private void generateMethods(StringBuilder builder) {
        builder.append("public void bind(" + mTypeElement.getQualifiedName() + " host ) {n");
        for (int id : mVariableElementMap.keySet()) {
            VariableElement element = mVariableElementMap.get(id);
            String name = element.getSimpleName().toString();
            String type = element.asType().toString();
            builder.append("host." + name).append(" = ");
            builder.append("(" + type + ")(((android.app.Activity)host).findViewById( " + id + "));n");
        }
        builder.append("  }n");
    }
    public String getProxyClassFullName()
    {
        return mPackageName + "." + mBindingClassName;
    }
    public TypeElement getTypeElement()
    {
        return mTypeElement;
    }
}

最终生成的XXX_ViewBinding.java可能如下(比如MainActivity中使用了)

public class MainActivity_ViewBinding {
    public void bind(com.example.gavin.apttest.MainActivity host) {
        host.mButton = (android.widget.Button) (((android.app.Activity) host).findViewById(2131165218));
        host.mTextView = (android.widget.TextView) (((android.app.Activity) host).findViewById(2131165321));
    }
}

使用该注解的入口 (类似于ButterKnife.bind(this)的操作),这部分需要用反射
加入依赖

dependencies {
    implementation project(':apt-annotation')
}

编写入口函数BindViewTools

public class BindViewTools {
    public static void bind(Activity activity) {
        Class clazz = activity.getClass();
        try {
            Class bindViewClass = Class.forName(clazz.getName() + "_ViewBinding");
            Method method = bindViewClass.getMethod("bind", activity.getClass());
            method.invoke(bindViewClass.newInstance(), activity);
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        }
    }
}

泡在网上的日子 Android APT

Android 打造编译时注解解析框架 这只是一个开始

泛型

泛型,即参数化类型。限制类型eg:List< String > 限制该List只能存储String类型。
泛型可以理解为一个编译时的辅助工具,List< String >和List< Integer >
本质上是同一个类型,运行时保存的都是Object。只不过为了方式ClassCastException而提出。
List< Number >和List< Integer >不存在父子关系

泛型:泛型接口、泛型类、泛型方法

通配符,即List< ? >,可以包容List< Integer >、List< String >等
类型通配符上限,List< ? extends Box >,List只能存储Box或Box的子类
类型通配符下限,List< ? super Box >,List只能存储Box或Box的父类
Java中不能创建一个确切的泛型类型的数组,没有泛型数组一说

动态代理

public interface BlogService{
    String getBlog();
}
public class HuangShunboBlog implements BlogService{
    public String getBlog(){
        return "I am HuangShunbo Blog";
    }
}
public class CSDN implements InvocationHadnler
{
    private Object blog;
    public CSDN(Object blog){
        this.blog = blog
    }
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable{
        method.invoke(blog,args);
        return null;
    }
}
public class Test {

    public static void main(String[] args) {
        HuangshunboBlog blog = new HuangshunboBlog();
        InvocationHandler handler= new CSDN(blog);
        BlogService service = (BlogService) Proxy.newProxyInstance(HuangshunboBlog.class.getClassLoader(),HuangshunboBlog.class.getInterfaces(), handler);
        service.getBlog();
    }

}

常见的应用实例是Retrofit

public interface BlogService {
    @GET("blog/{id}")
    Call<ResponseBody> getBlog(@Path("id") int id);
}
BlogService service = retrofit.create(BlogService.class);
Call<ResponseBody> call = service.getBlog(2);
call.enqueue(new Callback<ResponseBody>() {
    @Override
    public void onResponse(Call<ResponseBody> call, Response<ResponseBody> response) {
        try {
            System.out.println(response.body().string());
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    @Override
    public void onFailure(Call<ResponseBody> call, Throwable t) {
        t.printStackTrace();
    }
});

轻松学,Java 中的代理模式及动态代理

猜你喜欢

转载自blog.csdn.net/xiaoru5127/article/details/81167853