手写反射实现读取自定义注解中的属性值

背景

凡事我们都要知其然,知其所以然,我们既然要研究反射,就要知道
反射的原理是什么:在Java中,反射(Reflection)是指在运行时获取和操作类的信息的能力。它允许程序在运行时动态地检查类、获取类的属性和方法,并且可以调用类的方法或创建类的实例。
但是他到底是怎么做到运行时获取到信息的呢?我们一起来看一看吧,归根结底,还是匹配字符串,但是是如何匹配的我们还需要考究一下,下面我就手写自定义注解的例子讲解如何使用反射的,并把他手写出来。

过程

注解概念

当应用5W2H方法分析注解时,我们可以按照以下方式来描述注解的作用和含义:

What(什么):注解是一种特殊的标记,用于提供元数据。它可以应用于类、方法、字段等程序元素上,以附加额外的信息。

Why(为什么):注解的目的是为了丰富和增强代码的表达能力和功能。它可以提供额外的元数据,用于编译时检查、运行时处理和自动生成代码等。

Where(在哪里):注解可以应用于各种程序元素,包括类、方法、字段等。具体可以根据注解的定义和目的来决定它可以应用的位置。

When(何时):注解在编码过程中可以随时使用。根据需求,可以在需要注解的代码上使用它们,例如标记过时的方法、指定配置信息等。

Who(谁):注解可以由开发人员或框架进行定义和使用。开发人员可以为自己的代码添加注解,而框架可以使用注解来实现特定的行为或功能。

How(如何):注解通过使用@符号作为前缀,并提供注解名称和可选的参数来声明和使用。开发人员可以自定义注解,并通过反射机制来解析注解并采取相应的操作。

How much(多少):注解的数量和复杂性取决于具体的应用场景和需求。Java提供了一些内置的注解,同时也可以根据需要自定义注解。

明确自定义注解的边界

在Java中,注解(Annotation)是一种用于提供元数据的特殊标记。它可以应用于类、方法、字段等程序元素上,以提供关于这些元素的额外信息。注解本身不会直接影响程序的执行,但可以被编译器、工具和框架等处理,以实现特定的行为或功能。

1、定义注解:使用 @interface 关键字定义一个新的注解。注解可以包含元素,用于指定注解的属性。

// 定义一个自定义注解
public @interface MyAnnotation {
    String value();
    int count() default 0;
}

2、使用注解:在适当的地方使用自定义注解。可以将注解应用于类、方法、字段等。

// 使用自定义注解
@MyAnnotation(value = "Hello", count = 5)
public class MyClass {
    @MyAnnotation("World")
    private String message;

    @MyAnnotation(value = "Method")
    public void myMethod() {
        // 方法体
    }
}

3、解析注解:在程序中通过反射机制解析和处理注解。可以获取注解的属性值,并根据注解的定义执行相应的操作。

// 解析注解
Class<?> clazz = MyClass.class;
MyAnnotation annotation = clazz.getAnnotation(MyAnnotation.class);
String value = annotation.value();
int count = annotation.count();

手写反射实现自定义注解

我们主要是实现自定义注解步骤的第三步,上述的步骤是使用getAnnotation
方法获取的注解的相关信息,我们现在不使用这个API,自己手写一个实现文本匹配从而实现getAnnotation的效果:

public class Client {
    public static void main(String[] args) throws IOException, NoSuchFieldException, ClassNotFoundException {

        MapImpl map = new MapImpl();
        map.readFileInfo();
        System.out.println(map.min());
        System.out.println(map.max());
        System.out.println(map.errorMsg());

    }
}
@Target({ElementType.FIELD})
//表示自定义注解可保留到运行时
@Retention(RetentionPolicy.RUNTIME)
//自定义注解,注解名字是Length
public @interface Length {
    // 允许的字符串长度最小值
    int min();
    // 的允许字符串长度最大值
    int max();
    // 自定义错误提示
    String errorMsg();
}

在这个类里边写识别字符串的操作。

public class MapImpl implements Length {
    @Override
    public int min()  {
//        String min = this.readFile("min");
//        System.out.println("最小值为:"+min);
//        return Integer.valueOf(min) ;
        String minValue = this.getMethedValue(Thread.currentThread().getStackTrace()[1].getMethodName());
        return Integer.valueOf(minValue);
    }

    @Override
    public int max() {
//        String max = this.readFile("max");
//        System.out.println("最大值为:"+max);
//        return Integer.valueOf(max) ;

        String maxValue = this.getMethedValue(Thread.currentThread().getStackTrace()[1].getMethodName());
        return Integer.valueOf(maxValue);
    }

    @Override
    public String errorMsg() {
//        String errorMessage = this.readFile("errorMsg");
//        System.out.println("错误信息:"+  errorMessage);
//        return errorMessage;

        String errorMsgValue = this.getMethedValue(Thread.currentThread().getStackTrace()[1].getMethodName());
        return errorMsgValue;
    }


    @Override
    public Class<? extends Annotation> annotationType() {
        return null;
    }
    boolean state=true;
    private String getMethedValue(String name){

        if (state==true){
            state=false;
            methedValue= this.readFileInfo();
        }
        if(name.equals("errorMsg")){
            System.out.println("错误信息:"+  methedValue.get(name));
            return methedValue.get(name);
        }else if(name.equals("min")){
            return methedValue.get(name);
        }else if(name.equals("max")){
            System.out.println("最大值为:"+methedValue.get(name));
            return methedValue.get(name);
        }
        return null;
    }

    Map<String,String> methedValue = new HashMap<>();
    public Map<String, String> readFileInfo()  {
        try{
            BufferedReader br=new BufferedReader(new InputStreamReader(System.in));
            System.out.println("请输入要读取文件的路径");
            //E:\zy\TGB-zgy-2022\老师设计模式课相关资料必须留着\米老师设计模式课小例子\JAVAtest\customAnnotations\target\classes\LengthImpl1\Person.class
            String fileName=br.readLine();
            DataInputStream csanner = new DataInputStream(new FileInputStream(new File(fileName)));
            ClassFile classFile = new ClassFile(csanner);
            List<FieldInfo> fieldsList = classFile.getFields();
            for (FieldInfo field:fieldsList){
                for (AttributeInfo attributeInfo : field.getAttributes()) {
                    System.out.println(attributeInfo);
                    String annotationValue = attributeInfo.toString().replace("(", ",");
                    String annotationValue1 = annotationValue.replace(")", ",");
                    String[] fields = annotationValue1.split(",");
                    for (int i = 1; i < fields.length; i++) {
                        String[] fieldSplit = fields[i].split("=");
                        methedValue.put(fieldSplit[0].trim(),fieldSplit[1].trim());
                        System.out.println(methedValue);
                    }
                }

            }
        }catch (Exception e){
            System.out.println("获取文件失败"+e);
        }
        return methedValue;
    }
}

实现效果:

了解元注解(注解使用的地方,时间范围,是否生成文档,是否被继承)
@Retention:
指定注解的保留策略。它决定注解在编译后是否被保留在class文件中以及在运行时可见性。常用的保留策略包括:

RetentionPolicy.SOURCE:注解仅在源代码级别保留,不包含在编译后的class文件中。
RetentionPolicy.CLASS:注解在编译时被保留,并包含在class文件中,但在运行时不可见。
RetentionPolicy.RUNTIME:注解在编译时被保留,并包含在class文件中,可以在运行时通过反射机制获取到。
@Target:
指定注解可以应用的目标元素类型。它规定了注解可以用于哪些程序元素,如类、方法、字段等。常用的目标元素类型包括:

ElementType.TYPE:注解应用于类、接口、枚举等类型。
ElementType.METHOD:注解应用于方法。
ElementType.FIELD:注解应用于字段(属性)。
ElementType.PARAMETER:注解应用于方法的参数。
@Documented:
指定注解是否包含在生成的API文档中。如果一个注解被标记为@Documented,那么当使用javadoc工具生成API文档时,注解会包含在文档中。

@Inherited:
指定注解是否可以被继承。如果一个注解被标记为@Inherited,那么它可以被继承,即当一个类继承自有该注解的父类时,子类也会继承该注解。

结果

注解的好处:
代码文档化:注解可以用于生成代码文档,通过添加注解来标记类、方法或字段的说明,使得开发者和其他人员能够更好地理解代码的意图和用法。

编译时检查:注解可以用于在编译时对代码进行检查,以确保代码的正确性或进行静态分析。例如,通过自定义注解来标记特定的代码结构,编译器可以根据注解执行额外的检查或生成警告信息。

运行时处理:注解可以在运行时被解析和处理,用于执行特定的逻辑或配置应用程序。例如,通过注解可以配置依赖注入框架,实现对象的自动注入;或者使用注解来定义路由映射,简化Web应用程序的开发。

测试和调试:注解可以用于标记测试用例或调试相关的信息,以便测试框架或调试器能够自动化执行相应的操作。例如,通过注解标记测试方法,测试框架可以识别并执行这些方法作为测试套件的一部分。

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论
以下是使用Python和TensorFlow实现读取本地文件的MNIST手写数字识别代码: ``` # 导入所需的库 import tensorflow as tf import numpy as np import os # 设置参数 learning_rate = 0.001 training_epochs = 20 batch_size = 100 display_step = 1 # 创建输入占位符 x = tf.placeholder(tf.float32, [None, 784]) y = tf.placeholder(tf.float32, [None, 10]) # 创建模型 def multilayer_perceptron(x, weights, biases): layer_1 = tf.add(tf.matmul(x, weights['h1']), biases['b1']) layer_1 = tf.nn.relu(layer_1) out_layer = tf.matmul(layer_1, weights['out']) + biases['out'] return out_layer # 定义权重和偏置项 weights = { 'h1': tf.Variable(tf.random_normal([784, 256])), 'out': tf.Variable(tf.random_normal([256, 10])) } biases = { 'b1': tf.Variable(tf.random_normal([256])), 'out': tf.Variable(tf.random_normal([10])) } # 构建模型 pred = multilayer_perceptron(x, weights, biases) # 定义损失函数和优化器 cost = tf.reduce_mean(tf.nn.softmax_cross_entropy_with_logits(logits=pred, labels=y)) optimizer = tf.train.AdamOptimizer(learning_rate=learning_rate).minimize(cost) # 初始化变量 init = tf.global_variables_initializer() # 加载数据 data = np.load('data.npz') train_images = data['train_images'] train_labels = data['train_labels'] test_images = data['test_images'] test_labels = data['test_labels'] # 执行训练 with tf.Session() as sess: sess.run(init) for epoch in range(training_epochs): avg_cost = 0. total_batch = int(train_images.shape[0]/batch_size) for i in range(total_batch): batch_x = train_images[i*batch_size:(i+1)*batch_size].reshape((-1, 784)) batch_y = train_labels[i*batch_size:(i+1)*batch_size] _, c = sess.run([optimizer, cost], feed_dict={x: batch_x, y: batch_y}) avg_cost += c / total_batch if epoch % display_step == 0: print("Epoch:", '%04d' % (epoch+1), "cost=", "{:.9f}".format(avg_cost)) print("Optimization Finished!") # 测试模型 correct_prediction = tf.equal(tf.argmax(pred, 1), tf.argmax(y, 1)) accuracy = tf.reduce_mean(tf.cast(correct_prediction, tf.float32)) print("Accuracy:", accuracy.eval({x: test_images.reshape((-1, 784)), y: test_labels})) ``` 这个代码与前面的代码相似,唯一的区别是数据的来源。在这个代码,我们使用`numpy`库的`load`函数从本地文件加载数据。数据保存在`.npz`文件,其train_images、train_labels、test_images和test_labels是NumPy数组。我们需要将它们转换为TensorFlow可以处理的张量,并在训练和测试期间使用它们。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Circ.

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值