博文

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, '?');

PHP8 —— Match Expressions

假设我们要保存发生的事件,根据时间的 class 来获取入库时的名称,PHP8 之前可以用 switch 实现 class Conversation {} $obj = new Conversation(); switch (get_class($obj)) { case 'Conversation': $type = 'started_conversation'; break; case 'Replay': $type = 'replied_to_conversation'; break; case 'Comment': $type = 'commented_on_lesson'; break; } echo $type; 显然这样写起来很繁琐。PHP8之后,可以使用 match expression 来改写,用键值对直接表达对应关系,省略了 case 、break等关键字。 $type = match(get_class($obj)) { 'Conversation' => 'started_conversation', 'Replay' => 'replied_to_conversation', 'Comment' => 'commented_on_lesson' } 当然,除了直接返回值之外,也能返回表达式;对相同返回的分支也可以做合并。比如使用match来实现一个请求路由。 $status = match($request_method) { 'post' => $this->handlePost(), 'get', 'head' => $this->handleGet(), default => throw new \Exception

PHP 8 —— Nullsafe Operator

<?php class User { public function profile() { return getPorfileFromDb(); //数据库查询 } } class Profile { public function employment() { return 'web developer'; } } $user = new User(); echo $user->profile()->employment(); 如果profile() 返回了 null, 那么会报 Call a member function on null 。 PHP8之前,我们需要加个检查。 $profile = $user->profile(); if ($profile) { echo $profile->employment(); } PHP 8 提供了Nullsafe Operator,直接写成下面这种形式 $user->profile()?->employment(); 如果 $user->profile()为空,则上面的表达式最终值为 NULL。 另外这种写法是可以串起来的,像下面这样 $user?->profile()?->employment(); 也可以结合 ?? 写成下面这种形式 echo $user->profile()?->employment() ?? 'Not provided';

Vue3新特性(1) —— Vite

图片
Vite (法语 fast )是 Vue3 的默认构建工具,在开发环境下基于浏览器原生 ES module 开发,从而实现按需编译,在生产环境下基于 Rollup 打包。它具有如下几个特点: 快速的冷启动 即时的热模块更新 真正的按需编译 开始使用 终端执行 $ npm init vite-app vite-example 生成一个 vite 项目,结构如下 ├── index.html ├── package.json ├── public │   └── favicon.ico └── src ├── App.vue ├── assets │   └── logo.png ├── components │   └── HelloWorld.vue ├── index.css └── main.js 进入项目目录,执行 $ cd vite-example $ npm install && npm run dev 可以看到dev server启动速度非常快,大概2s不到 vite 特性 打开chrome的开发工具,看到浏览器直接加载了 vue 文件,后面跟了参数 type 看下源代码的 main.js import { createApp } from 'vue' import App from './App.vue' import './index.css' createApp(App).mount('#app') 对比一下浏览器返回的 main.js import { createApp } from '/@modules/vue.js' import App from '/src/App.vue' import '/src/index.css?import' createApp(App).mount('#app') 对vue引用转化为/@modules/vue.js 对./App.vue转换为/src/App.vue 对./index.css转化为/src/index.css

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

简介 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