反射
获取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()
反射常见异常
注解
元注解有五种@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();
}
}
}
泛型
泛型,即参数化类型。限制类型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();
}
});