
一、题目描述:
给定一个无重复元素的有序整数数组 nums 。
返回 恰好覆盖数组中所有数字 的 最小有序 区间范围列表。也就是说,nums 的每个元素都恰好被某个区间范围所覆盖,并且不存在属于某个范围但不属于 nums 的数字 x 。
列表中的每个区间范围 [a,b] 应该按如下格式输出:
- "a->b" ,如果 a != b
- "a" ,如果 a == b
示例
示例 1:
>输入:nums = [0,1,2,4,5,7]
输出:["0->2","4->5","7"]
解释:区间范围是:
[0,2] --> "0->2"
[4,5] --> "4->5"
[7,7] --> "7"
示例 2:
>输入:nums = [0,2,3,4,6,8,9]
输出:["0","2->4","6","8->9"]
解释:区间范围是:
[0,0] --> "0"
[2,4] --> "2->4"
[6,6] --> "6"
[8,9] --> "8->9"
示例 3:
>输入:nums = []
输出:[]
示例 4:
>输入:nums = [-1]
输出:["-1"]
示例 5:
>输入:nums = [0]
输出:["0"]
提示:
>0 <= nums.length <= 20
-231 <= nums[i] <= 231 - 1
nums 中的所有值都 互不相同
nums 按升序排列
二、解答
分析
解题思路:
1. 遍历整个数组,当相邻的数只相差1时,构成一个区间。
2. 当相邻的数相差大于1时,开始一个新的区间。
3. 当判断开始一个新的区间时,我们需要保存前面的区间。
4. 保存一个区间需要两个值分别记录区间开始的值,当前数的前一个数的值。
5. 唯一值得注意的是处理边界问题。
>时间复杂度:O(n),其中 n 为数组的长度。我们只需要遍历一次数组即可。
空间复杂度:O(1)。只需要常数空间存放若干变量。
代码
```cpp
class Solution {
public List<String> summaryRanges(int[] nums) {
int length = nums.length;
List<String> result = new ArrayList<>();
if(length == 0){
return result;
}
int start = nums[0];
int now = nums[0];
int i = 1;
for(;i < length;i++){
if(now + 1 == nums[i]){
now++;
} else{
result.add(start == now?String.valueOf(start):start+"->"+now);
start = now = nums[i];
}
}
result.add(start == now?String.valueOf(start):start+"->"+now);
return result;
}
}
```
> 执行用时:7 ms, 在所有 Java 提交中击败了82.54%的用户
内存消耗:36.9 MB, 在所有 Java 提交中击败了53.76%的用户
改进
1. 因为当前数的前一个数的值,我可以通过当前的下标来获取,所以该值完全没有必要记录,所以可以省略该值
2. 省略该值之后,我只要通过下标来判断即可,所以在循环里,我只要判断前一个数加一与当前值不相等即可
```java
public List<String> summaryRanges(int[] nums) {
int length = nums.length;
List<String> result = new ArrayList<>();
if(length == 0){
return result;
}
int start = nums[0];
int i = 1;
for(;i < length;i++){
if(nums[i - 1] + 1 != nums[i]){
result.add(start == nums[i - 1]?String.valueOf(start):start+"->"+nums[i - 1]);
start = nums[i];
}
}
result.add(start == nums[i - 1]?String.valueOf(start):start+"->"+nums[i - 1]);
return result;
}
```
这样做的好处就是节省了一定的空间。
三、官方解答
官方给出的题解也是一次遍历,但是是用双指针的方法,也就是使用维护下标low 和 high 分别记录区间的起点和终点,和我的大致思想也差不多。
```cpp
class Solution {
public List<String> summaryRanges(int[] nums) {
List<String> ret = new ArrayList<String>();
int i = 0;
int n = nums.length;
while (i < n) {
int low = i;
i++;
while (i < n && nums[i] == nums[i - 1] + 1) {
i++;
}
int high = i - 1;
StringBuffer temp = new StringBuffer(Integer.toString(nums[low]));
if (low < high) {
temp.append("->");
temp.append(Integer.toString(nums[high]));
}
ret.add(temp.toString());
}
return ret;
}
}
```
> 参考:
> 1、[题目](https://leetcode-cn.com/problems/summary-ranges/)
> 2、[官方解答](https://leetcode-cn.com/problems/summary-ranges/solution/hui-zong-qu-jian-by-leetcode-solution-6zrs/)
>本文首发于CSDN,作者:lomtom
原文链接:**[https://blog.csdn.net/lomtom/article/details/112306554](https://blog.csdn.net/lomtom/article/details/112306554)**
个人网站:**[https://lomtom.top](https://lomtom.top)**,公众号:**博思奥园**,同步更新。
你的支持就是我最大的动力。


一、题目描述:
给定一个数组,它的第 i 个元素是一支给定股票第 i 天的价格。
设计一个算法来计算你所能获取的最大利润。你可以尽可能地完成更多的交易(多次买卖一支股票)。
注意:你不能同时参与多笔交易(你必须在再次购买前出售掉之前的股票)。
示例
示例 1:
> **输入:** [7,1,5,3,6,4] 输出: 7
> **解释:** 在第 2 天(股票价格 = 1)的时候买入,在第 3 天(股票价格 =5)的时候卖出, 这笔交易所能获得利润 = 5-1 = 4 。
> 随后,在第 4 天(股票价格 = 3)的时候买入,在第 5 天(股票价格 = 6)的时候卖出, 这笔交易所能获得利润 = 6-3 = 3 。
示例 2:
> **输入:** [1,2,3,4,5] 输出: 4
> **解释:** 在第 1 天(股票价格 = 1)的时候买入,在第 5 天 (股票价格 = 5)的时候卖出, 这笔交易所能获得利润 = 5-1 = 4 。
> 注意你不能在第 1 天和第 2 天接连购买股票,之后再将它们卖出。
> 因为这样属于同时参与了多笔交易,你必须在再次购买前出售掉之前的股票。
示例 3:
> **输入:** [7,6,4,3,1] 输出: 0
> **解释:** 在这种情况下, 没有交易完成, 所以最大利润为 0。
提示:
> 1 <= prices.length <= 3 * 10 ^ 4
> 0 <= prices[i] <= 10 ^ 4
二、解答
分析
整个算法的精髓就是:**在最低点买入,在最高点卖出。** 恩,这钱真好赚。
而我们要做的就是找出n天当中的低谷和高谷。
例如七天中:7,1,5,3,6,4,找出低谷:1、3,高谷:5、6

所以,问题可以继续抽象成,我在数组中找到极小值与极大值
最终,我们可以用后一个是否比前一个小来判断,如果小我就卖出,并且在后一天买入。
这样带来的问题是,如果我最后持续上升 那么我就没办法卖出了(因为我通过后一天是否比前一天小来判断是否卖出),所以在最后再卖出一次。
>时间复杂度:O(n),其中 n 为数组的长度。我们只需要遍历一次数组即可。
空间复杂度:O(1)。只需要常数空间存放若干变量。
代码
```cpp
class Solution {
public int maxProfit(int[] prices) {
int sum = 0;
int length = prices.length;
if(length == 0){
return 0;
}
int temp = prices[0];
int i = 1;
for(;i < prices.length;i++){
//后一天是否比前一天小
if(prices[i] < prices[i - 1]){
//计算获得的利润
sum += prices[i - 1] - temp;
//重新买入
temp = prices[i];
}
}
//再卖出一次
sum += prices[i - 1] - temp;
return sum;
}
}
```
> 执行用时:1 ms, 在所有 Java 提交中击败了99.54%的用户
> 内存消耗:38 MB, 在所有 Java提交中击败了96.68%的用户
三、官方解答
官方有两种:一种是动态规划,另一种是贪心算法(比我的更简洁)
贪心:
```cpp
class Solution {
public int maxProfit(int[] prices) {
int ans = 0;
int n = prices.length;
for (int i = 1; i < n; ++i) {
ans += Math.max(0, prices[i] - prices[i - 1]);
}
return ans;
}
}
```
> 参考:
> 1、[题目](https://leetcode-cn.com/problems/best-time-to-buy-and-sell-stock-ii/)
> 2、[官方解答](https://leetcode-cn.com/problems/best-time-to-buy-and-sell-stock-ii/solution/mai-mai-gu-piao-de-zui-jia-shi-ji-ii-by-leetcode-s/)

由于业务的需求,我需要将每次写好的代码编译好后,并且通过ftp工具远程传到服务器上。
但是,这样的操作带来的问题是:整个的过程变得相当的复杂。
于是,就有了这篇文章。
我们可以这样理解:
1. 当我们把代码提交到github上时
2. github发起一次请求给服务器
3. 服务器接受请求后,执行拉取git的脚本
这样就实现了整个项目的自动部署。

准备
你需要安装**yum、git、go**
如果你已经安装过,那么你就可以跳过相应的步骤
并且默认你会ssh秘钥配对,如果不会请翻到最后。
一、安装yum
```cpp
wget http://yum.baseurl.org/download/3.2/yum-3.2.28.tar.gz
tar -xvf yum-3.2.28.tar.gz
touch /etc/ yum.conf
cd yum-3.2.28
./yummain.py install yum
```
二、安装git
```cpp
yum install -y git
git --version
```
三、安装Go
```cpp
yum install -y golang
```
四、安装Webhook
```cpp
1、使用go 安装
go get github.com/adnanh/webhook
2、使用apt安装
sudo apt-get install webhook
```
开源项目地址:https://github.com/adnanh/webhook
五、配置服务器
1、在一个目录下克隆github项目
我的在/root/test下拉取项目,项目名也叫test(拉取的项目存在于/root/test/test)
2、在同级目录新建hooks.json(位于/root/test/hooks.json)
>定义一些需要webhook服务的钩子。首先创建一个名为hooks.json. 此文件将包含webhook将提供的钩子数组。查看钩子定义页面,查看钩子可以包含哪些属性以及如何使用它们的详细描述。
```cpp
[
{
"id": "deploy",
"execute-command": "./deploy.sh",
"command-working-directory": "/root/test/"
}
]
```
3、新建你要执行的shell脚本,可以直接执行看脚本是否可用`./deploy.sh` 或者 `sh deploy.sh`
这里为了测试,只有拉取,实际应该比这更复杂
delploy.sh(位于/root/test/deploy.sh)
```cpp
! /bin/bash
cd /root/test/test
git pull
```
六、运行
运行webhook,默认端口9000,所以我们需要开放9000端口。
```cpp
/root/go/bin/webhook -hooks hooks.json -verbose
```

在浏览器中访问,控制台即可打印相应日志
这里的deploy与你上方hooks.json文件中的id一致
```cpp
http://ip:9000/hooks/deploy
```

七、配置github
如果上方能够测试成功,那么直接复制url到下方配置中,配置好后他会自动发起一次请求。

八、设置后台运行
使用nohup来使我们的webhook后台运行并且打印日志:log.txt 为存放日志的文件
```cpp
[root@master test] nohup /root/go/bin/webhook -hooks hooks.json -verbose >log.txt 2>&1&
[1] 3064
```
九、愉快玩耍
然后你就可以在自己电脑上写代码,写好后,他自己就会部署,爽歪歪。

> 参考:
> 1、[linux下yum](https://blog.csdn.net/iamhuanggua/article/details/60140867)
> 2、[git生成证书](http://www.iimt.me/article/34)
>3、 [钩子自动部署](https://blog.csdn.net/enoch612/article/details/105763647)
>4、[webhook](https://github.com/adnanh/webhook)
>5、[linux后台运行的几种方式](https://www.cnblogs.com/zsql/p/10827587.html)

**每天一个小知识**,不定期更新

一、问题
之前有人问我这样一个问题:
如果你运行下面的代码,你会得到什么?
```java
Integer a = 200, b = 200;
Integer c = 100, d = 100;
System.out.println(a == b);
System.out.println(c == d);
```
你会得到
```clike
false
true
```
为什么 Java 中`200==200`为false,而`100==100`为true?
> 答案只有一个:那就是200没有100帅气,就像正在看这篇文章的你一样没有写这篇文章的我一样帅气。
二、分析
**基本知识:我们知道,如果两个引用指向同一个对象,用`==`表示它们是相等的。如果两个引用指向不同的对象,用`==`表示它们是不相等的,即使它们的内容相同。**
因此,后面一条语句也应该是false 。
这就是它有趣的地方了。如果你看去看 Integer.java 类,你会发现有一个内部私有类,IntegerCache.java,它缓存了从-128到127之间的所有的整数对象。
所以事情就成了,所有的小整数在内部缓存,然后当我们声明类似——
```clike
Integer c = 100;
```
的时候,它实际上在内部做的是:
```clike
Integer i = Integer.valueOf(100);
```
现在,如果我们去看valueOf()方法,我们可以看到
```java
/**
* Returns an {@code Integer} instance representing the specified
* {@code int} value. If a new {@code Integer} instance is not
* required, this method should generally be used in preference to
* the constructor {@link Integer(int)}, as this method is likely
* to yield significantly better space and time performance by
* caching frequently requested values.
*
* This method will always cache values in the range -128 to 127,
* inclusive, and may cache other values outside of this range.
*
* @param i an {@code int} value.
* @return an {@code Integer} instance representing {@code i}.
* @since 1.5
*/
public static Integer valueOf(int i) {
if (i >= IntegerCache.low && i <= IntegerCache.high)
return IntegerCache.cache[i + (-IntegerCache.low)];
return new Integer(i);
}
```
如果值的范围在-128到127之间,它就从高速缓存返回实例。
所以…
```java
Integer c = 100, d = 100;
```
指向了同一个对象。
这就是为什么我们写
```java
System.out.println(c == d);
```
我们可以得到true。
现在你可能会问,为什么这里需要缓存?
**合乎逻辑的理由是,在此范围内的“小”整数使用率比大整数要高,因此,使用相同的底层对象是有价值的,可以减少潜在的内存占用。**
然而,通过反射API你会误用此功能。
运行下面的代码,享受它的魅力吧
```java
public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException {
Class cache = Integer.class.getDeclaredClasses()[0]; //1
Field myCache = cache.getDeclaredField("cache"); //2
myCache.setAccessible(true);//3
Integer[] newCache = (Integer[]) myCache.get(cache); //4
newCache[132] = newCache[133]; //5
int a = 2;
int b = a + a;
System.out.printf("%d + %d = %d", a, a, b); //
}
```
三、结论
**Integer 缓存是 Java 5 中引入的一个有助于节省内存、提高性能的特性。**
**Integer中有个静态内部类IntegerCache,里面有个cache[],也就是Integer常量池,常量池的大小为一个字节(-128~127)。**
**这种 Integer 缓存策略仅在自动装箱的时候有用,使用构造器创建的 Integer 对象不能被缓存。(例如in3和in4)**
```java
int i = 10;
int i1 = 10;
Integer in1 = 10;
Integer in2 = 10;
Integer in3 = new Integer(10);
Integer in4 = new Integer(10);
Integer in5 = 200;
Integer in6 = 200;
System.out.println(i == i1); // true
System.out.println(i == in1); // true
System.out.println(i == in2); // true
System.out.println(i == in3); // true
System.out.println(in1 == in2); // true
System.out.println(in5 == in6); // false
System.out.println(in1 == in3); // false
System.out.println(in3 == in4); // false
```
除此之外:
- 所有整数类型的类都有类似的缓存机制:
1、有 ByteCache 用于缓存 Byte 对象
2、有 ShortCache 用于缓存 Short 对象
3、有 LongCache 用于缓存 Long 对象
- Byte,Short,Long 的缓存池范围默认都是: -128 到 127。可以看出,Byte的所有值都在缓存区中,用它生成的相同值对象都是相等的。
- 所有整型(Byte,Short,Long)的比较规律与Integer是一样的。
- 同时Character 对象也有CharacterCache 缓存 池,范围是 0 到 127。
- 除了 Integer 可以通过参数改变范围外,其它的都不行。
> [1、为什么Java中1000`==`1000为false而100`==`100为true?原文](http://www.codeceo.com/article/why-java-1000-100.html)
> [2、为什么Java中1000`==`1000为false而100`==`100为true?英文](https://dzone.com/articles/why-1000-1000-returns-false-but-100-100-returns-tr)
> [3、Integer缓存池(IntegerCache)及整型缓存池](https://blog.csdn.net/maihilton/article/details/80101497)
关注公众号:**博奥思园**
还是那句话:**你的支持是我前进的最大动力**


概况
在处理后端的业务逻辑是常常会涉及表单数据的提交请求,我们不仅在前端对数据进行验证,而且在后端也需要对数据进行验证,以此来保证数据的完整性,而后端对于表单数据的验证使用的最多的莫过于JSR303。
你能get到的知识点?
1、表单验证的使用
2、由于表单验证引起的异常捕获
1、引入依赖
使用JSR303,我们需要引入依赖,一般来说我们只需要引入`javax.validation`即可,但是对于一些`javax.validation`无法验证的(例如URL)我们就需要引入`hibernate`来进行验证了。
```xml
<dependency>
<groupId>javax.validation</groupId>
<artifactId>validation-api</artifactId>
<version>2.0.1.Final</version>
</dependency>
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-validator</artifactId>
<version>6.0.18.Final</version>
<scope>compile</scope>
</dependency>
```
2、自定义验证规则
自定义验证规则,最方便的莫过于使用注解的方式对我们的bean进行验证,并且可以返回我们自己定义的返回消息。
JSR注释:
- @NotNull –验证带注释的属性值不为 null
- @AssertTrue –验证带注释的属性值为 true
- @Size –验证带注释的属性值的大小介于属性 min和 max之间;可以应用于 String, Collection, Map和数组属性
- @Min – v验证带注释的属性的值不小于 value属性
- @Max –验证带注释的属性的值不大于 value属性
- @Email –验证带注释的属性是有效的电子邮件地址
一些注释接受其他属性,但是message属性是所有这些属性共有的。这是通常在相应属性的值未通过验证时呈现的消息。
在JSR中可以找到一些其他注释:
- @NotEmpty –验证属性不为null或为空;可以应用于 String, Collection, Map或 Array值
- @NotBlank –只能应用于文本值,并验证该属性不是null还是空格
- @Positive和 @PositiveOrZero –适用于数值并验证其严格为正,或包含0的正数
- @Negative和 @NegativeOrZero –适用于数字值并验证其严格为负数,或包含0的负数
- @Past和 @PastOrPresent –验证日期值是过去还是现在(包括现在);可以应用于日期类型,包括Java 8中添加的日期类型
- @Future和@FutureOrPresent –验证日期值是将来的日期还是将来的日期(包括现在)
```java
@Data
@TableName("pms_brand")
public class BrandEntity implements Serializable {
private static final long serialVersionUID = 1L;
/**
* 品牌id
*/
@TableId
private Long brandId;
/**
* 品牌名
*/
@NotEmpty(message = "品牌名不能为空")
private String name;
/**
* 品牌logo地址
*/
@URL(message = "必须是一个合法的地址")
private String logo;
/**
* 介绍
*/
@NotEmpty(message = "介绍不能为空")
private String descript;
/**
* 显示状态[0-不显示;1-显示]
*/
private Integer showStatus;
/**
* 检索首字母
*/
@NotEmpty(message = "检索首字母不能为空")
@Pattern(regexp = "/^[a-zA-Z]$/",message = "检索必须是一个字母")
private String firstLetter;
/**
* 排序
*/
@NotNull(message = "排序不能为空")
@Min(value = 0,message = "排序的数必须大于等于零")
private Integer sort;
}
```
3、校验捕获异常错误
第一种:controller捕获
在对bean进行验证后,我们需要捕获我们的验证结果。
1. @Valid:首先使用`@Valid` 为验证级联标记属性、方法参数或方法返回类型。也就是说我们使用这个注解后验证才生效。
2. BindingResult:在验证的bean后紧跟BindingResult,用于获取我们的验证结果,使用`result.hasErrors()`判断是否有异常,使用 `result.getFieldErrors()`获取验证后的详细数据
3. R:我们常常使用JSon数据来进行前后端的数据发送与接收,这里同理,该R为自定的类,如果你不想写自定义的消息类,你可以直接用JSONObject进行数据的保存。
```java
/**
* 保存
*/
@RequestMapping("/save")
public R save(@Valid @RequestBody BrandEntity brand, BindingResult result){
Map<String,String> map = new LinkedHashMap<>();
if (result.hasErrors()){
result.getFieldErrors().forEach(item ->{
String message = item.getDefaultMessage();
String field = item.getField();
map.put(field, message);
});
return R.error(400,"数据不合法").put("data",map);
}else {
brandService.save(brand);
return R.ok();
}
}
```
第二种:统一异常捕获
所有的controller制作厂的逻辑处理,我们则需要使用一个统一的controller进行所有异常的捕获
```csharp
1、正常的控制器处理正常的逻辑
/**
* 保存
*/
@RequestMapping("/save")
public R save(@Valid @RequestBody BrandEntity brand){
brandService.save(brand);
return R.ok();
}
2、编写一个controllerAdvice进行异常统一处理
@RestControllerAdvice("com.lomtom.mall.product.controller")
public class ExceptionController {
@ExceptionHandler(value = MethodArgumentNotValidException.class)
public R handleValidException(MethodArgumentNotValidException e){
BindingResult result = e.getBindingResult();
Map<String,String> map = new LinkedHashMap<>();
result.getFieldErrors().forEach(item -> {
String message = item.getDefaultMessage();
String field = item.getField();
map.put(field, message);
});
return R.error(400, "数据不合法").put("data", map);
}
}
```
第三种:统一处理配合枚举
与第二种同理,只是新增枚举统一管理异常的状态码与消息提醒,翻遍代码的修改与查看
```csharp
1、编写枚举
public enum ExceptionEnum {
DATA_EXCEPTION(400,"数据不合法");
private Integer code;
private String message;
ExceptionEnum(Integer code, String message) {
this.code = code;
this.message = message;
}
public Integer getCode() {
return code;
}
public void setCode(Integer code) {
this.code = code;
}
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
}
2、与第二种唯一不同的是所有的状态码与消息提示交给枚举
return R.error(ExceptionEnum.DATA_EXCEPTION.getCode(), ExceptionEnum.DATA_EXCEPTION.getMessage()).put("data", map);
```
4、测试
发送请求获取返回,所有的返回结果都一样,只是实现的方式稍有不同而已。
```json
{
"msg": "数据不合法",
"code": 400,
"data": {
"name": "品牌名不能为空",
"descript": "介绍不能为空",
"sort": "排序不能为空",
"firstLetter": "检索首字母不能为空"
}
}
```

对于经常需要发博客的小伙伴来说,拥有一个属于自己的博客网站,听起来是不是很酷。
今天我就来告诉大家,怎么搭建一个属于自己的博客网站,我们需要的就是使用**hexo+github**来搭建我们自己博客系统。
你能学到什么?
> 1. 轻松搭建自己的博客网站
> 2. hexo的基本写作
什么是Hexo?
Hexo是一个快速,简单且功能强大的博客框架。相信经常用Markdown写文章的人肯定不会陌生,使用Markdown(或其他标记语言)编写帖子,然后Hexo会在几秒钟内生成带有精美主题的静态文件。
什么是github?
GitHub是一个面向开源及私有软件项目的托管平台,因为只支持git作为唯一的版本库格式进行托管,故名GitHub。
GitHub于2008年4月10日正式上线,除了Git代码仓库托管及基本的 Web管理界面以外,还提供了订阅、讨论组、文本渲染、在线文件编辑器、协作图谱(报表)、代码片段分享(Gist)等功能。目前,其注册用户已经超过350万,托管版本数量也是非常之多,其中不乏知名开源项目 Ruby on Rails、jQuery、python 等。
为什么选择hexo和github
- 1、全是静态文件,不需要书写自己的后台逻辑,访问速度快
- 2、免费方便,不用花一分钱就可以搭建一个自己的个人博客
- 3、可以集成很多的插件,只需要简单配置
- 4、样式多样可选,hexo有很多主题可供用户选择
- 5、自定义域名,可以绑定自己的域名
- 6、数据绝对安全,基于github的版本管理,历史版本可随意恢复
- 7、数据容易迁移
@[TOC]
一:准备
安装Hexo非常容易,并且只需要以下内容:
- Node.js(至少应为Node.js 8.10,建议为10.0或更高版本)
- git
如果您的计算机已经有这些,恭喜!您可以直接跳到Hexo安装步骤。
如果没有,请按照以下说明安装所有要求。
1、安装git
下载:[传送门](https://gitforwindows.org/)
2、安装NodeJs
下载:[传送门](https://nodejs.org/en/)
唯一需要注意的是请确保已选中**添加到PATH**(默认情况下已选中)。
3、查看git和node版本:

4、安装hexo(使用npm)
使用npm i -g hexo来安装,一步到位,查看hexo版本。

二、搭建博客
1、创建仓库
前提是你的有一个自己的github账号,这年头,谁没有个github账号。
注意:创建一个名为username .github.io的存储库,其中username是您在GitHub上的用户名。如果您已经上传到其他存储库,请重命名该存储库。
例如我的github名字是zero028,那么我的仓库名就是zero028.github.io,因为我写这篇文章的时候,我已经创建过了,所以他会报错已存在。

2、配置ssh
如果你要使用远程从你的电脑上传文件至你的github仓库,那么,你就需要配置ssh
```
1、配置全局变量
git config --global user.name "你自己的名字"
git config --global user.email "你自己的邮箱"
2、生成ssh密钥
ssh-keygen -t rsa -C "你自己的邮箱"
```
将你用户目录下`.ssh/id_rsa.pub`里的全部东西粘贴到key里面,名字随便取。
`id_rsa.pub`一般windows会在`C:\Users\用户名\.ssh`目录下

验证:输入`ssh -T git@github.com`,如果出现以下信息即为配置成功,到这里你已经成功了一大半了。

2、博客初始化
在一个空的文件夹内打开cmd,使用`hexo init` 进行初始化,他会下载一大堆东西。

```
目录结构:
.
├── _config.yml
├── package.json
├── scaffolds
├── source
| ├── _drafts
| └── _posts
└── themes
```
说明:
- node_modules:是依赖包
- public:存放的是生成的页面
- scaffolds:命令生成文章等的模板
- source:用命令创建的各种文章
- themes:博客使用的主题
- _config.yml:整个博客的配置
- db.json:source解析所得到的
- package.json:项目所需模块项目的配置信息
3、博客生成
只需要三句话你就能看到你的博客
```
1、清除
hexo clean
2、生成
hexo g
3、启动服务
hexo server
```

这时候你打开,http://localhost:4000,当当当当,那么你就大功告成了。到这里,你看一下你的watch,有没有一个小时,如果超过了的话,当我前面没说(手动狗头)。

4、上传至github
当然,如果只能自己看到,这远远是不够的,我们发博客就是为了让我们的文章能够帮助到更多人,这时候你就需要上传到github进行托管,这样别人就可以访问到你的博客,看到你的文章了。
你需要在你的根目录下的_config.yml配置
```yml
Deployment
Docs: https://hexo.io/docs/deployment.html
deploy:
type: git
repo: https://github.com/zero028/zero028.github.io.git(你自己的git)
branch: master
```
然后使用`hexo d` 或者 `hexo deploy`上传,它实现的原理就是将您的Hexo文件夹的文件推送到存储库。public/默认情况下,该文件夹不是(也不应该)上传的,请确保该.gitignore文件包含public/行。文件夹结构应与此存储库大致相似,但不包含.gitmodules文件
```
1、在此之前请先安装一个插件
npm install hexo-deployer-git --save
2、部署(上传到GitHub)
hexo d 或者 hexo deploy
```
最终,你可以使用你的https://username.github.io访问,例如我的是https://zero028.github.io,咦,我的怎么和你的不一样,那是我配置了域名和使用了其他的主题,如果你想知道我是怎么设置的,请持续关注,谢谢。

三:写作
1、创建新文章
要创建新帖子或新页面,可以运行以下命令:
```
$ hexo new [layout] <title>
例如
$ hexo new hello
INFO Created: D:\Projects\HEXO\text\source\_posts\hello.md
```
他就会在`source/_posts`目录下生成一个md文件`hello.md`
post是默认设置layout,但您可以提供自己的。您可以通过在中编辑`default_layout`设置来更改默认布局`_config.yml`。
2、语法
前题是文件开头的YAML或JSON块,用于配置作品的设置。使用YAML编写时,前题以三个破折号结尾,而使用JSON编写时,则以三个分号结尾。
```
---
title: hello
date: 2020-04-07 19:12:39
---
正文。。。。。
```
设置及其默认值:
设置 | 描述 | 默认
--|--|--
layout| 布局
title |标题| 文件名(仅帖子)
date | 发布日期| 文件创建日期
updated | 更新日期 | 文件更新日期
comments| 为帖子启用评论功能 |true
tags | 标签(不适用于页面)
categories| 类别(不适用于页面)
permalink| 覆盖帖子的默认永久链接
keywords | 仅在meta标签和Open Graph中使用的关键字(不推荐)
分类和标签
只有帖子支持类别和标签的使用。类别按顺序应用于职位,从而导致分类和子分类的层次结构。标签均在同一层次级别上定义,因此它们的显示顺序并不重要。
例
```
类别:
- 运动
- 棒球
标签:
- 伤害
- 搏斗
- 令人震惊
```
如果要应用多个类别层次结构,请使用名称列表而不是单个名称。如果Hexo看到在帖子上以此方式定义的任何类别,则它将该帖子的每个类别视为其自己的独立层次结构。
例
```
类别:
- [体育, 棒球]
- [美国职棒大联盟, 美国 联盟, 波士顿 红 红袜]
- [美国职棒大联盟, 美国的 同盟, 新的 纽约 洋基队]
- 对抗
```
作者有话
嗯,确实是挺简单的,前面我花了大量的时间为自己搭建了一个博客网站,从前端到后端都是自己完成,然而,实现的也只是刚好能用而已,很多的体验都不是很完善。
而使用hexo就可以轻松搭建自己的博客,而且学习成本四舍五入为零。
最后,还是那句话,你的支持就是作者最大的动力。
关注公众号:**博奥思园** ,精彩内容不错失