String类

实例化String对象

String 对象初始化方式有多种。

如下代码中,各种初始化方式的效果是一样的,初始化后,String 对象的内容为 "hello" 。

public static void main(String[] args) {
    // 直接赋值
    String str1 = "hello";
    
    // 构造函数方式,参数为 String
    String str2 = new String("hello");
    
    // 构造函数方式,参数为 StringBuilder
    String str3 = new String(new StringBuilder("hello"));
    
    // 构造函数方式,参数为 StringBuffer
    String str4 = new String(new StringBuffer("hello"));
    
    // 构造函数方式,参数为 char 数组
    char[] cz = {'h', 'e', 'l', 'l', 'o'};
    String str5 = new String(cz);
    
    // 构造函数方式,参数为 byte 数组
    byte[] bz = {'h', 'e', 'l', 'l', 'o'};
    String str6 = new String(bz);
    
    // 先使用默认构造函数,再赋值
    String str7 = new String();
    str7 = "hello";
}

以上方式可归纳为两类:

(1) 赋值方式

(2) 构造函数方式

当前的Java SE8中,String类有15种构造函数,详细看参见Java API。

传入的参数可以是String、StringBuilder、StringBuffer、char 数组、byte 数组等等。

两种实例化方式比较

一个字符串就是一个 String 类的匿名对象

匿名对象

匿名对象就是开辟了内存空间的并且可以直接使用的对象。

对于这样的代码:

String name = "Jack";               // 赋值方式初始化 String 对象

实际上就是在堆中开辟一个内存空间,这个空间的中存储的值为 "Jack"。然后这个空间被 name 变量所引用。

注:在JAVA中,如果一个字符串已经被一个名称所引用,则以后再有相同的字符串声明时,不会重新开辟空间,而是复用之前的空间。这样减少了不必要的空间开销。

String str1 = "hello";

String str2 = "hello";

String str3 = "hello";

以上三个String类变量本身是存放在栈内存中,但是它们指向同一块堆内存空间。

而如果使用构造函数方式初始化String类对象,和所有普通类一样,只要new一次,就会新开辟一块堆空间。

综上所述,可以看出赋值方式要优于构造函数方式

String的内容比较

1、使用 "=="

public static void main(String[] args) {

    String str1 = "hello";

    String str2 = new String("hello");

    String str3 = str2;

    System.out.println("str1 == str2 --> " + (str1 == str2));

    System.out.println("str1 == str3 --> " + (str1 == str3));

    System.out.println("str2 == str3 --> " + (str2 == str3));

}

运行结果

str1 == str2 --> false

str1 == str3 --> false

str2 == str3 --> true

从以上代码可以看出,虽然三个字符串内容完全一致,但是使用 "==" 去比较却发现并不完全相等。

这是因为每个String对象的内容实际上是保存在堆内存中的。所以,即使堆中的内容一致,并不代表它们的地址空间也一致。

"==" 是用来进行数值比较的,所以 str1str2 并不相等。

2、使用equals方法

如果要比较两个字符串的内容是否相等,可以使用 equals 方法。

public static void main(String[] args) {

    String str1 = "hello";

    String str2 = new String("hello");

    String str3 = str2;

    System.out.println("str1 == str2 --> " + (str1.equals(str2)));

    System.out.println("str1 == str3 --> " + (str1.equals(str3)));

    System.out.println("str2 == str3 --> " + (str2.equals(str3)));

}

运行结果

str1 == str2 --> true

str1 == str3 --> true

str2 == str3 --> true

String对象不可变

//: StringDemo03.java
public class StringDemo03 {
    public static String upcase(String s) {
        return s.toUpperCase();
    }
    public static void main(String[] args) {
        String a = "hello";
        System.out.println(a);    // hello
        String b = upcase(a);
        System.out.println(b);    // HELLO
        System.out.println(a);    // hello
    }
} /* Output:
hello
HELLO
hello
*///:~

当把a传给 upcase() 方法时,实际传递的是引用的一个拷贝。其实,每当把 String 对象作为方法的参数时,都会复制一份引用,而该引用所指的对象其实一直待在单一的物理位置上,从未动过。

所以指向 String 的任何引用都不可能改变它的值

不可变性会带来一定的效率问题。例如String类的重载操作符 "+"。

注:JAVA不同于C++,并不允许程序员自定义任何重载操作符。用于String的 "+" 和 "+=" 是JAVA中仅有的两个重载操作符。

操作符"+"可以用来拼接String。方式如下:

String s = "How " + "are " + "you?";
System.out.println(s);
/* Output:
How are you?
*///:~

这种方式的问题在于,会产生一大堆需要垃圾回收的中间对象。

那么,如何避免这种问题呢?

JAVA中提供了两个类:StringBuilderStringBuffer ,它们都有 append() 方法,效率高于 String 的 "+"。

这两个类的差别在于 StringBuffer线程安全的,因此开销也更大一些。

String类的常用方法

以下代码是String类的一些常用方法。

public class StringDemo {
    public static void main(String[] args) {
        // 获取字符串字符个数
        System.out.println(" Goodbye ".length());

        // 获取 String 中该索引位置上的 char
        System.out.println("Computer".charAt(4));

        // 复制 byte 到一个目标数组
        byte bytes[] = "Winter".getBytes();                // 将字符串转为 byte 数组
        System.out.println(new String(bytes));            // 将完整 byte 数组转为字符串
        System.out.println(new String(bytes, 1, 3));    // 将部分 byte 数组转为字符串

        // 复制 char 到一个目标数组
        char chars[] = new char[10];
        "Summer".getChars(0, 6, chars, 2);                // 将字符串0~6位置的内容拷贝到 char 数组中,从数组位置2开始
        System.out.println(new String(chars));            // 将完整 char 数组转为字符串
        System.out.println(new String(chars, 1, 3));    // 将部分 char 数组转为字符串

        // 字符串转char数组
        char[] data1 = "Baby".toCharArray();
        for (char c : data1) {
            System.out.print(c + " ");
        }
        System.out.println();

        // 如果String不包含此参数,返回-1,否则返回此参数在String中的起始索引。lastIndexOf是从后向前查找
        System.out.println("How are you".indexOf("o"));            // 查找返回位置
        System.out.println("How are you".indexOf("o", 5));        // 查找返回位置, 从位置5开始
        System.out.println("How are you".indexOf("z"));            // 没有查到返回-1
        System.out.println("How are you".lastIndexOf("o"));        // 查找返回位置
        System.out.println("How are you".lastIndexOf("o", 5));    // 查找返回位置, 从位置5开始
        System.out.println("How are you".lastIndexOf("z"));        // 没有查到返回-1

        // 根据参数截取字符串
        System.out.println("Hello World".substring(6));            // 从位置6开始截取
        System.out.println("Hello World".substring(0, 5));        // 截取0~5个位置的内容

        // 按照指定字符拆分字符串
        String[] s = "sample@sina.com".split("@");
        for (int i = 0; i < s.length; i++) {
            System.out.println(s[i]);
        }
        
        // 去除左右空格
        System.out.println("    Night       ".trim());            // 去除左右空格输出

        // 转换大小写
        System.out.println("China".toLowerCase());
        System.out.println("China".toUpperCase());

        // 判断是否以指定的字符串开头或结尾
        if ("**NAME".startsWith("**")) {
            System.out.println("**NAME 以**开头");
        }
        if ("NAME**".endsWith("**")) {
            System.out.println("NAME** 以**结尾");
        }

        // 替换源子字符串为目标子字符串
        System.out.println("good".replaceAll("o", "x"));
    }
}

StringBuilder类

注意

String类是不可改变的,所以你一旦创建了String对象,那它的值就无法改变了。 如果需要对字符串做很多修改,那么应该选择使用StringBuffer & StringBuilder 类。

StringBuilder 类的用法大部分与 String 类相似。

StringBuilder 类的字符串连接操作 append() 效率高于 String 类。而且此方法返回一个 StringBuilder 类的实例,这样就可以采用链式方式一直调用 append() 方法。

示例代码如下:

StringBuilder str = new StringBuilder();
str.append("How ").append("are ").append("you?");
System.out.println(str);
/* Output:
How are you?
*///:~

StringBuffer类

StringBuffer 类和 StringBuilder 类大致相同。

但是 StringBuffer线程安全的,因此开销也更大一些。

参考资料

JAVA 编程思想

JAVA 开发实战经典

[Java 基础]字符串的更多相关文章

  1. Java基础-字符串(String)常用方法

    Java基础-字符串(String)常用方法 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任. 一.java的API概念 Java的API(API:Application(应用) Pr ...

  2. Java基础-字符串连接运算符String link operator

    Java基础-字符串连接运算符String link operator 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任. 字符串链接运算符是通过“+”进行拼接的. 一.使用“+”进行字 ...

  3. java基础 字符串 “==” 和 “equals” 比较

    demo: public class TestStringEquals { public static void main(String[] args) { String a = "test ...

  4. [Java基础]字符串

    1.字符串特点 字符串是常量,创建之后不能修改: 字符串的内容一旦修改,就会马上创建一个新的对象: 字符串实际为一个char value[]={'a','a'};数组: 2.==与equal判断字符串 ...

  5. java基础---字符串string

    1.字符创的概念 java字符串就是Unicode字符序列.例如,串“Java\u2122”由5个Unicode字符J.a.v.a和TM.java没有内置的字符串类型,而是在标准库Java类库中提供了 ...

  6. Java基础——字符串构建器

    StringBuilder类: 可以将许多小段的字符串构建一个字符串. StringBuilder builder = new StringBuilder(); //构造一个空的字符串构建器 buil ...

  7. Java基础——字符串String

    String类 1. String类位于java.lang包中,使用时无需导包. 2. 创建字符串的两种方式: ① 直接指定(字面量声明):String str = "abc"; ...

  8. Java基础 -- 字符串(格式化输出、正则表达式)

    一 字符串 1.不可变String String对象是不可变的,查看JDK文档你就会发现,String类中每一个看起来会修改String值的方法,实际上都是创建一个全新的String对象,以包含修改后 ...

  9. Java基础 - 字符串 String

    字符串就是用字符拼接成的文本值,字符串在存储上类似数组,在java语言中把字符串当做对象进行处理 创建字符串 package com.mingri.chapter_02; public class d ...

随机推荐

  1. 浅谈 facebook .net sdk 应用

    今天看了一篇非常好的文章,就放在这里与大家分享一下,顺便也给自己留一份.这段时间一直在学习MVC,另外如果大家有什么好的建议或者学习的地方,也请告知一下,谢谢. 这篇主要介绍如何应用facebook ...

  2. java 调用 C# 类库搞定,三步即可,可以调用任何类及方法,很简单,非常爽啊

    java 调用 C# 类库搞定,三步即可,可以调用任何类及方法,很简单,非常爽啊 java 调用 C# 类库搞定,可以调用任何类及方法,很简单,非常爽啊 总体分三步走: 一.准备一个 C# 类库 (d ...

  3. pdf.js在IIS中配置使用笔记

    最近在手机App开发Android版本时候遇到需要显示PDF文件的需求,记得之前直接使用系统浏览器或者WebView就可以显示,但是现在不可以了,只能另寻其他办法. 最终找到PDF.JS来进行实现,但 ...

  4. 【Python】调用WPS V9 API,实现Word转PDF

    WPS 的API,即COM,主要分为V8与V9两个版本,网上容易查到的例子,都是V8的. 现在官网上可以下载的,2013抢鲜版,就是V9的API. Python 调用COM 需要安装 Python f ...

  5. 上层建筑——DOM元素的特性与属性(dojo/dom-prop)

    上一篇讲解dojo/dom-attr的文章中我们知道在某些情况下,attr模块中会交给prop模块来处理.比如: textContent.innerHTML.className.htmlFor.val ...

  6. 关于javascript模块加载技术的一些思考

    前不久有个网友问我在前端使用requireJs和seajs的问题,我当时问他你们公司以前有没有自己编写的javascript库,或者javascript框架,他的回答是什么都没有,他只是听说像requ ...

  7. Java提高篇(三二)-----List总结

    前面LZ已经充分介绍了有关于List接口的大部分知识,如ArrayList.LinkedList.Vector.Stack,通过这几个知识点可以对List接口有了比较深的了解了.只有通过归纳总结的知识 ...

  8. asp.net 验证码session为null的解决方案

    最近在做Y集团的订单系统时,登陆页面在测试时发现一个以前没有注意到的问题,登陆页面需要使用验证码,引用了一个生成验证码的aspx页面,在aspx页面中生成session和验证码图片,在登陆页面的后台处 ...

  9. Redis集群~StackExchange.redis连接Sentinel服务器并订阅相关事件(原创)

    回到目录 对于redis-sentinel我在之前的文章中已经说过,它是一个仲裁者,当主master挂了后,它将在所有slave服务器中进行选举,选举的原则当然可以看它的官方文章,这与我们使用者没有什 ...

  10. Bootstrap~Panel和Table

    回到目录 在我们对一个页面进行设计时,分块是必须的,没有一个网站是一栏而下的,除非你是在看小说,否则你的页面设计一定是分块的,即它由于多个panel组件,在bootstrap里叫到栅格系统,而在每行每 ...