当前位置: 首页 > article >正文

安卓车载app面经

java部分

  1. 常见集合类

List

继承了Collection接口的一个接口,List中的数据是有序的,可重复的

实现类

在Java中,List 是一个接口,它属于 Java Collections Framework 的一部分。List 接口代表了一个有序的集合(有时被称为序列),可以通过它们的整数索引位置(从0开始)访问元素,并且允许重复的元素。Java 提供了多个 List 接口的实现类,每个都有其特点和适用场景。

  1. ArrayList:

    1. 实现了 List 接口。

    2. 基于动态数组的数据结构,允许快速随机访问元素。

    3. 在列表末尾添加元素的操作通常很快,但如果需要插入或删除中间的元素,则可能比较慢,因为需要移动其他元素。

  2. LinkedList:

    1. 同样实现了 List 接口,并额外实现了 Deque 接口,因此它也可以作为队列使用。

    2. 基于双向链表的数据结构,使得在任何位置(开头、结尾或中间)插入和删除元素都非常快。

    3. 但是,访问特定索引的元素相对较慢,因为它需要从头或尾遍历链表直到找到目标节点。

  3. Vector:

    1. 类似于 ArrayList,但它是同步的,意味着它是线程安全的。

    2. 因为其实现了同步机制,在不需要线程安全的情况下使用它可能会导致性能下降。

  1. Stack:(因为历史原因,栈数据结构使用ArrayDeque来实现)

    1. 继承自 Vector,提供了一种“后进先出”(LIFO)的数据结构。

    2. 主要用于支持如 push, pop, peek 等栈操作。

当你选择使用哪种类型的 List 实现时,应考虑你的具体需求,例如是否需要频繁地在列表中间进行插入和删除操作,还是更注重快速的随机访问能力等。每种实现都有其优缺点,了解这些可以帮助你做出更好的决策。

例如,如果你的应用程序需要大量的插入和删除操作,特别是在列表的两端,那么 LinkedList 可能是一个更好的选择。相反,如果你的应用主要涉及随机访问而较少涉及插入和删除,那么 ArrayList 或者 Vector 可能更加合适。

Set

继承了Collection接口的一个接口,接受泛型类,Set接口包含HashSet,TreeSet等实现类

HashSet中的数据是无序的(是散列表),可以存储空值,不可重复的,都是线程不同步

TreeSet中的数据是有序的,不可以存储空值不可重复

Map

储存了键值对的一个接口,

它不是继承自Collection 的子接口,它自成一派,它里面存放的数据是一对一对存放的,称为键值对,键不能重复,键和值都是Object型

实现类:

HashMap和Hashtable

区别:

在于HashMap线程不同步,效率高,HashTable线程同步,效率低

HashMap允许使用null作为键,而Hashtable不可以使用null作为键

put()——存入键值对

HashMap 是 Java 中用于存储键值对的数据结构,它提供了快速的插入、删除和查找操作。

HashMap

HashMap 主要依赖于数组和链表(在 Java 8 及之后版本中引入了红黑树优化)来存储数据。具体来说,HashMap 内部包含了一个 Node<K,V>[] table 数组,每个 Node 实际上就是一个单向链表的节点。当多个键通过哈希函数映射到数组的同一个位置时,这些键值对会以链表的形式存在该位置上。如果链表长度超过一定阈值(默认为8),则会将链表转换成红黑树,以此提高查询效率。

关键特性

  1. 哈希函数HashMap 使用键对象的 hashCode() 方法生成一个哈希码,然后通过一定的算法将其映射到数组中的某个索引位置。这个过程是决定键值对存储位置的关键步骤。

细节:怎么判断两个对象的hashCode是相同的?

具体说说hashCode这个方法?

  1. 负载因子(Load Factor):这是 HashMap 在扩容之前允许的最大“满度”。默认的负载因子是 0.75,意味着当 HashMap 中元素的数量达到容量的 75% 时,HashMap 会自动进行扩容(通常是将容量翻倍),以便减少哈希冲突的概率并维持较好的性能。

  2. 扩容机制:当 HashMap 达到其容量与负载因子的乘积大小时,就会触发扩容操作。扩容过程中,HashMap 会创建一个新的更大的数组,并将现有的所有键值对重新分配到新的数组中。

  3. 链表转红黑树:为了防止因哈希碰撞导致的链表过长而影响查询效率,在 Java 8 中,当链表长度超过特定阈值(默认为8)且当前 HashMap 容量大于等于64时,链表会被转换成红黑树。这大大提高了在最坏情况下的查找效率,从 O(n) 提升到了 O(log n)。

  4. 线程不安全HashMap 不是线程安全的。如果需要在多线程环境中使用,则可以考虑使用 ConcurrentHashMap 或者通过其他同步手段来保证线程安全。

  1. 访问控制关键字:private, protected, public,

private修饰的成员变量和函数 仅类内部可以访问

不写时修饰的成员变量和函数默认是一种包访问,仅内部类和同一个包中的类可以访问

protected修饰的成员变量和函数 内部类,同一个包中的类以及子类可以访问

public修饰的成员变量和函数没有访问限制

类内部

同包

子类

无限制

private

不写

protected

public

细节:

什么时候会使用这些关键字?

结合项目说:private,不希望某个类在外部被修改,保证只能在类内部使用

注意观察项目中哪里使用这些关键字,意义是什么?

protected关键字使用的场景?不希望外部类使用,只希望子类/内部类能够使用

  1. 线程状态

  1. 创建线程的三种方式,各自阐述优缺点和作用

  1. 线程池:

  1. corePoolSize(核心线程数)

  2. maximumPoolSize(最大线程数)

  3. keepAliveTime(空闲线程存活时间)

  4. unit(存活时间单位)

  5. workQueue(任务队列)

  6. threadFactory(线程工厂)

  7. handler(拒绝策略)

细节:

线程池有哪些拒绝策略?

在车机安卓项目上什么时候使用了线程池?(使用耗时操作举例)

相比于线程池/子线程,自定义线程池的好处?

线程池相关的问题

 

/** * 在实际业务中,thread Runnable Callable启动线程都不用, * 将所有的多线程异步任务都交给线程池执行-资源控制 * * public ThreadPoolExecutor(int corePoolSize, * int maximumPoolSize, * long keepAliveTime, * TimeUnit unit, * BlockingQueue<Runnable> workQueue, * ThreadFactory threadFactory, * RejectedExecutionHandler handler) * * 七大参数: * corePoolSize:核心线程数【一直存在除非设置( allowCoreThreadTimeOut)】 * 线程池,创建好以后就准备就绪的线程数量,就等待来接受异步任务去执行 * maximumPoolSize:【200】最大线程数量,控制资源 * keepAliveTime:存活时间。如果当前的线程数量大于core数量 * 释放空闲的线程(maximumPoolSize-corePoolSize)。只要线程空闲大于指定的keepAliveTime就释放 * TimeUnit:时间单位 * BlockingQueue:阻塞队列,用来存储等待执行的任务,如果当前对线程的需求超过了 corePoolSize * 大小,就会放在这里等待空闲线程执行。 * ThreadFactory:创建线程的工厂,比如指定线程名等 * RejectedExecutionHandler:拒绝策略,如果线程满了,线程池就会使用拒绝策略。 * * 工作顺序: * 运行流程: * 1、线程池创建,准备好 core 数量的核心线程,准备接受任务 * 2、新的任务进来,用 core 准备好的空闲线程执行。 * (1) 、core 满了,就将再进来的任务放入阻塞队列中。空闲的 core 就会自己去阻塞队 * 列获取任务执行 * (2) 、阻塞队列满了,就直接开新线程执行,最大只能开到 max 指定的数量 * (3) 、max 都执行好了。Max-core 数量空闲的线程会在 keepAliveTime 指定的时间后自 * 动销毁。最终保持到 core 大小 * (4) 、如果线程数开到了 max 的数量,还有新任务进来,就会使用 reject 指定的拒绝策 * 略进行处理 * 3、所有的线程创建都是由指定的 factory 创建的。 * * new LinkedBlockingDeque<>(100000) 默认是Integer的最大值。内存不够 / 业务需求 * * 面试: * 一个线程池 core 7; max 20 ,queue:50,100 并发进来怎么分配的; * 先有 7 个能直接得到执行,接下来 50 个进入队列排队,在多开 13 个继续执行。现在 70 个 * 被安排上了。剩下 30 个默认拒绝策略。 * 如果不想抛弃还要执行:使用CallerRunsPolicy策略 * public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {//同步执行线程的run方法 * if (!e.isShutdown()) { * r.run(); * } * * @param args */ ThreadPoolExecutor executor = new ThreadPoolExecutor( 10, // corePoolSize 20, // maximumPoolSize 60, // keepAliveTime TimeUnit.SECONDS, // unit new LinkedBlockingQueue<>(100), // workQueue new ThreadFactory() { // threadFactory @Override public Thread newThread(Runnable r) { Thread t = new Thread(r); t.setName("CustomThreadPoolThread"); return t; } }, new ThreadPoolExecutor.CallerRunsPolicy() // handler );

  1. 多线程方法 sleep wait join notify notifyAll()

sleep() 和 wait() 有什么区别?

sleep()是Thread类的方法,调用此方法使得当前线程停止运行一段时间,让出CPU,使得其他线程有机会执行。但是sleep()并不会放弃对象锁

wait()是Object类的方法,调用此方法使得Object放弃对象锁,进入此对象的等待锁定池,在调用Notify方法后,才进入线程获取池获取对象。

在Java中,join(), notify(), 和 notifyAll() 是与线程同步和通信相关的几个重要方法。它们用于管理线程的执行顺序以及在线程之间进行协调。下面是每个方法的详细解释:

join()

  • 作用join() 方法用于等待该线程结束。调用某个线程A的 join() 方法会使当前线程(比如主线程)等待直到线程A死亡。

  • 使用场景:当你希望在一个线程完成其工作之后再继续执行后续代码时非常有用。

  • 示例

     

    Thread threadA = new Thread(() -> { // 线程A的工作 }); threadA.start(); try { threadA.join(); // 主线程将等待直到threadA完成 } catch (InterruptedException e) { e.printStackTrace(); } // 继续执行主线程的其他任务

    notify() 和 notifyAll()

    这两个方法都是用来唤醒正在等待某个特定监视器锁(monitor lock)的线程。

    • notify()

      • 作用:唤醒一个正在等待该对象监视器的单个线程。如果有多个线程都在等待,则随机选择其中一个线程来唤醒。

      • 注意事项:必须在同步上下文中调用(即,在synchronized方法或块内),否则会抛出IllegalMonitorStateException

    • notifyAll()

      • 作用:唤醒所有正在等待该对象监视器的所有线程。然而,尽管所有的线程都被唤醒了,但只有一个线程能够获取到锁并继续执行,其他的线程将继续等待。

      • 适用情况:当你不确定哪个线程应该被唤醒,或者你想要让所有等待的线程都有机会竞争锁的时候使用。

    1. synchronized 关键字:

    代表这个方法加锁,相当于不管哪一个线程(例如线程A),运行到这个方法时,都要检查有没有其它线程B(或者C、 D等)正在用这个方法(或者该类的其他同步方法),有的话要等正在使用synchronized方法的线程B(或者C 、D)运行完这个方法后再运行此线程A,没有的话,锁定调用者,然后直接运行。

    细节:

    synchronized锁非静态方法锁的是什么?

    synchronized修饰静态方法锁的是什么?

    可重入锁有了解吗?

    synchronized相关问题

    1. volatile关键字

    volatile

    1. 解释死锁

    死锁

    1. 生产者消费者

    生产者-消费者问题及Java实现

    1. 常用设计模式

    单例模式(理解其实现方式和适用场景)单例模式写法

    工厂模式:不太熟,提一嘴

    观察者模式:

    我说的是使用匿名内部类注册回调,结合项目说一下

    https://blog.csdn.net/qq_26460841/article/details/121335675

    通过观察者模式可以实现一种一对多的关系,使得当被观察者的状态发生改变的时候,所有的观察者都可以得到通知,并作出相应的更新操作。

    • 匿名内部类:Android中,最常见的点击事件,通过设置控件的OnClickListener并传入一个OnClickListener的实现类来回调点击事件。(观察者:OnClickListener,被观察者:控件)

    • 例四:Android中,我们常用的recyclerView,listView刷行数据时调用notifyDataSetChanged()来更新ui,想知道具体原因,那么请仔细往下看完这篇文章。

    • 例五:Android中,我们通常发送一个广播,凡是注册了该广播的都可以接收到该广播,这也是Android中典型的观察者模式。

    android部分

    1. 四大组件:

    Activity:

    用于用户交互的页面,顺便把生命周期和启动模式提一嘴

    BroadCastReceiver:

    广播

    ContentProvider :

    ContentProvider

    Service

    Service的用法

    1. Activity生命周期的七个状态:

    1. onCreate()

    当Activity正在被创建时调用。这是生命周期中的第一个回调方法,通常在这里初始化界面组件和一些基本设置。

    1. onStart()

    当Activity变得可见,但还未出现在前台时调用。此时,Activity已经准备好与用户交互。

    1. onResume()

    当Activity出现在前台并与用户交互时调用。这是用户真正开始使用Activity的时刻。

    1. onPause()

    当系统准备去启动另一个Activity,当前Activity即将停止交互时调用。在这个方法中,应该做一些资源释放或者保存数据的操作,但此时Activity仍然可见。

    1. onStop()

    当Activity不再可见时调用。这可能是因为另一个Activity已经启动或者系统正在销毁Activity。

    1. onDestroy()

    当Activity即将被销毁时调用。在这个方法中,应该释放所有的资源,如线程、注册的广播接收器等。

    1. onRestart()

    • 当Activity从前台状态回到可见状态时调用(例如,从onStop返回)。这通常发生在用户返回到这个Activity时。

    Activity启动模式

    启动模式(LaunchMode)是一个用于定义Activity如何启动的属性。启动模式决定了新的Activity实例是如何与当前任务关联的。Android支持以下几种启动模式:

    1. standard:每次调用startActivity()时,都会创建一个新的实例。这是默认的启动模式。

    2. singleTop:如果新的Activity位于任务的栈顶,则不会创建新实例,而是使用现有的实例。

    3. singleTask:任务栈中只有一个实例。如果实例已存在,则不会创建新的实例,而是将该实例移至栈顶并将其上方的实例全部出栈。再次onNewIntent()方法

    4. singleInstance:新建一个任务栈,并且这个任务栈只有这一个实例。这个实例可以被多个应用共享。

    启动模式可以在AndroidManifest.xml中通过<activity>标签的android:launchMode属性来设置。

    细节:

    Activity 在横竖屏切换的时候调用onStoreInstanceState缓存的当前UI状态信息,会在哪个函数中被调用?

    onCreate

    singleTop/singleTask:比如从A页面跳到B页面,再从B页面跳回A页面,不会调用onCreate方法,那么缓存的内容是怎么被加载到A页面中的?

    onNewIntent()

    1. Binder机制:源码

    https://blog.csdn.net/weixin_37390872/article/details/109447337?spm=1001.2101.3001.6650.3&utm_medium=distribute.pc_relevant.none-task-blog-2%7Edefault%7EBlogCommendFromBaidu%7ERate-3-109447337-blog-105314445.235%5Ev43%5Econtrol&depth_1-utm_source=distribute.pc_relevant.none-task-blog-2%7Edefault%7EBlogCommendFromBaidu%7ERate-3-109447337-blog-105314445.235%5Ev43%5Econtrol&utm_relevant_index=6

    1. AIDL机制:

    https://blog.csdn.net/weixin_37390872/article/details/109447337?spm=1001.2101.3001.6650.3&utm_medium=distribute.pc_relevant.none-task-blog-2%7Edefault%7EBlogCommendFromBaidu%7ERate-3-109447337-blog-105314445.235%5Ev43%5Econtrol&depth_1-utm_source=distribute.pc_relevant.none-task-blog-2%7Edefault%7EBlogCommendFromBaidu%7ERate-3-109447337-blog-105314445.235%5Ev43%5Econtrol&utm_relevant_index=6

    在Android平台,一个进程通常不能访问另一个进程的内存空间为了使其他的应用程序也可以访问本应用程序提供的服务。

    简化版:

    细节:

    1. 什么时候会使用AIDL?

    2. AIDL的实现?

    3. AIDL的数据类型?

    AIDL相关问题

    1. 进程间通信:

    我说的广播机制:结合项目说一下

    蓝牙实例化一个BroadCastReceiver对象,接受来自框架层的广播,并进行处理,触发上层的回调传至View层,做出响应

    进程间通信相关

    1. Handler机制

    Handler,Message,looper和MessageQueue构成了安卓的消息机制,handler创建后可以通过sendMessage将消息加入消息队列,然后looper不断的将消息从MessageQueue中取出来,回调到Hander的handleMessage方法,从而实现线程的通信。

    从两种情况来说,第一在UI线程创建Handler,此时我们不需要手动开启looper,因为在应用启动时,在ActivityThread的main方法中就创建了一个当前主线程的looper,并开启了消息队列,消息队列是一个无限循环,为什么无限循环不会ANR?因为可以说,应用的整个生命周期就是运行在这个消息循环中的,安卓是由事件驱动的,Looper.loop不断的接收处理事件,每一个点击触摸或者Activity每一个生命周期都是在Looper.loop的控制之下的,looper.loop一旦结束,应用程序的生命周期也就结束了。我们可以想想什么情况下会发生ANR,第一,事件没有得到处理,第二,事件正在处理,但是没有及时完成,而对事件进行处理的就是looper,所以只能说事件的处理如果阻塞会导致ANR,而不能说looper的无限循环会ANR

    另一种情况就是在子线程创建Handler,此时由于这个线程中没有默认开启的消息队列,所以我们需要手动调用looper.prepare(),并通过looper.loop开启消息

    主线程Looper从消息队列读取消息,当读完所有消息时,主线程阻塞。子线程往消息队列发送消息,并且往管道文件写数据,主线程即被唤醒,从管道文件读取数据,主线程被唤醒只是为了读取消息,当消息读取完毕,再次睡眠。因此loop的循环并不会对CPU性能有过多的消耗。

    Handler的使用理解

    handler使用:

    Service的用法

    handler设置wifi 10s 更新一次

    Handler postdelay实现蓝牙每隔30s扫描

    1. ANR

    ANR

    1. 内存泄漏:handler

    内存泄漏

    1. 内存溢出

    内存泄漏

    1. java/Android垃圾回收机制

    垃圾回收机制

    网络协议

    1. Tcp 三次握手,四次挥手

    三次握手建立连接

    1. 第一次握手:客户端向服务器发送一个带有SYN(Synchronize)标志的数据包,用来请求建立连接。这个数据包中包含客户端的初始序列号(Seq),即客户端从哪个序列号开始发送数据。此时,客户端进入SYN_SEND状态。

    2. 第二次握手:服务器收到客户端的SYN数据包后,回复一个SYN+ACK(同步应答)数据包,表示同意建立连接。在这个数据包中,服务器将自己的初始序列号Seq设置为服务器选择的初始序列号,并将客户端的序列号加1作为应答号(Ack=X+1),以告诉客户端它已经成功接收到客户端的请求。此时,服务器进入SYN_RECEIVED状态。

    3. 第三次握手:客户端收到服务器的SYN+ACK数据包后,发送一个确认数据包ACK,表示自己已经接收到服务器的应答,并且确认序列号为服务器的序列号加1。此时,客户端和服务器之间的连接就正式建立起来了,双方可以进入数据传输状态。客户端进入ESTABLISHED状态,服务器也同样进入ESTABLISHED状态。

    四次挥手关闭连接

    四次挥手(Four-Way Handshake)是TCP协议中用于关闭一个连接的过程。由于TCP连接是全双工的,意味着数据可以在两个方向上同时传输,因此每个方向上的连接关闭需要单独处理。四次挥手确保了双方都能正确地结束会话,避免任何一方的数据丢失。以下是四次挥手的具体步骤:

    1. 第一次挥手:主动关闭方(可以是客户端或服务器端,取决于谁先发起关闭请求)发送一个带有FIN(Finish)标志的TCP段给被动关闭方,表示自己已经没有数据要发送了,但是仍然可以接收对方的数据。此时,主动关闭方进入FIN_WAIT_1状态。

    1. 第二次挥手:被动关闭方收到FIN后,会向主动关闭方回复一个ACK(Acknowledgment),确认序号为收到的FIN段的序号加1。这表明被动关闭方已知晓主动关闭方将要终止连接,但它可能还有未发送完的数据或者未确认的数据需要处理。此时,主动关闭方进入FIN_WAIT_2状态,而被动关闭方则进入CLOSE_WAIT状态。

    1. 第三次挥手:当被动关闭方准备好关闭自己的连接时(即完成了所有数据的发送和确认),它会发送一个FIN标志给主动关闭方,以请求关闭从它到主动关闭方方向的连接。这时,被动关闭方进入LAST_ACK状态。

    1. 第四次挥手:主动关闭方收到FIN后,必须发送一个ACK给被动关闭方进行确认,并且设置确认序号为收到的FIN段的序号加1。之后,主动关闭方进入TIME_WAIT状态,等待一段足够长的时间(通常是2倍的最大报文生存时间MSL,Maximum Segment Lifetime),以确保被动关闭方收到了这个确认信号并关闭连接。一旦这个时间段过去,如果没有收到更多的数据,则主动关闭方也正式关闭连接。

    1. TCP/UDP区别:

    TCP

    关键特性

    1. 面向连接:在数据传输之前,TCP需要通过三次握手建立一个连接,保证双方都准备好进行通信。

    2. 可靠性:TCP通过序列号和确认应答机制确保数据包按正确的顺序到达接收方,并且没有丢失或损坏。如果发送方在规定时间内没有收到确认信息,则会重新发送数据包。

    3. 流量控制:采用滑动窗口机制来实现流量控制,防止发送方发送的数据量超出接收方处理能力,从而避免接收缓冲区溢出。

    4. 拥塞控制:TCP实现了多种算法来应对网络拥塞问题,如慢启动、拥塞避免、快速重传和快速恢复等,以适应网络状况调整发送速率。

    5. 错误检测与纠正:每个TCP段都包含校验和字段,用于检测数据是否在传输过程中发生了错误。如果发现错误,该段将被丢弃并请求重发。

    6. 有序交付:即使接收到的数据包顺序混乱,TCP也会根据序列号对它们进行排序,然后按照正确的顺序提交给上层应用程序。

    7. 全双工通信:支持双向同时发送数据。

    TCP适用于那些对数据准确性要求较高的应用场景,例如:

    • 文件传输(FTP)

    • 邮件传输(SMTP, POP3, IMAP)

    • Web浏览(HTTP/HTTPS)

    总之,TCP是一种非常重要的协议,它保证了互联网上的数据传输既高效又可靠,是现代网络通信的基础之一。

    UDP协议

    UDP(User Datagram Protocol,用户数据报协议)是互联网协议族中的一种无连接的传输层协议。与TCP(Transmission Control Protocol,传输控制协议)不同,UDP不提供可靠的数据传输服务,也不保证数据包的顺序到达。然而,正因为缺少这些额外的功能,UDP具有更低的延迟和更高的效率,适用于那些对实时性要求较高、能够容忍一定程度的数据丢失的应用场景。

    UDP的主要特点

    1. 无连接:在通信之前,UDP不需要建立连接,直接发送数据报即可。

    2. 不可靠传输:UDP不对数据报进行确认,也不会重传丢失的数据报。如果网络状况不佳导致数据丢失或损坏,UDP不会采取任何措施来恢复。

    3. 无序传递:接收方收到的数据报可能与发送方发送的顺序不同,UDP不负责对数据报进行排序。

    4. 头部开销小:UDP头部仅包含源端口、目的端口、长度和校验和四个字段,总共8个字节,比TCP的头部要小得多。

    5. 支持广播和多播:UDP支持向多个接收者同时发送消息,适合用于实现广播或多播应用。

    使用场景

    由于其特性,UDP通常用于以下场景:

    • 实时应用:如语音通话、视频会议等,这类应用更关注于数据传输的及时性而非完整性,少量的数据丢失是可以接受的。

    • 在线游戏:需要快速响应的游戏可以利用UDP的低延迟特性,即使偶尔丢失一些更新信息也不会严重影响游戏体验。

    • 域名系统(DNS):DNS查询通常使用UDP,因为请求和响应都很简短,并且期望得到快速响应。

    • 流媒体服务:对于播放连续的音频或视频内容来说,稍微丢弃几个数据包远不如等待重新传输重要。

    项目/个人:

    做什么工作?

    技术难点?

    有印象的一些问题?

    项目架构?


    http://www.kler.cn/a/613635.html

    相关文章:

  • 嵌入式单片机程序的映像文件解读
  • idea中如何使用git
  • git在实践使用中的操作流程
  • Java----用正则表达式爬取数据
  • SpringCloud Hystrix的用法详解
  • 【C#.NET】Web API项目Swagger配置扩展
  • vscode无法打开Terminal终端
  • 电脑连不上手机热点会出现的小bug
  • 博卡软件管理中心8:为美容美发行业量身打造的轻量级管理方案
  • swagger上传图片请求报错
  • 3.27学习总结
  • 介绍几种创意登录页(含完整源码)
  • Uniapp使用大疆SDK打包离线原生插件二
  • 力扣HOT100之普通数组:41. 缺失的第一个正数
  • uvm configuration
  • Starrocks架构及如何选择
  • 【Golang】第八弹----面向对象编程
  • Qt下载模板到本地文件内容丢失问题
  • 2025年最新自动化/控制保研夏令营预推免面试真题分享(东南大学苏州校区/华东理工/南航/天大)
  • Redis 中的过期策略和内存淘汰策略