博文

目前显示的是 2020的博文

PHP8 —— New String Helpers

新增了三个字符串函数,str_starts_with, str_ends_with, str_contains, PHP 的函数这么方便,很难想象竟然一直没有这几个。 str_starts_with 判断字符串是否以另一个字符串开头,在PHP7以及之前 $id = 'inv_abcdefgh'; $result = strpos($id, 'inv_') === 0; var_dump($result); // true PHP8 中可以直接这么写 $result = str_starts_with($id, 'inv_'); str_ends_with 判断字符串是否以另外一个字符串结尾,在 PHP7 及之前,比较麻烦,通常是这么写 $id = 'abcd_inv'; $result = strpos(strrev($id), strrev('_inv')) === 0; 或者 $result = substr($id, -1 * strlen('_inv')) === '_inv'; 或者上正则吧 $result = preg_match('/_inv$/', $id) === 1; 看起来都是比较麻烦的。PHP8 里面可以简化成下面这样了 $id = 'abcd_inv'; $result = str_ends_with($id, '_ind'); str_contains 字符串包含,PHP8 之前一般就是 strpos 来实现了 $url = 'https://example?for=bar'; $result = strpos($url, '?') !== FALSE; PHP8 就直接一点 $result = str_contains($url, '?');

如何获取两个日期之间的所有日期?

简介 Java 8 加入的新 Time Api可以方便的处理日期,我们不再需要引入额外的库。 本文对比一下不同版本的 Java 如何获取两个日期之间所有的日期。 Java 7 在 Java 7中,我们一般使用一个循环,使用add 方法 对 Calendar实例使用 add(Calendar.Date,1) ,直到满足条件为之。代码示例如下: public static List<Date> getDatesBetweenUsingJava7( Date startDate, Date endDate) { List<Date> datesInRange = new ArrayList<>(); Calendar calendar = new GregorianCalendar(); calendar.setTime(startDate); Calendar endCalendar = new GregorianCalendar(); endCalendar.setTime(endDate); while (calendar.before(endCalendar)) { Date result = calendar.getTime(); datesInRange.add(result); calendar.add(Calendar.DATE, 1); } return datesInRange; } Java 8 在 Java 8 中,我们可以创建一个无限的 Stream,然后取需要的部分。但是需要在满足条件时结束这个无限Stream, Java 8 语言层面没有提供直接的方法(Java 9 开始有了 takeWhile 和 dropWhile),所以我们需要提前计算日期的个数,然后使用limit()来终止Stream。 public static List<LocalDate> getDatesBetweenUsingJava8( LocalDate startDate, LocalDate endDate) { long numOfD

Java学习笔记 —— 在 getter 和setter 中使用 Optional

Optional 的主要目的,是为了使用可为null的值,却不抛NullPointerException异常。 Optional类被有意设计为不可serializable,所以我们不应该用它来包装类的字段。 在getter和setter中使用 optional 的最佳方案是:把getter的返回值包装到 optional 中,setter 不要使用optional。 举个🌰: public class Department { private Manager boss; public Optional<Manage> getBoss() { return Optional.ofNullable(boss); } public void setBoss(Manager boss) { this.boss = boss; } } 在Department类里面,boss 字段可以为空。如果我们把boss字段设为 Optional 类型,那么 boss 就不能序列化了,这样 Department 也就不能序列化了。 因为字段可以为null,所以setter方法接受的参数没有必要用 Optinonal类型了,直接用 Manager就行;为了让用户明确知道boss字段可以为null,getter的返回值可以用Optional进行包装。 这种方法也有缺点是,JavaBeans是根据属性成对的定义getter和setter的。而这种方案违反了标准的模式,getter 和setter 不再对称了。所以有一些开发着认为 Optinonal 没有必要 通过getter 和 setter 暴露给用户,应该把它当作内部实现细节。

Java学习笔记 —— Java 14 新特性 Switch 表达式

Switch 表达式 Java 12和13中已经提供了 switch 表达式,不过是作为预览功能提供,默认情况下未启用。 在Java 14中正式发布了。简单来说,这是一种新的简化形式的switch块,带有 case L-> ... 标签。大部分情况下,这么写有助于简化代码。 下面举几个🌰。 假设我们有一个描述工作日的枚举。 我们可以使用新的switch表达式编写以下代码: switch (day) { case MONDAY -> System.out.println("Aweful"); case TUESDAY, WEDNESDAY -> System.out.println("Okay"); case THURSDAY -> System.out.println("Good"); case FRIDAY -> System.out.println("Great"); case SATURDAY, SUNDAY -> System.out.println("Awesome"); } 在这里,我们只为每种情况使用一个表达式。 需要注意的是,我们没有使用 break 语句。 我们也可以用新的switch表达式来返回值: int numLetters = switch (day) { case MONDAY, FRIDAY, SUNDAY -> 6; case TUESDAY -> 7; case THURSDAY, SATURDAY -> 8; case WEDNESDAY -> 9; }; 还可以编写多行代码块并使用新关键字yield来返回值: int result = switch (s) { case "Foo" -> 1; case "Bar" -> 2; default -&

Java学习笔记 —— 使用现有实例创建 Dates 和 Times 对象

新的Date-Time API的特点之一是所有实例都是不可变的。 创建LocalDate,LocalTime,LocalDateTime或ZonedDateTime后,将无法再对其进行更改。 这具有使它们线程安全的巨大优势,但是如果你想基于现有实例创建一个新实例,该怎么办? LocalDate类具有几种从日期中添加和减去值的方法。 具体来说,有: LocalDate plusDays(long daysToAdd) LocalDate plusWeeks(long weeksToAdd) LocalDate plusMonths(long monthsToAdd) • LocalDate plusYears(long yearsToAdd) 每个方法都基于当前 date 增加或减少天数后返回一个新的LocalDate,LocalTime类具有类似的方法: LocalTime plusNanos(long nanosToAdd) LocalTime plusSeconds(long secondsToAdd) LocalTime plusMinutes(long minutesToAdd) LocalTime plusHours(long hoursToAdd) 同样,原始实例增加了一定时间后返回一个新实例。 LocalDateTime具有LocalDate和LocalTime的所有方法。 程序示例如下: @Test public void localDatePlus() throws Exception { DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd"); LocalDate start = LocalDate.of(2017, Month.FEBRUARY, 2); LocalDate end = start.plusDays(3); assertEquals("2017-02-05", end = start.plusWeeks(5); assertEquals("2017-0

Java学习笔记 —— 给Map排序

Map接口包含一个称为Map.Entry的公共、静态、内部接口,该接口表示键值对。 Map.entrySet方法返回一组Map.Entry元素。 在Java 8之前,给map排序时主要使用接口中的getKey和getValue方法。 Java8 中,增加了下表中的静态方法。 方法 描述 comparingByKey(Comparator<? super K> cmp) Returns a comparator that comparesMap.Entryby key using the givenComparator comparingByValue() Returns a comparator that comparesMap.Entryin natural order on value comparingByValue(Comparator<?superV>cmp) ReturnsacomparatorthatcomparesMap.Entryby value using the givenComparator 下面用计算字典中单词长度和数量映射,来说明怎么使用这些方法。 Unix系统在 /usr/share/dict/words 目录中包含一个文件,保存了Webster第二版词典的内容。 我们用 Files.lines方法读取文件行并生成包含这些行的字符串流。 System.out.println("\nNumber of words of each length:"); try (Stream<String> lines = Files.lines(dictionary)) { lines.filter(s -> s.length() > 20) .collect(Collectors.groupingBy( String::length, Collectors.counting())) .forEach((len, num) -> System.out.printf("%d: %d%n", len, num)); } catch (IOException e) {

Java学习笔记 —— Java 8 Map 新特性

Java 8引入了Map接口支持的几种默认方法。 我们从forEach开始介绍。 forEach 旧版Java遍历Map的键和值一直很尴尬,需要在Map上使用Map.Entry 的迭代器: for(Map.Entry<String, Integer> entry: ageOfFriends.entrySet()) { String friend = entry.getKey(); Integer age = entry.getValue(); System.out.println(friend + " is " + age + " years old"); } 从Java 8开始,Map接口支持forEach方法,该方法接受BiConsumer,并将键和值作为参数。 使用forEach可以让代码更为简洁: ageOfFriends.forEach((friend, age) -> System.out.println(friend + " is " + age + " years old")); Java 8引入了两种便捷的方法来比较Map中的条目,可以按值或键对Map进行排序: Entry.comparingByValue Entry.comparingByKey 以下代码: Map<String, String> favouriteMovies = Map.ofEntries(entry("Raphael", "Star Wars"), entry("Cristina", "Matrix"), entry("Olivia","James Bond")); favouriteMovies .entrySet() .stream() .sorted(Entry.comparingByKey()) .forEachOrdered(System.out::println); 的输出为: Cristina=Matrix Oliv

Java学习笔记 —— Java 8 中 List 和 Set 新方法

Java 8 在List和Set接口中引入了几种方法: removeIf 删除满足条件的元素。 所有实现List或Set的类均可用。 replaceAll在List上可用,并使用(UnaryOperator)替换元素功能。 sort在List接口上可用,对列表本身进行排序。 与Steam操作不同,它们会更改集合本身,而Steam操作会产生新的复制结果。 removeIf 举个🌰,下面的代码删除数字开头的事物: for (Transaction transaction : transactions) { if(Character.isDigit(transaction.getReferenceCode().charAt(0))) { transactions.remove(transaction); } } 上面的写法可能会导致ConcurrentModificationException。如果我们使用下面代码,通过for-each循环使用Iterator对象: for (Iterator<Transaction> iterator = transactions.iterator(); iterator.hasNext(); ) { Transaction transaction = iterator.next(); if(Character.isDigit(transaction.getReferenceCode().charAt(0))) { transactions.remove(transaction); } } 注意下面两种独立的管理集合操作: Iterator 对象, 使用next() 和 hasNext() 访问元素 Collection 对象本身, 使用 remove() 删除元素 Iterrator的状态不再与集合的状态同步,反之亦然。 要解决此问题,我们必须显式使用Iterator对象并调用其remove()方法: for (Iterator<Transaction> iterator = transactions.iterator(); iterator.hasNext(); ) {

Java学习笔记 —— Java 9 的 Collection 工厂方法

Java 9引入了一些方便的方法来创建小型集合对象。 先来看下传统写法: List<String> friends = new ArrayList<>(); friends.add("Raphael"); friends.add("Olivia"); friends.add("Thibaut"); 存储三个字符串需要写上面一堆代码。我们可以简化一下,使用Arrays.asList() 工厂方法: List<String> friends = Arrays.asList("Raphael", "Olivia", "Thibaut"); 这样我们获得一个固定大小的列表,不能向其中添加元素或从中删除元素,否则会导致UnsupportedModificationException。 不过我们可以set方法对元素进行更新: List<String> friends = Arrays.asList("Raphael", "Olivia"); friends.set(0, "Richard"); friends.add("Thibaut"); 这些行为有点奇怪,因为这里的list底层是由固定大小的可变数组支持的。 那要创建一个 Set 怎么样?没有Arrays.asSet()工厂方法,我们需要使用一些其他的技巧。 使用HashSet构造函数,接受List作为参数: Set<String> friends = new HashSet<>(Arrays.asList("Raphael", "Olivia", "Thibaut")); 或者可以使用 Stream API Set<String> friends = Stream.of("Raphael", "Olivia", "Thibaut") .collect(Collec

Java学习笔记 —— 使用 Date-Time 基本类

Date-Time中的类都产生不可变的实例,因此它们是线程安全的。 它们也没有公共构造函数,因此每个都使用工厂方法实例化。 需要特别注意两种方法:now和of。 now now方法用于基于当前日期或时间创建实例。 System.out.println("LocalDate.now(): " + LocalDate.now()); System.out.println("LocalTime.now(): " + LocalTime.now()); System.out.println("LocalDateTime.now(): " + LocalDateTime.now()); System.out.println("ZonedDateTime.now():" + ZonedDateTime.now()); System.out.println("Instant.now(): " + Instant.now()); 结果如下: LocalDate.now(): 2020-03-10 LocalTime.now(): 14:23:04.823013 LocalDateTime.now(): 2020-03-10T14:23:04.824918 ZonedDateTime.now():2020-03-10T14:23:04.826819+08:00[Asia/Shanghai] Instant.now(): 2020-03-10T06:23:04.833487Z 所有输出值均使用ISO 8601标准格式。 对于日期,基本格式为yyyy-MM-dd。 对于时间,格式为hh:mm:ss.sss。 LocalDateTime 则使用大写字母T连接日期和时间。 带时区的日期时间,带上了以UTC为基础的数字偏移量(+08:00),并附加地区名称(Asia/Shanghai)。 Instant的toString方法输出为UTC时间(Zulu),精确到纳秒。 Year,YearMonth和ZoneId类中也有now方法。 of of 方法用于产生新值。 对于LocalDate,参数为项是年,月(枚举或整数)和月中的某天。 对于Lo

Java 学习笔记 —— 从 Optional 中获取值

可以使用get方法获取Optinal中的值,但是,如果Optional为空,get方法将引发NoSuchElementException。 举个🌰: Optional<String> firstEven = Stream.of("five", "even", "length", "string", "values") .filter(s -> s.length() % 2 == 0) .findFirst(); findFirst方法返回Optional ,Stream中可能所有的值都不符合条件。如果我们用Optional的get来打印返回的值: System.out.println(firstEven.get()) // Don't do this, even if it works 除非能够100%确定Optional一定不为空才可以这么使用,否则程序会抛异常,如以下示例所示。 Optional<String> firstOdd = Stream.of("five", "even", "length", "string", "values") .filter(s -> s.length() % 2 != 0) .findFirst(); System.out.println(firstOdd.get()); // throws NoSuchElementException 如何解决这个问题?有几种选择。第一种方案,先判断Optional是否包含值,然后再获取。 Optional<String> firstEven = Stream.of("five", "even", "length", "string", "values") .filter(s -> s.length() % 2 == 0) .fi

Java 学习笔记 —— 在Java 8 中创建 Optional

像Java 8 API中的许多其他新类一样,Optional的实例是不可变的。 Optional是基于值的类,其实例有如下特性: 是final且不可变的(可能包含对可变对象的引用) 没有公共构造函数,因此必须通过工厂方法实例化 具有equals,hashCode和toString的实现,这些实现仅基于其状态 创建 Optional 的静态工厂方法为 empty , of 和 ofNullable ,其签名为: static <T> Optional<T> empty() static <T> Optional<T> of(T value) static <T> Optional<T> ofNullable(T value) empty 会返回一个空的Optional。 of 方法返回一个包装指定值的Optional,如果参数为null则引发异常。 public static <T> Optional<T> createOptionalTheHardWay(T value) { return value == null ? Optional.empty() : Optional.of(value); } 上面是一种复杂写法,简单点可以使用ofNullable 方法,如下: public static <T> Optional<T> createOptionalTheEasyWay(T value) { return Optional.ofNullable(value); } 实际上,Java 8 中ofNullable实现逻辑就是: 检查所包含的值是否为null,是则返回空的Optional,不是则使用Optional.of对其进行包装。 顺便说一下,类OptionalInt,OptionalLong和OptionalDouble包装了不会为null的primitives,所以他们只有一个 of 方法。 static OptionalInt of(int value) static OptionalLong of(longvalue) static OptionalDouble

Java 学习笔记 —— 获取文件Stream

java.nio.file.Files类中的静态列表方法将Path用作参数,并返回包装DirectoryStream的Stream。 DirectoryStream接口扩展了AutoCloseable,因此使用list方法最好通过try-with-resources构造完成。 try (Stream<Path> list = Files.list(Paths.get("src/main/java"))) { list.forEach(System.out::println); } catch (IOException e) { e.printStackTrace(); } 假设这是在具有标准Maven或Gradle结构的项目的根目录中执行的,这将打印 src/main/java 目录中所有文件和文件夹的名称。 因为使用了try-with-resources,在try块完成时,系统将在stream上调用close,然后在底层DirectoryStream上调用close。 src/main/java/collectors src/main/java/concurrency src/main/java/datetime ... src/main/java/Summarizing.java src/main/java/tasks src/main/java/UseFilenameFilter.java list方法的签名显示返回类型为Stream ,其参数为目录: public static Stream<Path> list(Path dir) throws IOException 在非目录资源上执行该方法将导致NotDirectoryExcep tion。Javadocs指出,结果流是弱一致性的,这意味着“它是线程安全的,但在迭代时不会冻结目录,因此它可能(也可能不会)反映从此方法返回后发生的目录更新。”

CSSOM 对象模型

CSSOM is the object model of CSS. In the W3C standard, it contains two parts: the model part (CSSOM) describing CSS such as style sheets and rules, and the view part (CSSOM View) related to element views. In practice, CSSOM View is more commonly used than CSSOM, because we rarely need to use code to dynamically manage style sheets. In today's article, I will introduce you to the two parts of the API separately. CSSOM First, let's introduce the model of style sheet in CSS, which is the ontology of CSSOM. We usually create style sheets using HTML tags. We use style tags and link tags to create style sheets. For example: <style title="Hello"> a { color:red; } </style> <link rel="stylesheet" title="x" href="data:text/css,p%7Bcolor:blue%7D"> After we have created the style sheet, we may need to do some operations on it. If we understand it from the perspective of the DOM, these tags are a node in the DOM. They ha

Difference between buffer and cache

free Buffer and Cache are the indicators we get with free. buffers Memory used by kernel buffers (Buffers in/proc/meminfo) cache Memory used by the page cache and slabs (Cached and SReclaimable in/proc/meminfo) buff/cache Sum of buffers and cache From the free manual, you can see the description of buffer and cache.   Memory used by kernel buffers, corresponding to the Buffers value in/proc/meminfo. Cache is the kernel page cache and the memory used by Slab, which corresponds to the sum of Cached and SReclaimable in/proc/meminfo. The description here tells us that these values are from/proc/meminfo, but the meaning of more specific Buffers, Cached, and SReclaimable is still not clear. To figure out what they are, I guess your first reaction is to go to Baidu or Google. Although most of the time, a web search will give an answer. However, not to mention the time and effort spent on filtering information, it is difficult for you to guarantee the accuracy of this answ

How to solve atomic problems?

图片
The source of the atomicity problem is thread switching, and the operating system's thread switching relies on CPU interrupts, so prohibiting CPU interrupts can disable thread switching. In the early single-core CPU era, this solution is indeed feasible, but it is not suitable for multi-core scenarios. Take a 32-bit CPU to write a long variable as an example. A long variable is 64-bit. A write operation performed on a 32-bit CPU will be split into two write operations. In a single-core CPU scenario, only one thread executes at a time, disabling CPU interrupts, meaning that the operating system does not reschedule threads, that is, thread switching is disabled, and threads that obtain CPU usage rights can execute without interruption. The second write operation must be: either all are executed, or none are executed, and are atomic. However, in a multi-core scenario, at the same time, there may be two threads executing at the same time, one thread executing on CPU-1 and one thread

JavaScript 数据类型

类型 JavaScript 语言的每个值都属于一种数据类型。JavaScript 语言规定了7中类型: Undefined Null Boolean String Number Symbol (ES6开始加入) Object Undefined, Null Undefined 表示未定义,有一个值 undefined。任何变量在赋值是 Undefined 类型,值为 undefined。一般我们可以用全局变量 undefined 表示这个值,或者 void 运算符将任意表达式变成 undefined。undefined 是一个变量而非关键字,为了避免被无意中篡改,建议使用 void 0 来获取 undefined 值。 Null 表示“定义了但为空”,与 Undefined 有一定的表义差别。Null类型只有一个值 null,它是语言关键字,在任何代码中都可以使用 null 来获得 null 值。 实际代码中,不要将变量赋值为 undefined,这样可保证所有值为 undefined 的变量,都是从来未赋值状态。 Boolean Boolean 类型有两个值,true 和 false,表示真和假,均为语言关键字。 String String 表示文本数据,最大长度为 2 53 - 1。 String 的意义并非“字符串”,而是字符串的 UTF-16 编码,字符串的最大长度,受编码长度影响。 JavaScript 字符串是不可变量,构造之后没有任何方法变更内容,具有值类型的特征。 JavaScript 将每个 UTF-16 单元作为一个字符,处理非 BMP(超出 U+000 ~ U+FFF范围)字符是,需要格外小心。 Number Number 类型有 18437736874454810627(即 2 64 - 2 53 +3)个值,基本符合 IEEE 754-2008 规定的双精度浮点数规则,除了一下例外: NaN,占用 9007199254740990,这是原来符合 IEEE 规则的数字 Infinity,无穷大 -Infinity,负无穷大 JavaScript中,+0 和 -0 在加法中无差别,在除法中有差别。1/+0 、1/-0 分别得到 Infinity 和 -Infinity。