Java注解使用与自定义

news2024/11/16 15:44:48

一、什么是注解

注解是元数据的一种形式,它提供有关程序的数据,该数据不属于程序本身。注解对其注释的代码操作没有直接影响。换句话说,注解携带元数据,并且会引入一些和元数据相关的操作,但不会影响被注解的代码的逻辑

/**
 * The common interface extended by all annotation types.  Note that an
 * interface that manually extends this one does <i>not</i> define
 * an annotation type.  Also note that this interface does not itself
 * define an annotation type.
 *
 * More information about annotation types can be found in section 9.6 of
 * <cite>The Java&trade; Language Specification</cite>.
 *
 * The {@link java.lang.reflect.AnnotatedElement} interface discusses
 * compatibility concerns when evolving an annotation type from being
 * non-repeatable to being repeatable.
 *
 * @author  Josh Bloch
 * @since   1.5
 */
public interface Annotation {
    ...
}

Java中所有的注解都扩展自 Annotation这个接口,注解本质就是一个接口。比如我们最常见的重写@Override,它没有参数,所以是一个标记注解。

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.SOURCE)
public @interface Override {
}

1.1 元注解

@Override为例,我们发现上面有两个注解@Target@Retention,像这种注解的注解被称为元注解。Java中的元注解有以下几个,

@Target

这个注解标识了被修饰注解的作用对象,看源码,

@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)
public @interface Target {
    /**
     * Returns an array of the kinds of elements an annotation type
     * can be applied to.
     * @return an array of the kinds of elements an annotation type
     * can be applied to
     */
    ElementType[] value();
}

value是一个数组,说明该注解的作用对象可以是多个,取值对象在ElementType中,

public enum ElementType {
    /** Class, interface (including annotation type), or enum declaration */
    TYPE,

    /** Field declaration (includes enum constants) */
    FIELD,

    /** Method declaration */
    METHOD,

    /** Formal parameter declaration */
    PARAMETER,

    /** Constructor declaration */
    CONSTRUCTOR,

    /** Local variable declaration */
    LOCAL_VARIABLE,

    /** Annotation type declaration */
    ANNOTATION_TYPE,

    /** Package declaration */
    PACKAGE,

    /**
     * Type parameter declaration
     *
     * @since 1.8
     */
    TYPE_PARAMETER,

    /**
     * Use of a type
     *
     * @since 1.8
     */
    TYPE_USE,

    /**
     * Module declaration.
     *
     * @since 9
     */
    MODULE
}

不同的值代表被注解可修饰的范围,例如TYPE只能修饰类、接口和枚举定义,METHOD只能修饰方法,比如@Override只能注解方法。这其中有个很特殊的值叫做 ANNOTATION_TYPE, 是专门表示元注解的。

@Retention

该注解指定了被修饰的注解的生命周期。定义如下:

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

value返回了一个RetentionPolicy枚举类型,

public enum RetentionPolicy {
    /**
     * Annotations are to be discarded by the compiler.
     */
    SOURCE,

    /**
     * Annotations are to be recorded in the class file by the compiler
     * but need not be retained by the VM at run time.  This is the default
     * behavior.
     */
    CLASS,

    /**
     * Annotations are to be recorded in the class file by the compiler and
     * retained by the VM at run time, so they may be read reflectively.
     *
     * @see java.lang.reflect.AnnotatedElement
     */
    RUNTIME
}
  • SOURCE:表示注解编译时可见(在原文件有效),编译完后就被丢弃。这种注解一般用于在编译期做一些事情,比如可以让注解处理器生成一些代码,或者是让注解处理器做一些额外的类型检查,等等;
  • CLASS:表示在编译完后写入 class 文件(在class文件有效),但在类加载后被丢弃。这种注解一般用于在类加载阶段做一些事情,比如Android的资源类型检查(@ColorRes、@DrawableRes、@Px);
  • RUNTIME:表示注解会一直起作用。

   @Override就是编译时可见,编译成class之后丢失。

@Documented

编译器在生成Java文档时将被注解的元素包含进去。这意味着使用了@Documented注解的注解,其注解的信息会被包含在生成的文档中,方便开发者查阅。@Documented是一个标记注解,没有成员。

@Inherited

用于指示子类是否继承父类的注解。当一个注解被标注为 @Inherited 时,如果一个类使用了被 @Inherited 标注的注解,那么其子类也会自动继承这个注解。这在一些框架或库中很有用,可以自动继承某些特性或行为。需要注意的是,@Inherited annotation类型是被标注过的class的子类所继承。类并不从它所实现的接口继承annotation,方法并不从它所重载的方法继承annotation



一个注解可以有多个target(ElementType),但只能有一个retention(RetentionPolicy)。

1.2 自定义注解 

使用@interface标识CustomeAnnotation是一个注解,表示扩展自java.lang.annotation.Annotation

public @interface CustomeAnnotation {}

然后需要指定注解的作用域与生命周期,使用元注解@Target@Retention来注解CustomeAnnotation

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface CustomeAnnotation {}

上面表示CustomeAnnotation注解只能作用于方法上,并且其所携带的元数据会保留到运行时。
CustomeAnnotation没有携带元数据,意义不大,下面给注解添加参数(元数据) 

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

注解中每一个方法就是声明了一个配置参数,方法的名称就是参数的名称,返回值类型就是参数的类型。使用default定义参数默认值。
注解参数的可支持数据类型

  1. 所有基本数据类型(int, float, boolean, byte, double, char, long, short)
  2. String类型
  3. Class类型
  4. enum类型
  5. Annotation类型
  6. 以上所有类型的数组

Annotation类型里面的参数该怎么设定:
第一,只能用public或默认(default)这两个访问权修饰。上面例子把方法设为defaul默认类型;
第二,参数成员只能用基本类型byte,short,char,int,long,float,double,boolean八种基本数据类型和String,Enum,Class,Annotations等数据类型,以及这一些类型的数组。例如,上面例子中参数名就是value,参数类型就是String。
第三,如果只有一个参数成员,最好把参数名称设为 value,后加小括号。
使用的时候通过value = <你的元数据>传递参数,如果只有一个参数,可以不写参数名,如果有多个,则需要显示声明参数名。

二  Android常见注解 

2.1 Nullness注解

  • @NonNull
@Documented
@Retention(CLASS)
@Target({METHOD, PARAMETER, FIELD, ANNOTATION_TYPE, PACKAGE})
public @interface NonNull {
}
  • @Nullable
@Documented
@Retention(CLASS)
@Target({METHOD, PARAMETER, FIELD, ANNOTATION_TYPE, PACKAGE})
public @interface Nullable {
}

从名字上就可以很明显的看出,@NonNull表示这个参数、变量等不能为空,而@Nullable则表示可以为空,举个例子:

    private void test(@NonNull String test) {
        
    }

如果我们有这样的一个函数,用@NonNull注解表示参数不能为空,如果我们这样调用这个函数

    test(null);

我们会得到这样的警告提示,告诉我们这个参数被注解为@NonNull

如果我们将这个函数改为:

    private void testNonNull(@Nullable String test) {
        
    }

或者没有任何注解时,就没有提示了。

2.2 资源类型注解

所有以“Res”结尾的注解,都是资源类型注解。大概包括:@AnimatorRes、@AnimRes、@AnyRes、@ArrayRes、@AttrRes、@BoolRes、@ColorRes、@DimenRes、@DrawableRes、@FractionRes、@IdRes、@IntRes、@IntegerRes、@InterpolatorRes、@LayoutRes、@MenuRes、@PluralsRes、@RawRes、@StringRes、@StyleableRes、@StyleRes、@TransitionRes、@XmlRes

使用方法也都是类似的,这里举个例子:

@Documented
@Retention(CLASS)
@Target({METHOD, PARAMETER, FIELD, LOCAL_VARIABLE})
public @interface StringRes {
}
    private String getStringById(@StringRes int stringId) {
        return getResources().getString(stringId);
    }

如果我们这样写String name = getStringById(R.string.app_name);是不会有问题的,但是团队中的其他小伙伴在调用的时候写错了怎么办?比如String name = getStringById(R.layout.activity_main);如果@StringRes注解,我们看不到任何的提醒,而运行时就会报错,但是如果加上了@StringRes注解,我们就可以看到这样的错误提示:

2.3 线程注解 

包括@AnyThread、@UiThread和@WorkerThread,表明需要运行在什么线程上。

@Documented
@Retention(CLASS)
@Target({METHOD,CONSTRUCTOR,TYPE})
public @interface WorkerThread {
}

例如有一个函数,需要做一些耗时操作,我们希望它不要运行在主线程上

    @WorkerThread
    private void testThread() {
        // 耗时操作
    }

如果我们在主线程中调用这个函数会怎么样呢?

而如果这样调用就不会有问题,这样就保证了我们这个耗时操作不会执行在主线程中。 

        new Thread(new Runnable() {
            public void run() {
                testThread();
            }
        }).start();

2.4 变量限制注解

变量限制注解主要包含@IntRange和@FloatRange两种,使用方法类似,都是限制了范围,这里以@IntRange为例。

@Retention(CLASS)
@Target({METHOD,PARAMETER,FIELD,LOCAL_VARIABLE,ANNOTATION_TYPE})
public @interface IntRange {
    /** Smallest value, inclusive */
    long from() default Long.MIN_VALUE;
    /** Largest value, inclusive */
    long to() default Long.MAX_VALUE;
}

源码中可以看到,这里包含了from()to(),默认值分别是Long的最小值Long.MIN_VALUE和Long的最大值Long.MAX_VALUE
例如我们有个方法,需要限制输入的范围,我可以这样写:

    private void testRange(@IntRange(from = 1, to = 10) int number) {
        
    }

如果调用者输入了一个超出范围的值时,会这样提示他。

2.5 权限注解 

如果我们有方法需要使用某种权限,可以加上@RequiresPermission这个注解。

    @RequiresPermission(Manifest.permission.CALL_PHONE)
    private void testPermission() {

    }

如这里需要打电话的权限,但是我并没有在应用中加入该权限。

没有错,AS就是这么强大,会告诉我们这个权限可能会被用户拒绝,所以我们应该在代码中对这个权限进行检查。

        if (ActivityCompat.checkSelfPermission(this, Manifest.permission.CALL_PHONE) != PackageManager.PERMISSION_GRANTED) {
            // TODO: Consider calling
            //    ActivityCompat#requestPermissions
            // here to request the missing permissions, and then overriding
            //   public void onRequestPermissionsResult(int requestCode, String[] permissions,
            //                                          int[] grantResults)
            // to handle the case where the user grants the permission. See the documentation
            // for ActivityCompat#requestPermissions for more details.
            return;
        }
        testPermission();

 这样就没有问题了。

2.6 结果检查注解

如果我们写了一个有返回值的函数,并且我们希望调用者对这个返回值进行使用或者检查。这个时候@CheckResult注解就派上用场了。

@Documented
@Retention(CLASS)
@Target({METHOD})
public @interface CheckResult {
    /** Defines the name of the suggested method to use instead, if applicable (using
     * the same signature format as javadoc.) If there is more than one possibility,
     * list them all separated by commas.
     * <p>
     * For example, ProcessBuilder has a method named {@code redirectErrorStream()}
     * which sounds like it might redirect the error stream. It does not. It's just
     * a getter which returns whether the process builder will redirect the error stream,
     * and to actually set it, you must call {@code redirectErrorStream(boolean)}.
     * In that case, the method should be defined like this:
     * <pre>
     *  @CheckResult(suggest="#redirectErrorStream(boolean)")
     *  public boolean redirectErrorStream() { ... }
     * </pre>
     */
    String suggest() default "";
}

比如有这样一个方法,返回了String。

    @CheckResult
    private boolean testCheckResult() {
        return true;
    }

如果我们不关心他的返回值。

提示我们结果没有被使用。如果改为boolean result = testCheckResult();就不会有问题了。@CheckResult注解保证了我们方法的返回值一定会得到使用。 

2.7 CallSuper注解 

如果我们有一个父类Father,有一个方法display(),有一个子类Child继承了Father,并重写了display()方法,并不会有任何问题。

class Father {
        public void display() {
            Log.i(TAG, "display: Father");
        }
    }

    class Child extends Father {
        @Override
        public void display() {
            Log.i(TAG, "display: Child");
        }
    }

但是,如果我想让子类Child在调用display()方式也将父类Father的某些信息一起打印出来怎么办?没错,在子类的display()方法中调用super.display();即可。那么,我们怎么保证子类就一定会调用super的方法呢?@CallSuper注解登场。

@Documented
@Retention(CLASS)
@Target({METHOD})
public @interface CallSuper {
}

@CallSuper注解表示重写的方法必须调用父类的方法,注意,这里的Target只有METHOD,并没有CONSTRUCTOR,所以构造函数是不能使用这个注解的。
还是刚才的例子,如果我们在父类的方法上加上@CallSuper注解,这个时候子类中重写的方法就会提示这样的错误。

这样就提醒我们需要加上super的方法。


2.8  枚举注解

Android官方强烈建议不要在Android程序里面使用到enum,官方的Training课程里面有下面这样一句话:Enums often require more than twice as much memory as static constants. You should strictly avoid using enums on Android.

既然官方都这样说了,那就尽量不要使用枚举了,可是不使用枚举使用什么呢?没错,静态常量。
举个例子,比如我自己写了一个提示框,需要提示用户一些信息,所以这样写:

public class MyTip {
    public static void show(String message) {
        // 显示提示框
    }
}

 我希望这个提示框在显示一定时间后自动关掉,所以定义了两个静态常量,一个长时间一个短时间,并且作为show方法的一个参数。

public class MyTip {
    public static final int LONG_TIME = 0;
    public static final int SHORT_TIME = 1;
    
    public static void show(String message, int type) {
        // 显示提示框
    }
}

我可以这样让我的提示框显示一个较长的时间。

MyTip.show("message", MyTip.LONG_TIME);
MyTip.show("message", MyTip.LONG_TIME);

但是有个问题,这里我传入的参数是MyTip.LONG_TIME,但是实际上不管传入的是1还是0,甚至是MyTip.show("message", 2);都不会提示错误,因为只要是int就可以,这显示不是我想要的。这里我们就需要用到枚举注解了,@IntDef和@StringDef

@Retention(SOURCE)
@Target({ANNOTATION_TYPE})
public @interface IntDef {
    /** Defines the allowed constants for this element */
    long[] value() default {};

    /** Defines whether the constants can be used as a flag, or just as an enum (the default) */
    boolean flag() default false;
}

这时候我再修改一下代码

public class MyTip {
    public static final int LONG_TIME = 0;
    public static final int SHORT_TIME = 1;

    @IntDef({LONG_TIME, SHORT_TIME})
    @Retention(RetentionPolicy.SOURCE)
    public @interface Type {}

    public static void show(String message, @Type int type) {
        // 显示提示框
    }
}

这里自己写了一个注解@Type,并且使用了@IntDef注解,value就是两个静态常量。这时候再看调用的地方。

是不是非常熟悉?没错,我们熟悉的Toast就是用@IntDef注解这么实现的。

    @IntDef(prefix = { "LENGTH_" }, value = {
            LENGTH_SHORT,
            LENGTH_LONG
    })
    @Retention(RetentionPolicy.SOURCE)
    public @interface Duration {}


    /**
     * Set how long to show the view for.
     * @see #LENGTH_SHORT
     * @see #LENGTH_LONG
     */
    public void setDuration(@Duration int duration) {
        mDuration = duration;
        mTN.mDuration = duration;
    }

    /**
     * Return the duration.
     * @see #setDuration
     */
    @Duration
    public int getDuration() {
        return mDuration;
    }

三  注解在安卓源码中的使用

3.1 注解在Lifecycle源码中的使用

Jetpack生命周期管理 -Lifecycle实战及源码分析

class ClassesInfoCache {

    private final Map<Class, CallbackInfo> mCallbackMap = new HashMap<>();//所有观察者的回调信息
    private final Map<Class, Boolean> mHasLifecycleMethods = new HashMap<>();//观察者是否有注解了生命周期的方法

    CallbackInfo getInfo(Class<?> klass) {
        CallbackInfo existing = mCallbackMap.get(klass);//如果已经存在当前观察者回调信息 直接取
        if (existing != null) {
            return existing;
        }
        existing = createInfo(klass, null);//没有就去收集信息并创建
        return existing;
    }

    private CallbackInfo createInfo(Class<?> klass, @Nullable Method[] declaredMethods) {
        Class<?> superclass = klass.getSuperclass();
        Map<MethodReference, Lifecycle.Event> handlerToEvent = new HashMap<>();//生命周期事件到来 对应的方法
        ...
        Method[] methods = declaredMethods != null ? declaredMethods : getDeclaredMethods(klass);//反射获取观察者的方法
        boolean hasLifecycleMethods = false;
        for (Method method : methods) {//遍历方法 找到注解OnLifecycleEvent
            OnLifecycleEvent annotation = method.getAnnotation(OnLifecycleEvent.class);
            if (annotation == null) {
                continue; //没有注解OnLifecycleEvent 就return
            }
            hasLifecycleMethods = true;//有注解OnLifecycleEvent
            Class<?>[] params = method.getParameterTypes(); //获取方法参数
            int callType = CALL_TYPE_NO_ARG;
            if (params.length > 0) { //有参数
                callType = CALL_TYPE_PROVIDER;
                if (!params[0].isAssignableFrom(LifecycleOwner.class)) {
                    throw new IllegalArgumentException(//第一个参数必须是LifecycleOwner
                            "invalid parameter type. Must be one and instanceof LifecycleOwner");
                }
            }
            Lifecycle.Event event = annotation.value();

            if (params.length > 1) {
                callType = CALL_TYPE_PROVIDER_WITH_EVENT;
                if (!params[1].isAssignableFrom(Lifecycle.Event.class)) {
                    throw new IllegalArgumentException(//第二个参数必须是Event
                            "invalid parameter type. second arg must be an event");
                }
                if (event != Lifecycle.Event.ON_ANY) {
                    throw new IllegalArgumentException(//有两个参数 注解值只能是ON_ANY
                            "Second arg is supported only for ON_ANY value");
                }
            }
            if (params.length > 2) { //参数不能超过两个
                throw new IllegalArgumentException("cannot have more than 2 params");
            }
            MethodReference methodReference = new MethodReference(callType, method);
            verifyAndPutHandler(handlerToEvent, methodReference, event, klass);//校验方法并加入到map handlerToEvent 中
        }
        CallbackInfo info = new CallbackInfo(handlerToEvent);//获取的 所有注解生命周期的方法handlerToEvent,构造回调信息实例
        mCallbackMap.put(klass, info);//把当前观察者的回调信息存到ClassesInfoCache中
        mHasLifecycleMethods.put(klass, hasLifecycleMethods);//记录 观察者是否有注解了生命周期的方法
        return info;
    }

    static class MethodReference {
        final int mCallType;
        final Method mMethod;

        MethodReference(int callType, Method method) {
            mCallType = callType;
            mMethod = method;
            mMethod.setAccessible(true);
        }

        void invokeCallback(LifecycleOwner source, Lifecycle.Event event, Object target) {
            //noinspection TryWithIdenticalCatches
            try {
                switch (mCallType) {
                    case CALL_TYPE_NO_ARG:
                        mMethod.invoke(target);
                        break;
                    case CALL_TYPE_PROVIDER:
                        mMethod.invoke(target, source);
                        break;
                    case CALL_TYPE_PROVIDER_WITH_EVENT:
                        mMethod.invoke(target, source, event);
                        break;
                }
            } catch (InvocationTargetException e) {
                throw new RuntimeException("Failed to call observer method", e.getCause());
            } catch (IllegalAccessException e) {
                throw new RuntimeException(e);
            }
        }
    }
}

        OnLifecycleEvent annotation = method.getAnnotation(OnLifecycleEvent.class);
            if (annotation == null) {
                continue; //没有注解OnLifecycleEvent 就return
            }

我们看下 OnLifecycleEvent注解

@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD})
public @interface OnLifecycleEvent {
    Lifecycle.Event value();
}
    public static enum Event {
        ON_CREATE,
        ON_START,
        ON_RESUME,
        ON_PAUSE,
        ON_STOP,
        ON_DESTROY,
        ON_ANY;

        private Event() {
        }
    }

value返回Event枚举类型

//Presenter
class MyPresenter implements LifecycleObserver {
    private static final String TAG = "Lifecycle_Test";
    private final IView mView;

    public MyPresenter(IView view) {mView = view;}

    @OnLifecycleEvent(value = Lifecycle.Event.ON_START)
    private void getDataOnStart(LifecycleOwner owner){
        Log.i(TAG, "getDataOnStart: ");

        Util.checkUserStatus(result -> {
                //checkUserStatus是耗时操作,回调后检查当前生命周期状态
                if (owner.getLifecycle().getCurrentState().isAtLeast(STARTED)) {
                    start();
                    mView.showView();
                }
            });        
    }
    @OnLifecycleEvent(value = Lifecycle.Event.ON_STOP)
    private void hideDataOnStop(){
        Log.i(TAG, "hideDataOnStop: ");
        stop();
        mView.hideView();
    }
}

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

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

相关文章

程序员自我修养

目录 一、技术能力二、持续学习三、问题解决能力四、代码质量五、团队合作六、责任心七、时间与任务管理八、适应变化九、客户导向十、安全性意识十一、文档撰写十二、伦理与法律意识十三、健康生活 程序员作为现代技术行业的核心成员&#xff0c;不仅需要掌握扎实的技术技能&…

在 Java 项目中扫描识别图片中的文字(详细教程)

目录 需求&#xff1a; 步骤&#xff1a; 1、maven配置&#xff08;pom.xml&#xff09;&#xff1a; 2、下载依赖文件&#xff1a; 3、代码&#xff1a; post进行测试&#xff1a; 测试图片&#xff1a; 测试结果&#xff1a; 需求&#xff1a; 上传图片文件进行扫描…

MongoDB CRUD操作:地理位置应用——通过地理空间查询查找餐厅

MongoDB CRUD操作&#xff1a;地理位置应用——通过地理空间查询查找餐厅 文章目录 MongoDB CRUD操作&#xff1a;地理位置应用——通过地理空间查询查找餐厅地图的扭曲搜索餐厅浏览数据查找当前邻居查找附近所有餐厅查找一定距离内的餐厅使用$geoWithin&#xff0c;不排序使用…

Windows 2000 Server:安全配置终极指南

"远古技术&#xff0c;仅供娱乐" &#x1f4ad; 前言&#xff1a;Windows 2000 服务器在当时的市场中占据了很大的比例&#xff0c;主要原因包括操作简单和易于管理&#xff0c;但也经常因为安全性问题受到谴责&#xff0c;Windows 2000 的安全性真的那么差吗&#x…

kube-promethesu调整coredns监控

K8s集群版本是二进制部署的1.20.4&#xff0c;kube-prometheus对应选择的版本是kube-prometheus-0.8.0 Coredns是在安装集群的时候部署的&#xff0c;采用的也是该版本的官方文档&#xff0c;kube-prometheus中也有coredns的监控配置信息&#xff0c;但是在prometheus的监控页…

主流 RTOS 实时操作系统介绍

主流 RTOS 实时操作系统介绍 1 介绍1.1 概述1.2 RTOS 应用领域1.3 知名实时系统VRTXpSOSOS-9embOSAzure RTOS 【ThreadX 】OpenWrt 【路由】Mbed OS 【ARM 公司开发】VxWorks 【KUKA、ABB、etc】LynxOSQNXNucleusTHREADXuC/OS –II/III 【Silicon Labs 收购&#xff0c;更新频率…

Qt CAN总线发送和接收案例

文章目录 设置比特率类设置比特率类实现 发送数据帧类发送数据帧类的实现m_ui 发送帧界面 连接类连接类实现连接类UI设计 主窗口类主窗口类实现主界面UI 整体UIQT案例 设置比特率类 // 文件: BitRateBox.h // 作用: 定义了一个用于选择比特率的组合框类 BitRateBox&#xff0c…

冯喜运:6.6周四汇市行情怎么看?黄金原油日内短线布局操作

【黄金消息面分析】&#xff1a;周三&#xff08;6月5日&#xff09;美市盘初&#xff0c;现货黄金震荡走高&#xff0c;目前交投于2342美元/盎司附近。黄金价格周二下跌超过1%&#xff0c;原因是美元在本周晚些时候美国就业数据公布前趋于稳定&#xff0c;该数据可能为美联储的…

敦煌网、ebay、速卖通等平台一直被差评怎么办?

在电商行业里一直都有一句话&#xff0c;有评行走天下&#xff0c;无评寸步难行 而没有review打造爆款的案例是少之甚少&#xff0c;众所周知&#xff0c;review已经成为用户衡量一件商品的标尺&#xff0c;目前我也是看到一个文章上面写到&#xff0c;一个卖家表示自己平时运…

CSRF+XSS组合

环境&#xff1a;dvwa靶场low等级CSRFXSS payload1 将如下代码 1.html 放置攻击者web服务器中&#xff08;dvwa靶场搭建的网站根目录&#xff09; <html> <body onload"javascript:fireForms()"> <script language"JavaScript"> var …

5.透明效果

实时渲染中要实现透明效果&#xff0c;通常会在渲染模型时控制它的透明通道&#xff08;Alpha channel&#xff09;。 当一个物体被渲染到屏幕上时&#xff0c;每个片元除了颜色和深度值之外&#xff0c;它还有另一个属性—透明度。 当透明度为1时&#xff0c;表示该像素是完…

php质量工具系列之phpmd

PHPMD PHP Mess Detector 它是PHP Depend的一个衍生项目&#xff0c;用于测量的原始指标。 PHPMD所做的是&#xff0c;扫描项目中可能出现的问题如&#xff1a; 可能的bug次优码过于复杂的表达式未使用的参数、方法、属性 PHPMD是一个成熟的项目&#xff0c;它提供了一组不同的…

网站安全小白也能搞定的SSL证书安装免费方法

大家都知道&#xff0c;部署一个网站&#xff0c;除了购买域名&#xff0c;现在基本标配SSL证书。 我们以aliyun为例 大家看到这个&#xff0c;收费的SSL证书几千-几万1年不等。这时候&#xff0c;你就会想有没有免费的可以搞。linux老鸟都知道&#xff0c; Let’s Encrypt 、…

痛风患者饮食指南

文章目录 &#x1f4d6; 介绍 &#x1f4d6;&#x1f4d2; 饮食指南 &#x1f4d2;&#x1f957; 可食用食品&#x1f966; 蔬菜类&#x1f34e; 水果类&#x1f33e; 全谷物&#x1f95b; 低脂奶制品&#x1f330; 坚果类&#xff08;适量&#xff09;&#x1fad8; 豆制品&am…

RunLoop小白入门

核心概念 什么是 RunLoop ? RunLoop 是 iOS 和 macOS 应用程序框架中的一个核心概念&#xff0c;用于管理线程的事件处理。它可以看作是一个循环&#xff0c;用于持续接收和处理各种事件&#xff0c;如用户输入、定时器、网络事件等。RunLoop 在保持应用程序响应用户交互和系…

docker create rm export exec命令详解

容器生命周期管理命令教程-3 1. 创建容器 docker create&#xff1a;创建一个新的容器但不启动它。 docker create -it --name mycontainer ubuntu bash通常使用 docker run(详细可看上一篇关于run命令的详细介绍) 2. 删除容器 docker rm&#xff1a;删除一个或多个容器。 d…

T-Pot多功能蜜罐实践@debian12@FreeBSD

T-Pot介绍 T-Pot是一个集所有功能于一身的、可选择分布式的多构架&#xff08;amd64&#xff0c;arm64&#xff09;蜜罐平台&#xff0c;支持20多个蜜罐和很多可视化选项&#xff0c;使用弹性堆栈、动画实时攻击地图和许多安全工具来进一步改善欺骗体验。GitHub - telekom-sec…

如何让 VSCode 认识你正在开发的 NPM 模块

假如你正在开发一个 NPM 模块 echox&#xff0c;并且在 src/index.js 里面导出了一系列方法: // ./src/index.js export function html() {// ... }然后在 tests/index.spec.js 里面新增了以下一行&#xff1a; // ./tests/index.spec.js import * as X from echox;如何让 VS…

【面试干货】SQL中count(*)、count(1)和count(column)的区别与用法

【面试干货】SQL中count&#xff08;*&#xff09;、count&#xff08;1&#xff09;和count&#xff08;column&#xff09;的区别与用法 1、count(*)2、count(1)3、count(column) &#x1f496;The Begin&#x1f496;点点关注&#xff0c;收藏不迷路&#x1f496; 在SQL中&a…

【Qt 学习笔记】Qt窗口 | 对话框 | Qt对话框的分类及介绍

博客主页&#xff1a;Duck Bro 博客主页系列专栏&#xff1a;Qt 专栏关注博主&#xff0c;后期持续更新系列文章如果有错误感谢请大家批评指出&#xff0c;及时修改感谢大家点赞&#x1f44d;收藏⭐评论✍ Qt窗口 | 对话框 | 模态对话框 文章编号&#xff1a;Qt 学习笔记 / 51…