Java基础总结——String

1 String是不可变类

这句话其实大家都很熟悉了,那么具体什么是不可变类呢?一般认为:当对象一旦创建完成后,在正常情况下,对象的状态不会因外界的改变而改变(对象的状态是指对象的属性,包括属性的类型及属性值)。

例如:

String s = "Google";
System.out.println("s = " + s);

s = "Runoob";
System.out.println("s = " + s);

输出结果为:

s = Google
s = Runoob

从结果上看是改变了,但为什么门说String对象是不可变的呢?
原因在于实例中的 s只是指向堆内存中的引用,存储的是对象在堆中的地址,而非对象本身,s本身存储在栈内存中

当执行 s = "Runoob"; 创建了一个新的对象 "Runoob",而原来的 "Google" 还存在于内存中。

那么为什么String类具有不可变性呢,显然,既然不可变说明String类中肯定没有提供对外可setters方法。接下来来具体看一下String类的定义。

下面是String类中主要属性的定义(Java 1.7源码):

public final class String implements java.io.Serializable, Comparable<String>, CharSequence{
    
    /** The value is used for character storage. */
    private final char value[];
    
    /** Cache the hash code for the string */
    private int hash; // Default to 0
    
}

与之前版本的Java String源码相比,String类减少了int offset 和 int count的定义。这样变化的结果主要体现在:

  • 避免之前版本的String对象subString时可能引起的内存泄露问题;
  • 新版本的subString时间复杂度将有O(1)变为O(n);

通过上面String类的定义,类名前面用了final class修饰,因此,String类不能被继承。对于其属性定义,可以看出,属性value[]和hash都是被定义成private类型,且由于没有提供对外的public setters方法,String类属性不可被改变。

其中,需要重点关注属性value[],其被final char修饰,因此字符型数组value只会被赋值一次就不可修改。其存储内容正好是String中的单个字符内容。

2 常量池

JVM为了提高性能和减少内存开销,内部维护了一个字符串常量池,每当创建字符串常量时,JVM首先检查字符串常量池,如果常量池中已经存在,则返回池中的字符串对象引用,否则创建该字符串对象并放入池中。

因此下述结果返回true。

String a = "abc";
String b = "abc";
System.out.print(a == b); //true

但与创建字符串常量方式不同的是,当使用new String(String str)方式等创建字符串对象时,不管字符串常量池中是否有与此相同内容的字符串,都会在堆内存中创建新的字符串对象。

因此,下面代码片段有如下结果。

String a = "Hello";
String b = new String("Hello");
System.out.println(a == b);  //false
System.out.println(a.equals(b)); //true

即使字符串内容相同,字符串常量池中的字符串与通过new String(..)等方式创建的字符串对象之间没有直接的关系,但是,可以通过字符串的intern()方法找到此种关联。

intern()方法返回字符串对象在字符串常量池中的对象引用,若字符串常量池中尚未有此字符串,则创建一新的字符串常量放置于池中。

于是,很据如上理解,很自然的,可以得到如下结果。

String a = "Hello";
System.out.println(a == a.intern()); //true

String b = new String("corn");
String c = b.intern();

System.out.println(b == c); //false

String d = "corn";

System.out.println(c == d); //true

3 String相关 +

String中使用 + 字符串连接符进行字符串连接时,连接操作最开始时如果都是字符串常量,编译后将尽可能多的直接将字符串常量连接起来,形成新的字符串常量参与后续连接(通过反编译工具jd-gui也可以方便的直接看出);

接下来的字符串连接是从左向右依次进行,对于不同的字符串,首先以最左边的字符串为参数创建StringBuilder对象,然后依次对右边进行append操作,最后将StringBuilder对象通过toString()方法转换成String对象(注意:中间的多个字符串常量不会自动拼接)。

也就是说

String c = "xx" + "yy " + a + "zz" + "mm" + b;

实质上的实现过程是:

String c = new StringBuilder("xxyy").append(a).append("zz")
        .append("mm").append(b).toString();

由于得出结论:当使用+进行多个字符串连接时,实际上是产生了一个StringBuilder对象和一个String对象。

4 String/StringBuilder/StringBuffer区别

String是不可变字符串对象,StringBuilder和StringBuffer是可变字符串对象(其内部的字符数组长度可变),StringBuffer线程安全,StringBuilder非线程安全。

5 既然String是不可变字符串对象,如何才能改变让其可变?

既然String对象中没有对外提供可用的public setters等方法,因此只能通过Java中的反射机制实现。因此,前文中说到的String是不可变字符串对象只是针对"正常情况下"。而非必然。

public static void stringReflection() throws Exception {

    String s = "Hello World";

    System.out.println("s = " + s); //Hello World

    //获取String类中的value字段
    Field valueField = String.class.getDeclaredField("value");

    //改变value属性的访问权限
    valueField.setAccessible(true);

    char[] value = (char[]) valueField.get(s);

    //改变value所引用的数组中的第5个字符
    value[5] = '_';

    System.out.println("s = " + s); //Hello_World
}

6 String类常用方法

  1. 求字符串长度
    public int length()//返回该字符串的长度
String str = new String("asdfzxc");
int strlength = str.length();//strlength = 7
  1. 求字符串某一位置字符
    public char charAt(int index)//返回字符串中指定位置的字符;注意字符串中第一个字符索引是0,最后一个是length()-1。
String str = new String("asdfzxc");
char ch = str.charAt(4);//ch = z
  1. 提取子串
    用String类的substring方法可以提取字符串中的子串,该方法有两种常用参数:
    1)public String substring(int beginIndex)//该方法从beginIndex位置起,从当前字符串中取出剩余的字符作为一个新的字符串返回。
    2)public String substring(int beginIndex, int endIndex)//该方法从beginIndex位置起,从当前字符串中取出到endIndex-1位置的字符作为一个新的字符串返回。
String str1 = new String("asdfzxc");
String str2 = str1.substring(2);//str2 = "dfzxc"
String str3 = str1.substring(2,5);//str3 = "dfz"
  1. 字符串比较
    1)public int compareTo(String anotherString)//该方法是对字符串内容按字典顺序进行大小比较,通过返回的整数值指明当前字符串与参数字符串的大小关系。若当前对象比参数大则返回正整数,反之返回负整数,相等返回0。
    2)public int compareToIgnore(String anotherString)//与compareTo方法相似,但忽略大小写。
    3)public boolean equals(Object anotherObject)//比较当前字符串和参数字符串,在两个字符串相等的时候返回true,否则返回false。
    4)public boolean equalsIgnoreCase(String anotherString)//与equals方法相似,但忽略大小写。
String str1 = new String("abc");
String str2 = new String("ABC");
int a = str1.compareTo(str2);//a>0
int b = str1.compareTo(str2);//b=0
boolean c = str1.equals(str2);//c=false
boolean d = str1.equalsIgnoreCase(str2);//d=true
  1. 字符串连接
    public String concat(String str)//将参数中的字符串str连接到当前字符串的后面,效果等价于"+"。
String str = "aa".concat("bb").concat("cc");
相当于String str = "aa"+"bb"+"cc";
  1. 字符串中单个字符查找
    1)public int indexOf(int ch/String str)//用于查找当前字符串中字符或子串,返回字符或子串在当前字符串中从左边起首次出现的位置,若没有出现则返回-1。
    2)public int indexOf(int ch/String str, int fromIndex)//改方法与第一种类似,区别在于该方法从fromIndex位置向后查找。
    3)public int lastIndexOf(int ch/String str)//该方法与第一种类似,区别在于该方法从字符串的末尾位置向前查找。
    4)public int lastIndexOf(int ch/String str, int fromIndex)//该方法与第二种方法类似,区别于该方法从fromIndex位置向前查找。
String str = "I am a good student";
int a = str.indexOf('a');//a = 2
int b = str.indexOf("good");//b = 7
int c = str.indexOf("w",2);//c = -1
int d = str.lastIndexOf("a");//d = 5
int e = str.lastIndexOf("a",3);//e = 2
  1. 字符串中字符的大小写转换
    1)public String toLowerCase()//返回将当前字符串中所有字符转换成小写后的新串
    2)public String toUpperCase()//返回将当前字符串中所有字符转换成大写后的新串
String str = new String("asDF");
String str1 = str.toLowerCase();//str1 = "asdf"
String str2 = str.toUpperCase();//str2 = "ASDF"
  1. 字符串中字符的替换
    1)public String replace(char oldChar, char newChar)//用字符newChar替换当前字符串中所有的oldChar字符,并返回一个新的字符串。
    2)public String replaceFirst(String regex, String replacement)//该方法用字符replacement的内容替换当前字符串中遇到的第一个和字符串regex相匹配的子串,应将新的字符串返回。
    3)public String replaceAll(String regex, String replacement)//该方法用字符replacement的内容替换当前字符串中遇到的所有和字符串regex相匹配的子串,应将新的字符串返回。
String str = "asdzxcasd";
String str1 = str.replace('a','g');//str1 = "gsdzxcgsd"
String str2 = str.replace("asd","fgh");//str2 = "fghzxcfgh"
String str3 = str.replaceFirst("asd","fgh");//str3 = "fghzxcasd"
String str4 = str.replaceAll("asd","fgh");//str4 = "fghzxcfgh"

9、其他类方法
1)String trim()//截去字符串两端的空格,但对于中间的空格不处理。

String str = " a sd ";
String str1 = str.trim();
int a = str.length();//a = 6
int b = str1.length();//b = 4

2)boolean statWith(String prefix)boolean endWith(String suffix)//用来比较当前字符串的起始字符或子字符串prefix和终止字符或子字符串suffix是否和当前字符串相同,重载方法中同时还可以指定比较的开始位置offset。

String str = "asdfgh";
boolean a = str.statWith("as");//a = true
boolean b = str.endWith("gh");//b = true

3)regionMatches(boolean ignoreCase, int toffset, String other, int ooffset, int len)

  • ignoreCase -- 如果为 true,则比较字符时忽略大小写。
  • toffset -- 此字符串中子区域的起始偏移量。
  • other -- 字符串参数。
  • ooffset -- 字符串参数中子区域的起始偏移量。
  • len -- 要比较的字符数。
String Str1 = new String("www.runoob.com");
String Str2 = new String("runoob");
String Str3 = new String("RUNOOB");
String Str4 = new String("runaab");
String Str5 = new String("aaaoob");

System.out.println(Str1.regionMatches(4, Str2, 0, 5)); //true

System.out.println(Str1.regionMatches(4, Str3, 0, 5)); //false

System.out.println(Str1.regionMatches(true, 4, Str3, 0, 5)); //true

System.out.println(Str1.regionMatches(4, Str4, 0, 4)); //false

System.out.println(Str1.regionMatches(4, Str5, 2, 4)); //false

4)contains(String str)//判断参数s是否被包含在字符串中,并返回一个布尔类型的值。

String str = "student";
str.contains("stu");//true
str.contains("ok");//false

5)String[] split(String str)//将str作为分隔符进行字符串分解,分解后的字字符串在字符串数组中返回。

String str = "asd!qwe|zxc#";
String[] str1 = str.split("!|#");
//str1[0] = "asd";str1[1] = "qwe";str1[2] = "zxc";

7 字符串与基本类型的转换

  1. 字符串转换为基本类型
    java.lang包中有Byte、Short、Integer、Float、Double类的调用方法:
    1)public static byte parseByte(String s)
    2)public static short parseShort(String s)
    3)public static short parseInt(String s)
    4)public static long parseLong(String s)
    5)public static float parseFloat(String s)
    6)public static double parseDouble(String s)
    例如:
int n = Integer.parseInt("12");
float f = Float.parseFloat("12.34");
double d = Double.parseDouble("1.124");
  1. 基本类型转换为字符串类型
    String类中提供了String valueOf()放法,用作基本类型转换为字符串类型。
    1)static String valueOf(char data[])
    2)static String valueOf(char data[], int offset, int count)
    3)static String valueOf(boolean b)
    4)static String valueOf(char c)
    5)static String valueOf(int i)
    6)static String valueOf(long l)
    7)static String valueOf(float f)
    8)static String valueOf(double d)
    例如:
String s1 = String.valueOf(12);
String s1 = String.valueOf(12.34);
  1. 进制转换
    使用Long类中的方法得到整数之间的各种进制转换的方法:
    Long.toBinaryString(long l)
    Long.toOctalString(long l)
    Long.toHexString(long l)
    Long.toString(long l, int p)//p作为任意进制
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,222评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,455评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,720评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,568评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,696评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,879评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,028评论 3 409
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,773评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,220评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,550评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,697评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,360评论 4 332
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,002评论 3 315
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,782评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,010评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,433评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,587评论 2 350

推荐阅读更多精彩内容

  • 1. Java基础部分 基础部分的顺序:基本语法,类相关的语法,内部类的语法,继承相关的语法,异常的语法,线程的语...
    子非鱼_t_阅读 31,598评论 18 399
  • 前面我们总结了数组操作,这里我们将总结字符串相关的知识,除了总结String的API用法,同时我们还会总结一些相关...
    HCherisher阅读 3,614评论 2 6
  • 一、String类 String类在java.lang包中,java使用String类创建一个字符串变量,字符串变...
    wlw_花田半亩阅读 430评论 0 1
  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 134,633评论 18 139
  • 最近发生了太多太多的千奇百怪的事。好的坏的。积极的,堕落的,伤心的,开心的。一点一滴我都不知道如何梳理众多的情绪集...
    怡然自得的先知阅读 547评论 3 4