前言: 这学期学习了编译原理,了解了代码中的注释是在编译的时候会被过滤——因为它是给人读的,不是给机器读的。所以,就自然想到了如何才能在代码中保留注释,但是又不影响到程序的执行?虽然,这个想法其实没有多大的作用,但是感觉很有趣,就简单的实现了一下。这里我们借助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();
        }
    }
}

运行截图

java 修改配置文件 java修改配置文件保留注释_java

PS:在JD-GUI中打开 APT.class文件,可以看见见过反编译的文件是没有注释的。

java 修改配置文件 java修改配置文件保留注释_反射_02


注解中的信息是属于代码的,所以可以得到保留,因此就间接实现了注释的保留。

java 修改配置文件 java修改配置文件保留注释_java 修改配置文件_03