(记录一下一些知识概念,用于自己遗忘时候的查缺补漏)
java注解知识
1.元注解
就是用来负责注解其他注解的注解. 主要有5个.
- @ Target 注解目标
- @ Retention 注解时长
- @ Documented 文档
- @ Inherited 继承
1.1 @ Target 注解
Annotation可被用于 packages、types(类、接口、枚举、Annotation类型)、类型成员(方法、构造方法、成员变量、枚举值)、方法参数和本地变量(如循环变量、catch参数)。在Annotation类型的声明中使用了target可更加明确其修饰的目标。 作用:用于更加精确描述注解的使用范围(即:被描述的注解可以用在什么地方) 取值(ElementType)有:
- CONSTRUCTOR:用于描述构造器
- FIELD:用于描述成员变量
- LOCAL_VARIABLE:用于描述局部变量
- METHOD:用于描述方法
- PACKAGE:用于描述包
- PARAMETER:用于描述参数
- TYPE:用于描述类、接口(包括注解类型) 或enum声明
1.2 @ Retention注解
@ Retention定义了该Annotation被保留的时间长短
作用:表示需要在什么级别保存该注释信息,用于描述注解的生命周期(即:被描述的注解在什么范围内有效)
取值(RetentionPoicy)有: 1.SOURCE:在源文件中有效(即源文件保留,使用场景比如语法检测,ATP:annotation processor tools) 2.CLASS:在class文件中有效(即class保留,使用比如说字节码插桩) 3.RUNTIME:在运行时有效(即运行时保留,使用比如说在反射机制里面)
1.3 @ Documented 注解
@ Documented用于描述其它类型的annotation应该被 javadoc工具记录
1.4 @ Inherited 注解
作用:指定被它修饰的注解具有继承性(比如InheritedAnnotation注解了BaseClass,SubClass继承了BaseClass,这时SubClass也会具有InheritedAnnotation注解,但方法跟参数等的注解不会被继承)
2. java1.8 多重注解
java 在1.8之后支持多重注解,以前注解只能注解一次.多次注解就会报错误提示信息"duplicate annotatiion". 现在以类似方法进行多重注解.
@interface Hints {
Hint[] value();
}
@Repeatable(Hints.class)
@interface Hint {
String value();
}
前一个注解用来存储后面一个注解.在使用时候,使用后面的注解进行代码的注解,但是解析注解时候需要使用前面的注解进行解析.
3.自定义注解
使用@ interface自定义注解时,自动继承了java.lang.annotation.Annotation接口,由编译程序自动完成其他细节。在定义注解时,不能继承其他的注解或接口。@ interface用来声明一个注解,其中的每一个方法实际上是声明了一个配置参数。方法的名称就是参数的名称,返回值类型就是参数的类型(返回值类型只能是基本类型、Class、String、enum)。可以通过default来声明参数的默认值。 自定义注解举例:
//注解目标:字段
@Target(ElementType.FIELD)
//注解存续期:java运行时
@Retention(RetentionPolicy.RUNTIME)
//可以继承的
@Inherited
public @interface FruitColor{
enum Color{RED,GREEN,BLUE};
public Color fruitColor() default Color.GREEN;
}
在例子里面使用了一个枚举类,列举三种水果颜色 red,green和blue. 注解类里面的方法值都是注解的参数,注解参数可以有默认值,但是不能为空. 注解类里面一个方法fruitColor(),就是一个注解使用时候的参数. 自定义注解使用:
@FruitColor(fruitColor = FruitColor.Color.GREEN)
private String appleColor;
参数里面就是注解类中的方法名=使用的枚举值.
4. 注解和注解解释器的实例
在代码中使用了注解,就要使用注解解释器,否则注解跟注释就没有什么区别.
4.1 水果注解实例
定义注解水果颜色FruitColor.java:
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@Inherited
public @interface FruitColor{
enum Color{RED,GREEN,BLUE};
public Color fruitColor() default Color.GREEN;
}
定义注解水果名字FruitName.java:
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface FruitName {
//value指是默认的
String value() default "";
}
定义注解水果供货商FruitProvider.java
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface FruitProvider{
/**
* 供应商编号
*/
int id() default -1;
/**
* 供应商名称
*/
String name() default "";
/**
* 供应商地址
*/
String address() default "";
}
注解解释器:
/**
* 注解解释器
*/
public class FruitInfoUtil{
public static void getFruitInfo(Class<?> clazz) {
String strFruitName = " 水果名称:";
String strFruitColor = " 水果颜色:";
String strFruitProvicer = "供应商信息:";
Field[] fields = clazz.getDeclaredFields();
for (Field field : fields) {
if (field.isAnnotationPresent(FruitName.class)) {
FruitName fruitName = (FruitName) field.getAnnotation(FruitName.class);
strFruitName = strFruitName + fruitName.value();
System.out.println(strFruitName);
} else if (field.isAnnotationPresent(FruitColor.class)) {
FruitColor fruitColor = (FruitColor) field.getAnnotation(FruitColor.class);
strFruitColor = strFruitColor + fruitColor.fruitColor().toString();
System.out.println(strFruitColor);
} else if (field.isAnnotationPresent(FruitProvider.class)) {
FruitProvider fruitProvider = (FruitProvider)field.getAnnotation(FruitProvider.class);
strFruitProvicer = " 供应商编号:" + fruitProvider.id() + " 供应商名称:" + fruitProvider.name() + " 供应商地址:" + fruitProvider.address();
System.out.println(strFruitProvicer);
}
}
}
}
类里面使用注解
public class Apple{
@FruitName("Apple")
private String appleName;
@FruitColor(fruitColor = FruitColor.Color.GREEN)
private String appleColor;
@FruitProvider(id = 1, name = "陕西红富士集团", address = "陕西省西安市延安路89号红富士大厦")
private String appleProvider;
public void setAppleColor(String appleColor) {
this.appleColor = appleColor;
}
public String getAppleColor() {
return appleColor;
}
public void setAppleName(String appleName) {
this.appleName = appleName;
}
public String getAppleName() {
return appleName;
}
public void setAppleProvider(String appleProvider) {
this.appleProvider = appleProvider;
}
public String getAppleProvider() {
return appleProvider;
}
public void displayName() {
System.out.println("水果的名字是:苹果");
}
}
运行输出结果:
/************ 输出结果 ***************/
public class FruitRun{
/**
* @param args
*/
public static void main(String[] args) {
FruitInfoUtil.getFruitInfo(Apple.class);
}
}
4.2 Method注解实例(含多重注解)
定义注解GetMethod.java:
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
@Repeatable(GetMethods.class)
public @interface GetMethod{
public enum Type{ONE, TWO}
Type setType() default Type.ONE;
}
定义多重注解包含体GetMethods.java:
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface GetMethods{
GetMethod[] value();
}
定义注解解释器MethodUtil,java:
/*** Created by WenpengL1 on 2017/4/19. */
public class MethodUtil{
public static void getInfo(Class<?> clazz) {
Method[] methods = clazz.getDeclaredMethods();
for (Method method : methods) {
method.getAnnotation(GetMethod.class);
method.getDeclaredAnnotations();
AnnotatedType annotatedType=method.getAnnotatedReturnType();
if (method.isAnnotationPresent(GetMethods.class)) {
if (GetMethod.Type.ONE==method.getAnnotation(GetMethods.class).value()[0].setType()) {
try {
method.invoke(clazz.newInstance());
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
}
}
使用注解MyMethod.java:
public class MyMethod{
@GetMethod(setType = GetMethod.Type.ONE)
@GetMethod(setType = GetMethod.Type.TWO)
public void printMessage() {
System.out.print("this is one message");
}
}
运行结果
public class MethodRun{
public static void main(String[] args) {
MethodUtil.getInfo(MyMethod.class);
}
}
运行结果:
4.3 apt注解举例
实现注解:
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.SOURCE)
public @interface MyAnnotation {
String value() default "1";
}
使用注解:
public class MainActivity extends AppCompatActivity {
@MyAnnotation
int anInt;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
}
定义注解处理module,记得使用Java library:
注解处理器的实现,此处仅打印提示字符:
@SupportedAnnotationTypes("com.example.myapplication.annotation.MyAnnotation")
public class MyClassProcessor extends AbstractProcessor {
@Override
public boolean process(Set<? extends TypeElement> set, RoundEnvironment roundEnvironment) {
Messager messager = processingEnv.getMessager();
messager.printMessage(Diagnostic.Kind.NOTE,"=================");
return false;
}
}
配置注解处理器(使用AutoService 容易有gradle的兼容问题)
文件里面声明处理的注解的全类名
com.example.lib.MyClassProcessor
然后编译就会发现,编译时候,我们的注解处理器打印的日志了
The following annotation processors are not incremental: lib.jar (project :lib).
Make sure all annotation processors are incremental to improve your build speed.
注: =================
注: =================
Java 反射内容
1. 反射
基本的反射内容很简单,放在这里备查吧.
- 在运行时判断任意一个对象所属的类;
- 在运行时构造任意一个类的对象;
- 在运行时判断任意一个类所具有的成员变量和方法;
- 在运行时调用任意一个对象的方法;
- 生成动态代理。
- Class类 代表类的实体,在运行的Java应用程序中表示类和接口 获取一个类的class对象,如String.Class或者对象调用getClass().
- Constructor类 代表类的构造方法
- 获取所有构造方法, getDeclaredConstructors() 获取所有的构造方法 getDeclaredConstructor(传入对应的参数)就可以获得相应的构造方法对象(无论私有与否)
- 获取公共的构造方法 getConstructors()得到所有的公共构造方法 getConstructor(传入相应参数)就可以获得响应的公共构造方法对象
- 获取类的实例对象 用获取到的constructor对象,传入响应的参数,就可以获得类对象 constructor.newInstance(传入参数)
- Field类 代表类的成员变量(成员变量也称为类的属性) 获取字段对象, getDeclaredFields()获取所有的字段对象
- Method类 代表类的方法 getDeclaredMethods()获取类中所有的方法 getMethods() 获取类中的公共方法 activityClass.getMethod("setContentView", int.class); method.invoke(activity, layoutId);这样传递进参数,可以用对象来调用对应的方法.
2. 字段修饰符
可以通过反射获取 修饰符,包括类、方法、字段,都有类似的方法.
Class.getModifiers;
Method.getModifiers;
Field.getModifiers;
返回是int 的编码值,代表了 public, protected, private, final, static, abstract这些的修饰符.
一般被final修饰的字段是不允许修改的,下面例子里面进行两次反射进行修改.
public class Test1228 {
private static final Integer aInt = 2;
@Override
public String toString() {
return "Test1228{aInt=" + aInt + "}";
}
public static void main(String[] args) {
Test1228 test1228 = new Test1228();
String modifiers = null;
try {
//获取aInt属性,并打印修饰符
System.out.println(test1228);
Field aInt = test1228.getClass().getDeclaredField("aInt");
modifiers = Modifier.toString(aInt.getModifiers());
System.out.println("先前修饰符" + modifiers);
//修改修饰符,去除掉final
Field modifierField = Field.class.getDeclaredField("modifiers");
modifierField.setAccessible(true);
modifierField.setInt(aInt, aInt.getModifiers() & ~Modifier.FINAL);
//修改aInt的值
aInt.setAccessible(true);
aInt.set(test1228, Integer.valueOf(6));
//aInt.setInt(test1228, 6);
System.out.println(test1228);
System.out.println("后来修饰符" + Modifier.toString(aInt.getModifiers()));
System.out.println("aInt值 :"+Test1228.aInt);
new Thread(() -> System.out.println("aInt值 :"+Test1228.aInt)).start();
System.out.println("反射获取aInt值 :" + aInt.get(test1228));
} catch (NoSuchFieldException | IllegalAccessException e) {
e.printStackTrace();
}
}
}
打印结果:
Test1228{aInt=2}
先前修饰符private static final
Test1228{aInt=6}
后来修饰符private static
aInt值 :6
反射获取aInt值 :6
aInt值 :6
代码里面的aInt 属性为Integer.当属性为对象时候反射修改完成后,调用属性都是正常的修改之后的数值了;当属性为基本数据类型或者是String a="ss"; 字符串对象时候,这时候用反射修改完成之后,用对象方式调用(如test.aInt)发现还是先前的数值,这是因为虚拟机会把属性优化成为基本的常量,只能使用反射来获取最新的数值.