前言: 这学期学习了编译原理,了解了代码中的注释是在编译的时候会被过滤——因为它是给人读的,不是给机器读的。所以,就自然想到了如何才能在代码中保留注释,但是又不影响到程序的执行?虽然,这个想法其实没有多大的作用,但是感觉很有趣,就简单的实现了一下。这里我们借助Java语言的一个功能——Annotation注解来实现。
注:关于注解,因为平常很少自己定义,主要就是使用。所以我对于它也只是有一点了解。但是对于这篇博客,需要的知识也不多,主要在CSDN上看几篇相关的博客就行了。
定义注解
说明:该注解的作用范围是 方法和实例变量。
注解在代码中保留的时间是运行时。
它主要有两个属性,第一个是String类型的 comment,我就使用它来给代码添加注解,这样的话,我就可以在代码中保留注释了。但是只有一个功能太单调了,然后我又添加了一个String类型的value,使用它来给被标注的变量赋值,如果是方法的话,那么就使用默认值即可。(当然了,这里我没有在方法上使用它。)
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target(value = {ElementType.METHOD, ElementType.FIELD})
@Retention(value = RetentionPolicy.RUNTIME)
public @interface Comment {
String comment();
String value() default "";
}
定义一个使用注解的类
/**
* 这是一个Java的注释,这里只是作为测试使用。
* 下面是一个 Boy 类,它具有name、age、isSingle属性。
* */
public class Boy {
@Comment(comment = "boy的姓名", value = "龙林夕")
private String name;
@Comment(comment = "boy的年龄", value = "22")
private int age;
@Comment(comment = "boy的感情状况,是否单身", value="true")
private boolean isSingle;
//省略了getter和setter,不过似乎也没有用到,哈哈!
@Override
public String toString() {
return "Boy{" +
"name='" + name + '\'' +
", age=" + age +
", isSingle=" + isSingle +
'}';
}
}
编写注解处理工具
注解并不会主动生效,必须使用相应的工具来处理,这些工具称之为——Annotation Process Tool(APT)。使用它来处理被注解标注的代码,并对之进行处理。我们这里来简单定义一个APT,它的功能很简单——读取注解中的注释和值,并将值赋给相应的变量。这个注释读取出来也没有用,只是简单的打印出来,但是我们的目的就是为了能在运行期看到它!
import java.lang.annotation.Annotation;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
public class APT {
public static Boy process() throws NoSuchMethodException,
IllegalAccessException,
InvocationTargetException,
InstantiationException {
Class<?> clazz = Boy.class; //获取 Class 对象
Constructor con = clazz.getConstructor(null); //获取默认的空构造器
Boy obj = (Boy) con.newInstance(null); //使用空构造器创建对象,并强制类型转换为Boy类型
Field[] fields = clazz.getDeclaredFields(); //获取所有声明的实例变量(包括私有的示例变量),注意不能使用 getFields(),否则无法获取到私有的实例变量。
/**
* 循环处理每一个field,如果它的类型是String类型,就直接把value值赋给它;
* 如果它的类型是 int 类型,就把value转为int类型;
* 如果它的类型是 boolean 类型,就把value转为 boolean 类型。
* 注意:这里处理引用类型和基本数据类型的区别。
* 对于引用类型,我使用了 Class 对象的 isInstance() 方法
* 对于基本数据类型,我使用了 == 的判断方式。
*/
for (Field field : fields) {
Comment comment = field.getAnnotation(Comment.class);
String strComment = comment.comment();
System.out.println(strComment);
String value = comment.value();
Class<?> cla = field.getType();
field.setAccessible(true); //设置可访问权限,否则无法修改私有变量
if (cla.isInstance("")) {
field.set(obj, value);
} else if (cla == int.class) { //基本类型无法使用 isInstance 方法
field.setInt(obj, Integer.parseInt(value));
} else if (cla == boolean.class) {
field.setBoolean(obj, Boolean.parseBoolean(value));
}
}
return obj;
}
}
测试代码
public class Test {
public static void main(String[] args) {
try {
Boy boy = APT.process(); //使用自定义APT处理注解。
System.out.println(boy.toString());
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
}
}
}
运行截图
PS:在JD-GUI中打开 APT.class文件,可以看见见过反编译的文件是没有注释的。
注解中的信息是属于代码的,所以可以得到保留,因此就间接实现了注释的保留。