执行者的博客

云淡风轻


  • 首页

  • 关于

  • 标签

  • 分类

  • 归档

  • 搜索

JDK版本变化

发表于 2018-10-23 | 更新于 2018-11-14 | 分类于 JDK | 阅读次数:
本文字数: 18k | 阅读时长 ≈ 16 分钟

简介

记录一下JDK版本的历史更新和重要特性。

目前JDK更新较快,每半年发布一个版本,但是对于生产环境采用仍然建议使用LTS(长期支持版本)版本

按照 Oracle 公布的支持路线图,Java 11 将会获得 Oracle 提供的长期支持服务,直至2026年9月。

根据官网从Java 11开始提供用开源许可证和商业许可证的组合

更新记录

  • 2018-11-09 补充JDK8的介绍和使用

一些术语

  • JCP 是 Java Community Process(Java社区进程)的简称,社会各界Java组成的社区,规划和领导Java的发展。
  • JEP 是 JDK Enhancement Proposals (Java 增强提案)的简称,JDK的版本变化将从这些提案中选取。
  • JSR 是 Java Specification Requests(Java规范请求)的简称,是 JCP 成员向委员会提交的 Java 发展议案,经过一系列流程后,如果通过会成为 JEP,最终会体现在未来的Java中。
  • TCK 是 Technology Compatibility Kit(技术兼容性测试)的简称, 如果一个平台型程序想要宣称自己兼容Java,就必须通过TCK测试

JDK6

  • 集合框架增强。
  1. 为了更好的支持双向访问集合。添加了许多新的类和接口。
  2. 新的数组拷贝方法。Arrays.copyOf和Arrays.copyOfRange
1
2
//以下为添加的新接口和类
Deque,BlockingDeque,NavigableSet,NavigableMap,ConcurrentNavigableMap,ArrayDeque, ConcurrentSkipListSet ,ConcurrentSkipListMap,ConcurrentSkipListMap ,AbstractMap.SimpleEntry ,AbstractMap.SimpleImmutableEntry
  • Scripting. 可以让其他语言在java平台上运行。 java6包含了一个基于Mozilla Rhino实现的javascript脚本引擎。
  • 支持JDBC4.0规范。

JDK7

  • 二进制前缀0b或者0B。整型(byte, short, int, long)可以直接用二进制表示。
  • 字面常量数字的下划线。用下划线连接整数提升其可读性,自身无含义,不可用在数字的起始和末尾。
  • switch 支持String类型。
  • 泛型实例化类型自动推断。
  • try-with-resources语句。
  • 单个catch中捕获多个异常类型(用| 分割)并通过改进的类型检查重新抛出异常。

JDK8

  • Lambda 表达式(Lambda Expressions) − Lambda允许把函数作为一个方法的参数(函数作为参数传递进方法中。
  • 方法引用(Method references) − 方法引用提供了非常有用的语法,可以直接引用已有Java类或对象(实例)的方法或构造器。与lambda联合使用,方法引用可以使语言的构造更紧凑简洁,减少冗余代码。
  • 默认方法 (Default methods)− 默认方法就是一个在接口里面有了一个实现的方法。
  • 新工具 − 新的编译工具,如:Nashorn引擎 jjs、 类依赖分析器jdeps。
  • Stream API −新添加的Stream API(java.util.stream) 把真正的函数式编程风格引入到Java中。
  • Date Time API − 加强对日期与时间的处理。
  • Optional 类 − Optional 类已经成为 Java 8 类库的一部分,用来解决空指针异常。
  • Nashorn, JavaScript 引擎 − Java 8提供了一个新的Nashorn javascript引擎,它允许我们在JVM上运行特定的javascript应用。
  • java.util 包下的改进,提供了几个实用的工具类。
    并行数组排序。
    标准的Base64编解码。
    支持无符号运算。

  • java.util.concurrent 包下增加了新的类和方法。
    java.util.concurrent.ConcurrentHashMap 类添加了新的方法以支持新的StreamApi和lambada表达式。
    java.util.concurrent.atomic 包下新增了类以支持可伸缩可更新的变量。
    java.util.concurrent.ForkJoinPool类新增了方法以支持 common pool。
    新增了java.util.concurrent.locks.StampedLock类,为控制读/写访问提供了一个基于性能的锁,且有三种模式可供选择。

  • HotSpot
    删除了 永久代(PermGen).
    方法调用的字节码指令支持默认方法。

  • 重复注解(Repeating Annotations)。重复注解提供了在同一声明或类型中多次应用相同注解类型的能力。

  • 类型注解(Type Annotation)。在任何地方都能使用注解,而不是在声明的地方。
  • 类型推断增强
  • 方法参数反射(Method Parameter Reflection)。
  • HashMap改进,在键值哈希冲突时能有更好表现。

部分特性说明:

1.Lambda 表达式

Lambda 表达式,也可称为闭包,允许把函数作为一个方法的参数

语法

lambda 表达式的语法格式如下:

1
2
3
(parameters) -> expression
或
(parameters) ->{ statements; }

以下是lambda表达式的重要特征:

1
2
3
4
可选类型声明:不需要声明参数类型,编译器可以统一识别参数值。
可选的参数圆括号:一个参数无需定义圆括号,但多个参数需要定义圆括号。
可选的大括号:如果主体包含了一个语句,就不需要使用大括号。
可选的返回关键字:如果主体只有一个表达式返回值则编译器会自动返回值,大括号需要指定明表达式返回了一个数值。

Lambda 表达式实例

1
2
3
4
5
6
7
8
9
10
# 1. 不需要参数,返回值为 5  
() -> 5
# 2. 接收一个参数(数字类型),返回其2倍的值
x -> 2 * x
# 3. 接受2个参数(数字),并返回他们的差值
(x, y) -> x – y
# 4. 接收2个int型整数,返回他们的和
(int x, int y) -> x + y
# 5. 接受一个 string 对象,并在控制台打印,不返回任何值(看起来像是返回void)
(String s) -> System.out.print(s)

2.方法引用

方法引用通过方法的名字来指向一个方法,可以使语言的构造更紧凑简洁,减少冗余代码。方法引用使用一对冒号 ::

语法

构造器引用:它的语法是Class::new,或者更一般的Class< T >::new实例如下:

1
2
final Car car = Car.create( Car::new );
final List< Car > cars = Arrays.asList( car );

静态方法引用:它的语法是Class::static_method,实例如下:

1
cars.forEach( Car::collide );

特定类的任意对象的方法引用:它的语法是Class::method实例如下:

1
cars.forEach( Car::repair );

特定对象的方法引用:它的语法是instance::method实例如下:

1
2
final Car police = Car.create( Car::new );
cars.forEach( police::follow );

方法引用实例

1
2
3
4
5
6
7
8
9
10
11
12
13
public class Java8Tester {
public static void main(String args[]){
List names = new ArrayList();

names.add("Google");
names.add("Runoob");
names.add("Taobao");
names.add("Baidu");
names.add("Sina");

names.forEach(System.out::println);
}
}

函数式接口

函数式接口(Functional Interface)就是一个有且仅有一个抽象方法,但是可以有多个非抽象方法的接口。

函数式接口可以被隐式转换为 lambda 表达式。

如定义了一个函数式接口如下:

1
2
3
4
5
@FunctionalInterface
interface GreetingService
{
void sayMessage(String message);
}

那么就可以使用Lambda表达式来表示该接口的一个实现(注:JAVA 8 之前一般是用匿名类实现的):

1
GreetingService greetService1 = message -> System.out.println("Hello " + message);

默认方法

Java 8 新增了接口的默认方法。
简单说,默认方法就是接口可以有实现方法,而且不需要实现类去实现其方法。
我们只需在方法名前面加个default关键字即可实现默认方法。

1
2
3
为什么要有这个特性?

首先,之前的接口是个双刃剑,好处是面向抽象而不是面向具体编程,缺陷是,当需要修改接口时候,需要修改全部实现该接口的类,目前的java 8之前的集合框架没有foreach方法,通常能想到的解决办法是在JDK里给相关的接口添加新的方法及实现。然而,对于已经发布的版本,是没法在给接口添加新方法的同时不影响已有的实现。所以引进的默认方法。他们的目的是为了解决接口的修改与现有的实现不兼容的问题。

语法

默认方法语法格式如下:

1
2
3
4
5
public interface Vehicle {
default void print(){
System.out.println("我是一辆车!");
}
}

多个默认方法
一个接口有默认方法,考虑这样的情况,一个类实现了多个接口,且这些接口有相同的默认方法,以下实例说明了这种情况的解决方法:

1
2
3
4
5
6
7
8
9
10
11
public interface Vehicle {
default void print(){
System.out.println("我是一辆车!");
}
}

public interface FourWheeler {
default void print(){
System.out.println("我是一辆四轮车!");
}
}

第一个解决方案是创建自己的默认方法,来覆盖重写接口的默认方法:

1
2
3
4
5
public class Car implements Vehicle, FourWheeler {
default void print(){
System.out.println("我是一辆四轮汽车!");
}
}

第二种解决方案可以使用 super 来调用指定接口的默认方法:

1
2
3
4
5
public class Car implements Vehicle, FourWheeler {
public void print(){
Vehicle.super.print();
}
}

静态默认方法
Java 8 的另一个特性是接口可以声明(并且可以提供实现)静态方法。例如:

1
2
3
4
5
6
7
8
9
public interface Vehicle {
default void print(){
System.out.println("我是一辆车!");
}
// 静态方法
static void blowHorn(){
System.out.println("按喇叭!!!");
}
}

Stream

Java 8 API添加了一个新的抽象称为流Stream,可以让你以一种声明的方式处理数据。

Stream 使用一种类似用 SQL 语句从数据库查询数据的直观方式来提供一种对 Java 集合运算和表达的高阶抽象。

Stream API可以极大提高Java程序员的生产力,让程序员写出高效率、干净、简洁的代码。

这种风格将要处理的元素集合看作一种流, 流在管道中传输, 并且可以在管道的节点上进行处理, 比如筛选, 排序,聚合等。

元素流在管道中经过中间操作(intermediate operation)的处理,最后由最终操作(terminal operation)得到前面处理的结果。

1
2
3
+--------------------+       +------+   +------+   +---+   +-------+
| stream of elements +-----> |filter+-> |sorted+-> |map+-> |collect|
+--------------------+ +------+ +------+ +---+ +-------+

以上的流程转换为 Java 代码为:

1
2
3
4
5
6
List<Integer> transactionsIds = 
widgets.stream()
.filter(b -> b.getColor() == RED)
.sorted((x,y) -> x.getWeight() - y.getWeight())
.mapToInt(Widget::getWeight)
.sum();

什么是 Stream?

Stream(流)是一个来自数据源的元素队列并支持聚合操作

元素是特定类型的对象,形成一个队列。 Java中的Stream并不会存储元素,而是按需计算。
数据源 流的来源。 可以是集合,数组,I/O channel, 产生器generator 等。
聚合操作 类似SQL语句一样的操作, 比如filter, map, reduce, find, match, sorted等。
和以前的Collection操作不同, Stream操作还有两个基础的特征:

Pipelining: 中间操作都会返回流对象本身。 这样多个操作可以串联成一个管道, 如同流式风格(fluent style)。 这样做可以对操作进行优化, 比如延迟执行(laziness)和短路( short-circuiting)。
内部迭代: 以前对集合遍历都是通过Iterator或者For-Each的方式, 显式的在集合外部进行迭代, 这叫做外部迭代。 Stream提供了内部迭代的方式, 通过访问者模式(Visitor)实现。

生成流

在 Java 8 中, 集合接口有两个方法来生成流:

stream() − 为集合创建串行流。

parallelStream() − 为集合创建并行流。

1
2
List<String> strings = Arrays.asList("abc", "", "bc", "efg", "abcd","", "jkl");
List<String> filtered = strings.stream().filter(string -> !string.isEmpty()).collect(Collectors.toList());

forEach

Stream 提供了新的方法 ‘forEach’ 来迭代流中的每个数据。以下代码片段使用 forEach 输出了10个随机数:

1
2
Random random = new Random();
random.ints().limit(10).forEach(System.out::println);

map

map 方法用于映射每个元素到对应的结果,以下代码片段使用 map 输出了元素对应的平方数:

1
2
3
List<Integer> numbers = Arrays.asList(3, 2, 2, 3, 7, 3, 5);
// 获取对应的平方数
List<Integer> squaresList = numbers.stream().map( i -> i*i).distinct().collect(Collectors.toList());

filter

filter 方法用于通过设置的条件过滤出元素。以下代码片段使用 filter 方法过滤出空字符串:

1
2
3
List<String>strings = Arrays.asList("abc", "", "bc", "efg", "abcd","", "jkl");
// 获取空字符串的数量
int count = strings.stream().filter(string -> string.isEmpty()).count();

limit

limit 方法用于获取指定数量的流。 以下代码片段使用 limit 方法打印出 10 条数据:

1
2
Random random = new Random();
random.ints().limit(10).forEach(System.out::println);

sorted

sorted 方法用于对流进行排序。以下代码片段使用 sorted 方法对输出的 10 个随机数进行排序:

1
2
Random random = new Random();
random.ints().limit(10).sorted().forEach(System.out::println);

并行(parallel)程序

parallelStream 是流并行处理程序的代替方法。以下实例我们使用 parallelStream 来输出空字符串的数量:

1
2
3
List<String> strings = Arrays.asList("abc", "", "bc", "efg", "abcd","", "jkl");
// 获取空字符串的数量
int count = strings.parallelStream().filter(string -> string.isEmpty()).count();

我们可以很容易的在顺序运行和并行直接切换。

Collectors

Collectors 类实现了很多归约操作,例如将流转换成集合和聚合元素。Collectors 可用于返回列表或字符串:

1
2
3
4
5
6
List<String>strings = Arrays.asList("abc", "", "bc", "efg", "abcd","", "jkl");
List<String> filtered = strings.stream().filter(string -> !string.isEmpty()).collect(Collectors.toList());

System.out.println("筛选列表: " + filtered);
String mergedString = strings.stream().filter(string -> !string.isEmpty()).collect(Collectors.joining(", "));
System.out.println("合并字符串: " + mergedString);

统计

另外,一些产生统计结果的收集器也非常有用。它们主要用于int、double、long等基本类型上,它们可以用来产生类似如下的统计结果。

1
2
3
4
5
6
7
8
List<Integer> numbers = Arrays.asList(3, 2, 2, 3, 7, 3, 5);

IntSummaryStatistics stats = numbers.stream().mapToInt((x) -> x).summaryStatistics();

System.out.println("列表中最大的数 : " + stats.getMax());
System.out.println("列表中最小的数 : " + stats.getMin());
System.out.println("所有数之和 : " + stats.getSum());
System.out.println("平均数 : " + stats.getAverage());

Optional 类

Optional 类是一个可以为null的容器对象。如果值存在则isPresent()方法会返回true,调用get()方法会返回该对象。

Optional 是个容器:它可以保存类型T的值,或者仅仅保存null。Optional提供很多有用的方法,这样我们就不用显式进行空值检测。

Optional 类的引入很好的解决空指针异常。

类声明
以下是一个 java.util.Optional 类的声明:

1
2
public final class Optional<T>
extends Object

Nashorn JavaScript

Nashorn 一个 javascript 引擎。

从JDK 1.8开始,Nashorn取代Rhino(JDK 1.6, JDK1.7)成为Java的嵌入式JavaScript引擎。Nashorn完全支持ECMAScript 5.1规范以及一些扩展。它使用基于JSR 292的新语言特性,其中包含在JDK 7中引入的 invokedynamic,将JavaScript编译成Java字节码。

与先前的Rhino实现相比,这带来了2到10倍的性能提升。

日期时间 API

ava 8通过发布新的Date-Time API (JSR 310)来进一步加强对日期与时间的处理。

在旧版的 Java 中,日期时间 API 存在诸多问题,其中有:

非线程安全 − java.util.Date 是非线程安全的,所有的日期类都是可变的,这是Java日期类最大的问题之一。

设计很差 − Java的日期/时间类的定义并不一致,在java.util和java.sql的包中都有日期类,此外用于格式化和解析的类在java.text包中定义。java.util.Date同时包含日期和时间,而java.sql.Date仅包含日期,将其纳入java.sql包并不合理。另外这两个类都有相同的名字,这本身就是一个非常糟糕的设计。

时区处理麻烦 − 日期类并不提供国际化,没有时区支持,因此Java引入了java.util.Calendar和java.util.TimeZone类,但他们同样存在上述所有的问题。

Java 8 在 java.time 包下提供了很多新的 API。以下为两个比较重要的 API:

Local(本地) − 简化了日期时间的处理,没有时区的问题。

Zoned(时区) − 通过制定的时区处理日期时间。

新的java.time包涵盖了所有处理日期,时间,日期/时间,时区,时刻(instants),过程(during)与时钟(clock)的操作。

Base64

在Java 8中,Base64编码已经成为Java类库的标准。

Java 8 内置了 Base64 编码的编码器和解码器。

Base64工具类提供了一套静态方法获取下面三种BASE64编解码器:

基本:输出被映射到一组字符A-Za-z0-9+/,编码不添加任何行标,输出的解码仅支持A-Za-z0-9+/。
URL:输出映射到一组字符A-Za-z0-9+_,输出是URL和文件。
MIME:输出隐射到MIME友好格式。输出每行不超过76字符,并且使用’\r’并跟随’\n’作为分割。编码输出最后没有行分割。

JDK9

JDK 9 于2017年9月21日发布

新特性有:

  • 模块化 —— Jigsaw(Java Platform Module System)
  • 交互式命令行 —— JShell
  • 进程操作改进
  • 竞争锁的性能优化
  • 分段代码缓存
  • 优化字符串占用空间
  • Javadoc
    简化Doclet API。
    支持生成HTML5格式。
    加入了搜索框,使用这个搜索框可以查询程序元素、标记的单词和文档中的短语。
    支持新的模块系统。
  • JVM
    增强了Garbage-First(G1)并用它替代Parallel GC成为默认的垃圾收集器。
    统一了JVM 日志,为所有组件引入了同一个日志系统。
    删除了JDK 8中弃用的GC组合。(DefNew + CMS,ParNew + SerialOld,Incremental CMS)。
  • properties文件支持UTF-8编码,之前只支持ISO-8859-1。
  • 支持Unicode 8.0,在JDK8中是Unicode 6.2。
  • 支持私有接口方法(您可以使用diamond语法与匿名内部类结合使用)。
  • 下划线不能用在变量名中。
  • diamond语法与匿名内部类结合使用。
  • 新的版本号格式。$MAJOR.$MINOR.$SECURITY.$PATCH

JDK10

北京时间 3 月 21 日,Oracle 官方宣布 Java 10 正式发布。新特性有:

  • JEP 286: 局部变量的类型推导。该特性在社区讨论了很久并做了调查,可查看 JEP 286 调查结果。
  • JEP 296: 将 JDK 的多个代码仓库合并到一个储存库中。
  • JEP 304: 垃圾收集器接口。通过引入一个干净的垃圾收集器(GC)接口,改善不同垃圾收集器的源码隔离性。
  • JEP 307: 向 G1 引入并行 Full GC。
  • JEP 310: 应用类数据共享。为改善启动和占用空间,在现有的类数据共享(“CDS”)功能上再次拓展,以允许应用类放置在共享存档中。
  • JEP 312: 线程局部管控。允许停止单个线程,而不是只能启用或停止所有线程。
  • JEP 313: 移除 Native-Header Generation Tool (javah)
  • JEP 314: 额外的 Unicode 语言标签扩展。包括:cu (货币类型)、fw (每周第一天为星期几)、rg (区域覆盖)、tz (时区) 等。
  • JEP 316: 在备用内存设备上分配堆内存。允许 HotSpot 虚拟机在备用内存设备上分配 Java 对象堆。
  • JEP 317: 基于 Java 的 JIT 编译器(试验版本)。
  • JEP 319: 根证书。开源 Java SE Root CA 程序中的根证书。
  • JEP 322: 基于时间的版本发布模式。“Feature releases” 版本将包含新特性,“Update releases” 版本仅修复 Bug 。

部分特性说明

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
1. var 类型推断。

这个语言功能在其他一些语言 (C#、JavaScript) 和基于 JRE 的一些语言 (Scala 和 Kotlin) 中,早已被加入。

在 Java 语言很早就在考虑,早在 2016 年正式提交了 JEP286 提议。后来举行了一次公开的开发者调查,获得最多建议的是采用类似 Scala 的方案,“同时使用 val 和 var”,约占一半;第二多的是“只使用 var”,约占四分之一。后来 Oracle 公司经过慎重考虑,采用了只使用 var 关键字的方案。

有了这个功能,开发者在写这样的代码时:

ArrayList<String> myList = new ArrayList<String>()
可以省去前面的类型声明,而只需要

var list = new ArrayList<String>()
编译器会自动推断出 list 变量的类型。对于链式表达式来说,也会很方便:

var stream = blocks.stream();

...

int maxWeight = stream.filter(b -> b.getColor() == BLUE)

.mapToInt(Block::getWeight)

.max();
开发者无须声明并且 import 引入 Stream 类型,只用 stream 作为中间变量,用 var 关键字使得开发效率提升。

不过 var 的使用有众多限制,包括不能用于推断方法参数类型,只能用于局部变量,如方法块中,而不能用于类变量的声明,等等。

另外,我个人认为,对于开发者而言,变量类型明显的声明会提供更加全面的程序语言信息,对于理解并维护代码有很大的帮助。一旦 var 被广泛运用,开发者阅读三方代码而没有 IDE 的支持下,会对程序的流程执行理解造成一定的障碍。所以我建议尽量写清楚变量类型,程序的易读维护性有时更重要一些。

2. 统一的 GC 接口

在 JDK10 的代码中,路径为 openjdk/src/hotspot/share/gc/,各个 GC 实现共享依赖 shared 代码,GC 包括目前默认的 G1,也有经典的 Serial、Parallel、CMS 等 GC 实现。



3. 应用程序类数据(AppCDS)共享

CDS 特性在原来的 bootstrap 类基础之上,扩展加入了应用类的 CDS(Application Class-Data Sharing) 支持。

其原理为:在启动时记录加载类的过程,写入到文本文件中,再次启动时直接读取此启动文本并加载。设想如果应用环境没有大的变化,启动速度就会得到提升。

我们可以想像为类似于操作系统的休眠过程,合上电脑时把当前应用环境写入磁盘,再次使用时就可以快速恢复环境。

我在自己 PC 电脑上做以下应用启动实验。

首先部署 wildfly 12 应用服务器,采用 JDK10 预览版作为 Java 环境。另外需要用到一个工具 cl4cds[1],作用是把加载类的日志记录,转换为 AppCDS 可以识别的格式。

A、安装好 wildfly 并部署一个应用,具有 Angularjs, rest, jpa 完整应用技术栈,预热后启动三次,并记录完成部署时间

分别为 6716ms, 6702ms, 6613ms,平均时间为 6677ms。

B、加入环境变量并启动,导出启动类日志

export PREPEND_JAVA_OPTS="-Xlog:class+load=debug:file=/tmp/wildfly.classtrace"
C、使用 cl4cds 工具,生成 AppCDS 可以识别的 cls 格式

/jdk-10/bin/java -cp src/classes/ io.simonis.cl4cds /tmp/wildfly.classtrace /tmp/wildfly.cls
打开文件可以看到内容为:

java/lang/Object id: 0x0000000100000eb0

java/io/Serializable id: 0x0000000100001090

java/lang/Comparable id: 0x0000000100001268

java/lang/CharSequence id: 0x0000000100001440

......

org/hibernate/type/AssociationType id: 0x0000000100c61208 super: 0x0000000100000eb0 interfaces: 0x0000000100a00d10 source: /home/shihang/work/jboss/wildfly/dist/target/wildfly-12.0.0.Final/modules/system/layers/base/org/hibernate/main/hibernate-core-5.1.10.Final.jar

org/hibernate/type/AbstractType id: 0x0000000100c613e0 super: 0x0000000100000eb0 interfaces: 0x0000000100a00d10 source: /home/shihang/work/jboss/wildfly/dist/target/wildfly-12.0.0.Final/modules/system/layers/base/org/hibernate/main/hibernate-core-5.1.10.Final.jar

org/hibernate/type/AnyType id: 0x0000000100c61820 super: 0x0000000100c613e0 interfaces: 0x0000000100c61030 0x0000000100c61208 source: /home/shihang/work/jboss/wildfly/dist/target/wildfly-12.0.0.Final/modules/system/layers/base/org/hibernate/main/hibernate-core-5.1.10.Final.jar

....
这个文件用于标记类的加载信息。

D、使用环境变量启动 wildfly,模拟启动过程并导出 jsa 文件,就是记录了启动时类的信息。

export PREPEND_JAVA_OPTS="-Xshare:dump -XX:+UseAppCDS -XX:SharedClassListFile=/tmp/wildfly.cls -XX:+UnlockDiagnosticVMOptions -XX:SharedArchiveFile=/tmp/wildfly.jsa"
查看产生的文件信息,jsa 文件有较大的体积。

/opt/work/cl4cds$ ls -l /tmp/wildfly.*

-rw-rw-r-- 1 shihang shihang 8413843 Mar 20 11:07 /tmp/wildfly.classtrace

-rw-rw-r-- 1 shihang shihang 4132654 Mar 20 11:11 /tmp/wildfly.cls

-r--r--r-- 1 shihang shihang 177659904 Mar 20 11:13 /tmp/wildfly.jsa
E、使用 jsa 文件启动应用服务器

export PREPEND_JAVA_OPTS="-Xshare:on -XX:+UseAppCDS -XX:+UnlockDiagnosticVMOptions -XX:SharedArchiveFile=/tmp/wildfly.jsa"
启动完毕后记录时长,三次分别是 5535ms, 5333ms, 5225ms,平均为 5364ms,相比之前的 6677ms 可以算出启动时间提升了 20% 左右。

这个效率提升,对于云端应用部署很有价值。

以上实验方法参考于技术博客 [2]。

4. JEP314,使用附加的 Unicode 语言标记扩展。

JDK10 对于 Unicode BCP 47 有了更多的支持,BCP 47 是 IETF 定义语言集的规范文档。使用扩展标记,可以更方便的获得所需要的语言地域环境。

如 JDK10 加入的一个方法,

java.time.format.DateTimeFormatter::localizedBy
通过这个方法,可以采用某种数字样式,区域定义或者时区来获得时间信息所需的语言地域本地环境信息。

附:从链接 [3] 可以看到 JDK10 所有的方法级别改动。

5. 查看当前 JDK 管理根证书。

自 JDK9 起在 keytool 中加入参数 -cacerts,可以查看当前 JDK 管理的根证书。而 OpenJDK9 中 cacerts 为空,这样就会给开发者带来很多不变。

EP318 就是利用 Oracle 开源出 Oracle JavaSE 中的 cacerts 信息,在 OpenJDK 中提供一组默认的根证书颁发机构证书,目前有 80 条记录。

/jdk-10/bin$ ./keytool -list -cacerts

Enter keystore password:

Keystore type: JKS

Keystore provider: SUN



Your keystore contains 80 entries



verisignclass2g2ca [jdk], Dec 2, 2017, trustedCertEntry,

Certificate fingerprint (SHA-256): 3A:43:E2:20:FE:7F:3E:A9:65:3D:1E:21:74:2E:AC:2B:75:C2:0F:D8:98:03:05:BC:50:2C:AF:8C:2D:9B:41:A1

......

JDK11

JDK 11 总共包含 17 个新的 JEP ,分别为:

  • 181: Nest-Based Access Control(基于嵌套的访问控制)
  • 309: Dynamic Class-File Constants(动态类文件常量)
  • 315: Improve Aarch64 Intrinsics(改进 Aarch64 Intrinsics)
  • 318: Epsilon: A No-Op Garbage Collector(Epsilon — 一个无操作的垃圾收集器)
  • 320: Remove the Java EE and CORBA Modules(删除 Java EE 和 CORBA 模块)
  • 321: HTTP Client (Standard)
  • 323: Local-Variable Syntax for Lambda Parameters(用于 Lambda 参数的局部变量语法)
  • 324: Key Agreement with Curve25519 and Curve448(Curve25519 和 Curve448 算法的密钥协议)
  • 327: Unicode 10
  • 328: Flight Recorder
  • 329: ChaCha20 and Poly1305 Cryptographic Algorithms(ChaCha20 和 Poly1305 加密算法)
  • 330: Launch Single-File Source-Code Programs(启动单一文件的源代码程序)
  • 331: Low-Overhead Heap Profiling(低开销的 Heap Profiling)
  • 332: Transport Layer Security (TLS) 1.3(支持 TLS 1.3)
  • 333: ZGC: A Scalable Low-Latency Garbage Collector (Experimental) (可伸缩低延迟垃圾收集器)
  • 335: Deprecate the Nashorn JavaScript Engine(弃用 Nashorn JavaScript 引擎)
  • 336: Deprecate the Pack200 Tools and API (弃用 Pack200 工具和 API)

支持Unicode 10.0,在jdk10中是8.0。
标准化HTTP Client
编译器线程的延迟分配。添加了新的命令-XX:+UseDynamicNumberOfCompilerThreads动态控制编译器线程的数量。
新的垃圾收集器—ZGC。一种可伸缩的低延迟垃圾收集器(实验性)。
Epsilon。一款新的实验性无操作垃圾收集器。Epsilon GC 只负责内存分配,不实现任何内存回收机制。这对于性能测试非常有用,可用于与其他GC对比成本和收益。
Lambda参数的局部变量语法。java10中引入的var字段得到了增强,现在可以用在lambda表达式的声明中。如果lambda表达式的其中一个形式参数使用了var,那所有的参数都必须使用var。

JDK12

参考资料

1.JDK 版本变化
2.Java 9 揭秘

Hexo(五):个性化 next theme

发表于 2018-10-23 | 更新于 2018-11-14 | 分类于 hexo | 阅读次数:
本文字数: 6.5k | 阅读时长 ≈ 6 分钟

个性化

主要有以下32种:
• 在右上角或者左上角实现fork me on github
• 添加RSS
• 添加动态背景
• 实现点击出现桃心效果
• 修改文章内链接文本样式
• 修改文章底部的那个带#号的标签
• 在每篇文章末尾统一添加“本文结束”标记
• 修改作者头像并旋转
• 博文压缩
• 修改代码块自定义样式
• 侧边栏社交小图标设置
• 主页文章添加阴影效果
• 在网站底部加上访问量
• 添加热度
• 网站底部字数统计
• 添加 README.md 文件
• 设置网站的图标Favicon
• 实现统计功能
• 添加顶部加载条
• 在文章底部增加版权信息
• 添加网易云跟帖(跟帖关闭,已失效,改为来必力)
• 隐藏网页底部powered By Hexo / 强力驱动
• 修改网页底部的桃心
• 文章加密访问
• 添加jiathis分享
• 博文置顶
• 修改字体大小
• 修改打赏字体不闪动
• 自定义鼠标样式
• 为博客加上萌萌的宠物
• DaoVoice 在线联系
• 点击爆炸效果

下面介绍几种常见配置,其他请参考文末链接

1.在右上角或者左上角实现fork me on github

点击这里https://blog.github.com/2008-12-19-github-ribbons/挑选自己喜欢的样式,并复制代码。 例如,我的代码:

1
2
<a href="https://github.com/Ahaochan" class="" target="_blank" title="我的Github" aria-label="我的Github"><svg width="80" height="80" viewBox="0 0 250 250" style="fill:#222; color:#fff; position: absolute; top: 0; border: 0; right: 0;" aria-hidden="true"><path d="M0,0 L115,115 L130,115 L142,142 L250,250 L250,0 Z"></path><path d="M128.3,109.0 C113.8,99.7 119.0,89.6 119.0,89.6 C122.0,82.7 120.5,78.6 120.5,78.6 C119.2,72.0 123.4,76.3 123.4,76.3 C127.3,80.9 125.5,87.3 125.5,87.3 C122.9,97.6 130.6,101.9 134.4,103.2" fill="currentColor" style="transform-origin: 130px 106px;" class="octo-arm"></path><path d="M115.0,115.0 C114.9,115.1 118.7,116.5 119.8,115.4 L133.7,101.6 C136.9,99.2 139.9,98.4 142.2,98.6 C133.8,88.0 127.5,74.4 143.8,58.0 C148.5,53.4 154.0,51.2 159.7,51.0 C160.3,49.4 163.2,43.6 171.4,40.1 C171.4,40.1 176.1,42.5 178.8,56.2 C183.1,58.6 187.2,61.8 190.9,65.4 C194.5,69.0 197.7,73.2 200.1,77.6 C213.8,80.2 216.3,84.9 216.3,84.9 C212.7,93.1 206.9,96.0 205.4,96.6 C205.1,102.4 203.0,107.8 198.3,112.5 C181.9,128.9 168.3,122.5 157.7,114.1 C157.9,116.9 156.7,120.9 152.7,124.9 L141.0,136.5 C139.8,137.7 141.6,141.9 141.8,141.8 Z" fill="currentColor" class="octo-body"></path></svg>
</a>

然后粘贴刚才复制的代码到themes/next/layout/_layout.swig文件中(放在

的下面),并把href改为你的github地址

2.添加RSS

执行以下命令安装 RSS 插件:

1
$ npm install hexo-generator-feed --save

编辑网站根目录下的 _config.yml,添加以下代码开启

1
2
3
4
5
6
7
8
9
# RSS订阅支持
plugin:
- hexo-generator-feed

# Feed Atom
feed:
type: atom
path: atom.xml
limit: 20

NexT 主题,默认就可以;其他主题请参考主题说明,配置完之后运行:

1
$ hexo clean && hexo g

重新生成一次,你会在./public 文件夹中看到 atom.xml 文件。然后启动服务器查看是否有效,之后再部署到 Github 中。

3. 侧边栏社交小图标设置

打开主题配置文件(_config.yml),配置social

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
social:
#GitHub: https://github.com/yourname || github
GitHub: https://github.com/victoryofymk || github
#E-Mail: mailto:yourname@gmail.com || envelope
E-Mail: mailto:starlord.yan@gmail.com || envelope
#Google: https://plus.google.com/yourname || google
#Twitter: https://twitter.com/yourname || twitter
#FB Page: https://www.facebook.com/yourname || facebook
#VK Group: https://vk.com/yourname || vk
#StackOverflow: https://stackoverflow.com/yourname || stack-overflow
#YouTube: https://youtube.com/yourname || youtube
#Instagram: https://instagram.com/yourname || instagram
#Skype: skype:yourname?call|chat || skype

social_icons:
enable: true
icons_only: false
transition: false
# Dependencies: exturl: true in Tags Settings section below.
# To encrypt links above use https://www.base64encode.org
# Example encoded link: `GitHub: aHR0cHM6Ly9naXRodWIuY29tL3RoZW1lLW5leHQ= || github`
exturl: false

4. 添加热度

5. 文章字数统计和阅读时间估计

运行

1
npm install hexo-symbols-count-time --save

NexT 主题也对这个插件进行了集成,可以进行一些高级设置

1
2
3
4
5
6
7
8
# 「在NexT配置文件里修改」

symbols_count_time:
separated_meta: true
item_text_post: true
item_text_total: false
awl: 4 # Average Word Length
wpm: 275 # Words Per Minute

6. 根据标签推荐相关文章

运行

1
npm install hexo-related-popular-posts --save

NexT 主题集成了这个插件的配置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
# 「在NexT配置文件里修改」

# Related popular posts
# Dependencies: https://github.com/tea3/hexo-related-popular-posts
related_posts:
enable: true
title: # custom header, leave empty to use the default one
display_in_home: false
params:
maxCount: 5
#PPMixingRate: 0.0
#isDate: false
#isImage: false
#isExcerpt: false

7. 设置阅读访问量

关于Hexo博客的文章阅读量设置问题,常见方案如下:
1、不蒜子,仅局限于在文章页面显示阅读数,首页是不显示的。

打开主题配置文件(_config.yml)

1
2
3
4
5
6
7
8
busuanzi_count:
enable: true
total_visitors: true
total_visitors_icon: user
total_views: true
total_views_icon: eye
post_views: true
post_views_icon: eye

2、LeanCloud
打开LeanCloud官网,进入注册页面注册。完成邮箱激活后,点击头像,进入控制台页面,如下:

创建应用

创建应用取名test

创建Class

创建Class命名为Counter,ACL权限默认即可

Next主题配置

基本配置
编辑主题目录下的site_page/themes/next/_config.yml配置,修改配置如下:

1
2
3
4
5
6
# Show number of visitors to each article.
# You can visit https://leancloud.cn get AppID and AppKey.
leancloud_visitors:
enable: true
app_id: xxx
app_key: xxx

其中,app_id和app_key在设置–应用Key中获取

Web安全

为了保证应用的统计计数功能仅应用于自己的博客系统,你可以在应用->设置->安全中心的Web安全域名中加入自己的博客域名,以保证数据的调用安全。

确认其他配置

1
2
3
themes\next\layout\_scripts\third-party\lean-analytics.swig  # 确保该文件存在
themes\next\layout\_macro\post.swig # 大约171行存在leancloud_visitors相关代码
themes\next\layout\_layout.swig # 大约83行引用了lean-analytics.swig文件

问题

报错:Cannot read property ‘enable_sync’ of undefined
解决:在blog站点下的配置文件_config.yml添加以下代码

1
2
3
4
5
6
leancloud_counter_security:
enable_sync: true
app_id: rjEGiCa******-gzGzoHsz
app_key: zWE2Bry******8HmPyTpBQa
username: # Will be asked while deploying if is left blank
password: # Recommmended to be left blank. Will be asked while deploying if is left blank

8. 添加sitemap和baidusitemap

安装sitemap

1
npm install hexo-generator-sitemap --save

安装baidusitemap

1
npm install hexo-generator-baidu-sitemap --save

在博客目录的_config.yml中添加如下代码,并修改url字段的值,其值默认为http://yoursite.com

1
2
3
4
5
6
7
# 自动生成sitemap
sitemap:
path: sitemap.xml
baidusitemap:
path: baidusitemap.xml

url: https://victoryofymk.github.io

9.为博客加上萌萌的宠物

安装:

1
npm install -save hexo-helper-live2d

请向Hexo的 _config.yml 文件或主题的 _config.yml 文件中添加配置.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
live2d:
enable: true
scriptFrom: local
pluginRootPath: live2dw/
pluginJsPath: lib/
pluginModelPath: assets/
tagMode: false
debug: false
model:
use: live2d-widget-model-wanko
display:
position: right
width: 150
height: 300
mobile:
show: true

然后hexo clean ,hexo g ,hexo d 就可以看到了。

10.开启动态背景

安装

1
git clone https://github.com/theme-next/theme-next-canvas-nest themes/next/source/lib/canvas-nest

参考的文章:

1. https://segmentfault.com/a/1190000009544924

VUE(一):VUE简介

发表于 2018-10-23 | 更新于 2018-11-14 | 分类于 vue | 阅读次数:
本文字数: 9.9k | 阅读时长 ≈ 9 分钟

简介

Vue.js是当下很火的一个JavaScript MVVM库,它是以数据驱动和组件化的思想构建的。相比于Angular.js,Vue.js提供了更加简洁、更易于理解的API,使得我们能够快速地上手并使用Vue.js。

MVVM模式

下图不仅概括了MVVM模式(Model-View-ViewModel),还描述了在Vue.js中ViewModel是如何和View以及Model进行交互的

ViewModel是Vue.js的核心,它是一个Vue实例。Vue实例是作用于某一个HTML元素上的,这个元素可以是HTML的body元素,也可以是指定了id的某个元素。

当创建了ViewModel后,双向绑定是如何达成的呢?

首先,我们将上图中的DOM Listeners和Data Bindings看作两个工具,它们是实现双向绑定的关键。
从View侧看,ViewModel中的DOM Listeners工具会帮我们监测页面上DOM元素的变化,如果有变化,则更改Model中的数据;
从Model侧看,当我们更新Model中的数据时,Data Bindings工具会帮我们更新页面中的DOM元素。

直接引用

在工程中直接引用vue,后续介绍使用vue-cli搭建vue工程化

Hello World示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title></title>
</head>

<body>
<!--这是我们的View-->
<div id="app">
{{ message }}
</div>
</body>
<script src="js/vue.js"></script>
<script>
// 这是我们的Model
var exampleData = {
message: 'Hello World!'
}

// 创建一个 Vue 实例或 "ViewModel"
// 它连接 View 与 Model
new Vue({
el: '#app',
data: exampleData
})
</script>
</html>

使用Vue的过程就是定义MVVM各个组成部分的过程的过程。

定义View
定义Model
创建一个Vue实例或”ViewModel”,它用于连接View和Model
在创建Vue实例时,需要传入一个选项对象,选项对象可以包含数据、挂载元素、方法、模生命周期钩子等等。

1
2
在这个示例中,选项对象的el属性指向View,el: '#app'表示该Vue实例将挂载到<div id="app">...</div>这个元素;data属性指向Model,data: exampleData表示我们的Model是exampleData对象。
Vue.js有多种数据绑定的语法,最基础的形式是文本插值,使用一对大括号语法,在运行时{{ message }}会被数据对象的message属性替换,所以页面上会输出"Hello World!"。

双向绑定示例

MVVM模式本身是实现了双向绑定的,在Vue.js中可以使用v-model指令在表单元素上创建双向数据绑定。

1
2
3
4
5
6
<!--这是我们的View-->
<div id="app">
<p>{{ message }}</p>
<input type="text" v-model="message"/>
</div>
将message绑定到文本框,当更改文本框的值时,<p>{{ message }}</p> 中的内容也会被更新。反过来,如果改变message的值,文本框的值也会被更新,我们可以在Chrome控制台进行尝试。

Vue.js的常用指令

Vue.js的指令是以v-开头的,它们作用于HTML元素,指令提供了一些特殊的特性,将指令绑定在元素上时,指令会为绑定的目标元素添加一些特殊的行为,我们可以将指令看作特殊的HTML特性(attribute)。

1
2
3
4
5
6
v-if指令
v-show指令
v-else指令
v-for指令
v-bind指令
v-on指令

v-if指令

v-if是条件渲染指令,它根据表达式的真假来删除和插入元素,它的基本语法如下:v-if=”expression”
expression是一个返回bool值的表达式,表达式可以是一个bool属性,也可以是一个返回bool的运算式。例如:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title></title>
</head>
<body>
<div id="app">
<h1>Hello, Vue.js!</h1>
<h1 v-if="yes">Yes!</h1>
<h1 v-if="no">No!</h1>
<h1 v-if="age >= 25">Age: {{ age }}</h1>
<h1 v-if="name.indexOf('jack') >= 0">Name: {{ name }}</h1>
</div>
</body>
<script src="js/vue.js"></script>
<script>

var vm = new Vue({
el: '#app',
data: {
yes: true,
no: false,
age: 28,
name: 'keepfool'
}
})
</script>
</html>

注意:yes, no, age, name这4个变量都来源于Vue实例选项对象的data属性。
这段代码使用了4个表达式:

  • 数据的yes属性为true,所以”Yes!”会被输出;
  • 数据的no属性为false,所以”No!”不会被输出;
  • 运算式age >= 25返回true,所以”Age: 28”会被输出;
  • 运算式name.indexOf(‘jack’) >= 0返回false,所以”Name: keepfool”不会被输出。
    注意:v-if指令是根据条件表达式的值来执行元素的插入或者删除行为。

v-show指令

v-show也是条件渲染指令,和v-if指令不同的是,使用v-show指令的元素始终会被渲染到HTML,它只是简单地为元素设置CSS的style属性。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title></title>
</head>
<body>
<div id="app">
<h1>Hello, Vue.js!</h1>
<h1 v-show="yes">Yes!</h1>
<h1 v-show="no">No!</h1>
<h1 v-show="age >= 25">Age: {{ age }}</h1>
<h1 v-show="name.indexOf('jack') >= 0">Name: {{ name }}</h1>
</div>
</body>
<script src="js/vue.js"></script>
<script>

var vm = new Vue({
el: '#app',
data: {
yes: true,
no: false,
age: 28,
name: 'keepfool'
}
})
</script>
</html>

在Chrome控制台更改age属性,使得表达式age >= 25的值为false,可以看到

Age: 24

元素被设置了style=”display:none”样式。

v-else指令

可以用v-else指令为v-if或v-show添加一个“else块”。v-else元素必须立即跟在v-if或v-show元素的后面——否则它不能被识别。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
<!DOCTYPE html>
<html>

<head>
<meta charset="UTF-8">
<title></title>
</head>
<body>
<div id="app">
<h1 v-if="age >= 25">Age: {{ age }}</h1>
<h1 v-else>Name: {{ name }}</h1>
<h1>---------------------分割线---------------------</h1>
<h1 v-show="name.indexOf('keep') >= 0">Name: {{ name }}</h1>
<h1 v-else>Sex: {{ sex }}</h1>
</div>
</body>
<script src="js/vue.js"></script>
<script>
var vm = new Vue({
el: '#app',
data: {
age: 28,
name: 'keepfool',
sex: 'Male'
}
})
</script>
</html>

v-else元素是否渲染在HTML中,取决于前面使用的是v-if还是v-show指令。
这段代码中v-if为true,后面的v-else不会渲染到HTML;v-show为tue,但是后面的v-else仍然渲染到HTML了。

v-for指令

v-for指令基于一个数组渲染一个列表,它和JavaScript的遍历语法相似:

1
v-for="item in items"

items是一个数组,item是当前被遍历的数组元素。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
<!DOCTYPE html>
<html>

<head>
<meta charset="UTF-8">
<title></title>
<link rel="stylesheet" href="styles/demo.css" />
</head>

<body>
<div id="app">
<table>
<thead>
<tr>
<th>Name</th>
<th>Age</th>
<th>Sex</th>
</tr>
</thead>
<tbody>
<tr v-for="person in people">
<td>{{ person.name }}</td>
<td>{{ person.age }}</td>
<td>{{ person.sex }}</td>
</tr>
</tbody>
</table>
</div>
</body>
<script src="js/vue.js"></script>
<script>
var vm = new Vue({
el: '#app',
data: {
people: [{
name: 'Jack',
age: 30,
sex: 'Male'
}, {
name: 'Bill',
age: 26,
sex: 'Male'
}, {
name: 'Tracy',
age: 22,
sex: 'Female'
}, {
name: 'Chris',
age: 36,
sex: 'Male'
}]
}
})
</script>

</html>

v-bind指

v-bind指令可以在其名称后面带一个参数,中间放一个冒号隔开,这个参数通常是HTML元素的特性(attribute),例如:v-bind:class

1
v-bind:argument="expression"

下面这段代码构建了一个简单的分页条,v-bind指令作用于元素的class特性上。
这个指令包含一个表达式,表达式的含义是:高亮当前页。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title></title>
<link rel="stylesheet" href="styles/demo.css" />
</head>
<body>
<div id="app">
<ul class="pagination">
<li v-for="n in pageCount">
<a href="javascripit:void(0)" v-bind:class="activeNumber === n + 1 ? 'active' : ''">{{ n + 1 }}</a>
</li>
</ul>
</div>
</body>
<script src="js/vue.js"></script>
<script>
var vm = new Vue({
el: '#app',
data: {
activeNumber: 1,
pageCount: 10
}
})
</script>
</html>

v-on指令

v-on指令用于给监听DOM事件,它的用语法和v-bind是类似的,例如监听元素的点击事件:

1
<a v-on:click="doSomething">

有两种形式调用方法:绑定一个方法(让事件指向方法的引用),或者使用内联语句。
Greet按钮将它的单击事件直接绑定到greet()方法,而Hi按钮则是调用say()方法。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title></title>
</head>
<body>
<div id="app">
<p><input type="text" v-model="message"></p>
<p>
<!--click事件直接绑定一个方法-->
<button v-on:click="greet">Greet</button>
</p>
<p>
<!--click事件使用内联语句-->
<button v-on:click="say('Hi')">Hi</button>
</p>
</div>
</body>
<script src="js/vue.js"></script>
<script>
var vm = new Vue({
el: '#app',
data: {
message: 'Hello, Vue.js!'
},
// 在 `methods` 对象中定义方法
methods: {
greet: function() {
// // 方法内 `this` 指向 vm
alert(this.message)
},
say: function(msg) {
alert(msg)
}
}
})
</script>
</html>

v-bind和v-on的缩写

Vue.js为最常用的两个指令v-bind和v-on提供了缩写方式。v-bind指令可以缩写为一个冒号,v-on指令可以缩写为@符号。

1
2
3
4
5
6
7
8
9
<!--完整语法-->
<a href="javascripit:void(0)" v-bind:class="activeNumber === n + 1 ? 'active' : ''">{{ n + 1 }}</a>
<!--缩写语法-->
<a href="javascripit:void(0)" :class="activeNumber=== n + 1 ? 'active' : ''">{{ n + 1 }}</a>

<!--完整语法-->
<button v-on:click="greet">Greet</button>
<!--缩写语法-->
<button @click="greet">Greet</button>

Hexo(二):关联 github

发表于 2018-10-23 | 更新于 2018-11-14 | 分类于 hexo | 阅读次数:
本文字数: 2.1k | 阅读时长 ≈ 2 分钟

创建仓库

登录你的Github帐号,新建仓库,名为用户名.github.io固定写法,如victoryofymk.github.io

修改blog配置

本地的blog文件夹下内容为:

1
2
3
4
5
6
7
_config.yml 
db.json
node_modules
package.json
scaffolds
source
themes

终端cd到blog文件夹下,vim打开_config.yml,命令如下:

1
$ vim _config.yml

打开后往下滑到最后,修改成下边的样子:

1
2
3
4
deploy:
type: git
repository: https://github.com/victoryofymk/victoryofymk.github.io.git
branch: master

将repository后victoryofymk换成你自己的用户名

注意:在配置所有的_config.yml文件时(包括theme中的),在所有的冒号:后边都要加一个空格,否则执行hexo命令会报错

则执行hexo deploy命令时终端会提示你输入Github的用户名和密码,即

1
2
Username for 'xxx':
Password for 'xxx':

hexo deploy命令执行成功后,浏览器中打开网址http://victoryofymk.github.io(将victoryofymk换成你的用户名)能看到和打开http://localhost:4000时一样的页面。

注意:若执行命令hexo deploy仍然报错:无法连接git或找不到git,则执行如下命令来安装hexo-deployer-git:

1
$ npm install hexo-deployer-git --save

再次执行hexo deploy命令。

未避免每次输入Github用户名和密码的麻烦,可参照下一节

添加ssh key到Github

1.检查SSH keys是否存在Github
执行如下命令,检查SSH keys是否存在。如果有文件id_rsa.pub或id_dsa.pub,则直接进入步骤3将SSH key添加到Github中,否则进入下一步生成SSH key。

1
$ ls -al ~/.ssh

2.生成新的ssh key
执行如下命令生成public/private rsa key pair,注意将your_email@example.com换成你自己注册Github的邮箱地址。

1
$ ssh-keygen -t rsa -C "your_email@example.com"

默认会在相应路径下(~/.ssh/id_rsa.pub)生成id_rsa和id_rsa.pub两个文件。
3.将ssh key添加到Github中
Find前往文件夹~/.ssh/id_rsa.pub打开id_rsa.pub文件,里面的信息即为SSH key,将这些信息复制到Github的Add SSH key页面即可。

1
进入Github --> Settings --> SSH keys --> add SSH key:

Title里任意添一个标题,将复制的内容粘贴到Key里,点击下方Add key绿色按钮即可。

将个人博客同时部署到 GitHub 和 Coding

1、首先到 Coding 上注册并开一个项目,项目名称和用户个性后缀相同(方便二级域名访问博客),拿到项目的 https 地址
2、打开本地 blog 目录下的 _config.yml 文件,修改如下

1
2
3
4
5
6
deploy:
type: git
repository:
github: https://github.com/xxx/xxx.github.io.git
coding: https://git.coding.net/xxx/xxx.git
branch: master

3、cd 到本地 blog/source 目录下执行如下命令新建 Staticfile 文件

1
$ touch Staticfile  #名字必须是Staticfile

原因是 coding.net 需要以这个文件来作为静态文件部署的标志,就是说看到这个 Staticfile 就知道按照静态文件来发布。
4、执行发布命令

1
hexo g 、 hexo d

5、个人域名添加两条 CNAME 解析。将 xxx.github.io. 解析为 [海外] ,将 xxx.coding.me. 解析为 [默认]
这样就是为了从国内访问 xxx.com 就是访问 Coding 上的博客项目,从国外访问 xxx.com 就是访问 GitHub 上的博客项目。
6、到 Coding 上的博客项目主页,点击 Pages服务 输入部署分支 master 立即开启

这样就可以访问自己在 Coding 上的个人博客了

Hexo(三):创建分类页面

发表于 2018-10-23 | 更新于 2018-11-14 | 分类于 hexo | 阅读次数:
本文字数: 538 | 阅读时长 ≈ 1 分钟

创建分类

添加一个 分类 页面,并在菜单中显示页面链接。

  1. 新建一个页面,命名为 categories 。命令如下:

    1
    hexo new page categories
  2. 编辑刚新建的页面,将页面的类型设置为 categories ,主题将自动为这个页面显示所有分类。

    1
    2
    3
    4
    title: 分类
    date: 2014-12-22 12:39:04
    type: "categories"
    ---

    注意:如果有启用多说 或者 Disqus 评论,默认页面也会带有评论。需要关闭的话,请添加字段 comments 并将值设置为 false,如:

    1
    2
    3
    4
    5
    title: 分类
    date: 2014-12-22 12:39:04
    type: "categories"
    comments: false
    ---
  3. 在菜单中添加链接。编辑主题的 _config.yml ,将 menu 中的 categories: /categories注释去掉,如下:

    1
    2
    3
    4
    5
    menu:
    home: / || home
    about: /about/ || user
    tags: /tags/ || tags
    categories: /categories/ || th

Hexo(一):安装 hexo

发表于 2018-10-23 | 更新于 2018-11-14 | 分类于 hexo | 阅读次数:
本文字数: 766 | 阅读时长 ≈ 1 分钟

常用依赖

• Hexo - 快速、简洁且高效的博客框架
• Github Pages - 完美的静态博客服务器(除大陆网速慢外)
• NexT - 一款高质量且简洁优雅的Hexo主题
• Travis CI - 一个持续集成测试工具

安装依赖

搭建环境:macos

1.安装node

1
Brew install node

2.安装git

1
brew install git

3.安装 hexo

1
npm install -g hexo

初始化

终端cd到一个你选定的目录,执行hexo init命令:

1
$ hexo init blog

blog是你建立的文件夹名称。cd到blog文件夹下,执行如下命令,安装npm:

1
$ npm install

执行如下命令,开启hexo服务器:

1
$ hexo s

此时,浏览器中打开网址http://localhost:4000

基本操作

终端cd到blog文件夹下,执行如下命令新建文章:

1
2
#名为postName.md的文件会建在目录/blog/source/_posts下
hexo new "postName"

在blog文件夹目录下执行生成静态页面命令:

1
$ hexo generate     或者:hexo g

此时若出现如下报错:

1
2
ERROR Local hexo not found in ~/blog
ERROR Try runing: 'npm install hexo --save'

则执行命令:

1
$ npm install hexo --save

若无报错,自行忽略此步骤。

重新部署

hexo clean:清空public
hexo g:静态文件重新生成
hexo d:部署

hexo generate hexo deploy 简写 hexo g -d

Hexo(四):安装 theme next

发表于 2018-10-23 | 更新于 2018-11-14 | 分类于 hexo | 阅读次数:
本文字数: 848 | 阅读时长 ≈ 1 分钟

安装theme

你可以到Hexo官网主题页去搜寻自己喜欢的theme。这里以hexo-theme-next为例

终端cd到 blog 目录下执行如下命令:

1
$ git clone https://github.com/theme-next/hexo-theme-next themes/next

将blog目录下_config.yml里theme的名称landscape修改为next
终端cd到blog目录下执行如下命令(每次部署文章的步骤):

1
2
3
$ hexo clean           //清除缓存文件 (db.json) 和已生成的静态文件 (public)
$ hexo g //生成缓存和静态文件
$ hexo d //重新部署到服务器

至于更改theme内容,比如名称,描述,头像等去修改blog/_config.yml文件和blog/themes/next/_config.yml文件中对应的属性名称即可, 不要忘记冒号:后加空格。 NexT 使用文档里有极详细的介绍。

升级 nexT 主题,cd 到 blog/themes/next/ 下执行命令 git pull 更新

设置语言

NexT 目前支持六种语言版本:
• English
• 中文简体 (zh-Hans)
• French (fr-FR)
• 正体中文 (zh-hk/zh-tw)
• Russian (ru)
• German (de)
默认语言是英文。编辑站点的_config.yml,将language字段更改为你所需要的语言版本代号:
language: default #默认是英文

1
2
3
4
5
6
7
language: zh-CN
language: fr-FR
language: zh-HK
language: zh-TW
language: en
language: ru
language: de

设置文章目录

Toc true

设置归档

Hexo(十):使用中的问题

发表于 2018-10-23 | 更新于 2018-11-14 | 分类于 hexo | 阅读次数:
本文字数: 625 | 阅读时长 ≈ 1 分钟

常见问题

Template render error

解析异常,通常在hexo g的时候出现,通常是某些代码被解析

1
2
Template render error: (unknown path)
Error: template not found: ./comments/livere.swig

如果让jekyll不解析,使用raw语法,中间写内容

1
2
3
4
5
6

{% raw %}

{% sometag %}

{% endraw %}

Found incompatible module

使用yarn管理依赖

1
error nunjucks@3.1.3: The engine "node" is incompatible with this module. Expected version ">= 6.9.0 <= 11.0.0-0". Got "11.0.0"

需要将node降级为7,在travis.yml中修改

1
2
# node版本
node_js: "7"

gitalk授权问题

使用github issue实现hexo评论功能,登陆时无法跳转到有效链接

解决办法:检查MD文件名称中是否有中文字符,例如:中文冒号:,如果有去掉即可,因为跳转时会转换成英文字符,所以路径就找不到,空格也可能有问题

Hexo(八):添加其他功能

发表于 2018-10-23 | 更新于 2018-11-14 | 分类于 hexo | 阅读次数:
本文字数: 606 | 阅读时长 ≈ 1 分钟

为Hexo搜索与统计

搜索与统计都比较简单,官方文档有详尽的明细,统计推荐不蒜子,简单粗暴。
搜索的话我使用的是本地搜索,即Local Search。他的原理是在你本地生成一个xml文件,搜索的时候对这个文件进行检索。下面说说安装步骤
1.执行下面2个命令

1
2
npm install hexo-generator-search --save
npm install hexo-generator-searchdb --save

2.打开站点配置文件,新增以下内容:

1
2
3
4
5
search:
path: search.xml
field: post
format: html
limit: 10000

3.打开主题配置文件,启用本地搜索功能:

1
2
3
# Local search
local_search:
enable: true

图片

先来看看hexo官方文档资源文件夹

设置好了以后每次你通过 hexo new blognames 在source文件夹下生成一个blognames.md文件以后会自动的生成一个文件夹,该文件夹也是以blognames命名,此时将你想要插入到md中的图片放进去,例如:我放了一个icon.jpg,name你就可以在md中这样引用了
图片描述
注意:按照官方的文档,引用的格式应该是:图片描述,其实不然,因为生成的相应的blognames博文和该资源位于同一个文件夹下,因此还是上面的引用方式是正确的

nginx项目实践(四)

发表于 2017-09-19 | 更新于 2018-11-14 | 分类于 nginx教程 | 阅读次数:
本文字数: 0 | 阅读时长 ≈ 1 分钟
123
执行者

执行者

活出自我

22 日志
4 分类
8 标签
RSS
GitHub E-Mail
© 2018 执行者 | 88k
由 Hexo 强力驱动 v3.8.0
|
主题 – NexT.Muse v6.4.2