推陈换新系列————java8新特性(编程语言的文艺复兴)

文章目录

  • 前言
  • 一、新特性秘籍
  • 二、Lambda表达式
    • 2.1 语法
    • 2.2 函数式接口
    • 2.3 内置函数式接口
    • 2.4 方法引用和构造器引用
  • 三、Stream API
    • 3.1 基本概念
    • 3.2 实战
    • 3.3 优势
  • 四、新的日期时间API
    • 4.1 核心概念与设计原则
    • 4.2 核心类详解
      • 4.2.1 LocalDate(本地日期)
      • 4.2.2 LocalTime(本地时间)
      • 4.2.3 LocalDateTime(本地日期时间)
      • 4.2.4 ZonedDateTime(带时区的日期时间)
      • 4.2.5 Instant(瞬时点)
    • 4.3 时间间隔与周期
      • 4.3.1 Duration(持续时间)
      • 4.3.2 Period(日期间隔)
    • 4.4 时间调整器(TemporalAdjuster)
    • 4.5 总结
  • 五、optional类
    • 5.1 概念
    • 5.2 创建实例
    • 5.3 常用的方法
    • 5.4 实战
    • 5.5 最佳实践
  • 六、接口中默认方法和静态方法
  • 献给读者


在这里插入图片描述
在这里插入图片描述


前言

想象一下,Java世界迎来了一场盛大的革命——Java 8隆重登场。这次更新不仅仅是技术上的进步,更是一场编程思维的转变,让开发者们仿佛从黑白电视时代一步跨入了4K超高清智能电视的新纪元。

首先,不得不提的是Lambda表达式,这是Java 8送给程序员的一份大礼。以往编写代码时,我们总是被繁琐的匿名内部类所困扰,而Lambda表达式的出现就像是给这段枯燥的旅程中注入了一股清泉。它让你能够以一种更加简洁、直观的方式操作数据和定义行为,就像在画布上用寥寥几笔勾勒出一幅生动的画卷。函数式接口则像是为这场表演搭建了一个舞台,让Lambda表达式可以在这个舞台上自由舞蹈。

接着是Stream API,它为处理集合提供了一种全新的方式。如果你曾经为了遍历一个列表并从中筛选出符合条件的元素而绞尽脑汁,那么现在只需要几行优雅的代码就能搞定这一切。Stream API就像是一个魔法棒,轻轻一点就能将你的数据转换成你想要的样子,无论是过滤、排序还是聚合,一切都变得轻而易举。

再来看看新的日期和时间API(java.time包),这简直是对旧版日期处理机制的一次彻底颠覆。曾经那些关于日期计算的噩梦,在这里都能找到简单而有效的解决方案。这个新API提供了强大的功能来处理瞬时点、时间段以及不同地区的日期和时间,使得即使是处理复杂的国际化应用也能游刃有余。

还有那令人兴奋的Optional类,它就像是一个贴心的小助手,帮助我们避免了空指针异常这个长久以来的敌人。通过使用Optional,我们可以明确地表示某个值可能存在也可能不存在的情况,从而写出更加健壮且易于理解的代码。

最后,不要忘了Java 8对注解的支持也得到了加强。重复注解和类型注解的引入,让我们的代码不仅能够传达更多的信息,同时也变得更加灵活。这就像是给你的工具箱里添加了几把多功能的瑞士军刀,无论遇到什么样的挑战,都能够从容应对。
在这里插入图片描述

一、新特性秘籍

Java 8的新特性,犹如一场编程语言的文艺复兴,开启了代码优雅与效率并存的新篇章。

  • Lambda表达式,是将行为抽象为艺术的画笔,用简洁的线条勾勒出逻辑的本质。
  • Stream API,则是数据流动的诗意,让集合操作如溪流般自然流畅,汇聚、过滤、映射一气呵成。
  • 新的日期时间API,如同精准的钟表匠,拨开了旧版日期处理的迷雾,赋予时间运算以秩序与美感。
  • Optional类,宛如一位守护者,用温柔的方式提醒我们关注可能的空值陷阱,让代码多了一份从容与稳健。
  • 接口中的默认方法与静态方法,仿佛给传统接口注入了灵魂,使其既能传承经典,又能灵活扩展。
  • 方法引用,则是对已有实现的致敬,通过简短的符号唤醒隐藏在代码深处的力量。

💡贴士:这一切,正如递归是探索自我重复的奇妙之旅,Java 8的新特性,则是引领开发者迈向简洁、优雅与高效的全新境界。

二、Lambda表达式

Lambda表达式是Java 8引入的一个重要特性,它为Java语言带来了函数式编程的能力。Lambda表达式允许你将行为作为参数传递给方法或存储为变量,简化了代码结构,使得编写更加简洁、清晰的代码成为可能。Lambda表达式是一个匿名函数,使用lambda表达式使代码更加简洁紧凑。

2.1 语法

  • 基本语法格式如下:
(parameters) -> expression
  • 或者对于需要多行语句的情况:
(parameters) -> { statements; }

这里,parameters是参数列表,->符号用来分隔参数列表和Lambda体,expressionstatements构成了Lambda体,即你要执行的操作。

假设我们有一个简单的例子,使用传统方式定义一个线程:

new Thread(new Runnable() {@Overridepublic void run() {System.out.println("Running in a thread");}
}).start();

使用Lambda表达式可以简化为:

new Thread(() -> System.out.println("Running in a thread")).start();

可以看到,使用Lambda表达式不仅减少了冗余代码,而且使代码意图更加明确。

2.2 函数式接口

Lambda表达式只能用于上下文为目标类型是“函数式接口”的地方。函数式接口是指仅包含一个抽象方法的接口(虽然可以有多个默认方法和静态方法)。为了方便识别,Java 8提供了@FunctionalInterface注解来标注这样的接口,但即使不加此注解,只要接口满足条件,就可以与Lambda表达式一起使用。
例如,Runnable接口就是一个函数式接口,因为它只定义了一个run()方法。

2.3 内置函数式接口

查看详细的内置函数👉👉👉 点我
查看详细的内置函数👉👉👉 点我
查看详细的内置函数👉👉👉 点我

Java 8在java.util.function包中提供了一系列通用的函数式接口,包括但不限于:

  • Predicate:接受一个参数,返回一个布尔值。
  • Function<T, R>:接受一个参数,返回一个结果。
  • Consumer:接受一个参数,没有返回值。
  • Supplier:不接受任何参数,返回一个结果。
  • BinaryOperator:接受两个同类型的参数,返回相同类型的结果。

💡贴士:这些内置接口极大地提高了Lambda表达式的灵活性和适用性。

2.4 方法引用和构造器引用

Lambda表达式还支持方法引用和构造器引用,进一步增强了其表现力。方法引用通过::操作符实现,可以直接引用已有方法或构造器。例如,String类的compareTo方法可以通过方法引用来使用:

Arrays.sort(strArray, String::compareTo);

这表示使用String类的compareTo方法对strArray数组进行排序。

三、Stream API

Java 8引入的Stream API(流)为处理集合提供了强大的功能,使得对数据的操作更加简洁和高效。Stream是一个从支持数据处理操作的源生成的元素序列,这些操作包括过滤、排序、映射等。Stream API的设计灵感来源于函数式编程语言中的概念,它允许开发者以声明式的方式定义数据处理管道。

查看详细的Stream API👉👉👉 点我
查看详细的Stream API👉👉👉 点我
查看详细的Stream API👉👉👉 点我

3.1 基本概念

  • 流(Stream):是从支持数据处理操作的源生成的元素序列。流本身并不存储数据,而是通过一系列中间操作来描述计算过程,最终由终端操作触发计算并产生结果。
  • 源(Source):提供流的数据来源,如集合、数组或I/O资源。
  • 中间操作(Intermediate Operations):返回一个新的流,允许进行链式调用。常见的有filter, map, sorted等。
  • 终端操作(Terminal Operation):触发流的执行,并产生一个结果或副作用。常见的有forEach, collect, reduce等。

3.2 实战

  1. 创建流
    可以从集合、数组等创建流:
List<String> list = Arrays.asList("apple", "banana", "orange");
Stream<String> stream = list.stream();
  1. 中间操作
  • 过滤(Filter): 根据给定的条件筛选元素。
stream.filter(s -> s.startsWith("a"));
  • 映射(Map): 将元素转换为其他形式或提取信息。
stream.map(String::toUpperCase);
  • 排序(Sorted): 对流中的元素进行排序。
stream.sorted();
  1. 终端操作
  • 遍历(ForEach): 对每个元素执行某个动作。、
stream.forEach(System.out::println);
  • 收集(Collect): 将流中的元素收集成集合或其他形式。
List<String> resultList = stream.collect(Collectors.toList());
  • 归约(Reduce): 将流中的元素组合成一个单一的结果。
Optional<String> result = stream.reduce((s1, s2) -> s1 + "," + s2);
  1. 并行流
    Stream API还支持并行流,可以轻松实现并行处理:
List<String> list = Arrays.asList("apple", "banana", "orange");
list.parallelStream().forEach(System.out::println);

💡贴士:使用并行流时需要注意线程安全问题,特别是在修改共享状态的情况下。

3.3 优势

  • 惰性求值:中间操作不会立即执行,只有当遇到终端操作时才会真正开始处理。
  • 内部迭代:与传统的外部迭代相比,Stream采用内部迭代,使得代码更加简洁。
  • 易于并行:通过简单的API即可实现并行处理,提高程序性能。

💡贴士:StreamAPI极大地简化了对集合数据的操作,让编写清晰、高效的代码变得更加容易。无论是进行复杂的数据分析还是简单的查询操作,StreamAPI都能提供灵活且强大的支持。

四、新的日期时间API

Java 8引入了全新的日期和时间API(java.time包及其子包),以解决旧版日期时间API(如java.util.Date和java.util.Calendar)存在的问题,例如非线程安全、设计不佳和时区处理困难。新的API不仅线程安全,还提供了更直观、更强大的功能,满足了现代应用对日期和时间处理的需求。

4.1 核心概念与设计原则

  • 不可变性
    所有日期和时间类都是不可变的(Immutable),一旦创建就无法修改。这使得它们天然适合多线程环境。

  • 清晰的职责划分
    LocalDate:仅表示日期(年月日),无时区信息。
    LocalTime:仅表示时间(小时分钟秒),无时区信息。
    LocalDateTime:结合日期和时间,无时区信息。
    ZonedDateTime:包含日期、时间和时区信息。
    Instant:表示时间线上的一个瞬时点(UTC时间戳)。

  • ISO标准
    遵循ISO 8601国际标准,日期格式为yyyy-MM-dd,时间格式为HH:mm:ss。

4.2 核心类详解

4.2.1 LocalDate(本地日期)

用于表示不带时区的日期,例如2025-03-26。

  • 常用方法:
    • LocalDate.now():获取当前日期。
    • LocalDate.of(year, month, day):创建指定日期。
    • plusDays(long daysToAdd):增加天数。
    • minusDays(long daysToSubtract):减少天数。
    • isBefore(LocalDate other):判断是否在某个日期之前。
    • isAfter(LocalDate other):判断是否在某个日期之后。
LocalDate today = LocalDate.now();
System.out.println("今天是:" + today);LocalDate birthday = LocalDate.of(1998, 1, 6);
System.out.println("生日是:" + birthday);LocalDate tomorrow = today.plusDays(1);
System.out.println("明天是:" + tomorrow);

4.2.2 LocalTime(本地时间)

用于表示不带时区的时间,例如21:41:30。

  • 常用方法:
    • LocalTime.now():获取当前时间。
    • LocalTime.of(hour, minute, second):创建指定时间。
    • plusHours(long hoursToAdd):增加小时数。
    • minusMinutes(long minutesToSubtract):减少分钟数。
    • isBefore(LocalTime other):判断是否在某个时间之前。
LocalTime nowTime = LocalTime.now();
System.out.println("当前时间是:" + nowTime);LocalTime specificTime = LocalTime.of(14, 30, 45);
System.out.println("指定时间是:" + specificTime);LocalTime laterTime = nowTime.plusHours(2);
System.out.println("两小时后的时间是:" + laterTime);

4.2.3 LocalDateTime(本地日期时间)

用于表示不带时区的日期和时间,例如2025-03-26T21:41:30。

  • 常用方法:
    • LocalDateTime.now():获取当前日期时间。
    • LocalDateTime.of(year, month, day, hour, minute, second):创建指定日期时间。
    • plusDays(long daysToAdd):增加天数。
    • minusHours(long hoursToSubtract):减少小时数。
LocalDateTime currentDateTime = LocalDateTime.now();
System.out.println("当前日期时间是:" + currentDateTime);LocalDateTime specificDateTime = LocalDateTime.of(2025, 3, 26, 21, 41, 30);
System.out.println("指定日期时间是:" + specificDateTime);LocalDateTime nextWeek = currentDateTime.plusDays(7);
System.out.println("一周后的日期时间是:" + nextWeek);

4.2.4 ZonedDateTime(带时区的日期时间)

用于表示带时区的日期和时间,例如2025-03-26T21:41:30+08:00[Asia/Shanghai]。

  • 常用方法:
    • ZonedDateTime.now(ZoneId zone):获取指定时区的当前日期时间。
    • withZoneSameInstant(ZoneId zone):转换到另一个时区的相同时刻。
ZonedDateTime nowInShanghai = ZonedDateTime.now(ZoneId.of("Asia/Shanghai"));
System.out.println("上海当前日期时间是:" + nowInShanghai);ZonedDateTime nowInNewYork = nowInShanghai.withZoneSameInstant(ZoneId.of("America/New_York"));
System.out.println("纽约当前日期时间是:" + nowInNewYork);

4.2.5 Instant(瞬时点)

用于表示时间线上的一个瞬时点(UTC时间戳),通常用于记录事件发生的时间或进行时间计算。

  • 常用方法:
    • Instant.now():获取当前瞬时点。
    • Duration.between(Instant start, Instant end):计算两个瞬时点之间的时间差。
Instant now = Instant.now();
System.out.println("当前瞬时点是:" + now);Instant oneHourLater = now.plus(Duration.ofHours(1));
System.out.println("一小时后的瞬时点是:" + oneHourLater);Duration duration = Duration.between(now, oneHourLater);
System.out.println("时间差是:" + duration.toMillis() + " 毫秒");

4.3 时间间隔与周期

4.3.1 Duration(持续时间)

用于表示两个瞬时点之间的时间间隔,适用于秒和纳秒级别的时间计算。

Duration duration = Duration.ofHours(2);
System.out.println("持续时间为:" + duration.toMinutes() + " 分钟");

4.3.2 Period(日期间隔)

用于表示两个日期之间的间隔,适用于年、月、日级别的时间计算。

LocalDate startDate = LocalDate.of(2024, 1, 1);
LocalDate endDate = LocalDate.of(2025, 3, 26);Period period = Period.between(startDate, endDate);
System.out.println("间隔为:" + period.getYears() + " 年 " + period.getMonths() + " 月 " + period.getDays() + " 天");

4.4 时间调整器(TemporalAdjuster)

用于执行复杂的日期调整操作,例如“本月的第一个周一”或“下个月的最后一天”。

LocalDate today = LocalDate.now();
LocalDate firstMondayOfMonth = today.with(TemporalAdjusters.firstInMonth(DayOfWeek.MONDAY));
System.out.println("本月的第一个周一是:" + firstMondayOfMonth);

4.5 总结

Java 8的新日期时间API解决了旧版API的诸多问题,提供了更加现代化、易用的功能。无论是简单的日期操作还是复杂的时区处理,它都能轻松胜任。以下是使用新API的一些最佳实践:

  • 尽量避免直接操作原始类型(如int),而使用LocalDate、LocalTime等专用类。
  • 在需要精确时间计算时,优先使用Duration和Period。
  • 使用ZonedDateTime和ZoneId处理跨时区的应用场景。

通过这些改进,Java 8让日期和时间的处理变得更加优雅和高效!

五、optional类

Optional是Java 8引入的一个容器类,旨在优雅地处理可能为null的值,避免潜在的NullPointerException。它鼓励开发者显式处理可能缺失的值,而不是依赖于null检查,从而编写出更清晰、更健壮的代码。

5.1 概念

核心概念

  1. 存在性:Optional对象可以包含一个非空值(通过isPresent()方法判断)或为空(即不包含任何值)。
  2. 不可变性:一旦创建了Optional实例,就不能更改其内部的值。
  3. 避免强制解包:与原始类型的包装类型不同,Optional设计来避免直接调用.get()获取值前没有进行null检查的情况。

5.2 创建实例

有几种方式可以创建Optional实例:

  1. Optional.of(value):当确定value不是null时使用。如果传入null,会抛出NullPointerException。
  2. Optional.ofNullable(value):允许传入null值。如果传入的是null,则返回一个空的Optional实例。
  3. Optional.empty():创建一个空的Optional实例。

5.3 常用的方法

  • isPresent():如果Optional包含值,则返回true。
  • ifPresent(Consumer<? super T> consumer):如果有值,则对其执行提供的Consumer操作,否则不执行任何操作。这通常用于对存在的值执行某些操作而不需要显式的null检查。
  • get():如果Optional包含值,则返回该值;如果没有值,则抛出NoSuchElementException。建议在调用此方法之前先检查是否有值存在。
  • orElse(T other):如果存在值则返回该值,否则返回指定的默认值。
  • orElseGet(Supplier<? extends T> supplier):功能类似于orElse,但它接受一个提供者函数,只有在需要时才计算默认值。
  • orElseThrow(Supplier<? extends X> exceptionSupplier):如果存在值,则返回该值;否则抛出由提供的异常供应商生成的异常。

5.4 实战

使用of和get

Optional<String> optionalValue = Optional.of("Hello, World!");
System.out.println(optionalValue.get()); // 输出: Hello, World!

使用ofNullable处理可能为null的情况

Optional<String> optionalNull = Optional.ofNullable(null);
System.out.println(optionalNull.isPresent()); // 输出: false

使用ifPresent执行操作

optionalValue.ifPresent(value -> System.out.println("Value is present: " + value));
// 如果optionalValue有值,则输出: Value is present: Hello, World!

提供默认值

String defaultValue = optionalNull.orElse("Default Value");
System.out.println(defaultValue); // 输出: Default Value

5.5 最佳实践

  • 避免过度使用Optional作为方法参数或字段类型,它更适合用于返回类型以表示可能存在也可能不存在的返回值。
  • 不要将Optional用于集合或数组类型的封装,因为这些类型本身已经提供了表达“无元素”的机制(如空集合或空数组)。
  • 在适当的情况下,优先考虑使用ifPresent、orElse等方法,而不是直接调用get(),这样可以减少运行时错误的风险。

💡贴士: 通过合理使用Optional,可以使代码更加清晰地表达意图,并有效减少因未处理null值而导致的错误。

六、接口中默认方法和静态方法

Java 8引入了对接口的重要增强,允许在接口中定义默认方法(default methods)和静态方法(static methods)。这些特性为接口添加了新的维度,使得它们不仅仅是抽象规范的集合,还可以包含具体的实现逻辑。这不仅简化了API的设计和演化,还为现有接口提供了向后兼容的方式进行扩展。

默认方法
默认方法允许你在接口中提供一个具体的方法实现。这意味着即使实现类没有提供该方法的具体实现,也可以使用接口中提供的默认实现。这对于库的设计者来说特别有用,因为他们可以在不破坏现有实现类的情况下向接口添加新功能。

定义默认方法
默认方法通过使用default关键字来定义:

public interface MyInterface {// 抽象方法void abstractMethod();// 默认方法default void defaultMethod() {System.out.println("This is a default method.");}
}

使用默认方法
当一个类实现了包含默认方法的接口时,它可以:

  • 直接使用接口中的默认方法。
  • 覆盖默认方法以提供自己的实现。
    例如:
public class MyClass implements MyInterface {@Overridepublic void abstractMethod() {System.out.println("MyClass's implementation of abstractMethod");}// 可选:覆盖默认方法@Overridepublic void defaultMethod() {System.out.println("MyClass's implementation of defaultMethod");}
}

静态方法
静态方法是另一个Java 8为接口新增的功能。它们类似于普通类中的静态方法,可以通过接口名直接调用。静态方法主要用于提供与接口相关的工具函数或辅助函数。

定义静态方法
静态方法通过使用static关键字来定义:

public interface MyInterface {static void staticMethod() {System.out.println("This is a static method.");}
}

使用静态方法

MyInterface.staticMethod(); // 输出: This is a static method.

解决冲突
当一个类实现多个接口,而这些接口包含了相同签名的默认方法时,会发生方法冲突。解决这种冲突有几种方式:

  • 类优先:如果实现类提供了该方法的具体实现,则使用类中的实现。
  • 明确指定:如果需要使用某个特定接口的默认实现,可以通过InterfaceName.super.methodName()的方式来调用。
    例如:
public interface InterfaceA {default void myMethod() {System.out.println("InterfaceA's default method");}
}public interface InterfaceB {default void myMethod() {System.out.println("InterfaceB's default method");}
}public class MyClass implements InterfaceA, InterfaceB {@Overridepublic void myMethod() {InterfaceA.super.myMethod(); // 调用InterfaceA的默认方法}
}

💡贴士:Java 8通过引入默认方法和静态方法极大地增强了接口的功能性,使接口能够更好地适应现代编程的需求。这些改进有助于减少样板代码,同时保持向后兼容性。


献给读者


💯 计算机技术的世界浩瀚无垠,充满了无限的可能性和挑战,它不仅是代码与算法的交织,更是梦想与现实的桥梁。无论前方的道路多么崎岖不平,希望你始终能保持那份初心,专注于技术的探索与创新,用每一次的努力和进步书写属于自己的辉煌篇章。

🏰在这个快速发展的数字时代,愿我们都能成为推动科技前行的中坚力量,不忘为何出发,牢记心中那份对技术执着追求的热情。继续前行吧,未来属于那些为之努力奋斗的人们。


亲,码字不易,动动小手,欢迎 点赞 ➕ 收藏,如 🈶 问题请留言(评论),博主看见后一定及时给您答复,💌💌💌


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

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

相关文章

树莓派5从零开发至脱机脚本运行教程——1.系统部署篇

树莓派5应用实例——工创视觉 前言 哈喽&#xff0c;各位小伙伴&#xff0c;大家好。最近接触了树莓派&#xff0c;然后简单的应用了一下&#xff0c;学习程度并不是很深&#xff0c;不过足够刚入手树莓派5的小伙伴们了解了解。后面的几篇更新的文章都是关于开发树莓派5的内容…

GPT Researcher 的win docker安装攻略

github网址是&#xff1a;https://github.com/assafelovic/gpt-researcher 因为docker安装方法不够清晰&#xff0c;因此写一个使用方法 以下是针对 Windows 系统 使用 Docker 运行 AI-Researcher 项目的 详细分步指南&#xff1a; 步骤 1&#xff1a;安装 Docker 下载 Docke…

【后端】【Django DRF】从零实现RBAC 权限管理系统

Django DRF 实现 RBAC 权限管理系统 在 Web 应用中&#xff0c;权限管理 是一个核心功能&#xff0c;尤其是在多用户系统中&#xff0c;需要精细化控制不同用户的访问权限。本文介绍如何使用 Django DRF 设计并实现 RBAC&#xff08;基于角色的访问控制&#xff09;系统&…

C#基础学习(五)函数中的ref和out

1. 引言&#xff1a;为什么需要ref和out&#xff1f; ​问题背景&#xff1a;函数参数默认按值传递&#xff0c;值类型在函数内修改不影响外部变量&#xff1b;引用类型重新赋值时外部对象不变。​核心作用&#xff1a;允许函数内部修改外部变量的值&#xff0c;实现“双向传参…

八纲辨证总则

一、八纲辨证的核心定义 八纲即阴、阳、表、里、寒、热、虚、实&#xff0c;是中医分析疾病共性的纲领性辨证方法。 作用&#xff1a;通过八类证候归纳疾病本质&#xff0c;为所有辨证方法&#xff08;如脏腑辨证、六经辨证&#xff09;的基础。 二、八纲分类与对应关系 1. 总…

【linux重设gitee账号密码 克隆私有仓库报错】

出现问题时 Cloning into xxx... remote: [session-1f4b16a4] Unauthorized fatal: Authentication failed for https://gitee.com/xxx/xxx.git/解决方案 先打开~/.git-credentials vim ~/.git-credentials或者创建一个 torch ~/.git-credentials 添加授权信息 username/pa…

绿联NAS安装内网穿透实现无公网IP也能用手机平板远程访问经验分享

文章目录 前言1. 开启ssh服务2. ssh连接3. 安装cpolar内网穿透4. 配置绿联NAS公网地址 前言 大家好&#xff0c;今天给大家带来一个超级炫酷的技能——如何在绿联NAS上快速安装cpolar内网穿透工具。想象一下&#xff0c;即使没有公网IP&#xff0c;你也能随时随地远程访问自己…

CSS 美化页面(一)

一、CSS概念 CSS&#xff08;Cascading Style Sheets&#xff0c;层叠样式表&#xff09;是一种用于描述 HTML 或 XML&#xff08;如 SVG、XHTML&#xff09;文档 样式 的样式表语言。它控制网页的 外观和布局&#xff0c;包括字体、颜色、间距、背景、动画等视觉效果。 二、CS…

空转 | GetAssayData doesn‘t work for multiple layers in v5 assay.

问题分析 当我分析多个样本的时候&#xff0c;而我的seurat又是v5时&#xff0c;通常就会出现这样的报错。 错误的原因有两个&#xff1a; 一个是参数名有slot变成layer 一个是GetAssayData 不是自动合并多个layers&#xff0c;而是选择保留。 那么如果我们想合并多个样本&…

UE4学习笔记 FPS游戏制作17 让机器人持枪 销毁机器人时也销毁机器人的枪 让机器人射击

添加武器插槽 打开机器人的Idle动画&#xff0c;方便查看武器位置 在动画面板里打开骨骼树&#xff0c;找到右手的武器节点&#xff0c;右键添加一个插槽&#xff0c;重命名为RightWeapon&#xff0c;右键插槽&#xff0c;添加一个预览资产&#xff0c;选择Rifle&#xff0c;根…

【JavaScript】七、函数

文章目录 1、函数的声明与调用2、形参默认值3、函数的返回值4、变量的作用域5、变量的访问原则6、匿名函数6.1 函数表达式6.2 立即执行函数 7、练习8、逻辑中断9、转为布尔型 1、函数的声明与调用 function 函数名(形参列表) {函数体 }eg&#xff1a; // 声明 function sayHi…

硬件基础--05_电压

电压(电势差) 有了电压&#xff0c;电子才能持续且定向移动起来&#xff0c;所有电压是形成电流的必要条件。 电压越大&#xff0c;能“定向移动”起来的电子就越多&#xff0c;电流就会越大。 有电压的同时&#xff0c;形成闭合回路才会有电流&#xff0c;不是有电压就有电流…

ES数据过多,索引拆分

公司企微聊天数据存储在 ES 中&#xff0c;虽然按照企业分储在不同的ES 索引中&#xff0c;但某些常用的企微主体使用量还是很大。4年中一个索引存储数据已经达到46多亿条数据&#xff0c;占用存储3.1tb, ES 配置 由于多一个副本&#xff0c;存储得翻倍&#xff0c;成本考虑…

存储服务器是指什么

今天小编主要来为大家介绍存储服务器主要是指什么&#xff0c;存储服务器与传统的物理服务器和云服务器是不同的&#xff0c;其是为了特定的目标所设计的&#xff0c;在硬件配置方式上也有着一定的区别&#xff0c;存储空间会根据需求的不同而改变。 存储服务器中一般会配备大容…

golang不使用锁的情况下,对slice执行并发写操作,是否会有并发问题呢?

背景 并发问题最简单的解决方案加个锁,但是,加锁就会有资源争用,提高并发能力其中的一个优化方向就是减少锁的使用。 我在之前的这篇文章《开启多个协程,并行对struct中的每个元素操作,是否会引起并发问题?》中讨论过多协程场景下struct的并发问题。 Go语言中的slice在…

Java知识整理round1

一、常见集合篇 1. 为什么数组索引从0开始呢&#xff1f;假如从1开始不行咩 数组&#xff08;Array&#xff09;&#xff1a;一种用连续的内存空间存储相同数据类型数据的线性数据结构 &#xff08;1&#xff09;在根据数组索引获取元素的时候&#xff0c;会用索引和寻址公式…

【C++指针】搭建起程序与内存深度交互的桥梁(下)

&#x1f525;&#x1f525; 个人主页 点击&#x1f525;&#x1f525; 每文一诗 &#x1f4aa;&#x1f3fc; 往者不可谏&#xff0c;来者犹可追——《论语微子篇》 译文&#xff1a;过去的事情已经无法挽回&#xff0c;未来的岁月还可以迎头赶上。 目录 C内存模型 new与…

JavaScript创建对象的多种方式

在JavaScript中&#xff0c;创建对象有多种方式&#xff0c;每种方式都有其优缺点。本文将介绍四种常见的对象创建模式&#xff1a;工厂模式、构造函数模式、原型模式和组合模式&#xff0c;并分析它们的特点以及如何优化。 1. 工厂模式 工厂模式是一种简单的对象创建方式&am…

muduo库的思路梳理

前言 对于muduo库源码的剖析我发现还是有些混乱的&#xff0c;所以这里再次梳理一下muduo网络库争取可以简单明了 首先对于muduo库来说&#xff0c;不能想的得太过于复杂&#xff0c;它无非就是一个线程池加上epoll组成的网络库 这里我们从用的角度出发理解muoduo网络库 #inc…

Keil5 安装全攻略

Keil5 安装全攻略 Keil5 是一款广泛用于嵌入式开发的 IDE&#xff0c;支持多种微控制器架构&#xff08;如 ARM、C51&#xff09;。本文将详细介绍 Keil5 的安装步骤、常见问题及解决方法&#xff0c;帮助您快速上手。 1. 安装前的准备工作 (1) 系统要求 操作系统&#xff1…