Java14: Record(预览)

Java 14相关

简述

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

Java 14增加了一个Record的预览功能.预览:在后续JDK版本中可能有变化,也可能成为永久功能.

Record和enum枚举类似,只是Record是半不可变类.在很多地方也可以将Record当做class一样使用.

特点简述:

  1. record没有set,只能通过构造函数初始化;
  2. record编译后会生成一个最终类(final class修饰),并且无法继承任何类(因为生成类的时候,系统会自动继承java.lang.Record类),但是可以实现接口;
  3. 系统会自动生成: toString(),hashCode(),equals(),属性名的方法(比如属性是name,则会生成name()方法,生成的方法不带get),不会生成set方法.

java标志
image-3035

定义一个简单的Record

定义一个User的record:

record User(String name, int age){ }

中间可以不写任何内容.

调用Record

可以像调用类一样,调用Record,如下:

var dur=new User("tea2",20);
var acs=new User("tea2",20);
System.out.println(dur.name());
System.out.println(dur.age());
System.out.println(dur.equals(acs));
System.out.println(acs.age());
System.out.println(acs.name());

 

这里的dur和acs只能通过构造函数初始化属性.系统会自动生成equals,可以直接比较两个record是否一致.

在Record中添加字段

在Record中,可以添加静态字段或者静态实例,静态方法:

record User(String name, int age){
    public User {
        callNumber++;
    }
    private static int callNumber;
    static int getCallNumber(){
        return callNumber;
    }
}

 

调用:

var dur=new User("tea2",20);
var acs=new User("tea2",20);
System.out.println(User.getCallNumber());

 

Record的构造函数

构造函数可以写成两种方式.同时在构造函数中,进行各种验证操作.

在IDEA中可以通过快捷方式进行转换.如下图:

Java 14 Record简化构造函数转换为传统构造函数:

image-3036
Java 14 Record简化构造函数转换为传统构造函数

Java 14 Record传统构造函数转换为简化构造函数:

image-3037
Java 14 Record传统构造函数转换为简化构造函数

传统构造函数

record User(String name, int age){
    public User(String name, int age) {
        // 进行各种验证操作.
        if(age<20){
            throw new IllegalArgumentException("age小于20.");
        }
        callNumber++;
        this.name = name;
        this.age = age;
    }

    private static int callNumber;
    static int getCallNumber(){
        return callNumber;
    }
}

 

简化构造函数

record User(String name, int age){
    public User {
        // 进行验证操作.
        if(age<20){
            throw new IllegalArgumentException("age小于20.");
        }
        callNumber++;
    }
    private static int callNumber;
    static int getCallNumber(){
        return callNumber;
    }
}

 

在Record中使用泛型

可以这样定义泛型:

record MobilePhone(T phone,double price){
}

 

然后定义一个Record,定义一个普通类:

record HuaWei(){
    private static final String name="Huawei";
    private static final String model="mate 20 pro";

    public String name(){
        return name;
    }
    public String model(){
        return model;
    }
}

class  XiaoMi{
    private static final String name="Xiaomi";
    private static final String model="9";

    public String name(){
        return name;
    }
    public String model(){
        return model;
    }
}

 

调用:

var huaWeiMobilePhone=new MobilePhone(new HuaWei(),6000);
var xiaomiMobilePhone=new MobilePhone(new XiaoMi(),6100);
System.out.println(huaWeiMobilePhone.phone().name());
System.out.println(xiaomiMobilePhone.phone().name());
System.out.println(huaWeiMobilePhone.toString());
System.out.println(xiaomiMobilePhone.toString());

 

输出结果:

Huawei
Xiaomi
MobilePhone[phone=HuaWei[], price=6000.0]
MobilePhone[phone=cn.bckf.java14demo.XiaoMi@25f38edc, price=6100.0]

 

可以看到,普通类和Record只在toString()的实现上有些差别.

Record文件读写

定义Record

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

 

将Record写入文件

static void write(User user, String filePath) {
        try (var oos = new ObjectOutputStream(Files.newOutputStream(Paths.get(filePath)))) {
            oos.writeObject(user);
        } catch (IOException e) {
            e.printStackTrace();
        }
}

 

从文件中读取Record

static User read(String filePath) {
        User user = null;
        try (var oos = new ObjectInputStream(Files.newInputStream(Paths.get(filePath)))) {
            // 此处使用了instanceof模式匹配.该特性为JDK 14新增加.
            if (oos.readObject() instanceof User user1) {
                user = user1;
            }

            /*
            上面的语句也可以写成:
            user= (User) oos.readObject();
            */
        } catch (IOException | ClassNotFoundException e) {
            e.printStackTrace();
        }
        return user;
}

 

读写文件完整代码

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

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

public class Main {

    static void write(User user, String filePath) {
        try (var oos = new ObjectOutputStream(Files.newOutputStream(Paths.get(filePath)))) {
            oos.writeObject(user);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    static User read(String filePath) {
        User user = null;
        try (var oos = new ObjectInputStream(Files.newInputStream(Paths.get(filePath)))) {
            // 此处使用了instanceof模式匹配.该特性为JDK 14新增加.
            if (oos.readObject() instanceof User user1) {
                user = user1;
            }

            /*
            上面的语句也可以写成:
            user= (User) oos.readObject();
            */
        } catch (IOException | ClassNotFoundException e) {
            e.printStackTrace();
        }
        return user;
    }

    public static void main(String[] args) {
        var user = new User("测试用户", 20);
        var filePath = "Java14Record";
        write(user, filePath);
        var readUser = read(filePath);
        System.out.println(readUser.toString());
    }
}

 

Record不是关键字

目前发布的版本中,record和var一样是受限制的标识符,并非关键字.所以record可以像下面这样使用:

var record=100;
void record(){}

 

但是不能用于类名使用,下面的代码会提示编译错误(‘record’是受限制的标识符,不能用于类型声明):

class record{}

 

编译后的Record

IDEA提供了一个反编译的功能,位置如下:

image-3038
编译后的Record.

编译之后的类:

// IntelliJ API Decompiler stub source generated from a class file
// Implementation of methods is not available

package cn.bckf.java14demo;

final class User extends java.lang.Record implements java.io.Serializable {
private final java.lang.String name;
private final int age;

public User(java.lang.String name, int age) { /* compiled code */ }

public java.lang.String toString() { /* compiled code */ }

public final int hashCode() { /* compiled code */ }

public final boolean equals(java.lang.Object o) { /* compiled code */ }

public java.lang.String name() { /* compiled code */ }

public int age() { /* compiled code */ }
}

会发现,编译之后,自动生成了toString(),hashCode(),equals(),和属性的获取方法(没有get前缀),同时,属性被private final修饰.

好了,可以开始愉快的使用Record了.☺

WordPress: 定时更新Twenty Fifteen主题的颜色配置

避免手动修改颜色的麻烦

最近在用Twenty Fifteen主题,用了一下颜色功能.发现挺好用.有个想法,就是白天颜色自动变成亮色,晚上颜色自动变成暗色.

找了一下资料,发现颜色配置存储在wp_options表里面,用列option_name值为’theme_mods_twentyfifteen’的字段存储.

一下就好办多了.

wordpress logo
image-3028

定时任务和Python脚本

如果使用了Autoptimize或者其它缓存插件,在更新颜色的Shell脚本里面一定要先更新颜色,然后清除一次缓存.

Python脚本

Python版本: 需要使用Python3.6+.

首先需要安装Python连接MySQL的插件:

pip install mysql-connector-python

有的可能需要使用:

pip3 install mysql-connector-python

import mysql.connector
from mysql.connector import errorcode
import sys

'''

定时更新Wordpress的Twenty Fifteen主题的颜色配置.

安装: pip install mysql-connector-python


调用: python3 BckfCNUpdateTheme.py blueStyle
调用: python3 BckfCNUpdateTheme.py darkStyle
调用: python3 BckfCNUpdateTheme.py yellowStyle
调用: python3 BckfCNUpdateTheme.py pinkStyle
调用: python3 BckfCNUpdateTheme.py purpleStyle

'''


# 配色yellow 黄色
yellowStyle='a:7:{i:0;b:0;s:18:"custom_css_post_id";i:2941;s:16:"background_color";s:6:"f4ca16";s:12:"color_scheme";s:6:"yellow";s:17:"sidebar_textcolor";s:7:"#111111";s:23:"header_background_color";s:7:"#ffdf00";s:12:"header_image";s:13:"remove-header";}'

# 配色 粉色
pinkStyle='a:7:{i:0;b:0;s:18:"custom_css_post_id";i:2941;s:16:"background_color";s:6:"ffe5d1";s:12:"color_scheme";s:4:"pink";s:17:"sidebar_textcolor";s:7:"#ffffff";s:23:"header_background_color";s:7:"#e53b51";s:12:"header_image";s:13:"remove-header";}'

# 配色 purple 紫色
purpleStyle='a:7:{i:0;b:0;s:18:"custom_css_post_id";i:2941;s:16:"background_color";s:6:"674970";s:12:"color_scheme";s:6:"purple";s:17:"sidebar_textcolor";s:7:"#ffffff";s:23:"header_background_color";s:7:"#2e2256";s:12:"header_image";s:13:"remove-header";}'

# 配色blue 蓝色
blueStyle='a:7:{i:0;b:0;s:18:"custom_css_post_id";i:2941;s:16:"background_color";s:6:"e9f2f9";s:12:"color_scheme";s:4:"blue";s:17:"sidebar_textcolor";s:7:"#ffffff";s:23:"header_background_color";s:7:"#55c3dc";s:12:"header_image";s:13:"remove-header";}'

# 配色dark. 暗色
darkStyle='a:7:{i:0;b:0;s:18:"custom_css_post_id";i:2941;s:16:"background_color";s:6:"111111";s:12:"color_scheme";s:4:"dark";s:17:"sidebar_textcolor";s:7:"#bebebe";s:23:"header_background_color";s:7:"#202020";s:12:"header_image";s:13:"remove-header";}'

def updateStyle(style):
    try:
      cnx = mysql.connector.connect(user='root',
                                    database='wordpress',host='127.0.0.1',password='123')
      cursor = cnx.cursor()
      result=cursor.execute("""update wp_options set option_value='{0}' where option_name='theme_mods_twentyfifteen' """.format(style))
      print(result)
      cnx.commit()
    except mysql.connector.Error as err:
      if err.errno == errorcode.ER_ACCESS_DENIED_ERROR:
        print("Something is wrong with your user name or password")
      elif err.errno == errorcode.ER_BAD_DB_ERROR:
        print("Database does not exist")
      else:
        print(err)
    else:
        cursor.close()
        cnx.close()
    print("处理完成!~")

if __name__ == '__main__':
    if len(sys.argv)>1:
        styleStr=sys.argv[1]
        if styleStr=='blueStyle':
            updateStyle(blueStyle)
        elif styleStr=='darkStyle':
            updateStyle(darkStyle)
        elif styleStr=='yellowStyle':
            updateStyle(yellowStyle)
        elif styleStr=='pinkStyle':
            updateStyle(pinkStyle)
        elif styleStr=='purpleStyle':
            updateStyle(purpleStyle)












 

Shell脚本(在Shell脚本中写下面这段代码,并保存文件):

python3 BckfCNUpdateTheme.py blueStyle

定时任务(第一条每天晚上20点执行,第二条每天早上8点执行.):

0 20 * * * sh /opt/style01.sh
0 8 * * * sh /opt/style02.sh

如果没有使用缓存插件,也可以将调用Python程序的语句直接写在定时任务里面.

IDEA: Options -Xverify:none and -noverify were deprecated in JDK 13 and will likely be removed in a future release.

问题

使用IDEA 2020.1 EAP版本时,如果使用JDK 14.在运行Spring Boot项目的时候,会提示:

Java HotSpot(TM) 64-Bit Server VM warning: Options -Xverify:none and -noverify were deprecated in JDK 13 and will likely be removed in a future release.

原因是在运行中勾选了对应方案导致的.

java标志
image-3026

解决方案

IDEA->Run->Edit Configurations-> Enable launch optimization(取消勾选).

再次运行就没有这个提示了.