Skip to main content

· One min read

KMP算法

KMP算法是一种高效的字符串匹配算法,在传统暴力遍历匹配的基础上做了一定的优化。

首先,KMP算法的实现也是使用了回退思想,不过与暴力遍历不同,KMP的回退是让子串进行匹配,而不是主串。

主要思想是:当出现字符串不匹配,可以知道一部分之前已经匹配的文本内容,利用这些信息避免从头再去匹配

· 2 min read
Ma Chu

Java中的long类型是64位的,而int类型是32位的,因此在将long转换为int时,需要注意可能会发生数据截断。
如果long类型的值超出了int类型的范围,转换后的值可能会失真或者出现错误。下面是两种可能的方法来将long类型转换为int类型:

  1. 使用强制类型转换
    可以使用强制类型转换将long类型转换为int类型,但需要注意可能会出现数据截断的问题,如下所示:
long l = 1234567890L;
int i = (int) l; // 数据截断,i的值为-53922298
  1. 使用Math类的方法
    Java提供了Math类来处理数学运算,其中有一些方法可以将long类型转换为int类型,如下所示:
long l = 1234567890L;
int i = Math.toIntExact(l); // i的值为1234567890

其中,Math.toIntExact()方法会将long类型的值转换为int类型,如果long类型的值超出了int类型的范围,会抛出ArithmeticException异常。

· One min read
Ma Chu

在HTML中,可以使用CSS属性来控制文本的自动换行。其中,word-wrapwhite-space 属性可以用来实现自动换行。

word-wrap 属性可以强制文本在单词内换行,避免长单词溢出容器边界。可以将其设置为 break-word,如下所示:

span {
word-wrap: break-word;
}

white-space 属性可以控制如何处理空格和换行符。可以将其设置为 normalpre-wrap 来启用自动换行,如下所示:

span {
white-space: normal; /* 或者 pre-wrap */
}

同时,也可以使用 max-width 属性来限制span的宽度,超出的内容会自动换行。例如:

span {
max-width: 200px; /* 将span的最大宽度设置为200像素 */
}

使用这些属性可以实现span的自动换行。

· 4 min read
Ma Chu

Gradle 支持使用以下几种语言编写 build.gradle 文件:

  1. Groovy:Groovy 是 Gradle 的默认语言,也是最常用的一种。使用 Groovy 编写的 build.gradle 文件通常更加简洁易懂。

  2. Kotlin:从 Gradle 3.0 开始,Gradle 开始支持使用 Kotlin 作为 build.gradle 文件的编写语言。与 Groovy 相比,Kotlin 具有更强的类型安全和表达能力。

  3. Scala:Gradle 还支持使用 Scala 语言编写 build.gradle 文件,Scala 是一种强类型的函数式编程语言。

可以使用Java中的Groovy语言库来解析build.gradle文件并获取依赖信息。下面是一个示例代码片段,展示了如何使用Groovy来解析build.gradle文件并获取其依赖项:

import groovy.util.Node
import groovy.util.NodeList
import groovy.util.XmlParser

public class GradleDependencyParser {

public static void main(String[] args) {
String buildFilePath = "path/to/build.gradle";
GradleDependencyParser parser = new GradleDependencyParser();
parser.parse(buildFilePath);
}

public void parse(String buildFilePath) {
XmlParser parser = new XmlParser(false, false);
Node rootNode = parser.parse(new File(buildFilePath));
NodeList dependencies = rootNode.depthFirst().findAll { it.name() == 'dependencies' }[0];
dependencies.children().each { dependency ->
String group = dependency.attributes().get("group");
String name = dependency.attributes().get("name");
String version = dependency.attributes().get("version");
System.out.println("Group: " + group + ", Name: " + name + ", Version: " + version);
}
}
}

这个例子使用了groovy.util.XmlParser来解析build.gradle文件,然后使用depthFirst()方法遍历了整个XML树,查找所有名为dependencies的节点,并且遍历每个依赖项节点,获取其中的group、name和version属性,并打印输出。

需要注意的是,Gradle支持使用不同的语言来编写build.gradle文件,比如Groovy、Kotlin等,因此需要根据实际情况调整代码来解析不同的build.gradle文件。

与解析 Groovy 编写的 build.gradle 文件类似,解析 Kotlin 编写的 build.gradle.kts 文件也可以使用 Groovy 提供的 API 进行解析。

以下是一个示例代码片段,展示了如何使用 Groovy 解析 Kotlin 编写的 build.gradle.kts 文件并获取其中的依赖项:

import groovy.util.Node
import groovy.util.NodeList
import groovy.util.XmlParser
import org.jetbrains.kotlin.script.jsr223.KotlinJsr223JvmLocalScriptEngineFactory
import javax.script.ScriptEngine

public class KotlinGradleDependencyParser {

public static void main(String[] args) {
String buildFilePath = "path/to/build.gradle.kts";
KotlinGradleDependencyParser parser = new KotlinGradleDependencyParser();
parser.parse(buildFilePath);
}

public void parse(String buildFilePath) {
ScriptEngine engine = new KotlinJsr223JvmLocalScriptEngineFactory().scriptEngine
engine.eval("file(\"" + buildFilePath + "\").readText()")
String dependencies = (String) engine.eval("kotlin.runCatching {"
+ "val deps = arrayListOf<String>()\n"
+ "dependencies {\n"
+ " classpath.forEach { dep -> deps.add(\"${dep.group}:${dep.name}:${dep.version}\") }\n"
+ "}\n"
+ "deps.joinToString(\"\n\")"
+ "}.getOrNull()");
System.out.println(dependencies);
}
}

这个例子使用了 org.jetbrains.kotlin.script.jsr223.KotlinJsr223JvmLocalScriptEngineFactory 类来创建一个可以解析 Kotlin 脚本的 ScriptEngine。然后使用 eval 方法将 Kotlin 脚本读入并执行,获取其中的依赖项,并将结果作为字符串返回。

需要注意的是,Kotlin 编写的 build.gradle.kts 文件与 Groovy 编写的 build.gradle 文件有所不同,因此需要根据实际情况调整代码来解析不同的 build.gradle.kts 文件。

· One min read
Ma Chu
@Configuration
public class CorsConfig {

@Value("${configure.cors.enable:true}")
private Boolean corsIsEnable;

private CorsConfiguration buildConfig() {
CorsConfiguration corsConfiguration = new CorsConfiguration();
// 你需要跨域的地址 注意这里的 127.0.0.1 != localhost
// * 表示对所有的地址都可以访问
//corsConfiguration.addAllowedOrigin("*");
// 升级boot版本后需要使用addAllowedOriginPattern
corsConfiguration.addAllowedOriginPattern("*");
// 跨域的请求头
corsConfiguration.addAllowedHeader("*"); // 2
// 跨域的请求方法
corsConfiguration.addAllowedMethod("*"); // 3
//加上了这一句,大致意思是可以携带 cookie
//最终的结果是可以 在跨域请求的时候获取同一个 session
corsConfiguration.setAllowCredentials(true);
return corsConfiguration;
}

@Bean
public CorsFilter corsFilter() {
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
//配置 可以访问的地址
if(corsIsEnable){
source.registerCorsConfiguration("/**", buildConfig()); // 4
}
return new CorsFilter(source);
}
}

· One min read
Ma Chu

不保留原List进行翻转

List<Integer> list = new ArrayList<>();
list.add(1);
list.add(2);
list.add(3);
System.out.println("翻转前:" + list);
Collections.reverse(list);
System.out.println("反转后:" + list);

· One min read
Ma Chu

在使用webmagic爬取页面时,在download的时候,提示No appropriate protocol (protocol is disabled or cipher suites are inappropriate)

原因是:jdk1.8高版本对ssl做了限制。

解决办法:

C:\Program Files\Java\jdk1.8.0_291\jre\lib\security目录下修改java.security文件。

image

找到jdk.tls.disabledAlgorithms,去除 SSLv3 即可(代表允许https)。

· 3 min read

前期通过github-page构建发布站点,但是在github服务器上,在不科学上网的前提下访问很慢。
因此准备将构建好的文件再上传到阿里云服务器,再通过nginx访问。

准备

正式开始之前,你需要掌握 GitHub Action 的基础语法:

  • name: 工作流的名称。
  • on: 指定次工作流的触发器。push 表示只要有人将更改推送到仓库就会触发工作流运行。(点击这里了解如何指定特定分支,路径或标签)
  • jobs: 将工作流运行的所有作业组合到一起。
  • build-and-deploy: 定义的作业的名称。
  • runs-on: 将作业配置为在最新版本的 Ubuntu Linux 上运行。这意味着作业将在 GitHub 托管的新虚拟机上执行。有关使用其他运行器的语法示例,请参阅 GitHub 操作的工作流语法
  • steps: 将作业中运行的所有步骤组合在一起。嵌套在此部分下的每个项都是一个单独的操作或 shell 脚本。
  • uses: 指定需要运行的 action。
  • env: 指定运行 action 时需要用到的环境变量的值

步骤

  1. 在服务器上使用ssh-keygen -m PEM -t rsa -b 4096生成公私钥。
  2. 将github中添加私钥。 image
  3. 在nginx中也要开启对应ssh私钥登录。

完整的文件

name: Deploy Github pages
on:
push:
branches:
- main
jobs:
build-and-deploy:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v2

- run: npm install && npm run build

- name: Build and Deploy
uses: peaceiris/actions-gh-pages@v3
#uses: JamesIves/github-pages-deploy-action@master
with:
github_token: ${{ secrets.GITHUB_TOKEN }}
publish_branch: master
publish_dir: build
# 部署到阿里云服务器
- name: 上传到阿里云
uses: easingthemes/ssh-deploy@main
env:
# 本地.ssh文件下的私钥id_rsa,存在secrets的TOKEN中
SSH_PRIVATE_KEY: ${{ secrets.TOKEN }}
# 复制操作的参数。"-avzr --delete"象征部署时清空服务器目标目录下的文件
ARGS: "-rlgoDzvc -i --delete"
# 源目录,绝对于仓库内容根目录的门路
SOURCE: "build"
# 近程服务器地址
REMOTE_HOST: HOST
# 近程服务器用户名
REMOTE_USER: "root"
# 目标目录(近程服务器门路)
TARGET: "/home/www"

· One min read
Ma Chu

背景

通过代码规范,修改了包名为全小写(修改了文件夹目录),但发现push后,git服务器的文件夹目录还是为大写

解决方法

git默认是不区分大小写的,意思是你修改一个文件名/文件夹的时候,git status 是不会提示你有修改的

可以通过 git config --get core.ignorecase 查看默认配置

通过 git config core.ignorecase false 设置为区分大小写

然后 git status 就可以看到变动

然后push到远程服务器

· One min read
Ma Chu

message from server: "Host '192.168.7.233' is not allowed to connect to this MySQL server
远程连接数据库时报错

原因

mysql数据库只允许自身所在的本机器连接,不允许远程连接。

解决

mysql -u root -p
use mysql;
select host from user where user = 'root';
update user set host = '%' where user = 'root';
flush privileges;
image

· One min read
Ma Chu

给解析添加A类型,以下地址任选一

185.199.108.153
185.199.109.153
185.199.110.153
185.199.111.153

image

· 3 min read
Ma Chu
    public static void main(String[] args) {
Integer a = 127;
Integer b = 127;
System.out.println(a == b); // true
System.out.println(a.equals(b)); // true

Integer c = 128;
Integer d = 128;
System.out.println(c == d); // false
System.out.println(c.equals(d)); // true
}

Integer在判断相等时,当值定义在-128~127之间时,使用==或者equal()比较是一样的。但当值定义在这个范围时,equal()比较的是值,==比较的是内存地址

-128到127的范围内,Integer不对创建对象,而是直接取系统缓存中的变量数据。

    /**
* Cache to support the object identity semantics of autoboxing for values between
* -128 and 127 (inclusive) as required by JLS.
*
* The cache is initialized on first usage. The size of the cache
* may be controlled by the {@code -XX:AutoBoxCacheMax=<size>} option.
* During VM initialization, java.lang.Integer.IntegerCache.high property
* may be set and saved in the private system properties in the
* sun.misc.VM class.
*/

private static class IntegerCache {
static final int low = -128;
static final int high;
static final Integer cache[];

static {
// high value may be configured by property
int h = 127;
String integerCacheHighPropValue =
sun.misc.VM.getSavedProperty("java.lang.Integer.IntegerCache.high");
if (integerCacheHighPropValue != null) {
try {
int i = parseInt(integerCacheHighPropValue);
i = Math.max(i, 127);
// Maximum array size is Integer.MAX_VALUE
h = Math.min(i, Integer.MAX_VALUE - (-low) -1);
} catch( NumberFormatException nfe) {
// If the property cannot be parsed into an int, ignore it.
}
}
high = h;

cache = new Integer[(high - low) + 1];
int j = low;
for(int k = 0; k < cache.length; k++)
cache[k] = new Integer(j++);

// range [-128, 127] must be interned (JLS7 5.1.7)
assert IntegerCache.high >= 127;
}

private IntegerCache() {}
}

/**
* 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);
}

· 4 min read
Ma Chu

get请求和post请求本质上就是TCP链接,并无差别。但是由于HTTP的规定和浏览器/服务器的限制,导致他们在应用过程中体现出一些不同。

GET

从指定的资源请求数据,用于获取数据,一般用于搜索排序和筛选之类的操作。

  1. 浏览器请求tcp连接(第一次握手)。
  2. 服务器答应进行tcp连接(第二次握手)。
  3. 浏览器确认,并发送get请求头和数据(第三次握手,这个报文比较小,所以http会在此时进行第一次数据发送)。
  4. 服务器返回200 OK响应。

post

向指定的资源提交要被处理的数据,用于将数据发送给服务器,一般用于修改和写入数据。

  1. 浏览器请求tcp连接(第一次握手)。
  2. 服务器答应进行tcp连接(第二次握手)。
  3. 浏览器确认,并发送post请求头(第三次握手,这个报文比较小,所以http会在此时进行第一次数据发送)。
  4. 服务器返回100 Continue响应。
  5. 浏览器发送数据。
  6. 服务器返回200 OK响应。

区别

  • 安全
    • post请求更安全,不会作为url的一部分,不会被缓存、保存在服务器日志、以及浏览器浏览记录中。
    • get请求的是静态资源,则会缓存,如果是数据,则不会缓存。
  • 数据包
    • post请求发送的数据更大。
    • get请求有url长度限制,http协议本身不限制,请求长度限制是由浏览器和web服务器决定和设置。
  • post请求能发送更多的数据类型(get请求只能发送ASCII字符)。
  • 传参方式不同(get请求参数通过url传递,post请求放在request body中传递)。
  • get请求的是静态资源,则会缓存,如果是数据,则不会缓存。
  • get请求产生一个TCP数据包;post请求产生两个TCP数据包(get请求,浏览器会把http header和data一并发送出去,服务器响应200返回数据;post请求,浏览器先发送header,服务器响应100 continue,浏览器再发送data,服务器响应200 返回数据)。

在发送 POST 的时候都没有带 Expect 头,server 也自然不会发 100 continue。

· One min read
Ma Chu

路由转发

location / {
proxy_pass http://127.0.0.1:8001; # 反向代理到 8001 端口
add_header Access-Control-Allow-Origin *;
}

· One min read
Ma Chu
select @@global.sql_mode;
set @@global.sql_mode='STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION'