Go 1.15.1和Go 1.14.8发布:一个安全问题修复

thbcm阅读(298)

Go Team 发布了 Go 1.15.1Go 1.14.8,以解决最近报告的安全问题。建议所有受影响的用户更新到以下版本之一(如果不确定哪个版本,请选择 Go 1.15.1)。

如果处理程序未明确设置 Content-Type 响应头,则 net/http/cginet/http/fcgi包将默认设置为 “text/html”,如果攻击者可以控制响应的内容,则可能会导致跨站点脚本漏洞。

现在修改改为根据第一次 Write 的内容通过 http.DetectContentType 来设置Content-Type 响应头,这与 net/http 包的行为一致。

尽管这可以保护某些验证上传文件内容的应用程序,但未在攻击者控制的任何文件上明确设置 Content-Type 响应头是不安全的,应避免出现。也就是说,你应该总是明确设置 Content-Type 响应头。

RedTeam Pentesting GmbH 报告此问题。此问题为CVE-2020-24553,相应的 issue 见:github.com/golang/go/issues/40928

以前偶然发现的 Java Bug(JDK 9及之前仍未修复)

thbcm阅读(298)

文章转载自公众号:叨叨软件测试 作者: 蒋李恒

背景

15年在中信银行做持续集成时,由于当时的项目是基于三方采购的 Java 配置开发平台做的,平台自己基于 Ant 插件实现了增量和热部署。

其中有几个项目在持续集成部署时,经常发现 Linux 平台部署成功后(Windows 不会出现,Linux 也是偶发现象),新版本代码并没有生效(反编译 class)。

起初我是在本地 windows 上跟踪调试基于 Ant 插件的代码,但始终重现不了(最后测试发现 Windows 无此 Bug)。

后来,通过分析代码逻辑,其中有段逻辑是通过文件的最后修改时间(File.lastModified())来判断要不要覆盖部署的,最后通过单测发现,是由于 JavaFile.lastModified() 方法在 WindowsLinux/Unix平台获取的精度不一样导致的,Windows 精度为毫秒,而 Linux/Unix 只能到秒(JDK Bug:JDK-8177809)。

所以也解释了,为什么是偶发现象,文件修改时间如果判断的两个值正好跨秒时,部署就是成功的,否则失败。

Bug 重现

测试代码:FileTest.java

import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.text.SimpleDateFormat;


public class FileTest {
    private static final long LM = 1599276952718L;


    public static void main(String[] args) throws IOException {
        // java版本号
        System.out.println("Java Version:" + System.getProperty("java.version"));


        File f = new File("test.txt");
        f.createNewFile();


        // 设置最后修改时间
        f.setLastModified(LM);


        // 获取修改时间,存在 bug
        System.out.printf("Test f.lastModified [%s]: %b\n",
f.lastModified(), f.lastModified() == LM);
        // 格式化输出,正确不存在 bug
        System.out.printf("Test f.lastModified DateFormat [%s]\n",new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.sss").format(f.lastModified()));


        // Files.getLastModifiedTime() 获取修改时间,同样存在 bug
        System.out.printf("Test Files.getLastModifiedTime [%s]: %b\n",
Files.getLastModifiedTime(f.toPath()).toMillis(),
(Files.getLastModifiedTime(f.toPath()).toMillis() == LM));
        // 格式化输出,正确不存在 bug
        System.out.printf("Test Files.getLastModifiedTime DateFormat [%s]\n",new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.sss").format(f.lastModified()));


        f.delete();
    }
}

命令行下编译、执行:

# 编译执行
$ javac FileTest.java && java FileTest 

Windows 执行结果

Windows 平台不存在此 Bug。

# 编译执行
$ javac FileTest.java && java FileTest
Java Version:1.8.0_202
Test f.lastModified [1599276952718]: true
Test f.lastModified DateFormat [2020-09-05 11:35:52.052]
Test Files.getLastModifiedTime [1599276952718]: true
Test Files.getLastModifiedTime DateFormat [2020-09-05 11:35:52.052]

Mac 执行结果

JDK 8 最新版本,目前仍然没有修复该问题。

# 编译执行
$ javac FileTest.java && java FileTest
Java Version:1.8.0_261
Test f.lastModified [1599276952000]: false
Test f.lastModified DateFormat [2020-09-05 11:35:52.052]
Test Files.getLastModifiedTime [1599276952000]: false
Test Files.getLastModifiedTime DateFormat [2020-09-05 11:35:52.052]

Linux 执行结果

# 编译执行
$ javac FileTest.java && java FileTest
Java Version:1.8.0_171
Test f.lastModified [1599276952000]: false
Test f.lastModified DateFormat [2020-09-05 11:35:52.052]
Test Files.getLastModifiedTime [1599276952000]: false
Test Files.getLastModifiedTime DateFormat [2020-09-05 11:35:52.052]

官方 Bug 链接

JDK10 已修复,但是之前版本目前仍然未修复。

  • bugs.openjdk.java.net/browse/JDK-8177809

以上就是W3Cschool编程狮关于以前偶然发现的 Java Bug(JDK 9及之前仍未修复)的相关介绍了,希望对大家有所帮助。

Linux/Unix 效率工具:快速路径切换 z 命令

thbcm阅读(300)

文章转载自公众号:叨叨软件测试

简介

z 是一个开源的 Linux 快速路径切换工具(类似工具还有z.luaautojumpfasd)。通过 Frecency 机制对日常访问的路径进行 Frecent 权重计算,z 会帮你切换到所有匹配正则关键字的路径中权重值最高的那条路径。

Frecency 是由 Mozilla 开发,将访问的频率和新近度(上一次访问到现在的时间差)合并为一个度量的启发式方法。

详细内容见:github.com/rupa/z

安装

# /usr/local 安装
$ cd /usr/local
$ sudo git clone https://github.com/rupa/z.git
$ sudo chmod +x z.sh


# zsh 配置变量
$ echo '. /usr/local/z/z.sh' >> ~/.zshrc
$ source ~/.zshrc


# bash 配置变量
$ echo '. /usr/local/z/z.sh' >> ~/.bash_profile
$ source ~/.bash_profile


# 安装 manpage
$ cp z.1 /usr/local/share/man/man1


# 验证安装
$ z -h
$ man z

使用

技巧z 命令可以使用 tab 来进行补齐,以提高切换效率。

# 查看帮助
$ man z
或
$ tldr z


# 显示记录的路径
$ z


# 切换到一个名字带有 "foo" 的路径
$ z foo


# 切换到一个名字带有 "foo" 并且后面带有 "bar" 的路径(例:fooesbar):
$ z foo bar


# 切换到名字带有 "foo" 并且拥有最高访问次数的路径
$ z -r foo


# 切换到最近使用的名字带有 "foo" 的路径
$ z -t foo


# 列出在 z 的数据库中名字带有 "foo" 的路径
$ z -l foo


# 将当前路径从 z 的数据库中移除
$ z -x .

以上就是W3Cschool编程狮关于Linux/Unix 效率工具:快速路径切换 z 命令的相关介绍了,希望对大家有所帮助。

(Add sumOf)一道字节前端原题

thbcm阅读(291)

文章转载自公众号:蓝色的秋风

前言

最近学弟去面了字节跳动,但是由于面试经验少,面试的时候紧张了,一时之间没有写出来,之后来我交流了一下。那我就来分析分析这道题目。

正文

这题的规则是这样的

给定有一个 Add 函数,要支持以下形式的调用


Add(1)(2)(3).sumOf(); // 输出 6
Add(1,2)(3)(4).sumOf(); // 输出 10
Add(1,2,...)(3)(4)(...).sumOf();  // ...

拿到这种题目,我先来说说我自己的做题流程,一般会去找它最简单的形态。我们一步一步来拆解。

先去掉 sumOf() 变成了以下形态

Add(1,2,...)(3)(4)(...)

嗯….有点熟悉…但是还是有点复杂,那我们再去掉无限调用这个限制。

Add(1,2,...)(3)(4)

唔,还是有点难呀…没关系,再砍, 不要传入多个参数。

Add(1)(2)(3)

有….有….有那味了….这….这不就是柯里化吗….

有些小朋友可能没有听过,对于大朋友而言耳熟能详,融会贯通。

我们还是来介绍一下。

在《javascript高级程序设计》这本书中有如下介绍:

与函数绑定紧密相关的主题是函数柯里化,它用于创建已经设置好的一个或者多个参数的函数。函数柯里化的基本方法和函数绑定是一样的:使用一个闭包返回一个函数。两者的区别在于,当函数被调用时,返回的函数还需要设置一些传入的参数。

我们来写写看:

function Add(x) {
 return function (y) {
  return return functio (z) {
   return x + y + z;
  }
 }
}
// 简洁写法
const Add = x => y => z => x+y+z;

执行一下

Add(1)(2)(3) // 6 

是我们要的那味~

那么我们既然已经写出了这个形态,我们就一步一步反推。

这个时候千万别紧张,我们从最低级的形态出发,写出一个最基本的形态,能够有效地帮助我们建立自信心,吃下定心丸,按照这种方式,哪怕我们最终没有写出完美的结果,让面试官看到你思考解题的过程,也是一种加分。

好,接着说~

那我们接下来需要实现这个样子。

Add(1,2,...)(3)(4)

传入参数不止一个

我们知道,对于不确定参数个数,我们可以使用 arguments 这个对象来获取到所有的入参,但是 arguments 不是一个 Array,但是我们可以使用 ES6 中的 Spread syntax展开语法)去将他变成一个数组。表演继续。

function Add() {
 const nums = [...arguments];
 return function() {
  nums.push(...arguments);
  return function() {
   nums.push(...arguments);
   return nums.reduce((a, b) => a + b);
  }
 }
}

nice!已经离我们最终的形态越来越近了。接下来是这个函数能够无限的进行调用。

Add(1,2,...)(3)(4)(...)

那么怎么样才能无限调用呢?没错,用递归。

function Add() {
 const nums = [...arguments];
 function AddPro() {
  nums.push(...arguments);
    return AddPro;
 }
 return AddPro;
}

嗯,其实我们写到这里发现了… 由于是无限递归,我们没办法确定最后一次函数调用,因此我们需要最后显式调用一个结束的方法来打印出最后的数据。

很自然地,我们可以在 AddPro 添加一个方法 sumOf 来解决这个问题。

学弟就是卡在这里地方,被函数添加上一个方法搞懵了。你是否知道呢?

function Add() {
 const nums = [...arguments];
 function AddPro() {
  nums.push(...arguments);
    return AddPro;
 }
 AddPro.sumOf = () => {
  return nums.reduce((a, b) => a + b);
 }
 return AddPro;
}

好啦好啦,结束啦。

等等

在最后,我再来补充一种方案,function 不仅可以继续挂载 function ~ 还可以挂载变量哦~

function Add() {
 if (!Add.nums) {
   Add.nums = [];
  }
  Add.nums.push(...arguments);
  return Add;
}
Add.sumOf = () => {
 return Add.nums.reduce((a, b) => a + b);
}

我们总结一下,小小的面试题涉及到的基础知识。

闭包、递归、作用域、函数与对象

基础就是基础,永远是你爸爸,掌握好基础,以不变应万变。

后记

也许你觉得这题有点简单,通过简单的重复练习就能轻松记住,但是最主要的是思路,很多事情都是一样,掌握事情的方法和方向是最重要的。毕竟淘宝也不是一蹴而就的~ 但是只要方向正确了,都会好起来的。

以上就是W3Cschool编程狮关于(Add sumOf)一道字节前端原题的相关介绍了,希望对大家有所帮助。

webpack 构建进度条 ProgressPlugin 源码剖析

thbcm阅读(301)

文章转载自公众号:墨筝

前言

我们在使用 webpack 的时候经常会用到 webpackbar 或者 progress-bar-webpack-plugin 之类的 webpack 插件通过进度条等方式来展示 webpack 的构建进度,以提升构建过程中的反馈体验。对于不同的插件来说,它们只是进度条的 UI 展示形式不同而已,但最核心的 webpack 构建的实时进度的数据来源却是一致的,均由 webpack 内部的 ProgressPlugin 这个插件提供。下面我会结合源码来讲解该插件是如何计算 webpack 的构建进度并将进度数据暴露给第三方的进度条插件。在阅读下文之前可以试着问下自己:如果是你,你会如何计算 webpack 的构建进度。

构建进度的计算

该插件主要根据 webpack 的构建阶段来定义当前进度值。webpack 的构建过程分为很多不同的阶段,在每个阶段 webpack 都暴露了对应的事件钩子。ProgressPlugin 正是通过这些事件钩子对每个阶段都定义了一个基础进度值,代码如下所示:

上述代码中的 interceptHook 方法可以先忽略,这个后续会提到。 通过上述代码你会发现 ProgressPlugincompiler 中的每个钩子都设置了一个指定的进度值。但这些进度值还不够细致到反映 webpack 的详细构建过程,中间还差了 0.06 到 0.69 以及 0.69 到 0.95 两个阶段的数值。webpack构建的具体执行过程主要在 compilation 中,这两个阶段的数值由 compilation 的钩子填充。

0.06~0.69

update 方法的调用由 compilation 的钩子触发,如下所示:

这个阶段的主要工作是 module,entry 以及 dependencies 的处理和构建。换个角度从 ProgressPlugin 给该阶段设置的进度值来看,这部分工作也是 webpack 最耗时的地方。

0.69~0.95

从上述代码中可以看出这个间隔段就完全是根据 compilation 的 hooks 来计算和指定当前的构建进度值,从hook 的描述中可以看出这个阶段主要是 module, chunk 以及assets等资源的优化工作。 基本上整个 webpack 构建过程的进度值就是根据上述中的 compilercompilation 的 hooks 来设置的。

进度数据的透出

webpack 的构建进度确定之后剩下的任务就是将进度数据透出给第三方的进度条插件进行展示。要完成该任务需要ProgressPlugin完成两件事情,一是提供回调函数的切入口;二是需要能在对应的 hook 节点执行该回调函数进行进度的百分比值的传入。以下是这两点的实现原理

回调函数

ProgressPlugin 定义了 handler 函数来作为回调函数切入,代码如下所示:

hook 劫持

hook 劫持的实现非常简单,主要利用 webpack hook 原生提供的intercept方法,前文中提到的interceptHook方法只是对于 intercept 方法的封装,示例代码如下:

结语

webpack 构建的进度条实现原理就是如此简单,给每个构建阶段对应的 hook 设置一个进度值,然后通过 handler 回调和 hook 劫持切入到构建环节将进度信息传入回调函数,最终第三插件通过 handler 获取到进度值后将其展示出来。 webpack 的很多其他功能其实也没有想象中的那么复杂和高大上,通过阅读其源代码了解其实现原理后,你很可能会一拍大腿,大叹一声原来如此,这本身就是一件挺有意思的事儿。

以上就是W3Cschool编程狮关于webpack 构建进度条 ProgressPlugin 源码剖析的相关介绍了,希望对大家有所帮助。

do-while,Java中容易被忽略的语句

thbcm阅读(291)

文章转载自公众号:Java中文社群

最近在看 Java 的基础知识,其中有部分是关于循环的,在 Java 中,循环的语法总共分为 3 种:forwhiledo-while,如下图所示:

但我惊奇的发现,在之前的职业生涯中(11 年),竟从未用过 do-while(尴尬),于是问了群里的小伙伴,发现也是鲜有人用。

do-while 语法分析

我们先来了解一下 do-while 的语法:

do {
     // statements
} while (expression);

注意:最后一个分号不能省略,否则会提示编译出错

它的执行流程如下图所示:

那它究竟在什么地方用呢?

do-while 使用场景

在我多次的搜索和请教下,终于找到了两个相对满意的使用场景,接下来一起来看。

使用场景一:抢票

对于抢票业务来说,无论三七二十一,先抢了再说,然后在判断是否抢票成功,如果抢票成功则退出循环,否则继续执行抢票,实现的伪代码如下所示:

do {
    // 抢票代码...
} while (没抢到票);

思路提供者:贾鲲

使用场景二:进制转换

经过大量搜索发现,在 JDK 的源码中,也存在少量使用 do-while 的场景,比如 Integer 中进制转换,相关源码如下:

static int formatUnsignedInt(int val, int shift, char[] buf, int offset, int len) {
    int charPos = len;
    int radix = 1 << shift;
    int mask = radix - 1;
    do {
        buf[offset + --charPos] = Integer.digits[val & mask];
        val >>>= shift;
    } while (val != 0 && charPos > 0);


    return charPos;
}

比如十进制转二进制就会执行此方法,在进制转换的业务中,无论任何情况,一定会至少执行一次进制转换的,因此这种业务场景就非常适合 do-while

总结

孔子说:温故而知新。当我们学完很多知识之后,回过头来再琢磨这些知识,发现很有趣,这就是知识的一大乐趣吧。本文我们介绍了两种 do-while 的使用场景,抢票和进制转换,你还知道哪些 do-while 的使用场景吗?

以上就是W3Cschool编程狮关于do-while,Java中容易被忽略的语句的相关介绍了,希望对大家有所帮助。

使用Vue3.0,我收获了哪些知识点(一)

thbcm阅读(314)

这篇文章主要是将自己前期学习Vue3.0时候整理的一些笔记内容进行了汇总,通过对本文的阅读,你将可以自己完成Vue3.0环境搭建,同时还会对Vue3.0的一些新的特性进行了解,方便自己进行Vue3.0的学习。

本文所有的示例均使用ant design vue2.0实现,关于ant design vue2.0请参考 2x.antdv.com/docs/vue/introduce-cn/

初始化环境

在前面的文章中,我们通过vite搭建了一个开发环境,但是实际上现在vite并没有完善到支撑一个完整项目的地步,所以本文我们依然选择使用vue-cli脚手架进行环境搭建。

小编使用的vue-cli版本是4.5.4,如果您的版本比较旧可以通过npm update @vue/cli来升级脚手架版本,如果没有安装可以通过npm install @vue/cli -g进行安装

使用脚手架新建项目

  1. 在工作空间打开终端(cmd),然后通过vue create my-vue3-test命令初始化项目
  1. 在第一步先选择Manually select features,进行手动选择功能
  1. 然后通过Space和上下键依次选择
   Choose Vue version
   Babel
   TypeScript
   Router
   Vuex
   CSS Pre-processors
   Linter/Formatter

4.然后回车

  1. 然后提示选择Vue版本,选择3.x(Preview)
  2. Use class-style component syntax?选择n,即输入n然后回车
  3. 然后提示Use Babel alongside TypeScript,输入y`
  4. Use history mode for router输入n
  5. 然后css预处理器选择Less
  6. eslint选择ESLint + Prettier
  7. 然后是Lint on saveIn dedicater config files
  8. 最后一路回车即可完成项目搭建

启动项目

新建完项目之后,进入到项目中cd my-vue3-test,然后执行 yarn serve即可启动项目

启动之后即可通过访问http://localhost:8080/访问项目

配置ant design vue

在当前Vue3.0正式版还未发布之际,国内比较出名的前端UI库中率先将Vue3.0继承到自家的UI库中的,PC端主要是ant-design-vue,移动端主要是vant, 本文所有示例代码都会基于ant-design-vue来进行,首先我们先安装ant-design-vue

  1. 安装依赖
   yarn add ant-design-vue@2.0.0-beta.6
   yarn add babel-plugin-import -D

  1. 配置ant-design-vue按需加载

进入项目根目录,然后打开babel.config.js文件,将里面的内容修改为

   module.exports = {
     presets: ["@vue/cli-plugin-babel/preset"],
     plugins: [
       // 按需加载
       [
         "import",
         // style 为 true 加载 less文件
         { libraryName: "ant-design-vue", libraryDirectory: "es", style: "css" }
       ]
     ]
   };

  1. 尝试使用vue3 + antdv来添加一个小页面, 我们直接将views/Home.vue文件里面的代码替换为
<template>
  <a-form layout="inline" :model="state.form">
    <a-form-item>
      <a-input v-model:value="state.form.user" placeholder="Username">
        <template v-slot:prefix
          ><UserOutlined style="color:rgba(0,0,0,.25)"
        /></template>
      </a-input>
    </a-form-item>
    <a-form-item>
      <a-input
        v-model:value="state.form.password"
        type="password"
        placeholder="Password"
      >
        <template v-slot:prefix
          ><LockOutlined style="color:rgba(0,0,0,.25)"
        /></template>
      </a-input>
    </a-form-item>
    <a-form-item>
      <a-button
        type="primary"
        :disabled="state.form.user === '' || state.form.password === ''"
        @click="handleSubmit"
      >
        登录
      </a-button>
    </a-form-item>
  </a-form>
</template>
<script>
import { UserOutlined, LockOutlined } from "@ant-design/icons-vue";
import { Form, Input, Button } from "ant-design-vue";
import { reactive } from "vue";


export default {
  components: {
    UserOutlined,
    LockOutlined,
    [Form.name]: Form,
    [Form.Item.name]: Form.Item,
    [Input.name]: Input,
    [Button.name]: Button
  },
  setup() {
    const state = reactive({
      form: {
        user: "",
        password: ""
      }
    });


    function handleSubmit() {
      console.log(state.form);
    }


    return {
      state,
      handleSubmit
    };
  }
};
</script>

然后重启一下项目,就可以发现已经可以正常使用ant-design-vue了。

Vue3.0新体验之setup

对于Vue3.0的问世,最吸引大家注意力的便是Vue3.0Composition API,对于Componsition API,可以说是两极分化特别严重,一部分人特别喜欢这个新的设计与开发方式,而另一部分人则感觉使用Composition API很容易写出来意大利面式的代码(可能这部分人不知道兰州拉面吧)。到底Composition API是好是坏,小编不做评论,反正我只是一个搬砖的。而本小节介绍的setup就是Composition API的入口。

setup介绍

setupVue3.0提供的一个新的属性,可以在setup中使用Composition API,在上面的示例代码中我们已经使用到了setup,在上文代码中我们在setup中通过reactive初始化了一个响应式数据,然后通过return返回了一个对象,对象中包含了声明的响应式数据和一个方法,而这些数据就可以直接使用到了template中了,就像上文代码中的那样。关于reactive,我将会在下一小节为你带来说明。

setup 的参数说明

setup函数有两个参数,分别是propscontext

  1. props

propssetup函数的第一个参数,是组件外部传入进来的属性,与vue2.0props基本是一致的,比如下面代码

   export  default {
     props: {
       value: {
         type: String,
         default: ""
       }
     },
     setup(props) {
       console.log(props.value)
     }
   }

但是需要注意的是,在setup中,props是不能使用解构的,即不能将上面的代码改写成

   setup({value}) {
       console.log(value)
    }

虽然template中使用的是setup返回的对象,但是对于props,我们不需要在setup中返回,而是直接可以在template使用,比如上面的value,可以直接在template写成

   <custom-component :value="value"></custom-component>

  1. context

contextsetup函数的第二个参数,context是一个对象,里面包含了三个属性,分别是

  • attrs

attrsVue2.0this.$attrs是一样的,即外部传入的未在props中定义的属性。对于attrsprops一样,我们不能对attrs使用es6的解构,必须使用attrs.name的写法

  • slots

slots对应的是组件的插槽,与Vue2.0this.$slots是对应的,与propsattrs一样,slots也是不能解构的。

  • emit

emit对应的是Vue2.0this.$emit, 即对外暴露事件。

setup 返回值

setup函数一般会返回一个对象,这个对象里面包含了组件模板里面要使用到的data与一些函数或者事件,但是setup也可以返回一个函数,这个函数对应的就是Vue2.0render函数,可以在这个函数里面使用JSX,对于Vue3.0中使用JSX,小编将在后面的系列文章中为您带来更多说明。

最后需要注意的是,不要在setup中使用this,在setup中的this和你真正要用到的this是不同的,通过propscontext基本是可以满足我们的开发需求的。

了解Composition API,先从reactiveref开始

在使用Vue2.0的时候,我们一般声明组件的属性都会像下面的代码一样

export  default {
  data() {
    return {
      name: '编程狮',
      sex: '男'
    }
  }
}

然后就可以在需要用到的地方比如computed,watch,methods,template等地方使用,但是这样存在一个比较明显的问题,即我声明data的地方与使用data的地方在代码结构中可能相距很远,有一种君住长江头,我住长江尾,日日思君不见君,共饮一江水的感觉。而Composition API的诞生的一个很重要的原因就是解决这个问题。在尤大大在关于Composition API的动机中是这样描述解决的问题的:

  1. 随着功能的增长,复杂组件的代码变得越来越难以阅读和理解。这种情况在开发人员阅读他人编写的代码时尤为常见。根本原因是 Vue 现有的 API 迫使我们通过选项组织代码,但是有的时候通过逻辑关系组织代码更有意义。
  2. 目前缺少一种简洁且低成本的机制来提取和重用多个组件之间的逻辑。

现在我们先了解一下Compositon API中的reactiveref

介绍reactive

Vue2.6中, 出现了一个新的api,Vue.observer,通过这个api可以创建一个响应式的对象,而reactive就和Vue.ovserver的功能基本是一致的。首先我们先来看一个例子

<template>
  <!--在模板中通过state.name使用setup中返回的数据-->
  <div>{{ state.name }}</div>
</template>
<script>
import { reactive } from "vue";
export default {
  setup() {
    // 通过reactive声明一个可响应式的对象
    const state = reactive({
      name: "编程狮"
    });
    // 5秒后将编程狮修改为 W3Cschool
    setTimeout(() => {
      state.name = "W3Cschool";
    }, 1000 * 5);
    // 将state添加到一个对象中然后返回
    return {
      state
    };
  }
};
</script>

上面的例子就是reactive的一个基本的用法,我们通过上面的代码可以看到reactiveVue.observer声明可响应式对象的方法是很像的,但是他们之间还是存在一些差别的。我们在使用vue2.0的时候,最常见的一个问题就是经常会遇到一些数据明明修改了值,但是界面却并没有刷新,这时候就需要使用Vue.set来解决,这个问题是因为Vue2.0使用的Object.defineProperty无法监听到某些场景比如新增属性,但是到了Vue3.0中通过Proxy将这个问题解决了,所以我们可以直接在reactive声明的对象上面添加新的属性,一起看看下面的例子

<template>
  <div>
    <div>姓名:{{ state.name }}</div>
    <div>网站:{{ state.wz }}</div>
  </div>
</template>
<script>
import { reactive } from "vue";
export default {
  setup() {
    const state = reactive({
      name: "编程狮"
    });
    // 5秒后新增属性wz W3Cschool.cn
    setTimeout(() => {
      state.wz = "W3Cschool.cn";
    }, 1000 * 5);
    return {
      state
    };
  }
};
</script>

上面的例子虽然在state中并没有声明gzh属性,但是在5s后我们可以直接给state添加gzh属性,这时候并不需要使用Vue.set来解决新增属性无法响应的问题。

在上面的代码中,reactive通过传入一个对象然后返回了一个state,需要注意的是state与传入的对象是不用的,reactive对原始的对象并没有进行修改,而是返回了一个全新的对象,返回的对象是Proxy的实例。需要注意的是在项目中尽量去使用reactive返回的响应式对象,而不是原始对象。

const obj = {}
const state = reactive(obj)
// 输出false
console.log(obj === state)

介绍ref

假如现在我们需要在一个函数里面声明用户的信息,那么我们可能会有两种不一样的写法

// 写法1
let name = '编程狮'
let wz = 'W3Cschool.cn'
// 写法2
let userInfo = {
  name: '编程狮',
  wz: 'W3Cschool.cn'
}

上面两种不同的声明方式,我们使用的时候也是不同的,对于写法1我们直接使用变量就可以了,而对于写法2,我们需要写成userInfo.name的方式。我们可以发现userInfo的写法与reactive是比较相似的,而Vue3.0也提供了另一种写法,就像写法1一样,即ref。先来看一个例子。

<template>
  <div>
    <div>姓名:{{ name }}</div>
  </div>
</template>
<script>
import { ref } from "vue";
export default {
  setup() {
    const name = ref("编程狮");
    console.log('姓名',name.value)
    // 5秒后修改name为 W3Cschool
    setTimeout(() => {
      name.value = "W3Cschool";
    }, 1000 * 5);
    return {
      name
    };
  }
};
</script>

通过上面的代码,可以对比出来reactiveref的区别

  1. reactive传入的是一个对象,返回的是一个响应式对象,而ref传入的是一个基本数据类型(其实引用类型也可以),返回的是传入值的响应式值
  2. reactive获取或修改属性可以直接通过state.prop来操作,而ref返回值需要通过name.value的方式来修改或者读取数据。但是需要注意的是,在template中并不需要通过.value来获取值,这是因为template中已经做了解套。

Vue3.0优雅的使用v-model

v-model并不是vue3.0新推出的新特性,在Vue2.0中我们已经大量的到了v-model,但是V3V2还是有很大的区别的。本节我们将主要为大家带来如何在Vue3.0中使用v-model,Vue3.0中的v-model提供了哪些惊喜以及如何在Vue3.0中自定义v-model

Vue2.0Vue3.0中使用v-model

Vue2.0中如何实现双向数据绑定呢?常用的方式又两种,一种是v-model,另一种是.sync,为什么会有两种呢?这是因为一个组件只能用于一个v-model,但是有的组件需要有多个可以双向响应的数据,所以就出现了.sync。在Vue3.0中为了实现统一,实现了让一个组件可以拥有多个v-model,同时删除掉了.sync。如下面的代码,分别是Vue2.0Vue3.0使用v-model的区别。

  1. Vue2.0中使用v-model
   <template>
     <a-input v-model="value" placeholder="Basic usage" />
   </template>
   <script>
   export default {
     data() {
       return {
         value: '',
       };
     },
   };
   </script>

  1. Vue3.0中使用v-model
   <template>
     <!--在vue3.0中,v-model后面需要跟一个modelValue,即要双向绑定的属性名-->
     <a-input v-model:value="value" placeholder="Basic usage" />
   </template>
   <script>
   export default {
     // 在Vue3.0中也可以继续使用`Vue2.0`的写法
     data() {
       return {
         value: '',
       };
     },
   };
   </script>

vue3.0中,v-model后面需要跟一个modelValue,即要双向绑定的属性名,Vue3.0就是通过给不同的v-model指定不同的modelValue来实现多个v-model。对于v-model的原理,下文将通过自定义v-model来说明。

自定义v-model

使用Vue2.0自定义一个v-model示例

  1. 组件代码
<template>
  <div class="custom-input">
    <input :value="value" @input="$_handleChange" />
  </div>
</template>
<script>
export default {
  props: {
    value: {
      type: String,
      default: ''
    }
  },
  methods: {
    $_handleChange(e) {
      this.$emit('input', e.target.value)
    }
  }
}
</script>

2 . 在代码中使用组件

<template>
      <custom-input v-model="value"></custom-input>
</template>
<script>
 export default {
    data() {
      return {
        value: ''
      }
    }
  }
</script>

Vue2.0中我们通过为组件设置名为value属性同时触发名为input的事件来实现的v-model,当然也可以通过model来修改属性名和事件名,可以看我以前的文章中有详解。

使用Vue3.0自定义一个v-model示例

  1. 组件代码
   <template>
     <div class="custom-input">
       <input :value="value" @input="_handleChangeValue" />
     </div>
   </template>
   <script>
   export default {
     props: {
       value: {
         type: String,
         default: ""
       }
     },
     name: "CustomInput",
     setup(props, { emit }) {
       function _handleChangeValue(e) {
         // vue3.0 是通过emit事件名为 update:modelValue来更新v-model的
         emit("update:value", e.target.value);
       }
       return {
         _handleChangeValue
       };
     }
   };
   </script>

到了Vue3.0中,因为一个组件支持多个v-model,所以v-model的实现方式有了新的改变。首先我们不需要使用固定的属性名和事件名了,在上例中因为是input输入框,属性名我们依然使用的是value,但是也可以是其他任何的比如name,data,val等等,而在值发生变化后对外暴露的事件名变成了update:value,即update:属性名。而在调用组件的地方也就使用了v-model:属性名来区分不同的v-model

  1. 在代码中使用组件
<template>
  <!--在使用v-model需要指定modelValue-->
  <custom-input v-model:value="state.inputValue"></custom-input>
</template>
<script>
import { reactive } from "vue";
import CustomInput from "../components/custom-input";
export default {
  name: "Home",
  components: {
    CustomInput
  },
  setup() {
    const state = reactive({
      inputValue: ""
    });
    return {
      state
    };
  }
};
</script>

总结

在本文中我们主要讲解了开发环境的搭建,setup,reactive,ref,v-model等的介绍,同时通过对比Vue3.0Vue2.0的不同,让大家对Vue3.0有了一定的了解。

以上就是W3Cschool编程狮关于使用Vue3.0,我收获了哪些知识点(一)的相关介绍了,希望对大家有所帮助。

JVM的Xms和Xmx参数设置为什么要设置为相同值

thbcm阅读(280)

文章转载自公众号:程序新视界

最近正在重新学习 JVM 的内存结构及相关优化内容,无意中看到 IDEA 的 VM 配置(安装时默认配置)中有如下的配置:

# custom IntelliJ IDEA VM options


-Xms2048m
-Xmx2048m

看到 Xms 和 Xmx 的参数设置一样,是不是稍微有些奇怪?这里就写篇文章分析一下,JVM 的 Xms 和 Xmx 参数设置为相同的值有什么好处?首先来了解一下相关参数的概念及功能。

Xms和Xmx参数定义

在启动 Java 应用程序时,我们通常可以通过参数XmsXmx来配置 JVM 的堆信息。不配置虽然会有默认值,但如果受硬件所限或需对 JVM 进行调优,则需要根据情况指定这两个参数的值。

-Xms:堆内存的最小Heap值,默认为物理内存的1/64,但小于1G。默认当空余堆内存大于指定阈值时,JVM 会减小heap的大小到-Xms指定的大小。

-Xmx:堆内存的最大Heap值,默认为物理内存的1/4。默认当空余堆内存小于指定阈值时,JVM 会增大Heap-Xmx指定的大小。

内存情况的变化

常规的JVM参数使用如下:

java -Xms512m -Xmx1g

在这种配置下,JVM 启动时会分配512M的堆内存空间,随着程序的执行,所需的堆空间越来越大,则会逐渐增大堆内存空间,直到Xmx参数指定的堆最大空间1G。

当堆内存使用率降低,则会逐渐减小该内存区域的大小。整个过程看似非常合理,但为什么很多生产环境却也将两个值配置为相同的值呢?

JVM垃圾回收的不足

当堆内存使用情况变化时,并不是单纯的扩大和缩小堆内存就完事了。在此之前还会执行GC(垃圾回收)操作。如果-Xms起初值设置的比较小,那么就频繁触发GC操作。当GC操作无法释放更多内存时,才会进行内存的扩充。

我们都知道GC操作是需要耗时的,而且Full GC会引起“Stop the World”,也就是说会引起线程停止,不可避免就会引起性能问题。

相同值的好处

面对上面的问题,为了避免在生产环境由于heap内存扩大或缩小导致应用停顿,降低延迟,同时避免每次垃圾回收完成后JVM 重新分配内存。所以,-Xmx-Xms一般都是设置相等的。

当然,如果生产系统上线前有一段预热时间的话,也可以不设置相等。对于需要高吞吐量的应用来说,可以不在乎这种停顿,比如一些后台的应用之类,那么内存可以适当调大一些。(停顿时间越长,吞吐量反而越大),需要根据具体情况权衡。

其实关于在生产环境中把XmsXmx设为相同值也是 Oracle 官方推荐的。在Xms的参数描述中有这样一段话:

Oracle recommends setting the minimum heap size (-Xms)equal to the maximum heap size (-Xmx) to minimize garbage collections.

其实这里还有一个小前提,那就是生产环境往往一台服务器或一个容器只有一个服务,独占服务器意味着没有必要调整 JVM 大小,每次调整反而会加大开销。只有在多开发环境,比如个人电脑等运行进程比较多时,动态调整JVM才有必要。

注意事项

其实虽然设置为相同值有很多好处,但也会有一些不足。比如,如果两个值一样,会减少 GC 的操作,也意味着只有当 JVM 即将使用完时才会进行回收,此前内存会不停的增长。

并且同一 JDK 的 GC 策略也有很多种,不能一概而论。另外,对于Hotspot虚拟机,XmsXmx设置为一样的,可以减轻伸缩堆大小带来的压力。但对于IBM虚拟机,设置为一样会增大堆碎片产生的几率,并且这种负面影响足以抵消前者产生的益处。

小结

最近研究 Java 虚拟机比较多一些,越研究发现越有意思,越研究发现很多之前没弄明白的问题都慢慢融会贯通了。强烈建议大家有时间的话读读相关的书籍,研究一些用法的底层逻辑。

以上就是W3Cschool编程狮关于JVM的Xms和Xmx参数设置为什么要设置为相同值的相关介绍了,希望对大家有所帮助。

为什么配置文件加密了数据库配置信息,Spring Boot仍能成功连接数据库

thbcm阅读(287)

文章转载自公众号:java旅途

Spring Boot 最大的特点就是自动配置了,大大的减少了传统 Spring 框架的繁琐配置,通过几行简单的配置就可以完成其他组件的接入。比如你想要连接 mysql 数据库,只需要的配置文件里面加入 mysql 的一些配置信息就可以了。为了保护数据的安全性,越来越多的公司选择加密这些重要信息。接下来一起来看看如何实现配置加密文件并且成功连接数据库的。

配置信息加密有好几种方式,这里我只详细的写一下我比较常用的一种方式。首先通过某种加密算法将用户名和密码进行加密,然后在配置文件中用加密串代替原来的明文。然后自定义数据源,在自定义数据源中解密用户名和密码。

SpringBoot自动装配

Spring Boot 的自动装配,以前的推文中也详细的讲到过,今天简单来复习一下。在每个 Spring Boot 的应用的启动类上都能发现有一个注解@SpringBootApplication,这个注解包含的注解@EnableAutoConfiguration就是用来完成自动装配的。这个注解通过导入类AutoConfigurationImportSelector,这个类中有一个方法selectImports,其作用就是扫描所有jar包中的META-INF/spring.factories文件,去加载里面的具体实现类,完成自动装配。

spring-boot-autoconfigurejar包的META-INF/spring.factories文件中指定了一个类用来加载数据库配置信息,这个类就是org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration

自定义数据源

这里使用HikariDataSource作为自定义的数据源,自定义的数据源目的就是为了解密配置文件中的配置信息。

@Configuration
public class DataSourceConfiguration {


    @Autowired
    DataSourceProperties properties;


    @Bean
    public DataSource dataSource() throws Exception{
        String username = Des3.decryptThreeDESECB(properties.getUsername(),Des3.DES3KEY);
        String password = Des3.decryptThreeDESECB(properties.getPassword(),Des3.DES3KEY);
        HikariDataSource dataSource = new HikariDataSource();
        dataSource.setDriverClassName(properties.getDriverClassName());
        dataSource.setJdbcUrl(properties.getUrl());
        dataSource.setUsername(username);
        dataSource.setPassword(password);
        return dataSource;
    }
}

配置文件信息如下:

spring:
  datasource:
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://localhost:3306/xxx
    username: aMkeRCLWqNw=
    password: rq-fzucH32I=

具体的加解密算法这里就不在提及了,根据具体要求选择一种可逆的加密算法就可以了。

以上就是W3Cschool编程狮关于为什么配置文件加密了数据库配置信息,Spring Boot仍能成功连接数据库的相关介绍了,希望对大家有所帮助。

String中删除空格的7种方法!

thbcm阅读(298)

文章转载自公众号:Hollis

字符串,是 Java 中最常用的一个数据类型了。我们在日常开发时候会经常使用字符串做很多的操作。比如字符串的拼接、截断、替换等。

本文我们介绍一个比较常见又容易被忽略的一个操作,那就是移除字符串中的空格。

其实,在 Java 中从字符串中删除空格有很多不同的方法,如trimreplaceAll等。但是,在 Java 11 添加了一些新的功能,如stripstripLeadingstripTrailing等。

大多数时候,我们只是使用trim方法来删除多余的空格。但是好像很多人并没有去思考过,是否有更好的方式呢?

当然,trim()在大多数情况下都工作得很好,但是 Java 中有许多不同的方法。每一种都有自己的优点和缺点。我们如何决定哪种方法最适合我们呢?

接下来我们将介绍几种方法,并对比下他们的区别和优缺点等。

在java中从字符串中删除空格的不同方法

首先,我们来看一下,想要从String中移除空格部分,有多少种方法,作者根据经验,总结了以下7种(JDK原生自带的方法,不包含第三方工具类库中的类似方法):

  • trim() : 删除字符串开头和结尾的空格。
  • strip() : 删除字符串开头和结尾的空格。
  • stripLeading() : 只删除字符串开头的空格
  • stripTrailing() : 只删除字符串的结尾的空格
  • replace() : 用新字符替换所有目标字符
  • replaceAll() : 将所有匹配的字符替换为新字符。此方法将正则表达式作为输入,以标识需要替换的目标子字符串
  • replaceFirst() : 仅将目标子字符串的第一次出现的字符替换为新的字符串

需要注意的最重要的一点是,在 JavaString对象是不可变的,这意味着我们不能修改字符串,因此以上所有的方法我们得到的都是一个新的字符串。

接下啦,我们分别针对以上这几个方法学习下用法,了解下其特性。

PS:本文代码都是使用在线运行工具(www.jdoodle.com/online-java-compiler/)执行的,因为我的测试机并未安装 Java 11,并且Unicode字符也不完整。如果大家也想实验,建议使用在线工具,选择对应的 JDK 即可。

trim

trim()Java 开发人员最常用的删除字符串开头和结尾的空格方法。其用法也比较简单:

public class StringTest {


    public static void main(String[] args) {


        String stringWithSpace = "   Hollis   Is   A   Java   Coder   ";


        StringTest.trimTest(stringWithSpace);


    }


    private static void trimTest(String stringWithSpace){


        System.out.println("Before trim : \'" + stringWithSpace + "\'");


        String stringAfterTrim = stringWithSpace.trim();


        System.out.println("After trim : \'" + stringAfterTrim + "\'");


    }


}

输出结果:

Before trim : '   Hollis   Is   A   Java   Coder   '


After trim : 'Hollis   Is   A   Java   Coder'

如上,使用 trim 之后,原字符串中开头和结尾部分的空格内容都被移除掉了。

但是不知道大家有没有思考过,trim方法移除的空白内容都包含哪些东西?除了空格以外,还有其他的字符吗?

其实,trim移除的空白字符指的是指ASCII值小于或等于32的任何字符(‘ U+0020 ‘)

其中包含了空格、换行、退格等字符。

strip()

不知道大家有没有注意到,在Java 11的发行版中,添加了新的strip()方法来删除字符串中的前导和末尾空格。

已经有了一个trim方法,为什么还要新增一个strip呢?

这其实是是因为trim方法只能针对ASCII值小于等于32的字符进行移除,但是根据Unicode标准,除了ASCII中的字符以外,还是有很多其他的空白字符的。

而且为了识别这些空格字符,从 Java 1.5 开始,还在Character类中添加了新的isWhitespace(int)方法。该方法使用unicode来标识空格字符。你可以在jkorpela.fi/chars/spaces.html 了解更多关于unicode空格字符的信息。

而在 Java 11 中新增的这个strip方法就是使用这个Character.isWhitespace(int)方法来判断是否为空白字符并删除它们的:

下面我们来看一个使用strip例子:

public class StringTest {


    public static void main(String args[]) {


      String stringWithSpace ='\u2001' + "  Hollis   Is   A   Java   Coder  " + '\u2001';


        System.out.println("'" + '\u2001' + "' is space : " +  Character.isWhitespace('\u2001'));


        StringTest.stripTest(stringWithSpace);


    }


    private static void stripTest(String stringWithSpace){


        System.out.println("Before strip : \'" + stringWithSpace + "\'");


        String stringAfterTrim = stringWithSpace.strip();


        System.out.println("After strip : \'" + stringAfterTrim + "\'");


    }


}

我们在字符串前后都增加了一个特殊的字符\u2001,这个字符是不在ASCII中的,经过Character.isWhitespace判断他是一个空白字符。然后使用strip进行处理,输出结果如下:

' ' is space : true


Before strip : '   Hollis   Is   A   Java   Coder   '


After strip : 'Hollis   Is   A   Java   Coder'

所以,Java 11 中的 strip 方法要比trim方法更加强大,他可以移除很多不在ASCII中的空白字符,判断方式就是通过Character.isWhitespace方法。

trim 和 strip 方法的区别

上面我们介绍了两个都可以移除字符串开头和结尾的方法,分别是trimstrip,再来对比下他们的区别:

stripLeading() 和 stripTrailing()

stripLeading()和stripTrailing()方法也都是在Java 11中添加的。作用分别是删除字符串的开头的空格以及删除字符串的末尾的空格。

strip方法类似,stripLeadingstripTrailing也使用Character.isWhitespace(int)来标识空白字符。用法也和strip类似:

public class StringTest {


    public static void main(String args[]) {


      String stringWithSpace ='\u2001' + "  Hollis   Is   A   Java   Coder  " + '\u2001';


        System.out.println("'" + '\u2001' + "' is space : " +  Character.isWhitespace('\u2001'));


        StringTest.stripLeadingTest(stringWithSpace);


        StringTest.stripTrailingTest(stringWithSpace);


    }




    private static void stripLeadingTest(String stringWithSpace){


        System.out.println("Before stripLeading : \'" + stringWithSpace + "\'");


        String stringAfterTrim = stringWithSpace.stripLeading();


        System.out.println("After stripLeading : \'" + stringAfterTrim + "\'");


    }




     private static void stripTrailingTest(String stringWithSpace){


        System.out.println("Before stripTrailing : \'" + stringWithSpace + "\'");


        String stringAfterTrim = stringWithSpace.stripTrailing();


        System.out.println("After stripTrailing : \'" + stringAfterTrim + "\'");


    }


}

输出结果:

' ' is space : true


Before stripLeading : '   Hollis   Is   A   Java   Coder   '


After stripLeading : 'Hollis   Is   A   Java   Coder   '


Before stripTrailing : '   Hollis   Is   A   Java   Coder   '


After stripTrailing : '   Hollis   Is   A   Java   Coder'

replace

移除字符串中的空白字符,除了使用trimstrip以外,还有一个办法,那就是使用replace方法把其中的空白字符替换掉。

replace是从java 1.5中添加的,可以用指定的字符串替换每个目标子字符串。

此方法替换所有匹配的目标元素,使用方式如下:

 public class StringTest {


    public static void main(String args[]) {


        String stringWithSpace ="  Hollis   Is   A   Java   Coder  ";


        StringTest.replaceTest(stringWithSpace);


    }






    private static void replaceTest(String stringWithSpace){


        System.out.println("Before replace : \'" + stringWithSpace + "\'");


        String stringAfterTrim = stringWithSpace.replace(" ", "");


        System.out.println("After replace : \'" + stringAfterTrim + "\'");


    }


}

结果:

Before replace : '  Hollis   Is   A   Java   Coder  '


After replace : 'HollisIsAJavaCoder'

可见,以上使用replace方法可以替换掉字符串中的所有空白字符。特别需要注意的是,replace方法和trim方法一样,只能替换掉ASCII中的空白字符。

replaceAll

replaceAll是 Java 1.4 中添加的最强大的字符串操作方法之一。我们可以将这种方法用于许多目的。

使用replaceAll()方法,我们可以使用正则表达式来用来识别需要被替换的目标字符内容。使用正则表达式,就可以实现很多功能,如删除所有空格,删除开头空格,删除结尾空格等等。

我们只需要用正确的替换参数创建正确的正则表达式。一些正则表达式的例子如下:

\s+   所有的空白字符


^\s+      字符串开头的所有空白字符


\s+$      字符串结尾的所有空白字符

注意,在 java 中要添加/我们必须使用转义字符,所以对于\s+ 我们必须使用\\s+

public class StringTest {


    public static void main(String args[]) {


        String stringWithSpace ="  Hollis   Is   A   Java   Coder  ";


        StringTest.replaceAllTest(stringWithSpace," ");


        StringTest.replaceAllTest(stringWithSpace,"\\s+");


        StringTest.replaceAllTest(stringWithSpace,"^\\s+");


        StringTest.replaceAllTest(stringWithSpace,"\\s+$");


    }




    private static void replaceAllTest(String stringWithSpace,String regex){


        System.out.println("Before replaceAll with '"+ regex +"': \'" + stringWithSpace + "\'");


        String stringAfterTrim = stringWithSpace.replaceAll(regex, "");


        System.out.println("After replaceAll with '"+ regex +"': \'" + stringAfterTrim + "\'");


    }


}

结果:

Before replaceAll with ' ': '  Hollis   Is   A   Java   Coder  '


After replaceAll with ' ': 'HollisIsAJavaCoder'


Before replaceAll with '\s+': '  Hollis   Is   A   Java   Coder  '


After replaceAll with '\s+': 'HollisIsAJavaCoder'


Before replaceAll with '^\s+': '  Hollis   Is   A   Java   Coder  '


After replaceAll with '^\s+': 'Hollis   Is   A   Java   Coder  '


Before replaceAll with '\s+$': '  Hollis   Is   A   Java   Coder  '


After replaceAll with '\s+$': '  Hollis   Is   A   Java   Coder'

正如我们所看到的,如果将replaceAll()与适当的正则表达式一起使用,它将是非常强大的方法。

replaceFirst

replaceFirst方法也是在 java 1.4 中添加的,它只将给定正则表达式的第一个匹配项替换为替换字符串。

如果您只需要替换第一次出现的情况,那么这个方法非常有用。例如,如果我们只需要删除前导空格,我们可以使用\\s+^\\s+

我们还可以通过使用\\s+$正则表达式使用此方法来删除末尾空格。因为这个表达式将只匹配行的最后一个空格。因此最后的空格被认为是这个方法的第一个匹配。

让我们举一个从字符串中删除前导和尾随空格的例子

public class StringTest {


    public static void main(String args[]) {


        String stringWithSpace ="  Hollis   Is   A   Java   Coder  ";


        StringTest.replaceFirstTest(stringWithSpace," ");


        StringTest.replaceFirstTest(stringWithSpace,"\\s+");


        StringTest.replaceFirstTest(stringWithSpace,"^\\s+");


        StringTest.replaceFirstTest(stringWithSpace,"\\s+$");


    }




    private static void replaceFirstTest(String stringWithSpace,String regex){


        System.out.println("Before replaceFirst with '"+ regex +"': \'" + stringWithSpace + "\'");


        String stringAfterTrim = stringWithSpace.replaceFirst(regex, "");


        System.out.println("After replaceFirst with '"+ regex +"': \'" + stringAfterTrim + "\'");


    }


}

结果:

Before replaceFirst with ' ': '  Hollis   Is   A   Java   Coder  '


After replaceFirst with ' ': ' Hollis   Is   A   Java   Coder  '


Before replaceFirst with '\s+': '  Hollis   Is   A   Java   Coder  '


After replaceFirst with '\s+': 'Hollis   Is   A   Java   Coder  '


Before replaceFirst with '^\s+': '  Hollis   Is   A   Java   Coder  '


After replaceFirst with '^\s+': 'Hollis   Is   A   Java   Coder  '


Before replaceFirst with '\s+$': '  Hollis   Is   A   Java   Coder  '


After replaceFirst with '\s+$': '  Hollis   Is   A   Java   Coder'

总结

本文介绍了7种移除字符串中的空白字符的方法。

想要直接移除掉字符串开头的空白字符,可以使用stripLeadingreplaceAllreplaceFirst

想要直接移除掉字符串末尾的空白字符,可以使用stripTrailingreplaceAllreplaceFirst

想要同时移除掉字符串开头和结尾的空白字符,可以使用striptrim

想要移除掉字符串中的所有空白字符,可以使用replacereplaceAll

而 Java 11 种新增的stripstripTrailing以及stripLeading方法,可以移除的字符要比其他方法多,他可以移除的空白字符不仅仅局限于ASCII中的字符,而是Unicode中的所有空白字符,具体判断方式可以使用Character.isWhitespace进行判断。

以上就是W3Cschool编程狮关于String中删除空格的7种方法!的相关介绍了,希望对大家有所帮助。

联系我们