编程思想 之「字符串」

温馨提示:本系列博文(含示例代码)已经同步到 GitHub,地址为「java-skills」,欢迎感兴趣的童鞋StarFork,纠错。

字符串

字符串(String)对象是不可变的,把String对象作为方法的参数时,其实都是复制一份引用,而该引用所指的对象一直待在单一的物理位置上,从未动过。我们可以给一个String对象起任意多的别名,因为String对象具有只读特性,所以指向它的任何引用都不能改变它的值。字符串的不可变性会带来一定的效率问题,为String对象重载过的+操作符就是一个例子,其中重载的含义为:一个操作符在应用于特定的类时,被赋予了特殊的意义。在此,值得我们注意的是:用于String对象的++=是 Java 中仅有的两个重载过的操作符。

package com.hit.thought.chapter11;

/**
 * author:Charies Gavin
 * date:2018/3/11,14:20
 * https:github.com/guobinhit
 * description:连接字符串测试
 */
public class ConnectionString {
    public static void main(String[] args) {
        String hello = "Hello";
        String helloWorld = hello + "World";
        System.out.println(helloWorld);
    }
}

001

如上图所示,通过javap反编译ConnectionString.class文件后,我们可以看到:在进行字符串拼接的时候,编译器是自动引入了StringBuilder对象并调用其append()方法来实现字符串拼接的,这是编译器对我们的代码进行优化的结果,因为StringBuilder更高效。自然而然的,我们会想到用操作符进行字符串拼接的时候会产生很多需要垃圾回收器来回收的中间对象,这正是其效率较低的原因所在。

特别地,在循环中直接使用StringBuilder对象显然比使用操作符来处理String对象更高效。如果我们已经知道最终的字符串的大概长度,那么预先指定StringBuilder的大小更是可以避免多次自动扩容。因此,当我们为一个类编写toString()方法的时候,应该首选用StringBuilder对象来构造输出结果。此外,StringBuilder是 Java SE5 引入的,在这之前 Java 用的是StringBuffer,两者的 API 完全相同,唯一的区别是:StringBuffer是线程安全的,可以用于多线程。一般来说,

  • 对于操作效率而言,StringBuilder > StringBuffer > String
  • 对于线程安全而言,StringBuffer是线程安全的,可用于多线程;而StringBuilder是非线程安全的,用于单线程;
  • 对于频繁的字符串操作而言,无论是StringBuffer还是StringBuilder,都优于String

由于 Java 中所有类都继承根类Object,标准容器类自然也不例外。因此容器类都有toString()方法,并且覆盖了该方法,使得它生成的String结果能够表达容器自身,以及容器所包含的对象。如果我们希望toString()方法打印出对象的内存地址,也许我们会考虑使用this关键字,就像这样:

public class UnconsciousRecursion {
    public static void main(String[] args) {
        List<UnconsciousRecursion> list = new ArrayList<UnconsciousRecursion>();
        for (int i = 0; i < 5; i++) {
            UnconsciousRecursion ur = new UnconsciousRecursion();
            list.add(ur);
        }
        System.out.println(list);
    }

    @Override
    public String toString(){
        return "UnconsciousRecursion address: " + this + "\n";
    }
}

002

如上述代码及结果所示,在打印list的时候,发生了栈溢出,究其原因:

return "UnconsciousRecursion address: " + this + "\n";

这一行代码无意中触发了递归调用,因为如果字符串对象后面跟着+操作符,但+操作符后面却不是字符串对象的话,编译器就会强制将非字符串对象转换为字符串对象,而转换的方法正是调用this对象的toString()方法。因此,如果我们真想打印对象的内存地址的话,应该调用super.toString()而非this,就像这样:

return "UnconsciousRecursion address: " + super.toString() + "\n";

正则表达式

正则表达式又称规则表达式,在代码中常简写为regexregexpRE,是对字符串操作的一种逻辑公式,就是用事先定义好的一些特定字符及这些特定字符的组合,组成一个“规则字符串”,这个“规则字符串”用来表达对字符串的一种过滤逻辑。正则表达式的特点包括:

  • 灵活性、逻辑性和功能性非常的强;
  • 可以迅速地用极简单的方式达到字符串的复杂控制;
  • 对于初学者来说,比较晦涩难懂。

正则表达式对字符串的操作主要表现在三个方面,分别为:

  • 匹配;
  • 分割;
  • 替换。

对上述三个功能,String类也提供了一些方法进行支持,如matches()splitreplace等。

public class StringRegularExpression {
    public static void main(String[] args) {
        String str = "123_456_789";
        // matches() 方法用于匹配字符串
        System.out.println("匹配字符串:" + str.matches("-?\\S+"));
        // split() 方法用于分割字符串
        System.out.println("分割字符串:" + Arrays.toString(str.split("_")));
        // replace() 方法用于替换字符串
        System.out.println("替换字符串:" + str.replace("_", "$"));
    }
}

003

在 Java 中,\\的意思是“我要插入一个正则表达式的反斜线,其后面的字符具有特殊的意义”,如示例中我们用\\S+表示“一个或多个非空白符”。如果我们想要插入一个普通的反斜线,则应该使用\\\\,不过换行和制表符之类的符号只需要使用单反斜线,如\t等。在正则表达式中,括号()有着将表达式分组的效果,而竖线|则表示或操作。此外,在使用split()方法的时候,原始字符串中与正则表达式匹配的部分,在最终的结果中都不存在了。而且,如果正则表达式不是只使用一次的话,非String对象的正则表达式具有更佳的性能。为了更好的使用正则表达式对象,我们先来看看一些典型的字符类以及预定义的类:

字符类含义
.任意字符
[abc]包含 a、b 和 c 中的任一字符(和 a | b | c 的作用相同)
[^abc]除了 a、b 和 c 之外的任何字符
[a-zA-Z]从 a 到 z 或 从 A 到 Z 的任何字符
[abc[xyz]]包含 a、b、c、x、y 和 z 中的任一字符
[a-z&&[xyz]]包含 x、y 和 z 中的任一字符
\s空白符(空格、制表符、换行、回车等)
\S非空白符 [^\s]
\d数字[0-9]
\D非数字[^0-9]
\w词字符[a-zA-Z0-9]
\W非词字符[^\w]

接下来,再来看看边界匹配符:

边界匹配符含义
^一行的开始
$一行的结束
\b词的边界
\B非词的边界
\G前一个匹配的结果

此外,还有量词的概念,量词描述了一个模式吸收输入文本的方式,包含三种类型,分别为:

  • 贪婪型:为所有可能的模式发现尽可能多的匹配结果;
  • 勉强型:用问号来指定,匹配满足模式所需的最少字符数;
  • 占有型:仅 Java 语言中可用,不保存匹配的中间状态,常用于防止表达式失控。
贪婪型勉强型占有型如何匹配
X?X??X?+一个或零个 X
X*X*?X*?+零个或多个 X
X+X+?X++一个或多个 X
X{n}X{n}?X{n}+恰好 n 次 X
X{n,}X{n,}?X{n,}+至少 n 次 X
X{n,m}X{n,m}?X{n,m}+X 至少 n 次,且不超过 m 次

现在,我们一起来看看创建正则表达式对象的过程:

  • 首先,定义一个正则表达式字符串,如String regex = "\\d"
  • 然后,用Pattern类的静态compile方法编译正则表达式字符串生产Pattern对象,如Pattern.compile(regex)
  • 再调用Pattern对象的matcher()方法,生成一个Matcher对象;
  • 最后,使用Matcher对象提供的各种方法处理字符串。
public class RegularExpressionExample {
    public static void main(String[] args) {
        obtainRegex();
    }

    public static void obtainRegex() {
        String str = "Hi girl, I like you!";
        String regex = "\\b[a-z]{3}\\b";

        // 将正则表达式封装成对象
        Pattern pattern = Pattern.compile(regex);

        // 使用 Matcher 对象的方法对字符串进行操作,为了获取三个字母组成的单词,可以用查找 find() 方法
        Matcher matcher = pattern.matcher(str);
        System.out.println(str);

        while (matcher.find()) {
            // 获取匹配的字符串子序列
            System.out.println(matcher.group());
        }
    }
}

004

如上述所示,演示了如何通过PatternMatcher对象使用正则表达式,其中Pattern对象表示编译后的正则表达式,Matcher对象则提供了众多可供正则表达式使用的方法。例如,

  • find(),用来在CharSequence中查找多个匹配;
  • groupCount(),返回该匹配器的模式中的分组数目,不包括第 0 组;
  • group(),返回前一次匹配操作的第 0 组,即整个匹配;
  • group(i),返回前一次匹配操作期间指定的组号,可能返回null
  • reset(),将现有的Matcher对象应用于一个新的字符序列。

多数的正则表达式操作都接受CharSequence类型的参数。在 Unix / Linux 上,命令行中的正则表达式必须用引号括起来。此外,正则表达式中还有一个组的概念,组就是用括号划分的正则表达式,可以根据组的编号来引用某个组。组号为 0 表示整个表达式,组号为 1 表示被第一对括号括起来的组,依次类推。因此,在下面的表达式中

A(B(C(D)))E

中含有 4 个组,分别为:组 0 是 ABCDE,组 1 是 BCD,组 2 是 CD,组 3 是 D。

public class ObjectRegularExpression {
    /**
     * 自定义编译期常量
     */
    public static final String POEM = "If you were a teardrop in my eye,\n" +
            "For fear of losing you,\n" +
            "I would never cry.\n" +
            "And if the golden sun,\n" +
            "Should cease to shine its light,\n" +
            "Just one smile from you,\n" +
            "would make my whole world bright.";

    public static void main(String[] args) {
        // 定义正则表达式字符串,含义为:找出每行后三个单词
        String regex = "(?m)(\\S+)\\s+((\\S+)\\s+(\\S+))$";
        // 编译正则表达式字符串,获取 Pattern 对象
        Pattern pattern = Pattern.compile(regex);
        // 调用 Pattern 对象的 matcher() 方法,获取 Matcher 对象
        Matcher matcher = pattern.matcher(POEM);
        // 使用 find() 查找多个匹配结果
        while (matcher.find()) {
            // groupCount() 方法返回该匹配器的模式中的分组数目,不包括第 0 组
            for (int i = 0; i <= matcher.groupCount() ; i++) {
                // group(i) 返回前一次匹配的第 0 组,即整个匹配
                System.out.println("[" + matcher.group(i) + "]");
            }
        }
    }
}

005

如上述所示,我们用正则表达式(?m)(\\S+)\\s+((\\S+)\\s+(\\S+))$来匹配每行的后三个单词。正常来说,符号$是与整个输入序列的末端相匹配,但是为了让正则表达式注意到输入序列的换行符,我们通过输入序列开头的模式标记(?m)来完成。至于模式标记是什么?我们可以简单的将其理解为“作用于正则表达式,并让正则表达式起特定效果的标记”。


———— ☆☆☆ —— 返回 -> 那些年,关于 Java 的那些事儿 <- 目录 —— ☆☆☆ ————

Python网络爬虫与推荐算法新闻推荐平台:网络爬虫:通过Python实现新浪新闻的爬取,可爬取新闻页面上的标题、文本、图片、视频链接(保留排版) 推荐算法:权重衰减+标签推荐+区域推荐+热点推荐.zip项目工程资源经过严格测试可直接运行成功且功能正常的情况才上传,可轻松复刻,拿到资料包后可轻松复现出一样的项目,本人系统开发经验充足(全领域),有任何使用问题欢迎随时与我联系,我会及时为您解惑,提供帮助。 【资源内容】:包含完整源码+工程文件+说明(如有)等。答辩评审平均分达到96分,放心下载使用!可轻松复现,设计报告也可借鉴此项目,该资源内项目代码都经过测试运行成功,功能ok的情况下才上传的。 【提供帮助】:有任何使用问题欢迎随时与我联系,我会及时解答解惑,提供帮助 【附带帮助】:若还需要相关开发工具、学习资料等,我会提供帮助,提供资料,鼓励学习进步 【项目价值】:可用在相关项目设计中,皆可应用在项目、毕业设计、课程设计、期末/期中/大作业、工程实训、大创等学科竞赛比赛、初期项目立项、学习/练手等方面,可借鉴此优质项目实现复刻,设计报告也可借鉴此项目,也可基于此项目来扩展开发出更多功能 下载后请首先打开README文件(如有),项目工程可直接复现复刻,如果基础还行,也可在此程序基础上进行修改,以实现其它功能。供开源学习/技术交流/学习参考,勿用于商业用途。质量优质,放心下载使用。
项目描述:建立购物小商城平台. 实现了前台页面系统。 技术描述:通过Spring 主框架来管理Struts2和Hibernate 框架搭建的电商小平台,用MySQL数据库并创建了表有用户表,订单表,商品表,商品分类表,商品内容表,购物车表等来存储数据。用到hibernate….zip项目工程资源经过严格测试可直接运行成功且功能正常的情况才上传,可轻松复刻,拿到资料包后可轻松复现出一样的项目,本人系统开发经验充足(全领域),有任何使用问题欢迎随时与我联系,我会及时为您解惑,提供帮助。 【资源内容】:包含完整源码+工程文件+说明(如有)等。答辩评审平均分达到96分,放心下载使用!可轻松复现,设计报告也可借鉴此项目,该资源内项目代码都经过测试运行成功,功能ok的情况下才上传的。 【提供帮助】:有任何使用问题欢迎随时与我联系,我会及时解答解惑,提供帮助 【附带帮助】:若还需要相关开发工具、学习资料等,我会提供帮助,提供资料,鼓励学习进步 【项目价值】:可用在相关项目设计中,皆可应用在项目、毕业设计、课程设计、期末/期中/大作业、工程实训、大创等学科竞赛比赛、初期项目立项、学习/练手等方面,可借鉴此优质项目实现复刻,设计报告也可借鉴此项目,也可基于此项目来扩展开发出更多功能 下载后请首先打开README文件(如有),项目工程可直接复现复刻,如果基础还行,也可在此程序基础上进行修改,以实现其它功能。供开源学习/技术交流/学习参考,勿用于商业用途。质量优质,放心下载使用。
1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 、4下载使用后,可先查看README.md或论文文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。 5、资源来自互联网采集,如有侵权,私聊博主删除。 6、可私信博主看论文后选择购买源代码。 1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md或论文文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。 5、资源来自互联网采集,如有侵权,私聊博主删除。 6、可私信博主看论文后选择购买源代码。 1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看rEADME.md或论文文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。 5、资源来自互联网采集,如有侵权,私聊博主删除。 6、可私信博主看论文后选择购买源代码。
1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md或论文文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。 5、资源来自互联网采集,如有侵权,私聊博主删除。 6、可私信博主看论文后选择购买源代码。 1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md或论文文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。 5、资源来自互联网采集,如有侵权,私聊博主删除。 6、可私信博主看论文后选择购买源代码。 1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md或论文文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。 5、资源来自互联网采集,如有侵权,私聊博主删除。 6、可私信博主看论文后选择购买源代码。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

安正勋

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值