Java注解详解

news2024/11/20 15:39:35

什么是注解

​ 用一个词就可以描述注解,那就是元数据,即一种描述数据的数据。所以,可以说注解就是源代码的元数据

元注解

JDK1.5之后内部提供的注解:

  • @Deprecated 意思是“废弃的,过时的
  • @Override 意思是“重写、覆盖
  • @SuppressWarnings 意思是“压缩警告
  • @Documented –注解是否将包含在JavaDoc中
  • @Target –注解用于什么地方
  • @Inherited – 是否允许子类继承该注解
  • @Retention –什么时候使用该注解
  • @Repeatable (since jdk1.8)-多次赋值
  • @SafeVarargs(since jdk1.7)
  • @FunctionalInterface (since jdk1.8)-函数式接口

@Retention

定义注解的生命周期

  • RetentionPolicy.SOURCE

    在编译阶段丢弃。这些注解在编译结束之后就不再有任何意义,所以它们不会写入字节码。@Override, @SuppressWarnings都属于这类注解。

  • RetentionPolicy.CLASS

    在类加载的时候丢弃。在字节码文件的处理中有用。注解默认使用这种方式

  • RetentionPolicy.RUNTIME

    始终不会丢弃,运行期也保留该注解,因此可以使用反射机制读取该注解的信息。我们自定义的注解通常使用这种方式。

@Documented
 @Retention(RetentionPolicy.RUNTIME)
 @Target(ElementType.ANNOTATION_TYPE)
 public @interface Retention {
   /**
   * Returns the retention policy.
   * @return the retention policy
   */
   RetentionPolicy value();
 }

@Target

表示该注解用于什么地方。如果不明确指出,该注解可以放在任何地方。以下是一些可用的参数。需要说明的是:属性的注解是兼容的,如果你想给7个属性都添加注解,仅仅排除一个属性,那么你需要在定义target包含所有的属性。

  • ElementType.TYPE: 用于描述类、接口或enum声明

  • ElementType.FIELD: 用于描述字段

  • ElementType.METHOD :用于描述方法

  • ElementType.PARAMETER:用于描述参数

  • ElementType.CONSTRUCTOR:用于描述构造函数

  • ElementType.LOCAL_VARIABLE:用于描述本地变量

  • ElementType.ANNOTATION_TYPE:用于描述注解

  • ElementType.PACKAGE :用于记录java文件的package信息

    ​ 包注解不能修饰常规Java文件中包声明或包引入语句,而只能用于修饰package-info.java文件中的包声明语句(也不能修饰package-info.java文件中的包引入语句)。

@Documented
 @Retention(RetentionPolicy.RUNTIME)
 @Target(ElementType.ANNOTATION_TYPE)
 public @interface Target {
   ElementType[] value();
 }

JDK1.8新增ElementType

  • ElementType.TYPE_PARAMETER:表示该注解能写在类型变量的声明语句中(如:泛型声明)

        class C<@AAA T> {
            T get() {
                return null;
            }
        }
    
        @Target(ElementType.TYPE_PARAMETER)
        @interface AAA {
    
        }
    

    image-20230114100836748

  • ElementType.TYPE_USE:表示该注解能写在使用类型的任何语句中

@Inherited

Inherited 是继承的意思,但是它并不是说注解本身可以继承,而是说如果一个超类被 @Inherited 注解过的注解进行注解的话,那么如果它的子类没有被任何注解应用的话,那么这个子类就继承了超类的注解。
说的比较抽象。代码来解释

 @Inherited
 @Retention(RetentionPolicy.RUNTIME)
 @interface Test {}


 @Test
 public class A {}

 public class B extends A {}

//被其他注解 应用了
@Other
public class C extends A

{}

注解 Test 被 @Inherited 修饰,之后类 A 被 Test 注解,类 B 继承 A,类 B 也拥有 Test 这个注解。

但是C不拥有@Test注解,因为@Other注解应用了

@Repeatable

在JDK1.8之前注解同时只能被应用一次,应用2次会编译错误。

什么样的注解会多次应用呢?通常是注解的值可以同时取多个。
举个例子,一个人他既是程序员又是产品经理,同时他还是个画家。

@interface Persons {
   Person[] value();
 }

@Repeatable(Persons.class)
 @interface Person{
   String role default "";
 }


 @Person(role="artist")
 @Person(role="coder")
 @Person(role="PM")
 public class SuperMan{

}

​ 注意上面的代码,@Repeatable 注解了 Person。而 @Repeatable 后面括号中的类相当于一个容器注解

​ 什么是容器注解呢?就是用来存放其它注解的地方。它本身也是一个注解

​ 我们再看看代码中的相关容器注解。

@interface Persons {
   Person[] value();
 }

按照规定,它里面必须要有一个 value 的属性,属性类型是一个@Repeatable 注解过的注解数组,注意它是数组
如果不好理解的话,可以这样理解。Persons 是一张总的标签,上面贴满了 Person 这种同类型但内容不一样的标签。把 Persons 给一个 SuperMan 贴上,相当于同时给他贴了程序员、产品经理、画家的标签。
我们可能对于 @Person(role=”PM”) 括号里面的内容感兴趣,它其实就是给 Person 这个注解的 role 属性赋值为 PM .

​ 当一个可重复的注解应用于某个元素时,获取不到这个注解的实例,而是 注解容器的实例,例如是获取不到Person的实例的,获取到的是Persons的实例。

@SafeVarargs

参数安全类型注解。它的目的是提醒开发者不要用参数做一些不安全的操作,它的存在会阻止编译器产生 unchecked 这样的警告。它是在 Java 1.7 的版本中加入的。

 @SafeVarargs // Not actually safe!
   static void m(List<String>... stringLists) {
   Object[] array = stringLists;
   List<Integer> tmpList = Arrays.asList(42);
   array[0] = tmpList; // Semantically invalid, but compiles without warnings
   String s = stringLists[0].get(0); // Oh no, ClassCastException at runtime!
 }

上面的代码中,编译阶段不会报错,但是运行时会抛出 ClassCastException 这个异常,所以它虽然告诉开发者要妥善处理,但是开发者自己还是搞砸了。

@FunctionalInterface

函数式接口注解,这个是 Java 1.8 版本引入的新特性。函数式编程很火,所以 Java 8 也及时添加了这个特性。

函数式接口 (Functional Interface) 就是一个具有一个方法的普通接口。

注解定义

定义

import java.lang.annotation.Target;
 import java.lang.annotation.ElementType;
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;


 @Retention(RetentionPolicy.RUNTIME )
 @Target(ElementType.TYPE)
 public @interface Controller {
   String name() default "";
 }

注解不支持继承

注解是不支持继承的,因此不能使用关键字extends来继承某个@interface,但注解在编译后,编译器会自动继承java.lang.annotation.Annotation接口

属性

注解内部可以定义一些属性。

语法:

类型 属性名() [ default 默认值 ];
@Retention(RetentionPolicy.RUNTIME )
 @Target(ElementType.TYPE)
 public @interface Controller {
   String name() default "";
 }

其实从代码的写法上来看,注解更像是一种特殊的接口,注解的属性定义方式就和接口中定义方法的方式一样,而应用了注解的类可以认为是实现了这个特殊的接口。

注解属性支持以下类型:

  • 所有基本类型(int,float,boolean,byte,double,char,long,short)
  • String
  • Class
  • enum
  • Annotation
  • 上述类型的数组

注解的属性必须有明确的值,要么使用默认值,要么在应用注解时指定值。

注解合并

每个开发人员都应该有过这样的经历:在编写某个类或接口的时候,需要声明Spring本身的注解(@Controller、@Service,@Dao),又需要声明自己公司编写的注解来完成公司的独特业务,然后就悲剧了,一个类上边声明了五六个注解,茫茫然不知所云。注解本身是好的,它可以替我们完成一些事情。但和XML一样,过度使用就编程了一种灾难。

于是,一种新的替代方案出现了,那就是组合注解。比较经典的组合注解就是SpringBoot的@SpringBootApplication注解。

@Target(ElementType.TYPE)
 @Retention(RetentionPolicy.RUNTIME)
 @Documented
 @Inherited
 @Configuration
 @EnableAutoConfiguration
 @ComponentScan
 public @interface SpringBootApplication {


   Class<?>[] exclude() default {};


   String[] excludeName() default {};

 

  //spring 在解析此处时,做特殊处理
   @AliasFor(annotation = ComponentScan.class, attribute = "basePackages")
   String[] scanBasePackages() default {};
 
   //spring 在解析此处时,做特殊处理
   @AliasFor(annotation = ComponentScan.class, attribute = "basePackageClasses")
   Class<?>[] scanBasePackageClasses() default {};
 
 }

注解应用

import java.lang.annotation.Target;
 import java.lang.annotation.ElementType;
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;


 enum EnumType
 {
   First ,
     Second
     
 }

@interface Reference
 {
   
 }

@interface Test
 {
   String value();
 }

@Retention(RetentionPolicy.RUNTIME )
 @Target(value={ElementType.TYPE,ElementType.METHOD})
 public @interface RequestMapping {

   //基本类型
   int size() default 0;
   //String
   String name() default "";
   //枚举
   EnumType Order() default EnumType.First;
   //Class类
   Class<?> ClassType() default Void.class;
   //注解
   Reference Reference() default @Reference;
   //数组
   int[] Ids() default {1,3,4};

}


 @RequestMapping(
     size = 5
     ,name="jack"
     ,Order=EnumType.Second
     ,ClassType=Integer.class
     ,Reference=@Reference
     ,Ids={4,5,7}
     )

@Test("mytest") //当注解仅有一个名称为value的属性时,可以省略属性名称
 class AAA
 {

 }

注解提取

通过反射可以获取注解的信息。

Java反射的各个类型都实现了AnnotatedElement接口。AnnotatedElement接口,标识一个可以被Java Annotation注解的Java语言元素。在java.lang.reflect包下,像Class,Method,Field,Constructor,GenericDeclaration(泛型声明)等Java语法元素对应的实现,都需要实现这个接口。

AnnotatedElement接口有个子接口 AnnotatedType,其有4个子接口,分别对应反射中Type的4个泛型相关的类。

image-20230114114753944

public interface AnnotatedElement {
    /** 如果一个注解 present 在这个元素上,则返回true。
 	等价于:{@code getAnnotation(annotationClass) != null}
     */
    default boolean isAnnotationPresent(Class<? extends Annotation> annotationClass) {
        return getAnnotation(annotationClass) != null;
    }

   /** 返回应用于此元素的指定注解类型的注解实例。
     * 没有应用指定的注解类型,则返回null。
     */
    <T extends Annotation> T getAnnotation(Class<T> annotationClass);

    /**
     * 返回所有应用于此元素的注解(实例)
     */
    Annotation[] getAnnotations();

    /**
      考虑父类 @Inherited。
     */
    default <T extends Annotation> T[] getAnnotationsByType(Class<T> annotationClass) {
 
         T[] result = getDeclaredAnnotationsByType(annotationClass);

         if (result.length == 0 && // Neither directly nor indirectly present
             this instanceof Class && // the element is a class
             AnnotationType.getInstance(annotationClass).isInherited()) { // Inheritable
             Class<?> superClass = ((Class<?>) this).getSuperclass();
             if (superClass != null) {
                 // Determine if the annotation is associated with the
                 // superclass
                 result = superClass.getAnnotationsByType(annotationClass);
             }
         }

         return result;
     }

    /** 返回 直接present 于 此元素的指定注解类型的注解实例。
     * 没有应用指定的注解类型,则返回null。
     */
    default <T extends Annotation> T getDeclaredAnnotation(Class<T> annotationClass) {
         Objects.requireNonNull(annotationClass);
         // Loop over all directly-present annotations looking for a matching one
         for (Annotation annotation : getDeclaredAnnotations()) {
             if (annotationClass.equals(annotation.annotationType())) {
                 // More robust to do a dynamic cast at runtime instead
                 // of compile-time only.
                 return annotationClass.cast(annotation);
             }
         }
         return null;
     }

    /**
    返回 直接 present 和 间接 present 的注解,忽略  @Inherited
    与 getDeclaredAnnotation 的区别是:此方法识别是否是可重复注解。
    如果是,则即可查找容器注解(返回一个array,只有一个元素),也可以查找元素注解(返回一个array,可能多个元素)
    注意:  TYPE_USE 类型的 获取不到。
     */
    default <T extends Annotation> T[] getDeclaredAnnotationsByType(Class<T> annotationClass) {
        Objects.requireNonNull(annotationClass);
        return AnnotationSupport.
            getDirectlyAndIndirectlyPresent(Arrays.stream(getDeclaredAnnotations()).
                                            collect(Collectors.toMap(Annotation::annotationType,
                                                                     Function.identity(),
                                                                     ((first,second) -> first),
                                                                     LinkedHashMap::new)),
                                            annotationClass);
    }

    /**
      返回直接应用在元素上的注解,而不是派生的。
     * @since 1.5
     */
    Annotation[] getDeclaredAnnotations();
}

getDeclaredAnnotationsByTypegetAnnotationsByType 获取不到仅使用了TYPE_USE 的注解。

Present(存在)

present 指示一个注解使用应用于某个元素。

present是指满足以下任一条件,则代表元素上present某个注解:

  1. 元素上**直接 应用(apply to)**某个注解
  2. 某个被@Inherited标注的注解(能够继承的注解)应用于该元素的父类上(此元素没有标记任何其他注解)

直接present:元素上**直接 应用(apply to)**某个注解

间接present:通过@Repeatable标记一个注解,这个注解应用于某个元素时。

关联的(associated),是指满足以下任一条件,则代表元素上关联某个注解:

  1. 元素上直接present或者间接present某个注解
  2. 某个被@Inherited标注的注解(能够继承的注解)与该元素的父类是关联的

AnnotatedElement中的方法都是基于注解以何种形式存在于对象上获取元素上的注解的, 下表总结了此接口中各方法获取元素注解的范围:

注解存在的形式
方法方法直接present间接presentpresent关联的
TgetAnnotation(Class annotationClass)
Annotation[]getAnnotations()
T[]getAnnotationsByType(Class annotationClass)
TgetDeclaredAnnotation(Class annotationClass)
Annotation[]getDeclaredAnnotations()
T[]getDeclaredAnnotationsByType(Class annotationClass)
public interface AnnotatedType extends AnnotatedElement {
    //返回被注解的Type
    public Type getType();
}
public interface AnnotatedArrayType extends AnnotatedType {
    /** 返回泛型的元素type
     */
    AnnotatedType  getAnnotatedGenericComponentType();
}
public interface AnnotatedParameterizedType extends AnnotatedType {

    /**
      烦恼会泛型Type的被注解的实际类型参数
     */
    AnnotatedType[] getAnnotatedActualTypeArguments();
}
public interface AnnotatedTypeVariable extends AnnotatedType {
    /**
     * 返回类型变量的边界
     */
    AnnotatedType[] getAnnotatedBounds();
}
public interface AnnotatedWildcardType extends AnnotatedType {
    AnnotatedType[] getAnnotatedLowerBounds();
    AnnotatedType[] getAnnotatedUpperBounds();
}

首先可以通过 Class 对象的 isAnnotationPresent() 方法判断它是否应用了某个注解

public boolean isAnnotationPresent(Class<? extends Annotation> annotationClass) {}

然后通过 getAnnotation() 方法来获取 Annotation 对象。

public <A extends Annotation> A getAnnotation(Class<A> annotationClass) {}
//AA:class
//P3:@Merge的属性
AA.class.getAnnotation(Merge.class).P3();

Class 注解提取相关的方法

//获取super 类型
public AnnotatedType getAnnotatedSuperclass() ;
//获取接口类型
public AnnotatedType[] getAnnotatedInterfaces();

Method

//返回被注解的参数的 AnnotatedType 。即使被多个注解应用于一个参数,也只返回一个AnnotatedType,里面的annotations属性,包含多个注解实例。
//每个参数返回一个AnnotatedType,即使参数没有应用注解。
public AnnotatedType[] getAnnotatedParameterTypes();
//返回参数上应用的Annotation,每个参数对应一个 Annotation[],即使参数没有应用注解,也返回一个0元素的Annotation[]
public Annotation[][] getParameterAnnotations();
//获取返回值的AnnotatedType
public AnnotatedType getAnnotatedReturnType();

image-20230114142243959

getParameterAnnotations 获取不到仅使用了TYPE_USE 的注解。

Field

//获取字段类型上的注解
public AnnotatedType getAnnotatedType();

Constructor

public Annotation[][] getParameterAnnotations();
public AnnotatedType getAnnotatedReturnType();
public AnnotatedType getAnnotatedReceiverType();

示例

package demon.research.anno;

import java.lang.annotation.*;
import java.lang.reflect.AnnotatedType;
import java.lang.reflect.Method;

import static java.lang.annotation.ElementType.*;

public class AnnoTest {
    public static void main(String[] args) {
        try {
            test1();
        } catch (Exception ex) {
            ex.printStackTrace();
        }
    }

    public static void test1() throws NoSuchMethodException, NoSuchFieldException {
        C<Integer> c = new C();
        Integer x = c.m(5);

        Annotation[] annotations = C.class.getAnnotations();
        System.out.println("类上的注解 = " + annotations);

        annotations = C.class.getDeclaredAnnotations();
        System.out.println("类上的直接定义的注解 = " + annotations);

        Method method = C.class.getMethod("m", Integer.class, Integer.class, Integer.class);

        AnnotatedType[] methods = method.getAnnotatedParameterTypes();
        Annotation[] annotations1 = methods[0].getAnnotations();
        System.out.println("形参上的注解 = " + annotations1[0]);

        AnnotatedType methods2 = method.getAnnotatedReturnType();
        //CCC 可以获取到
        System.out.println("方法返回值的注解 ->CCC = " + methods2.getAnnotation(CCC.class));
        //BBB 获取不到
        System.out.println("方法返回值的注解 ->BBB = " + methods2.getAnnotation(BBB.class));
        /// BBBS 可以获取到
        System.out.println("方法返回值的注解 ->BBBS = " + methods2.getAnnotation(BBBS.class));

        // 获取不到DDD,因为他仅使用了 TYPE_USE
        Annotation[][] parameterAnnotations = method.getParameterAnnotations();
        System.out.println("方法返回值的注解 = " + parameterAnnotations);

        AnnotatedType a = C.class.getDeclaredField("a").getAnnotatedType();
        BBBS bbb = a.getAnnotation(BBBS.class);
        System.out.println("成员变量上的注解 = " + bbb);


    }

    @BBB("Class")
    @CCC("Class CCC")
    @DDD("Class DDD")
    static class C<@BBB("Type Param") T> {
        @BBB("Field")
        @CCC("Field CCC")
        @DDD("Field DDD")

        private @BBB("Field 222") String a;

        // 注解放在此处,与放在返回类型前 是一样的效果,通过 getAnnotatedReturnType 获取。
        @BBB("Method")
        @CCC("Method CCC")
        @DDD("Method DDD")
        public @BBB("Method 222") T m(@BBB("Param") @CCC("PARAM CCC") Integer i) {
            @BBB("Local Variable")
            Integer xx = 5;

            return null;
        }

        @BBB("Method")
        @CCC("Method CCC")
        @DDD("Method DDD")
        public @BBB("Method 222") T m(
                @BBB("Param") @CCC("PARAM CCC") Integer i
                , Integer j
                , @BBB("Param K") @CCC("PARAM CCC K") @DDD("PARAM DDD K ") Integer k) {
            @BBB("Local Variable")
            Integer xx = 5;

            return null;
        }
    }

    @Retention(RetentionPolicy.RUNTIME)
    @Target(value = {TYPE, FIELD, METHOD, PARAMETER, CONSTRUCTOR, LOCAL_VARIABLE, ANNOTATION_TYPE, PACKAGE, TYPE_PARAMETER,
            TYPE_USE})
    @Repeatable(BBBS.class)
    @interface BBB {
        String value() default "";
    }

    @Retention(RetentionPolicy.RUNTIME)
    @Target(value = {TYPE, FIELD, METHOD, PARAMETER, CONSTRUCTOR, LOCAL_VARIABLE, ANNOTATION_TYPE, PACKAGE, TYPE_PARAMETER,
            TYPE_USE})
    @interface BBBS {
        BBB[] value();
    }

    @Retention(RetentionPolicy.RUNTIME)
    @Target(value = {TYPE, FIELD, METHOD, PARAMETER, CONSTRUCTOR, LOCAL_VARIABLE, ANNOTATION_TYPE, PACKAGE, TYPE_PARAMETER,
            TYPE_USE})
    @interface CCC {
        String value() default "";
    }

    @Retention(RetentionPolicy.RUNTIME)
    @Target(value = {TYPE_USE})
    @interface DDD {
        String value() default "";
    }
}

输出:

类上的注解 = [Ljava.lang.annotation.Annotation;@180bc464
类上的直接定义的注解 = [Ljava.lang.annotation.Annotation;@1324409e
形参上的注解 = @demon.research.anno.AnnoTest$BBB(value=Param)
方法返回值的注解 ->CCC = @demon.research.anno.AnnoTest$CCC(value=Method CCC)
方法返回值的注解 ->BBB = null
方法返回值的注解 ->BBBS = @demon.research.anno.AnnoTest$BBBS(value=[@demon.research.anno.AnnoTest$BBB(value=Method), @demon.research.anno.AnnoTest$BBB(value=Method 222)])
方法返回值的注解 = [[Ljava.lang.annotation.Annotation;@34b7bfc0
成员变量上的注解 = @demon.research.anno.AnnoTest$BBBS(value=[@demon.research.anno.AnnoTest$BBB(value=Field), @demon.research.anno.AnnoTest$BBB(value=Field 222)])

附录

package-info.java文件

package-info.java文件是一个特殊的Java文件,它里面主要包含3类语句:包注释、包注解和包声明,它可以被放在任意Java包对应的路径下。

package-info.java文件的作用

​ 其中 package-info.java文件主要有下述的作用:

提供包级别的注释(Comment)

​ 我们都知道可以为类、变量、方法等元素编写注释,但是如果我们想给某个Java包编写注释怎么办?在Java5之前,需要在Java包对应的路径下创建一个package.html文件,并在其body标签中编写Javadoc注释。而在Java5及之后的版本中,我们在Java包对应的路径下创建一个package-info.java文件来存放包的包声明、包注释和包注解。

提供包级别的注解(Annotation)

​ 在Java5及之后的版本中,我们可以在某个Java包对应路径下的package-info.java文件中用包注解来修饰包声明语句。

@Repeatable 是个语法糖

应用多个可重复的注解时,会自动转化为@Repeatable注解,value 属性为 多个此注解。

但是,仅应用一次的时候,则不会自动转换

前面的示例中的代码 Class C 编译之后变为:


    @AnnoTest.BBB("Class")  //注意:此处没有自动转换。
    @AnnoTest.CCC("Class CCC")
    @AnnoTest.DDD("Class DDD")
    static class C<T> {
        @AnnoTest.BBBS({@AnnoTest.BBB("Field"), @AnnoTest.BBB("Field 222")})
        @AnnoTest.CCC("Field CCC")
        @AnnoTest.DDD("Field DDD")
        private String a;

        C() {
        }
        //注意: 此处自动转换为 注解容器BBBS。
        //可以看到,把返回值类型 前面的注解 放到 方法上了。
        @AnnoTest.BBBS({@AnnoTest.BBB("Method"), @AnnoTest.BBB("Method 222")})
        @AnnoTest.CCC("Method CCC")
        public T m(@AnnoTest.BBB("Param") @AnnoTest.CCC("PARAM CCC") Integer i) {
            Integer xx = 5;
            return null;
        }

        @AnnoTest.BBBS({@AnnoTest.BBB("Method"), @AnnoTest.BBB("Method 222")})
        @AnnoTest.CCC("Method CCC")
        public T m(@AnnoTest.BBB("Param") @AnnoTest.CCC("PARAM CCC") Integer i, Integer j, @AnnoTest.BBB("Param K") @AnnoTest.CCC("PARAM CCC K") Integer k) {
            Integer xx = 5;
            return null;
        }
    }

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/162932.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

算法训练营 day18 二叉树 找树左下角的值 路径总和 从中序与后序遍历构建二叉树

算法训练营 day18 二叉树 找树左下角的值 路径总和 从中序与后序遍历构建二叉树 找树的左下角 513. 找树左下角的值 - 力扣&#xff08;LeetCode&#xff09; 给定一个二叉树的 根节点 root&#xff0c;请找出该二叉树的 最底层 最左边 节点的值。 假设二叉树中至少有一个节…

Java --- JUC之原子类

目录​​​​​​​ 一、基本类型原子类 二、数组类型原子类 三、引用类型原子类 四、对象的属性修改类型原子类 五、原子操作增强类 5.1、高性能热点商品应用 5.2、LongAdder架构图 5.3、源码分析 一、基本类型原子类 public class AtomicTest1 {public static final…

canvas:基础知识【直线和矩形】

canvas&#xff0c;就是画布&#xff0c;是HTML5和核心技术之一&#xff0c;结合JavaScript&#xff0c;可以绘制各种各样的图形&#xff0c;比如矩形、曲线、圆形等等。另外&#xff0c;canvas可以绘制图表、动画效果、游戏开发。 基本图形汇中有直线和曲线。常见的直线图形是…

arduino rc522模块使用

rfid IC卡 先了解IC卡一些前置知识。 首先我们会有一张ic卡&#xff08;M1类型IC卡&#xff0c;一般买到的都是1K存储空间&#xff09;&#xff0c;在rc522代码中会出现这个&#xff0c;就是对IC卡进行检查PICC_TYPE_MIFARE_4K和PICC_TYPE_MIFARE_1K就是一种卡片类型不同大小…

零基础学MySQL(二)-- 表的创建,修改,删除

文章目录&#x1f388;一、创建表1️⃣基本语法2️⃣入门案例&#x1f386;二、MySQL常用数据类型1️⃣数值型&#xff08;整型&#xff09;默认有符号2️⃣数值型&#xff08;bit&#xff09;3️⃣数值型&#xff08;浮点型&#xff09;默认有符号4️⃣字符串的基本使用5️⃣字…

1584_AURIX_TC275_SMU的调试以及部分寄存器

全部学习汇总&#xff1a; GreyZhang/g_TC275: happy hacking for TC275! (github.com) 前面学习的过程中&#xff0c;突然间减速了不少。但是为了保证学习的推进&#xff0c;还是得有每天的稳定输出。我的策略是多看&#xff0c;多处理&#xff0c;之后每天整理10页标注的文档…

设计模式相关内容介绍

1.学习设计模式好处 提高编程能力、思维能力、设计能力程序设计更加标准化、代码编制更加工程化&#xff0c;软件开发效率大大提高&#xff0c;缩短项目周期设计的代码可重用性高、可读性强、可靠性高、 灵活性好、可维护性强 2.设计模式分类 创建型模式 提供创建对象的机制…

一文读懂工业级交换机的规范使用

工业交换机具备电信级特性特点&#xff0c;可承受严苛的工作环境&#xff0c;产品种类丰富多彩&#xff0c;交换机配置灵便&#xff0c;可以满足各类工业应用的应用标准。那么&#xff0c;大家使用工业级交换机的过程当中应该如何规范使用呢&#xff1f; 工业级交换机其实质运…

蓝队攻击的四个阶段(四)

目录 一&#xff0c; 外网纵向突破 1.1 何为外网纵向突破 1.2外网纵向突破的主要工作 二&#xff0c; 外网纵向突破的途径 1. Web 网站 2.外部邮件系统 3.边界网络设备 4.外部应用平台 三&#xff0c;内网横向拓展 1. 1何为内网横向拓展 1.2 内网横向拓展的主要工作 …

电商价格监测,关注这些,才算实际到手价

品牌控价的第一项工作&#xff0c;是先找出低价乱价链接&#xff0c;这就需要进行电商价格监测。但是我们搜索品牌链接的时候&#xff0c;会发现网页上的价格是多种多样&#xff1a;有原价&#xff08;但是划掉了&#xff09;、促销价、折扣价、惊喜价&#xff0c;优惠活动也是…

localStorage

localStorage localStorage了解 有些数据确实需要存储在本地&#xff0c;但是它却不需要发送到服务器&#xff0c;所以并不适合放在cookie中 localStorage 也是一种浏览器存储数据的方式&#xff08;本地存储&#xff09;&#xff0c;它只是存储在本地&#xff0c;不会发送…

【Linux】进程间通信(1)

信号 什么是信号&#xff1f;信号是给程序提供一种可以处理异步事件的方法&#xff0c;它利用软件中断来实现。不能自定义信号&#xff0c;所有信号都是系统预定义的。 信号由谁产生&#xff1f; 由shell终端根据当前发生的错误&#xff08;段错误、非法指令等&#xff09;Ctr…

商品详情的APP原数据接口测试

一、原数据接口的来源&#xff1a; 原数据接口来源于手机端&#xff0c;随着智能化的发展与普及&#xff0c;越来越多的人都是使用智能手机&#xff0c;这样极大的方便了人民的生活&#xff0c;各大电商平台看准了这个商家&#xff0c;把目光都瞄准这个商机&#xff0c;伴随而…

BP靶场中SQL注入练习

BP靶场中SQL注入练习1.Bp靶场介绍1.1.访问靶场1.2.注意事项2.SQL注入靶场2.1.注意事项2.2.检索隐藏数据2.2.1.开启靶场2.2.2.点击礼物2.2.3.测试类型2.2.4.爆出全部物品(包括隐藏)2.3.登录逻辑2.3.1.开启靶场2.3.2.登录账户2.3.3.注释验证2.3.4.成功登陆2.4.判断列2.4.1.开启靶…

会话技术--cookie和session

一、会话跟踪技术的概述 对于会话跟踪这四个词&#xff0c;我们需要拆开来进行解释&#xff0c;首先要理解什么是会话&#xff0c;然后再去理解什么是会 话跟踪: 会话:用户打开浏览器&#xff0c;访问web服务器的资源&#xff0c;会话建立&#xff0c;直到有一方断开连接&#…

变量、作用域与内存

目录 原始值与引用值 动态属性 复制值 传递参数 确定类型 执行上下文与作用域 作用域链增强 变量声明 1.使用var 的函数作用域声明 2. 使用let 的块级作用域声明 3.使用const 的常量声明 标识符查找 垃圾回收 标记清理&#xff08;最常用&#xff09; 引用计数 内…

2022__我的嵌入式入坑之路

目录 一、学习篇 51单片机&#xff1a; python爬虫&#xff1a; stm32单片机&#xff1a; ad&#xff1a; 立创EDA&#xff1a; openmv&#xff1a; ardunio&#xff1a; ESP32&#xff1a; 汇编语言&#xff1a; ROS&#xff1a; FreeRTOS&#xff1a; matlab&a…

【学习】大数据关键技术

学习内容描述&#xff1a; 大数据涉及的四个环节是什么&#xff1f; 云计算服务的三种服务类型是什么&#xff1f; 重点知识&#xff1a; 大数据涉及的四个环节&#xff1a;1、数据采集&#xff1b;2、数据存储&#xff1b;3、数据管理&#xff1b;4、数据分析与挖掘。云计算…

大型智慧灌区信息化管理系统云平台 智慧灌区信息化管理系统解决方案

平升电子大型智慧灌区信息化管理系统云平台/智慧灌区信息化管理系统解决方案&#xff0c;对灌区的渠道水位、流量、水雨情、土壤墒情、气象等信息进行监测&#xff0c;同时对泵站、闸门进行远程控制&#xff0c;对重点区域进行视频监控&#xff0c;实现了信息的采集、统计、分析…

基于pyautogui的自动识别定位原神风物之诗琴按键弹奏程序

前言&#xff1a;为了学习pyautogui这个库的使用&#xff0c;我准备用它做点东西。比如一个自动弹琴的程序。不过这个琴不是现实里的琴&#xff0c;而是原神里的风物之诗琴。&#xff08;这里有个网页版模拟器可以试试&#xff1a;风物之诗琴模拟器 (haveyouwantto.github.io)&…