杂项2

简介: 本文介绍了Java编程中的多个实用知识点,涵盖集合操作、循环控制、Map遍历、类型安全、多线程等内容。包括不可变列表、集合添加判断、死循环登录验证、Map键值对遍历方式、链式调用的潜在问题、泛型类型擦除处理、增强for循环应用、列表合并与去重方法、输入流读取机制、asList的使用特点以及多线程Callable和FutureTask的协作执行流程,适用于Java开发者提升编码技巧和理解底层机制。

1.h1.stream().toList() 返回的是一个不可变列表(h1是hashset)

2.集合提供的add方法,返回值是boolean类型,所以可以根据返回值类型判断,是否添加成功

3.一般判断用户登录的时候,用while(true)死循环,根据判断结果来终止死循环(可以用"break;"

或者"return;"来终止

4.map是根据键找值,而不是索引(不存在),所有fori循环map是无法使用的;要根据键去找值;或者调用entry方法,切记不可链式调用会容易丢失类型

下面三行均是使用快捷键生成的

示例:

第一行:Map<Integer,String> t=entry.getValue();

第二行:Set<Map.Entry<Integer, String>> s1 = t.entrySet();

  • 作用:先将 entry.getValue() 的结果赋值给变量 t,再调用 t.entrySet()
  • 优势
  • 显式声明 t 的类型,增强代码可读性。
  • 便于后续对 t 进行其他操作(如修改、复用)。

第三行: Set set = entry.getValue().entrySet(); (链式调用)

  • 问题
  • 未指定泛型,set 的类型为 Set(原始类型),失去类型安全。
  • 无法直接获取 Map.Entry<Integer, String> 的类型信息,需要强制转换

形式上第一行+第二行等价于第三行,区别主要在于 类型安全代码可读性,本质上两者的功能是一致的。

5.如果不知道索引,可以用增强for循环(在嵌套中)

6.元素去重合并(两个list类型)

合并:使用stream提供的concat方法或者 l1,add(l2)

去重:(思想转换为set)使用工具类collect方法或者Set<String>s1=new HashSet<>(l3);

7.类型擦除

Set<String>s1 =new TreeSet<>((o1, o2) -> o1.compareTo(o2));

//虽然你显式创建了 TreeSet,但通过匿名 Comparator 赋值给 Set 接口变量时,
// 类型信息被擦除,导致编译器无法识别 TreeSet 的特有方法(如 first()、last())。
  1. 解决思路
  • 方法一:将变量类型声明为 TreeSet,而非 Set
  • 方法二:通过显式类型转换调用 last()

8

针对for循环中嵌套if语句,但是不想else语句先执行,而是在for循环终结时在执行else,操作步骤

int a[]={1,2,3};
    for (int i = 0; i < a.length; i++) {
        if(3==a[i]){
            System.out.println(a[i]);
        }
        else
            System.out.println(1);
    }
}
int a[] = {1, 2, 3};
boolean hasThree = false;
for (int num : a) {
    if (num == 3) {
        hasThree = true;
        System.out.println(num);
    }
}
if (!hasThree) {
    System.out.println(1);  // 仅当数组中无3时执行
}

不使用else语句

针对输入流的read在流没有关闭的时候,调用一次,读取一次

对于指定读取长度的

byte[] byte1 =new byte[6];
in.read(byte1);

一次读取六个字节,in.read(byte1)本身就是6个字节,等价于等于的字节数组长度;含义是,读取6个字符,并把这6个字节存到字节数组中

read调用一次读取一次,如果调用后剩余的字符长度不足数组定义的长度,会进行覆盖,覆盖的是上一次读取的数组,而且是从头开始覆盖

示例

97 100 228 189 160 229 165 189
 a  d   你         好

第一次调用 read (byte1)

java

in.read(byte1); // 读取前6个字节


此时 byte1 数组内容为:


plaintext

[97, 100, 228, 189, 160, 229]
 a   d   你       ?


注意最后一个字节 229 是 "好" 字的第一个字节,单独无法构成完整字符。

第二次调用 read (byte1)

java

System.out.println(in.read(byte1)); // 剩余2个字节,返回2


此时方法会读取剩余的两个字节 [165, 189],并覆盖 byte1 数组的前两个位置


java

byte1[0] = 165;  // 覆盖原有的 'a' (97)
byte1[1] = 189;  // 覆盖原有的 'd' (100)


但数组的后四个位置保持不变,因此最终 byte1 的内容为:


plaintext

[165, 189, 228, 189, 160, 229]
 ?    ?   你       ?


aslist特点

  • List<T> list = Arrays.asList(T... a)
  • 示例java
List<String> list = Arrays.asList("apple", "banana", "cherry");
// 等价于:List<String> list = Arrays.asList(new String[]{"apple", "banana", "cherry"});


  • 特点
  • 返回的 List固定大小的,不支持添加或删除元素(调用 add()remove() 会抛出 UnsupportedOperationException)。

若需要可变大小的 List,可通过 new ArrayList<>(Arrays.asList(...)) 创建副本:

java

List<String> mutableList = new ArrayList<>(Arrays.asList("a", "b"));
mutableList.add("c");  // 正常添加元素

多线程理解:

//创建类l实现接口callable
//创建l的实例化对象
//创建futuretask的实例化对象,并将的l的对象传给futuretesk的构造器即是:FutureTask f1 =new FutureTask(c1);
//创建thread对象,后续步骤同上一步 Thread t1=new Thread(f1);
//thread.start线程启动

代码执行流程

  1. 主线程创建了 MyCallable 对象和 FutureTask 对象,并且启动了新线程。
  2. 新线程开始执行 FutureTask.run() 方法,该方法会调用 MyCallable.call() 方法。
  3. 主线程调用 futureTask.get() 方法后被阻塞,等待子线程计算结束。
  4. 子线程完成计算后,get() 方法返回结果,主线程继续执行后续操作。


  • 不要手动调用 call() 方法:要是手动调用了 call() 方法,就相当于在主线程里直接执行计算任务,这就失去了多线程并行执行的意义。
  • get() 方法的阻塞特性get() 方法会使当前线程暂停执行,直到子线程完成任务。如果你希望主线程和子线程能够并发执行其他任务,可以在调用 get() 方法之前先执行一些操作。
  • 对于get阻塞特性的理解:一旦使用thread.start,则子线程就在和主线程并行的运行,在主线程运行到子线程的get方法时,如果子线程处理完成,会立即返回,否则会阻塞主线程;实质上,一旦子线程启动,子线程会在后台运行,主线程也在同时运行,只有主线程遇到get()时才会阻塞检查子线程是否运行结束
相关文章
|
4月前
|
消息中间件 存储 缓存
再次了解kafka
Kafka通过offset机制解决消息重复消费问题,支持手动提交偏移量及唯一ID去重。它保证分区内的消息顺序消费,结合集群、副本与重平衡实现高可用。高性能设计包括顺序读写、分区、页缓存、零拷贝等。数据清理依赖保留时间或大小策略,点对点和发布订阅模式则通过消费者组实现。
|
4月前
|
消息中间件 NoSQL Java
延时实现
本节介绍了多种关闭过期订单的实现方案,包括定时任务、JDK延迟队列、Redis过期监听、Redisson延迟队列、RocketMQ延迟消息及RabbitMQ死信队列。各自优缺点明显,适用于不同业务场景,如定时任务适合小数据量,RocketMQ适合高并发解耦场景,而Redisson则使用简单且高效。选择时需综合考虑系统复杂度、数据量及可靠性要求。
|
4月前
|
存储 缓存 Linux
CPU上下文切换的原理及其在系统调用和进程切换中的应用
本内容深入解析了CPU上下文切换的原理及其在系统调用和进程切换中的应用。详细说明了CPU寄存器、程序计数器在任务切换中的作用,以及系统调用与进程上下文切换的区别。同时探讨了上下文切换带来的性能开销,涉及TLB和虚拟内存管理机制,帮助理解操作系统如何高效调度进程。
|
4月前
|
Docker 容器
初始ollama
Ollama 按需加载模型,不持续运行,闲置时自动卸载,节省内存。模型响应请求时驻留内存,保留时间由 OLLAMA_KEEP_ALIVE 控制。类似 Docker 部署方式,但无单模型启停命令,默认时间内自动停止。可间接通过停止服务或配置多端口实现管理。
|
4月前
|
存储 算法 Sentinel
熔断降级
本内容介绍了微服务中熔断降级的实现原理及Sentinel的底层机制。通过OpenFeign集成Sentinel,利用断路器统计异常和慢请求比例,触发熔断并降级,提升系统稳定性。还讲解了Sentinel使用的限流算法,如滑动窗口、令牌桶和漏桶算法,以应对不同场景下的流量控制需求。
|
4月前
|
负载均衡 网络性能优化
了解EMQ
EMQ通过MQTT协议的QoS机制保障消息可靠传输,支持QoS 0、1、2三个等级,分别实现消息最多一次、至少一次和恰好一次传递。对于延迟消息,EMQ X支持通过特殊主题前缀`$delayed/{DelayInterval}`实现延迟发布。点对点通信可通过不带群组的共享订阅(如`$queue/t/1`)实现,结合负载均衡策略如随机、轮询等,确保消息仅由一个订阅者接收;发布订阅模式则通过带群组的共享订阅(如`$share/组名称/t/1`)实现,确保每组一个订阅者收取消息。
|
4月前
|
SQL JavaScript Java
三层架构理解(实现前后端分离)
本文介绍了三层架构实现前后端分离的流程,从前端Vue发起请求,到后端Spring处理数据,最后返回结果并由前端渲染展示。同时详细解析了Bean重复问题的解决方案,包括使用@Service、@Primary、@Qualifier和@Resource注解进行依赖注入控制。此外还介绍了MyBatis中#{}与${}的区别及使用场景,以及三层架构中各组件的协作方式。
|
4月前
|
SQL Java 数据库连接
事务的七种传播行为及其应用场景
本文介绍了事务的七种传播行为及其应用场景,包括 PROPAGATION_REQUIRED、PROPAGATION_SUPPORTS、PROPAGATION_REQUIRES_NEW 等,帮助开发者理解事务管理机制。同时讲解了 Java 中 SQL 操作与对象数据不同步的问题,强调重新查询与手动管理的必要性,并说明 MyBatis 批量操作的最佳实践。
|
4月前
|
XML JSON Java
Spring框架中常见注解的使用规则与最佳实践
本文介绍了Spring框架中常见注解的使用规则与最佳实践,重点对比了URL参数与表单参数的区别,并详细说明了@RequestParam、@PathVariable、@RequestBody等注解的应用场景。同时通过表格和案例分析,帮助开发者正确选择参数绑定方式,避免常见误区,提升代码的可读性与安全性。
|
4月前
|
负载均衡 Java Nacos
微服务架构中的服务注册与发现流程
本内容介绍了微服务架构中的服务注册与发现流程,包括服务注册中心(如Nacos)、服务提供者和调用者的角色分工。服务启动时自动注册信息至注册中心,调用者通过客户端负载均衡(如Spring Cloud Loadbalancer)选取服务实例进行远程调用。同时,内容还讲解了OpenFeign的工作原理,其作为HTTP客户端集成负载均衡,通过接口定义、代理生成、请求发送与结果解析,实现服务间的高效通信。