Java注解使用与自定义

一、什么是注解

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

/*** 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 {
}

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

    @WorkerThreadprivate 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。

    @CheckResultprivate 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 {@Overridepublic 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*/@Durationpublic 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) {//遍历方法 找到注解OnLifecycleEventOnLifecycleEvent annotation = method.getAnnotation(OnLifecycleEvent.class);if (annotation == null) {continue; //没有注解OnLifecycleEvent 就return}hasLifecycleMethods = true;//有注解OnLifecycleEventClass<?>[] 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 TryWithIdenticalCatchestry {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.mzph.cn/bicheng/23289.shtml

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

相关文章

导入地址表钩取技术解析

前置知识 导入表 在一个可执行文件需要用到其余DLL文件中的函数时&#xff0c;就需要用到导入表&#xff0c;用于记录需要引用的函数。例如我们编写的可执行文件需要用到CreateProcess函数&#xff0c;就需要用到kernel32.dll文件并且将其中的CreateProcess函数的信息导入到我…

重复文件查找?6款电脑重复文件清理软件很靠谱!

在日常使用电脑过程中&#xff0c;很多人下载文件后常常会忘记它们的存在&#xff0c;导致同一份资料在系统中存在多个副本。虽然你可以手动删除 Windows 系统中的所有重复文件&#xff0c;但这样做很费时间&#xff0c;而且有可能会遗漏很多文件。 而且随着重复文件的不断累积…

基于springboot实现餐饮管理系统项目【项目源码+论文说明】计算机毕业设计

基于springboot实现餐饮管理系统演示 摘要 互联网发展至今&#xff0c;无论是其理论还是技术都已经成熟&#xff0c;而且它广泛参与在社会中的方方面面。它让信息都可以通过网络传播&#xff0c;搭配信息管理工具可以很好地为人们提供服务。针对信息管理混乱&#xff0c;出错率…

使用 Navicat 工具查看 SQLite 数据库中的 PNG 图片

Navicat 是一款功能强大的数据库管理工具&#xff0c;支持多种数据库类型&#xff0c;包括 SQLite。它提供了一个直观的用户界面&#xff0c;可以轻松查看、编辑和管理数据库数据。 SQLite 是一种轻量级的嵌入式数据库&#xff0c;常用于移动应用程序和小型项目。它支持存储各…

量化投资分析平台 迅投 QMT(四)获取标的期权的代码

量化投资分析平台 迅投 QMT [迅投 QMT](https://www.xuntou.net/?user_code7NYs7O)我目前在使用有了底层标的如何获取期权的交易代码呢&#xff1f;上代码历史帖子 迅投 QMT 我目前在使用 两个月前&#xff08;2024年4月&#xff09;迅投和CQF有一个互动的活动&#xff0c;进…

Linux--Socket编程基础

一、Socket简介 套接字&#xff08; socket &#xff09;是 Linux 下的一种进程间通信机制&#xff08; socket IPC &#xff09;&#xff0c; 使用 socket IPC 可以使得在不同主机上的应用程序之间进行通信&#xff08;网络通信&#xff09;&#xff0c;当然也可以是同一台…

【Excel】Excel中将日期格式转换为文本格式,并按日期显示。

【问题需求】 在使用excel进行数据导入的过程中&#xff0c; 有的软件要求日期列必须是文本格式。 但是直接将日期列的格式改为文本后&#xff0c;显示一串数字&#xff0c;而不按日期显示。 进而无法导入使用。 【解决方法】 使用【TXET】函数公式进行处理&#xff0c; 在单…

EDA数据跨网交换解决方案,一文了解

EDA数据通常与电子设计自动化相关&#xff0c;这是一种利用计算机辅助设计&#xff08;CAD&#xff09;软件来完成超大规模集成电路&#xff08;VLSI&#xff09;芯片的功能设计、综合、验证、物理设计等流程的技术。以下是一些会涉及到EDA数据的行业&#xff1a; 集成电路设计…

淘宝扭蛋机源码解析:功能实现与技术细节

随着在线购物和娱乐的融合&#xff0c;淘宝扭蛋机作为一种创新的购物娱乐方式&#xff0c;受到了广大用户的喜爱。本文将深入解析淘宝扭蛋机的源码&#xff0c;探讨其功能实现与技术细节&#xff0c;以期为开发者们提供一些有价值的参考。 一、功能实现 1.用户登录与注册 淘宝…

《深入浅出OCR》项目实战:基于CRNN的文字识别

基于CRNN的文本字符验证码识别 1项目介绍链接&#xff1a; 为方便大家快速上手OCR实战&#xff0c;本次实战项目采用开源框架PaddleOCR&#xff0c;大家可以参考官网文档快速了解基本使用&#xff0c;项目数据为2022 DCIC赛题中提供的验证码数据集&#xff0c;大家可以参考其他…

圈子社区系统源码 开源 多端圈子社区论坛系统 社区圈子管理系统

介绍 圈子论坛小程序&#xff0c;是一款为用户提供交流分享、互动沟通的平台。在这个小程序中&#xff0c;用户可以轻松地加入各种不同兴趣爱好的圈子&#xff0c;与志同道合的朋友们交流互动。圈子论坛小程序不仅仅是一个简单的社交工具&#xff0c;更是一个打开新世界大门的…

el-table 固定前n行

el-table 固定前n行 第一种&#xff0c;通过设置前几行粘性布局 <el-table:data"tableData2"borderheight"calc(98% - 40px)"// 设置行样式:row-class-name"TableRowClassName"selection-change"handleSelectionChange" ><…

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

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

CentOS 9安装Kubernetes(k8s)集群

前言 1、版本说明 系统版本&#xff1a;CentOS 9 k8s版本&#xff1a;v1.29.5 docker版本&#xff1a;26.1.3 harbor&#xff1a;v2.9.4 2、提前准备好1台虚拟机&#xff0c;可以参考博客&#xff1a;Vmware 17安装 CentOS9 3、虚拟机提前安装好docker&#xff0c;参考博客&a…

微信小程序多端框架打包后发布到华为市场

app上架华为应用市场 一、android 发布到华为应用市场 1、华为应用市场注册开发者账号 https://developer.huawei.com/consumer/cn/?ha_sourcesem&ha_sourceId89000605 2、进行企业认证 3、app隐私弹窗 miniapp-privacy.json 1、协议弹窗内容&#xff1a; {"tit…

C语言中的整型提升

一. 简介 本文来简单学习一下&#xff0c;C语言中的整型提升。 二. C语言中的整型提升 1. 什么是整型提升&#xff1f; C语言中的算数运算表达式总是以整型的精度进行的&#xff0c;为了获得这个精度&#xff0c;表达式的字符和短整型操作数在使用之前被隐式转换为普通类型…

Linux中Apache网站基于Http服务的访问限制(基于地址/用户)

&#x1f3e1;作者主页&#xff1a;点击&#xff01; &#x1f468;‍&#x1f4bb;Linux高级管理专栏&#xff1a;点击&#xff01; ⏰️创作时间&#xff1a;2024年6月3日11点44分 &#x1f004;️文章质量&#xff1a;95分 为了更好地控制对网站资源的访问&#xff0c;可…

谈谈JDK 漏洞 6260652

JDK 漏洞 6260652 在看ArrayList源码的过程中 发现带参构造里有一个注释&#xff1a; // c.toArray might (incorrectly) not return Object[] (see 6260652) public ArrayList(Collection<? extends E> c) {elementData c.toArray();if ((size elementData.length) …

动物收容所

题目链接 动物收容所 题目描述 注意点 若没有可以收养的动物&#xff0c;则返回[-1,-1]收纳所的最大容量为20000编号随着收养动物的增加自增 解答思路 利用队列先进先出的特点将猫和狗分别存进两个队列中&#xff0c;关键是dequeueAny这个方法中如果此时猫和狗的队列中都有…