java注解全网最细

引言

在java编程中,注解(Annotation)是一种元数据,它提供了关于程序代码的额外信息。注解不直接影响程序的执行,但可以在运行时提供有关程序的信息,或者让编译器执行额外的检查。

下面笔者通过循序渐进的方式一步步介绍注解的相关内容,帮助大家消化吸收知识点。

一、何谓java注解

Java注解又称Java标注,是在 JDK5 时引入的新特性,注解(也被称为元数据)。
Java注解它提供了一种安全的类似注释的机制,用来将任何的信息或元数据(metadata)与程序元素(类、方法、成员变量等)进行关联。

Java注解是附加在代码中的一些元信息,用于一些工具在编译、运行时进行解析和使用,起到说明、配置的功能。

二、java注解分类

1. 内置注解(Built-in Annotations):

这些注解是Java标准库(java.lang包或相关包)中预先定义的,用于特定的编程目的。例如:

  • @Override:表示方法重写父类的方法。
  • @Deprecated:标记过时的方法或类,编译器会发出警告。
  • @SuppressWarnings:抑制特定的编译器警告。
  • @FunctionalInterface:标识一个函数式接口,即只有一个抽象方法的接口。
  • @SafeVarargs:表示方法的安全可变参数列表,避免泛型警告。
@Override示例
/**
 * override注解
 * @author hulei
 * @date 2024/5/10 13:52
 */

public class OverrideAnnotations {

    static class BaseUser implements UserInterface {
        @Override
        public void method() {
            new BaseUser().method();
        }
    }

    interface UserInterface {
        void method();
    }
}

这个注解没什么好说的,一般用在子类覆写父类的方法上,比较简单基础的注解。上图代码表示的是:BaseUser 类实现了 UserInterface 接口,并重写了method()方法,方法上标识了 @Override注解。不一定非要是实现一个接口,继承一个普通类或者抽象类,重写父类的方法也可。

在Java中,如果子类重写父类的方法但不使用@Override注解,会有以下几点需要注意:

  1. 编译器提示
    如果你没有使用@Override,但实际上是重写了父类方法,某些IDE(如Eclipse, IntelliJ IDEA)会在方法上显示警告,提示你可能遗漏了@Override注解。虽然这不是强制性的,但添加它有助于提高代码的可读性和清晰度。

  2. 编译错误
    如果方法签名(包括方法名、参数列表和返回类型)与父类方法不完全匹配,编译器不会报错,因为你实际上并没有重写方法。这可能导致意外的行为,因为你可能以为你在调用子类的方法,但实际上调用了父类的方法。

  3. 方法覆盖的确认
    使用@Override可以确保编译器在编译时检查你是否真正重写了父类的方法。如果签名不匹配,编译器会报错,防止因意外的非重写而导致的问题。

  4. 代码可读性
    添加@Override注解使代码更易读,因为它清楚地表明该方法是用于重写父类方法的。

  5. 未来修改的保护
    如果父类的签名在未来发生变化,而你没有更新子类的方法签名,没有@Override的子类方法将不再重写父类方法。而如果有@Override,编译器会报错,提醒你需要更新子类的方法。

因此,尽管不是必须的,但推荐在重写父类方法时使用@Override注解,以确保代码的正确性和一致性。

@Deprecated示例
package com.datastructures;

/**
 * Deprecated注解
 * @author hulei
 * @date 2024/5/10 14:06
 */


public class DeprecatedAnnotation {
    public static void main(String[] args) {
        DeprecatedAnnotation deprecatedAnnotation = new DeprecatedAnnotation();
        deprecatedAnnotation.method();
    }

    @Deprecated
    public void method() {
        System.out.println("DeprecatedAnnotation.method");
    }
}

在这里插入图片描述

调用一个过时的方法,大部分编译器比如IntelliJ IDEA会给出警告信息,不推荐使用。像我们在开发过程中使用很多的第三方库或者框架包括jdk自身的大量类库时,可能早期提供的方法或函数有缺陷,但是又被大量的开发者使用,所以不能删除,这些第三方库的作者就在过时的方法加上这个注解,api调用者在调用这个过时方法就会收到提示,从而查看源码,根据作者的注释指引调用新的更加安全的方法。

@SuppressWarnings示例
package com.datastructures;
import java.util.ArrayList;
import java.util.List;


/**
 * @author hulei
 * @date 2024/5/10 14:31
 */


public class SuppressWarningsAnnotation {

    @SuppressWarnings("all")
    public static void addItems(String item){
        List items = new ArrayList();
        items.add(item);
    }

    public static void main(String[] args) {
        addItems("item");
    }
}

如果不加@SuppressWarnings注解,则会出现如下提示
在这里插入图片描述
看着很不舒服,都是一些无关紧要的提示,比如类型检查操作的警告,装箱、拆箱操作时候的警告等等。
加了 @SuppressWarnings(“all”) 这个注解,告警信息就没有了,抑制类所有类型的告警信息,清清爽爽,这对强迫症患者极为友好。

在这里插入图片描述
就算是加了过时注解的方法,加了@SuppressWarnings(“all”),也会把过时告警信息隐蔽掉。
在这里插入图片描述

我们一般常用的是如下三种

  1. @SuppressWarnings(“unchecked”) :抑制单类型的警告
  2. @SuppressWarnings(value={“unchecked”, “rawtypes”}) :抑制多类型的警告
  3. @SuppressWarnings(“all”) :抑制所有类型的警告

抑制警告的关键字对照表

关键字用途描述
allto suppress all warnings抑制所有警告
boxingto suppress warnings relative to boxing/unboxing operations抑制装箱、拆箱操作时候的警告
castto suppress warnings relative to cast operations抑制映射相关的警告
dep-annto suppress warnings relative to deprecated annotation抑制启用注释的警告
deprecationto suppress warnings relative to deprecation抑制过期方法警告
fallthroughto suppress warnings relative to missing breaks in switch statements抑制确在switch中缺失breaks的警告
finallyto suppress warnings relative to finally block that don’t return抑制finally模块没有返回的警告
hidingto suppress warnings relative to locals that hide variable抑制相对于隐藏变量的局部的警告
incomplete-switchto suppress warnings relative to missing entries in a switch statement (enum case)忽略没有完整的switch语句
nlsto suppress warnings relative to non-nls string literals忽略非nls格式的字符
nullto suppress warnings relative to null analysis忽略对null的操作
rawtypesto suppress warnings relative to un-specific types when using generics on class params使用generics时忽略没有指定相应的类型
restrictionto suppress warnings relative to usage of discouraged or forbidden references抑制禁止引用的使用相关的警告
serialto suppress warnings relative to missing serialVersionUID field for a serializable class忽略在serializable类中没有声明serialVersionUID变量
static-accessto suppress warnings relative to incorrect static access抑制不正确的静态访问方式警告
synthetic-accessto suppress warnings relative to unoptimized access from inner classes抑制子类没有按最优方法访问内部类的警告
uncheckedto suppress warnings relative to unchecked operations抑制没有进行类型检查操作的警告
unqualified-field-accessto suppress warnings relative to field access unqualified抑制没有权限访问的域的警告
unusedto suppress warnings relative to unused code抑制没被使用过的代码的警告
@FunctionalInterface示例
@FunctionalInterface
public interface Function<T, R> {
    R apply(T t);

    default <V> Function<V, R> compose(Function<? super V, ? extends T> before) {
        Objects.requireNonNull(before);
        return (V v) -> apply(before.apply(v));
    }
    
    default <V> Function<T, V> andThen(Function<? super R, ? extends V> after) {
        Objects.requireNonNull(after);
        return (T t) -> after.apply(apply(t));
    }

    static <T> Function<T, T> identity() {
        return t -> t;
    }
}

这里就拿JDK官方的Function函数式接口为例,注释被我删除了
打上@FunctionalInterface注解的接口,就可以使用java8提供的lamda表达式来表示该接口的一个实现(注:JAVA 8 之前一般是用匿名类实现的)。
如下代码调用实例
在这里插入图片描述
表示给定一个入参,经过一定的逻辑处理,返回一个出参结果
还有BiFunction,给定两个入参,返回一个出参结果,
也可以自定义,多个入参,比如笔者自定义的 ThreeBiFunction就是三个入参,一个出参
在这里插入图片描述

@SafeVarargs示例
package com.datastructures;

import java.util.List;
import java.util.Optional;

/**
 * 注解:SafeVarargs示例
 */
public class SafeVarargsAnnotations {

    @SafeVarargs
    static void function(List<String>... stringLists) {
    }

    abstract static class BaseUser implements UserInterface {

        @SafeVarargs
        final <T> void gamma(T... ts) {
        }

        @Override
        @SafeVarargs
        public final void method(Optional<Object>... optionals) {
            UserInterface.super.method(optionals);
        }
    }

    interface UserInterface {

        default void method(Optional<Object>... optionals) {
        }

        @SafeVarargs
        static <T> void gamma(Class<T>... classes) {
        }

        void method();
    }

}

方法的参数包含可变参数列表时,不加这个@SafeVarargs注解就会有告警信息,比如上面的代码,method方法有可变参数列表,没有加注解,产生类型安全和泛型相关提示
在这里插入图片描述

2.元注解(Meta-Annotations):

元注解是用于注解其他注解的注解,是所有其他注解的基础,它们定义了注解的行为和生命周期。主要包括:

  • @Retention:定义注解的保留策略,可以是SOURCE(只存在于源码中)、CLASS(编译时丢弃,存在于字节码中但不运行时可用)或RUNTIME(运行时可通过反射访问)。
  • @Target:指定注解可以应用于哪些程序元素,如类、方法、字段等。
  • @Documented:指示是否将注解包含在生成的Javadoc中。
  • @Inherited:允许子类继承父类的注解(仅适用于类,不适用于方法或字段)。
@Retention示例

在这里插入图片描述
上面的@SuppressWarnings注解源码,就只有一个 @Retention注解
打上@Retention注解的其他注解,有三个保留策略,上面已经说明。
在这里插入图片描述

@Target示例

如果一个注解上有@Target注解,则@Target注解声明了这个注解可以使用的地方
在这里插入图片描述
比如这个自定义注解,就只能在方法上使用,ElementType.METHOD枚举就是方法声明限制,关于ElementType枚举,可以自行查看里面的枚举信息
当然,后面可以写多个使用场景的枚举声明
在这里插入图片描述

在这里插入图片描述
还有的注解,没有加@Target注解,比如上面的@SuppressWarnings注解。一个注解上没有加使用范围的注解@Targe,那这个注解可以使用在任何能够使用注解的地方。所以 @SuppressWarnings 不包含自己的 @Target 注解,意味着它理论上可以应用于 Java 规范中任何允许注解的地方。然而,它实际上的使用受到限制,尤其是不能在表达式上下文中使用,这是因为其设计目的和 Java 语言规范的限制

  • 设计目的:@SuppressWarnings 的设计初衷是为了告诉编译器在特定的范围(如类、方法、字段等)内忽略特定类型的警告。它是为了简化开发过程,允许开发者在明知某些代码可能引起编译器警告,但确认这些警告不影响程序正确性的情况下,有选择地忽略这些警告。因此,它主要应用于编译单位的较大结构上。

  • 表达式上下文限制:表达式上下文通常涉及更细粒度的操作,如赋值、方法调用、算术运算等。在这些上下文中使用 @SuppressWarnings 不符合其设计逻辑,因为这些地方通常不涉及整体性的类型或结构警告,而是更具体的、即时的操作。如果允许在表达式中使用,不仅会增加语言的复杂性,还可能引发滥用,使得代码难以理解和维护。

  • 类型注解与普通注解的区别:类型注解(自 Java 8 引入)专门设计用于标注类型声明,包括泛型类型参数、返回类型、参数类型等,而 @SuppressWarnings 并不属于这一类别。类型注解可以在某种程度上改变编译器对类型的理解,而 @SuppressWarnings 仅用于指示编译器如何处理警告信息,不改变代码的类型系统或结构。

  • Java 语言规范限制:即使 @SuppressWarnings 没有限定其 @Target,Java 语言规范和编译器实现也决定了哪些注解可以用在哪些上下文中。表达式上下文通常不接受注解,特别是像 @SuppressWarnings 这样旨在影响编译器警告处理的注解,因为这不符合语言的语义和设计哲学。

举例如下:
在这里插入图片描述
在这里插入图片描述
综上所述,@SuppressWarnings 不能在表达式上下文中使用,主要是由于其设计意图、语言规范的限制以及为了保持语言的清晰度和简洁性。

@Documented示例

这个注解不重要,表示是否将注解包含在生成的Javadoc中。加不加完全在于我们自己,只要知道的用途就行了

@Inherited示例

这个注解还是比较重要的,允许子类继承父类的注解(仅适用于类,不适用于方法或字段)。什么意思呢?
一个注解上有@Inherited注解,那么当我们把这个注解打在一个类上时,如果这个类有子类,那么这个子类继承父类的这个注解

package com.datastructures;

import java.lang.annotation.Inherited;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;

/**
 * @author hulei
 * @date 2024/5/10 17:01
 */


public class InheritedAnnotation {


    /**
     * 自定义注解
     */
    @Inherited
    @Retention(RetentionPolicy.RUNTIME)
    @interface InnerAnnotation{
        String value();
    }

    /**
     * 父类,使用了自定义注解
     */
    @InnerAnnotation(value = "父类注解")
    public static class ParentClass {
    }

    /**
     * 子类继承父类
     */
    public static class ChildClass extends ParentClass {
    }


    public static void main(String[] args) {
        Class<?> childClass = ChildClass.class;
        if (childClass.isAnnotationPresent(InnerAnnotation.class)) {
            InnerAnnotation annotation = childClass.getAnnotation(InnerAnnotation.class);
            System.out.println("Value from InnerAnnotation: " + annotation.value());
        } else {
            System.out.println("No InnerAnnotation found.");
        }
    }

}

运行可以看到,子类也获取到了这个注解

在这里插入图片描述
那我们改造下,把注解上的@Inherited注解去掉,再执行看看
在这里插入图片描述
在这里插入图片描述
可以看到子类没有获取到父类的注解了,即没有从父类继承

3.自定义注解(Custom Annotations):

开发者可以使用**@interface**关键字创建自己的注解,根据需求定义注解的行为和用途。自定义注解可以结合元注解来定义其行为,例如通过@Retention和@Target来控制自定义注解的生命周期和应用范围。下面给出几个示例:

1. 用于记录日志的注解
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD})
public @interface LogExecution {
    String message() default "";
}

这个注解可以应用于方法,表示在执行该方法前/后需要记录日志。

切面类aop

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;

@Aspect
@Component
public class LoggingAspect {

    private static final Logger logger = LoggerFactory.getLogger(LoggingAspect.class);

    @Around("@annotation(LogExecution)")
    public Object logExecutionTime(ProceedingJoinPoint joinPoint) throws Throwable {
        String methodName = joinPoint.getSignature().getName();
        String className = joinPoint.getSignature().getDeclaringTypeName();
        String message = joinPoint.getSignature().getAnnotation(LogExecution.class).message();

        long start = System.currentTimeMillis();
        logger.info("Starting method: {}.{} with message: {}", className, methodName, message);

        Object result = joinPoint.proceed(); // 继续执行目标方法

        long elapsedTime = System.currentTimeMillis() - start;
        logger.info("Completed method: {}.{} in {}ms", className, methodName, elapsedTime);

        return result;
    }
}

实际代码调用

@Service
public class SomeService {

    @LogExecution(message = "Executing business logic")
    public String performTask() {
        // 示例业务逻辑
        try {
            Thread.sleep(100); // 模拟耗时操作
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
            throw new RuntimeException(e);
        }
        return "Task completed";
    }
}

2.用于数据验证的注解

比如邮箱格式验证

import javax.validation.Constraint;
import javax.validation.Payload;
import java.lang.annotation.*;

@Constraint(validatedBy = EmailValidator.class)
@Target({ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
public @interface EmailVaild{
    String message() default "邮箱格式不正确";
    Class<?>[] groups() default {};
    Class<? extends Payload>[] payload() default {};
}

3.用于事务管理的注解
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface Transactional {
    boolean readOnly() default false;
}

这个注解用于标记一个方法需要在数据库事务中执行,readOnly 参数表示是否为只读事务。

4.用于权限控制的注解
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD, ElementType.TYPE})
public @interface RequiresRole {
    String[] roles() default {};
}

这个注解用于标记一个方法或类需要特定的角色才能访问,roles 参数是角色的数组。

5.用于缓存结果的注解
import java.lang.annotation.ElementType;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface CacheResult {
    long cacheTime() default 60; // 缓存60秒
}

这个注解用于标记一个方法的结果应该被缓存一定时间,cacheTime 参数表示缓存的秒数。

在实际使用中,这些注解通常会与AOP(面向切面编程)框架结合,如Spring AOP,以便在运行时动态地处理注解的逻辑。

三、注解中的属性省略问题

这一节是笔者在学习时遇到的疑问,这里作为记录
​​​​在这里插入图片描述
这个注解有两个属性,value和logical ,@HasPermissions(“system:user:query”)

这种写法会把属性值默认给value,注意必须要有名为value的属性,并且其他属性都有默认值才可以

否则得显示给属性赋值

@HasPermissions(value = {"om:deviceCascade:edit","om:deviceCascade:add"},logical = Logical.OR)

总结如下

  • 如果注解只有一个属性,那么肯定是赋值给该属性。

  • 如果注解有多个属性,而且前提是这多个属性都有默认值,那么你不写注解名赋值,会赋值给名字为“value”这属性。

  • 如果注解有多个属性,其中有没有设置默认值的属性,那么当你不写属性名进行赋值的时候,是会报错的。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

快速上手文心一言指令

人不走空 &#x1f308;个人主页&#xff1a;人不走空 &#x1f496;系列专栏&#xff1a;算法专题 ⏰诗词歌赋&#xff1a;斯是陋室&#xff0c;惟吾德馨 目录 &#x1f308;个人主页&#xff1a;人不走空 &#x1f496;系列专栏&#xff1a;算法专题 ⏰诗词歌…

PHP基础教程

&#x1f40c;博主主页&#xff1a;&#x1f40c;​倔强的大蜗牛&#x1f40c;​ &#x1f4da;专栏分类&#xff1a;PHP &#x1f4da;参考教程&#xff1a;菜鸟\编程网❤️感谢大家点赞&#x1f44d;收藏⭐评论✍️ 目录 一、PHP语法 基本的 PHP 语法 PHP 注释 PHP空白不敏…

Kafka分级存储概念(一)

Kafka分级存储及实现原理 概述 Kafka社区在3.6版本引入了一个十分重要的特性: 分级存储,本系列文章主要旨在介绍Kafka分级存储的设计理念、设计细节以及具体的代码实现 背景:为什么要有分级存储? 场景 作为一款具有高吞吐及高性能的消息中间件,Kafka被广泛应用在大数据、…

Linux添加IP地址的方法

1.nmcli&#xff1a;命令式的添加IP地址 [rootlocalhost ~]#nmcli connection modify eno16777736 ipv4.addresses 192.168.126.100/24 ipv4.gateway 192.168.126.1 ipv4.method manual connection.autoconnect yes [rootlocalhost ~]# nmcli connection modify eno16777736 i…

Spring Cloud Alibaba Sentinel 集成与限流实战(6)

项目的源码地址 Spring Cloud Alibaba 工程搭建&#xff08;1&#xff09; Spring Cloud Alibaba 工程搭建连接数据库&#xff08;2&#xff09; Spring Cloud Alibaba 集成 nacos 以及整合 Ribbon 与 Feign 实现负载调用&#xff08;3&#xff09; Spring Cloud Alibaba Ribbo…

RFID在汽车制造中的应用如何改变行业

随着工业4.0和中国制造2025的推进&#xff0c;企业对于智能化、自动化的需求日益增长&#xff0c;RFID射频技术在制造业中已经相当普遍了。在如今这瞬息万变的行业与时代中&#xff0c;RFID技术可以帮助企业获得竞争优势&#xff0c;简化日益复杂的生产流程&#xff0c;推动企业…

*args和**kwargs的使用

*args传入的是按照顺序的不定长度的参数列表 **kwargs传入的是不定长度的键值对

AI 资料汇总专栏

包含AI资料、大模型资料、AI最新行业发展 人工智能&#xff08;Artificial Intelligence&#xff0c;简称AI&#xff09;是一门研究如何使计算机能够具备智能行为的科学与技术。它致力于开发出能够像人类一样思考、学习、理解和决策的计算机系统。自20世纪50年代以来&#xff…

《第一行代码》第二版学习笔记(11)——最佳的UI体验

文章目录 一、Toolbar二、滑动菜单1、DrawerLayout——抽屉2、NavigationView 三、悬浮按钮和可交互提示1、FloatingActionButton——悬浮按钮2、Snackbar——提示工具3、CoordinatorLayout 四、卡片式布局1、cardView2、AppBarLayout 五、下拉刷新——SwipeRefreshLayout六、可…

栈与递归的实现

1. 栈的概念及结构 栈&#xff1a;一种特殊的线性表&#xff0c;其只允许在固定的一端进行插入和删除元素操作。 进行数据插入和删除操作的一端 称为栈顶&#xff0c;另一端称为栈底。 栈中的数据元素遵守后进先出LIFO&#xff08;Last In First Out&#xff09;的原则&#x…

教你解决PUBG绝地求生登不进去 无法进入游戏 启动很慢的问题

尽管《绝地求生》&#xff08;PUBG&#xff09;以它那扣人心弦的战术竞技和逼真模拟的战场氛围风靡全球&#xff0c;揽获无数玩家的喜爱&#xff0c;但一些玩家在经历了一场血脉喷张的生存较量后&#xff0c;却不得不面对一个不那么愉悦的后续&#xff1a;游戏在结算阶段后出现…

docker学习笔记(五):harbor仓库搭建与简单应用

harbor私有仓库 简介 Docker容器应用的开发和运行离不开可靠的镜像管理&#xff0c;虽然Docker官方也提供了公共的镜像仓库&#xff0c;但是从安全和效率等方面考虑&#xff0c;部署私有环境内的Registry也是非常必要的。Harbor是由VMware公司开源的企业级的Docker Registry管…

文献速递:深度学习医学影像心脏疾病检测与诊断--基于迁移学习的生成对抗网络用于静态和动态心脏PET的衰减校正

Title 题目 Transfer learning‑based attenuation correction for static and dynamic cardiac PET using a generative adversarial network 基于迁移学习的生成对抗网络用于静态和动态心脏PET的衰减校正 01 文献速递介绍 心脏正电子发射断层扫描&#xff08;PET&#xf…

JAVA入门1.1.0

前言&#xff1a; 不一样的编程——基于两个大前提&#xff0c;语言随便选一个&#xff0c;作者选java和c&#xff0c;在后续的内容会有c和java的共同使用 第一大前提&#xff1a;编程语言起源于语言 第二大前提&#xff1a;计算机理解不了语言的含义 这两大前提构成了不一样的…

Word设置代码块格式

前言 Word中无法像Markdown和LaTeX一样插入代码块&#xff0c;若要在Word中插入代码块可以手动设置代码块格式或自动粘贴代码块格式。若不追求完美高亮效果&#xff0c;可使用前者方案&#xff1b;若追求完美的高亮效果&#xff0c;可使用后者方案。下文介绍这2种方案。 手动…

渲染农场评测:6大热门云渲染平台全面比较

在3D行业中&#xff0c;选择一个合适的云渲染平台可能会令许多专业人士感到难以抉择。为此&#xff0c;我们精心准备了6家流行云渲染平台的详尽评测&#xff0c;旨在为您的决策过程提供实用的参考和支持。 目前&#xff0c;市面上主要的3D网络渲染平台包括六大服务商&#xff0…

SQL编程

用户变量的语法使用 #MySQL变量的定义与使用 #一、标识符命名规范 #1、字母加数字&#xff0c;但不允许使用数字开头 #2、不允许使用关键字或保留字 #3、符号只可以使用“_”或“$" #二、变量的声明 #set用于声明变量&#xff0c;update声明修改的表&#xff0c;set是声明…

OpenGL入门第三步:矩阵变换、坐标系统

1、矩阵变换 这里矩阵变换,使用4*4的矩阵,既可以表示位移,也可以表示缩放。 原因: 添加4维矩阵变量 initializeGL()函数:在着色器里面添加变换矩阵,改变坐标位置 设计一个随时间变换 ,所有重写TimerEvent 调用update触发paintGL()函数: 2、坐标系统

NSSCTF中的web学习(md5())

目录 MD5的学习 [BJDCTF 2020]easy_md5 [LitCTF 2023]Follow me and hack me [LitCTF 2023]Ping [SWPUCTF 2021 新生赛]easyupload3.0 [NSSCTF 2022 Spring Recruit]babyphp MD5的学习 md5()函数&#xff1a; md5($a)&#xff1a;返回a字符串的散列值 md5($a,TRUE)&…

C#中字典Dictionary与自定义类型CustomType之间的转换

C#中字典Dictionary与自定义类型CustomType之间的转换 思路&#xff1a; 可以使用反射System.Reflection来获取类的具体属性&#xff0c; 属性名称就映射字典的键Key。 新建控制台程序DictionaryCustomClassConversionDemo 第一步、新建关键转换类ConversionUtil。 类Con…
最新文章