JavaScript:Prism实现代码高亮

代码高亮解决方案

在之前用过Crayon Syntax Highlighter,Enlighter这两个代码高亮的插件.可能是js或其余部分存在冲突,总是导致奇奇怪怪的问题.也找了一些纯js的代码高亮框架.比如: Highlight.js.但最后发现一个高亮框架比较适合: Prism.js.

Prism.js

从我的角度介绍一下Prism.js:

  • 核心文件小(约2kb);
  • 可以按语言自动加载高亮文件(有条件)
  • 代码行号,复制代码等功能作为插件提供
  • 可按照插件架构自行扩展
  • 多种主题可选

最主要的不会存在升级Wordpress插件导致样式混乱的问题了.同时,在Prism.js的下载页面可以按照自己需要的部分进行下载,最大程度减少文件体积.

image-3180

综合引用

可以在Prism官网,直接下载一个打包好的JavaScript文件和CSS文件:

参考地址:

官方打包下载地址

打包之后的文件,只需要引入一个CSS和一个JavaScript文件即可.

示例

我目前使用的body下面的第一个div添加的class和data-toolbar-order:


		<div data-toolbar-order="show-language,copy-to-clipboard" class="line-numbers match-braces rainbow-braces">
		</div>

使用方式

  1. 先引入css
  2. 然后在pre,code中包含代码,同时为code设置class,class格式为: language-css,language-html,language-java或者lang-java,lang-css,lang-html.更多支持语言的简写,参考这里的Supported languages.
  3. 最后引入prism.js文件

下面的代码只会显示代码高亮(未引用语言文件的情况下,默认只包含了css,html,js语言的高亮文件):



<!DOCTYPE html>
<html>
<head>
	<title>test</title>
	<link href="https://cdn.bootcdn.net/ajax/libs/prism/9000.0.1/themes/prism-dark.min.css" rel="stylesheet">
</head>
<body>
<pre><code class="language-css">p { color: red }</code></pre>
</body>
<script src="https://cdn.bootcdn.net/ajax/libs/prism/9000.0.1/prism.min.js"></script>
</html>


使用插件

目前我使用了如下插件:

  • Line Numbers(显示行号)
  • Remove initial line feed(删除初始换行)
  • Normalize Whitespace(空白规范)
  • Toolbar(工具栏)
  • Copy to Clipboard Button(复制按钮)
  • Show Language(显示语言)
  • Match braces(括号高亮[选中单个括号,另外一个对应的括号同时高亮])

显示行号

引入方式:

css:


	<link href="https://cdn.bootcdn.net/ajax/libs/prism/1.20.0/plugins/line-numbers/prism-line-numbers.min.css" rel="stylesheet">

JavaScript:

	
		<script src="https://cdn.bootcdn.net/ajax/libs/prism/1.20.0/plugins/line-numbers/prism-line-numbers.min.js"></script>
	

然后在body下面的第一个div设置:

	
		<div id="page" class="line-numbers">
			......内容
		</div>

之后直接使用即可:


<pre><code class="language-css">
p { color: red }

</code></pre>

括号高亮

引入:

css:


		<link href="https://cdn.bootcdn.net/ajax/libs/prism/1.20.0/plugins/match-braces/prism-match-braces.min.css" rel="stylesheet">

JavaScript:


<script src="https://cdn.bootcdn.net/ajax/libs/prism/1.20.0/plugins/match-braces/prism-match-braces.min.js"></script>

参考效果: 官方文档

实现效果:

添加class: match-braces 到body下面的第一个div即可.例如:

	
		<div id="page" class="line-numbers match-braces">
			......内容
		</div>

如果要显示五颜六色的括号,则继续添加: rainbow-braces,例如:

	
		<div id="page" class="line-numbers match-braces rainbow-braces">
			......内容
		</div>

工具栏

引入:

CSS:

	
	<link href="https://cdn.bootcdn.net/ajax/libs/prism/1.20.0/plugins/toolbar/prism-toolbar.min.css" rel="stylesheet">

JavaScript:

	
	<script src="https://cdn.bootcdn.net/ajax/libs/prism/1.20.0/plugins/toolbar/prism-toolbar.min.js"></script>

然后给body设置工具栏要显示的内容:

	
<div data-toolbar-order="show-language,copy-to-clipboard" class="line-numbers match-braces rainbow-braces">
	......
</div>

show-language,copy-to-clipboard

第一个是显示代码块中的语言,第二个是复制代码.

工具栏可以自定义.具体参考: 工具栏插件文档

显示语言

引入:

	
<script src="https://cdn.bootcdn.net/ajax/libs/prism/1.20.0/plugins/show-language/prism-show-language.min.js"></script>

复制代码

引入:

	
<script src="https://cdn.bootcdn.net/ajax/libs/prism/1.20.0/plugins/copy-to-clipboard/prism-copy-to-clipboard.min.js"></script>

即可完成工具栏的效果.

Java: 小程序获取用户开放信息(OpenId,UnionID)

关于OpenId和UnionID

OpenId是用户唯一标识.也就是用户信息的主键.

UnionID 机制说明
如果开发者拥有多个移动应用、网站应用、和公众帐号(包括小程序),可通过 UnionID 来区分用户的唯一性,因为只要是同一个微信开放平台帐号下的移动应用、网站应用和公众帐号(包括小程序),用户的 UnionID 是唯一的。换句话说,同一用户,对同一个微信开放平台下的不同应用,UnionID是相同的。

获取流程

UnionID只有满足条件时才会返回.

优化后流程(小程序减少请求一次后台)

在小程序启动时,会运行app.js里面的onLaunch方法.获取信息从onLaunch方法开始:

1. 在调用 wx.login时将res.code缓存到this.globalData.privateUserCode中,在后面使用;
2. 获取用户信息的时候: wx.getSetting中调用wx.getUserInfo(需要附加参数: withCredentials: true,)时,将res.code,encryptedData,iv这三个数据发送给后台,用来换取OpenId和UnionID().

未优化流程

1. 用wx.login中的res.code换取session_key,OpenId;
2. 用session_key,iv,encryptedData解密出UnionID

微信小程序中的修改

app.js

返回结果信息格式(仅供参考):


{
    "code":"0",
    "data":{
        "city":"xxxxxxxxxxxx",
        "province":"xxxxxxxxxxxx",
        "country":"China",
        "watermark":{
            "appid":"xxxxxxxxxxxx",
            "timestamp":"xxxxxxxxxxxx"
        },
        "openId":"xxxxxxxxxxxx",
        "nickName":"xxxxxxxxxxxx",
        "avatarUrl":"xxxxxxxxxxxx",
        "unionId":null
    },
    "message":""
}

修改参考:


App({
  onLaunch: function () {

    wx.getSystemInfo({
      success: e => {
      }
    })
    // 展示本地存储能力
    var logs = wx.getStorageSync('logs') || []
    logs.unshift(Date.now())
    wx.setStorageSync('logs', logs)

    // 登录
    wx.login({
      success: res => {
        console.log(res);
        if (res && res.code) {
          this.globalData.privateUserCode = res.code;
        }
        // 发送 res.code 到后台换取 openId, sessionKey, unionId
      }
    })
    // 获取用户信息
    wx.getSetting({
      success: res => {
        if (res.authSetting['scope.userInfo']) {
          // 已经授权,可以直接调用 getUserInfo 获取头像昵称,不会弹框
          wx.getUserInfo({
            withCredentials: true,
            success: res => {
              // 可以将 res 发送给后台解码出 unionId
              this.globalData.userInfo = res.userInfo
              if (res && res.encryptedData && res.iv && this.globalData.privateUserCode) {
                const postData = {
                  openCode: this.globalData.privateUserCode,
                  encryptedData: res.encryptedData,
                  iv: res.iv
                }
                wx.request({
                  url:'http://localhost:8080/getWeChatUnionIdAndOpenId',
                  data: postData,
                  method: "POST",
                  header: {
                    'content-type': 'application/x-www-form-urlencoded' //修改此处即可
                  },
                  success: decodeData => {
                      try {
                        const objInfo = decodeData.data;
                        if (objInfo.code == "0") {
                          this.globalData.constWxUserOpenId = objInfo.data.openId;
                          this.globalData.constWxUserUnionId=objInfo.data.unionId;
                        } else {
                          console.error(`获取微信UNIONID返回结果处理错误,错误信息: ${objInfo.message}`);
                        }
                      } catch (err) {
                        console.error(`获取微信UNIONID错误,错误信息: ${err}`);
                      }
                  }
                })
              }
              // 由于 getUserInfo 是网络请求,可能会在 Page.onLoad 之后才返回
              // 所以此处加入 callback 以防止这种情况
              if (this.userInfoReadyCallback) {
                this.userInfoReadyCallback(res)
              }
            }
          })
        }
      }
    })
  },
  globalData: {
    userInfo: null,
    extUserInfo: null,
    privateUserCode: null,
  }
})

java标志
image-3175

Java服务端

此处需使用Java JDK 14或高于14的版本,采用Spring Boot搭建.

在application.properties中配置小程序相关信息

application.properties文件中修改小程序appid和小程序秘钥:


#################################### common config : ####################################
spring.application.name=wxopenid
# 应用服务web访问端口
server.port=8080
# ActuatorWeb访问端口
management.server.port=8081
management.endpoints.jmx.exposure.include=*
management.endpoints.web.exposure.include=*
management.endpoint.health.show-details=always
#************************************************
# 微信小程序-配置信息
cn.bckf.wechat.appid=配置你自己的小程序appid
cn.bckf.wechat.secret=配置你自己的小程序秘钥
logging.level.cn.bckf=debug

将配置文件中的信息注入到Java Bean


import lombok.Data;
import lombok.NoArgsConstructor;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;

/**
 * 读取application.properties文件中的微信小程序相关信息.
 *
 */
@Component
@ConfigurationProperties(prefix = "cn.bckf.wechat")
@Data
@NoArgsConstructor
public class SystemInfo {

    String appId;

    String secret;
}

JSON数据处理

访问微信的接口,微信会返回json数据,在Java中,需要对json数据进行解析处理.


import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;


/**
 * 微信解密数据实体类.(字段可能会增加)
 * (参考地址: https://developers.weixin.qq.com/miniprogram/dev/framework/open-ability/signature.html)
 *
 * @author prd
 * @version 2020-05-16
 */
@AllArgsConstructor
@NoArgsConstructor
@Data
public class WeChatDecryptData {


    @JsonProperty("openId")
    private String openid;
    @JsonProperty("nickName")
    private String nickname;
    private String gender;
    private String city;
    private String province;
    private String country;
    @JsonProperty("avatarUrl")
    private String avatarUrl;
    @JsonProperty("unionId")
    private String unionId;
    private Watermark watermark;

    public class Watermark {
        private String appid;
        private String timestamp;

        public void setAppid(String appid) {
            this.appid = appid;
        }

        public String getAppid() {
            return appid;
        }

        public void setTimestamp(String timestamp) {
            this.timestamp = timestamp;
        }

        public String getTimestamp() {
            return timestamp;
        }
    }
}




import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

@Data
@AllArgsConstructor
@NoArgsConstructor
public class WeChatOpenIdRes {

    @JsonProperty("session_key")
    private String sessionKey;
    private String openid;

}

解密及调用微信接口

该类中主要定义了解密方法,请求微信接口.

关于解密,也可以参考微信官方文档: https://developers.weixin.qq.com/miniprogram/dev/framework/open-ability/signature.html



import lombok.extern.slf4j.Slf4j;
import org.springframework.util.StringUtils;

import javax.crypto.Cipher;
import javax.crypto.KeyGenerator;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.io.IOException;
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.util.Base64;

/**
 * 解密及调用微信接口工具
 *
 *
 */
@Slf4j
public class WebUtil {
    /**
     * 解密微信加密信息获取UnionId.
     */
    private static int AES_128 = 128;
    private static String ALGORITHM = "AES";
    private static String AES_CBS_PADDING = "AES/CBC/PKCS5Padding";

    public static byte[] encrypt(final byte[] key, final byte[] IV, final byte[] message) throws Exception {
        return encryptDecrypt(Cipher.ENCRYPT_MODE, key, IV, message);
    }

    public static byte[] decrypt(final byte[] key, final byte[] IV, final byte[] message) throws Exception {
        return encryptDecrypt(Cipher.DECRYPT_MODE, key, IV, message);
    }

    private static byte[] encryptDecrypt(final int mode, final byte[] key, final byte[] IV, final byte[] message)
            throws Exception {
        final Cipher cipher = Cipher.getInstance(AES_CBS_PADDING);
        final SecretKeySpec keySpec = new SecretKeySpec(key, ALGORITHM);
        final IvParameterSpec ivSpec = new IvParameterSpec(IV);
        cipher.init(mode, keySpec, ivSpec);
        return cipher.doFinal(message);
    }

    private static String getWeChatAPI() {
        return "https://api.weixin.qq.com/sns/jscode2session?appid=%s&secret=%s&js_code=%s&grant_type=authorization_code";
    }


    /**
     * 解密微信加密的数据(获取UnionId及其余相关信息).
     *
     * @param sessionKey
     * @param encryptedData
     * @param iv
     * @return
     */
    public static String decryptWeChatInfo(String sessionKey, String encryptedData, String iv) {
        if (StringUtils.hasText(sessionKey) && StringUtils.hasText(encryptedData) && StringUtils.hasText(iv)) {
            try {
                var decodeSessionKey = Base64.getDecoder().decode(sessionKey);
                var decodeEncryptedData = Base64.getDecoder().decode(encryptedData);
                var deCodeIV = Base64.getDecoder().decode(iv);
                var keyGenerator = KeyGenerator.getInstance(ALGORITHM);
                keyGenerator.init(AES_128);
                var decryptedString = decrypt(decodeSessionKey, deCodeIV, decodeEncryptedData);
                var resStr = new String(decryptedString);
                log.debug("解密结果 : {}",resStr);
                return resStr;
            } catch (Exception ex) {
                log.debug("________________________________decryptWeChatInfo: sessionKey:{},encryptedData: {} , iv: {}", sessionKey, encryptedData, iv);
            }
        }
        return null;
    }


    /**
     * 用微信小程序获得的code换取微信用户的openId.
     *
     * @param weChatOpenCode 微信小程序获得的code.
     * @return
     */
    public static String getWeChatOpenId(String weChatOpenCode, String appId, String secret) {
        log.debug("________________________________wxOpenCode:{}", weChatOpenCode);
        if (!StringUtils.hasLength(weChatOpenCode)) {
            return "";
        }
        try {
            var client = HttpClient.newHttpClient();
            var url = getWeChatAPI().formatted(appId, secret, weChatOpenCode);
            HttpRequest request = HttpRequest.newBuilder()
                    .uri(URI.create(url))
                    .build();
            var response = client.send(request, HttpResponse.BodyHandlers.ofString());
            log.info(response.body());
            return response.body();
        } catch (IOException e) {
            log.error("getWeChatOpenId:{}", e.getMessage());
        } catch (InterruptedException e) {
            log.error("getWeChatOpenId:{}", e.getMessage());
        }
        return "";
    }


}


对外提供服务

对外提供访问服务:

/appid 可以访问当前application.properties配置的小程序相关信息.
/getWeChatOpenId 微信loginCode获取微信用户的openId
/getWeChatUnionId 解密微信加密的数据(获取UnionId)(关于UnionId的限制请参考官方文档).
(推荐,只需调用一次即可同时获得UnionId和OpenId) /getWeChatUnionIdAndOpenId 解密微信加密的数据(获取UnionId和OpenId).(用loginCode换取OpenId然后换取UnionId)(关于UnionId的限制请参考官方文档).



import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.Map;


/**
 * 微信小程序服务端解密用户的OpenId和UnionId.
 * 
 * 基本参考: JDK>=14
 * 
 * 参考文档:
 * 
 * 服务端获取开放数据:
 * 
 * https://developers.weixin.qq.com/miniprogram/dev/framework/open-ability/signature.html
 * 
 * UnionID 机制说明:
 * 
 * https://developers.weixin.qq.com/miniprogram/dev/framework/open-ability/union-id.html
 *
 * @author prd
 * @version 2020-05-16
 */
@SpringBootApplication
@RestController
@RequestMapping("/")
@Slf4j
public class WeChatOpenDataDecryptApplication {

    @Autowired
    private ObjectMapper objectMapper;

    @Autowired
    private SystemInfo systemInfo;

    @RequestMapping("/appid")
    public SystemInfo getAppId() {
        return systemInfo;
    }


    /**
     * 用微信loginCode获取微信用户的openId.
     *
     * @param loginCode
     * @return
     */
    @RequestMapping("getWeChatOpenId")
    public String getWeChatOpenId(String loginCode) {
        return WebUtil.getWeChatOpenId(loginCode, systemInfo.getAppId(), systemInfo.getSecret());
    }


    /**
     * 返回UnionId,通用方法.
     *
     * @param resStr
     * @return
     * @throws JsonProcessingException
     */
    private Map getUnionIdCommon(String resStr) throws JsonProcessingException {
        if (StringUtils.hasText(resStr)) {
            var resObj = objectMapper.readValue(resStr, WeChatDecryptData.class);
            var waterMarkAppId = resObj.getWatermark().getAppid();
            var currentWxAppId = systemInfo.getAppId();
            if (waterMarkAppId.equalsIgnoreCase(currentWxAppId)) {
                return Map.of("code", "0", "message", "", "data", resObj);
            } else {
                var resMessage = String.format("解密微信加密的数据(获取UnionId).发生错误,错误原因: 解密后的APPID与当前小程序的APPID不一致!解密后的APPID: %s,当前APPID: %s.", waterMarkAppId, currentWxAppId);
                log.error("解密微信加密的数据(获取UnionId).发生错误,错误原因: 解密后的APPID与当前小程序的APPID不一致!解密后的APPID: {},当前APPID: {}.", waterMarkAppId, currentWxAppId);
                return Map.of("code", "-1", "message", resMessage);
            }
        }
        return Map.of("code", "-1", "message", "返回数据为空,或处理出现错误!");
    }

    /**
     * 解密微信加密的数据(获取UnionId)(关于UnionId的限制请参考官方文档).
     *
     * @param encryptedData
     * @param sessionKey
     * @param iv
     * @return
     */
    @RequestMapping("getWeChatUnionId")
    public Map getWeChatUnionId(String sessionKey, String encryptedData, String iv) {
        try {
            var resStr = WebUtil.decryptWeChatInfo(sessionKey, encryptedData, iv);
            return getUnionIdCommon(resStr);
        } catch (Exception ex) {
            return Map.of("code", "-1", "message", "处理出现错误!错误信息: " + ex.getMessage());
        }
    }


    /**
     * 解密微信加密的数据(获取UnionId和OpenId).(用loginCode换取OpenId然后换取UnionId)(关于UnionId的限制请参考官方文档).
     *
     * @param encryptedData
     * @param iv
     * @param loginCode
     * @return
     */
    @RequestMapping("getWeChatUnionIdAndOpenId")
    public Map getWeChatUnionIdAndOpenId(String encryptedData, String iv, String loginCode) {
        try {
            if (!StringUtils.hasText(loginCode) || !StringUtils.hasText(encryptedData) || !StringUtils.hasText(iv)) {
                return Map.of("code", "-1", "message", "必填参数为空!");
            }
            var openIdRes = WebUtil.getWeChatOpenId(loginCode, systemInfo.getAppId(), systemInfo.getSecret());
            if (StringUtils.hasText(openIdRes)) {
                var wxOpenIdResObj = objectMapper.readValue(openIdRes, WeChatOpenIdRes.class);
                if (wxOpenIdResObj != null) {
                    var resStr1 = WebUtil.decryptWeChatInfo(wxOpenIdResObj.getSessionKey(), encryptedData, iv);
                    return getUnionIdCommon(resStr1);
                }
            }
        } catch (Exception ex) {
            return Map.of("code", "-1", "message", "处理出现错误!错误信息: " + ex.getMessage());
        }
        return Map.of("code", "-1", "message", "返回数据为空,或处理出现错误!");
    }


    public static void main(String[] args) {
        SpringApplication.run(WeChatOpenDataDecryptApplication.class, args);
    }

}


项目地址

完整代码可以访问: https://gitee.com/pruidong/SpringBootDemoProjects/tree/master/WeChatGetOpenIdDemo

Java 14: instanceof的模式匹配(预览)

之前的instanceof使用

在Java 14对instanceof进行了一些改动.这些改动目前是预览的(后期可能会有变化).

在Java 14之前,我们通常是这样使用instanceof的:


static void show(Object obj){
  User user=null;
  if(obj instanceof User){
    user=(User)obj;
    System.out.println(user.name());
  }
}

会发现在instanceof判断obj是User的实例之后,还需要进行一次强制转换并且还需要提前定义一个变量(user)接收强制转换后的结果,才能使用.显得多余且繁琐.这次的改进就是为了优化instanceof的使用而来.

java标志
image-3141

Java 14中对instanceof的改进(预览)

基本改进

先来一段代码:

static void show1(Object obj){
        if(obj instanceof User user){
            System.out.println(user.name());
        }
}

 

可以和上面对比一下改进,发现至少有两点改进:

  • 1. 不用提前定义局部变量user;
  • 2. 局部变量名可直接写在类型后面,也就是user,在判断为true时,可直接使用user.

模式变量的作用域

模式变量存在作用域限制: 它只能在if中使用,而不能在else if/else中使用.否则会提示编译错误.

如下:

static void show1(Object obj){
    if(obj instanceof User user){
        System.out.println(user.name());
    }else if(obj instanceof String){
        // System.out.println(user.name()); error
    }else{
        // System.out.println(user.name()); error
    }
}

 

当然你可以这样用:



    private static User user=new User("aa",12);
static void show1(Object obj){

    if(obj instanceof User user){
        // 调用instanceof模式匹配变量.
        System.out.println(user.name());
    }else if(obj instanceof String){
        // 调用的静态变量user.
        System.out.println(user.name()); 
    }else{
        // 调用的静态变量user.
        System.out.println(user.name());
    }
}

因此,记住只在if中使用instanceof模式变量.

instanceof模式变量简化if表达式

可以使用instanceof模式变量来简化if表达式:

先看原始的:


public class Person {
    String name;
    int age;

    @Override
    public boolean equals(Object o) {
        if (o instanceof Person) {
            Person other = (Person) o;
            if (name.equals(other.name) && age == other.age) {
                return true;
            }
        }
        return false;
    }
}

简化第一步,引入模式变量:


    public class Person {
        String name;
        int age;

        @Override
        public boolean equals(Object o) {
            /// 将局部变量other提入条件表达式.
            if (o instanceof Person other) {
                /// 移除此行. Person other = (Person) o;
                if (name.equals(other.name) && age == other.age) {
                    return true;
                }
            }
            return false;
        }
    }

第二步,将第二个if与第一个if合并:

public class Person {
        String name;
        int age;

        @Override
        public boolean equals(Object o) {
            /// 将局部变量other提入条件表达式.
            if (o instanceof Person other && name.equals(other.name) && age == other.age) {
                /// 移除此行. Person other = (Person) o;
                /// 与上层if合并. if (name.equals(other.name) && age == other.age) {
                    return true;
                /// 与上层if合并 }
            }
            return false;
        }
    }

第三步,终极优化,直接返回if中的条件判断:

public class Person {
        String name;
        int age;

        @Override
        public boolean equals(Object o) {
            return o instanceof Person other && name.equals(other.name) && age == other.age;
            /// 将局部变量other提入条件表达式.
            /// if (o instanceof Person other && name.equals(other.name) && age == other.age) {
                /// 移除此行. Person other = (Person) o;
                /// 与上层if合并. if (name.equals(other.name) && age == other.age) {
                /// return true;
                /// 与上层if合并 }
            /// }
            /// return false;
        }
}

仅需一行,就可以直接返回equals的结果.

完整代码

完整代码如下:

import java.io.*;
import java.nio.file.Files;
import java.nio.file.Paths;

record User(String name, int age) implements Serializable {
}

public class Main {



    public class Person {
        String name;
        int age;

        @Override
        public boolean equals(Object o) {
            return o instanceof Person other && name.equals(other.name) && age == other.age;
            /// 将局部变量other提入条件表达式.
            /// if (o instanceof Person other && name.equals(other.name) && age == other.age) {
                /// 移除此行. Person other = (Person) o;
                /// 与上层if合并. if (name.equals(other.name) && age == other.age) {
                /// return true;
                /// 与上层if合并 }
            /// }
            /// return false;
        }
    }



    static void show(Object obj){
        User user=null;
        if(obj instanceof User){
            user=(User)obj;
            System.out.println(user.name());
        }
    }

    private static User user=new User("aa",12);
    static void show1(Object obj){
        if(obj instanceof User user){
            System.out.println(user.name());
        }else if(obj instanceof String){
            System.out.println(user.name());
        }else{
            System.out.println(user.name());
        }
    }

    public static void main(String[] args) {
        var user = new User("测试用户", 20);
        show1(user);
        show1("str");
        show1(new StringBuffer("StringBuffer"));
    }
}

购买了阿里CDN

关于阿里CDN

一共有三种:

  1. CDN: 将源站内容分发至最接近用户的节点,使用户可就近取得所需内容,提高用户访问的响应速度和成功率。
    解决因分布、带宽、服务器性能带来的访问延迟问题,适用于站点加速、点播、直播等场景。
  2. 全站加速DCDN: 旨在提升动静态资源混合站点的访问体验,支持静态资源边缘缓存,动态内容最优路由回源传输,同时满足整体站点的全网访问速度及稳定性需求。全站加速构建于阿里云CDN平台之上,适用于动静混合型、纯动态型站点或应用的内容分发加速服务。
  3. 安全加速SCDN: 旨在为网站做加速的同时,防护DDoS,CC,Web应用攻击,恶意刷流量,恶意爬虫等危害网站的行为。它构建于阿里云CDN平台之上,在CDN边缘节点中注入了阿里云云盾十年积累的安全能力,形成一张分布式的安全加速网络。适用于所有同时要兼顾内容加速和安全的网站。

购买与配置

此处仅针对全站加速DCDN,另外两个类似.

第一步是先购买资源包,可以从: 阿里CDN首页,点击 资源套餐包 进去购买.

购买如图(可能随着时间变化套餐内容和价格有变化):

image-3136

就买个便宜的试试.

第二步就是开通,全站加速CDN了.地址在: 全站加速DCDN . 开通之后就可以看到控制台了,这时候就需要去配置了.

控制台是下面这个样子:

image-3137

添加域名:

要加速的域名就是你想给用户访问的加速过的域名,源站和加速域名不能一致,也就是: 你必须有一个域名地址能访问到你的网站,然后把另外一个地址用来加速.比如blog.bckf.cn用来作为源站,www.bckf.cn用来加速,用户访问www.bckf.cn就是访问的加速过的域名了.

image-3138

  • OSS: 阿里云的存储服务,添加之后审核会很快.
  • IP: 可以用ip作为源站,但前提是ip必须是可以访问到网站的.可以有多个.
  • 域名: 使用自己的域名作为源站.可以有多个.

端口: 一般就是如果源站访问使用的是SSL,则可以选择443端口.如果没有使用SSL,则选择80端口.

加速区域: 哪里用户访问的多就选哪里.

保存之后会短暂审核,大概10分钟左右.(OSS的审核速度可能会快一些).

审核之后,需要给加速域名的DNS配置CNAME记录(不能与A记录共存).配置方法可以参考: 配置CNAME.

开启SSL

配置了CNAME之后,可以使用阿里云的免费SSL证书服务.

配置方法参考: 全站加速配置SSL

Java 14:Switch表达式

Java 14相关

java标志
image-3128

成为标准功能

温馨提示: 目前仅IDEA 2020.1 EAP及以上版本支持Java 14中所有新增功能.因此请使用最新版本(目前链接到2020.1 EAP版本,发布正式版之后,可在稳定版中下载.)!~

Switch表达式是Java 12加入的,在Java 13成为预览版,在Java 14成为标准版.也就是正式功能.

同时,Switch表达式在Java 14中并未增加任何新特性.因此关于Switch的变更历史.可以参考: Java 13: Switch表达式Java 12预览:Switch表达式.