首先我们要知道,它是由class定义的引用类型。(演示转到定义&复习:值类型和引用类型)
但是,字符串的一系列行为表现得就像值类型一样!
一般来说,如果是(演示)
我们看看string的比较:
String fg = "飞哥"; String dfg = "飞哥"; //String dfg = new String("飞哥"); System.out.println(fg == dfg);
如果没有new String()捣乱,我们可以简单(但实际上错误)的理解为:String就像值类型一样……
但真正正确的答案是:fg和dfg就真的指向了同一对象!
eclipse演示:fg和dfg具有同一/不同的id(C#查看地址更方便)
在编译(注意:是编译的时候,不是运行的时候)的时候,编译器会设置一个字符串池(pool)。
每次要实例化一个新字符串的时候,首先在池中进行检查:
这样,就可以节省很多的堆空间,尤其是当相同的字符串非常多的时候。
PPT动画演示:
“池”这种优化技术,我们以后还会多次接触到。^_^
C#中“堵漏”堵得更严:
所以需要通过
String dfg = string.Join("", "飞", "哥");配合使用指针查看地址确认:
@想一想@:既然使用了池,多个变量可以指向同一个对象,那么如果修改一个变量(所对应这个对象),岂不是要影响所有其他(指向这个对象的)变量?
但实际上,这是不可能的:
你没有办法修改一个字符串,因为它:
PS:所有包装类也都这样,这种类被称之为不可更改类。
String newGreet = greet.replace('一', '1'); String newGreet = greet.replace("一起帮", "17bang");
String newGreet = greet.substring(4); String newGreet = greet.substring(0, 3);
String newGreet = greet.toUpperCase(); String newGreet = greet.toLowerCase();
String newGreet = greet.trim();
注意:所有的方法都不会改变字符串slagan本身,而是返回一个新的字符串。(@想一想@:为什么?)
String slagon = "大神小班,拎包入住"; slagon += "!"; System.out.println(slagon); //大神小班,拎包入住!
@想一想@:是这样的吗?
不,这是一个重新赋值的过程,等于:
slagon = slagon + "!";slagon完完全全的指向了另外一个对象。
如果不明白这一点,你就会对下面这个方法感到迷惑:
static void change(String slagon) { slagon += "!"; }方法change明明改变了传入参数slagon:调用方法:
change(slagon); System.out.println(slagon);
运行的结果,slagon没变,“感觉”就像是传递了一个slagon的副本给方法say()一样——当然其实并不一样。
上述代码类似于:
static void change(Person slagon) { slagon = new Person(); }
这样的代码会影响传入参数Person么?
无论如何,String在太多方面表现得和值类型类似,以至于JavaScript中字符串就是值类型,而C#中将string归为基本类型。
System.out.println(slagon.length()); //Java
Console.WriteLine(slagon.Length); //C#字符串转字符数组(char[]):
System.out.println(slagon.toCharArray().length); System.out.println(slagon.toCharArray()[0]);
这样转换之后,就可以对字符串的每个字符进行过滤筛选。
返回值为boolean值的判断,是否:
String slagon = ""; System.out.println(slagon.isEmpty()); //true注意和null值的区分,null值调用empty会直接报空引用异常
String slagon = null; System.out.println(slagon == null); //正确写法C#中使用IsNullOrEmpty()方法:
string.IsNullOrEmpty(slagon)
System.out.println(slagon.contains("小"));
System.out.println(slagon.startsWith("大")); System.out.println(slagon.endsWith("住"));
System.out.println(slagon.indexOf("小")); System.out.println(slagon.lastIndexOf("小"));
此外,还有连接和拆分:
System.out.println(slagon.concat("!"));
System.out.println(String.join("-", "atai", "fg", "bo")); //结果为:atai-fg-bo
String[] names = slagon.split("-"); for (int i = 0; i < names.length; i++) { System.out.println(names[i]); }
//%标记需要插入的位置 //s表示字符串 //f表示浮点数(小数),.2表示显示两位小数,不指定的话默认6为 String slagon = "大神(%s)小班,拎包入住(住宿费%.2f)"; //format System.out.println(String.format(slagon, "飞哥", 98.6)); //如果是98.6F的话且没有指定小数位数的话,会出现精度损失PS:C#的模板格式有所区别,分部内容细讲
你或许看到有这样的建议:不要使用加号(+)或concat进行字符串拼接,而总是应该使用StringBuilder。
#常见面试题:真的是这样么?#
同学们注意一定要警惕这样的绝对的简单化的论断。
你可以反过来想一想:StringBuilder和string是同时推出的,如果“用加号(+)进行字符串拼接”真的不行,为什么微软要搞这么一个语法出来?
虽说字符串被称之为“串”,但其实它不是像链表那样把字符一个一个串起来的,而是由一个字符数组(char[])予以存放。
演示:查看源代码
所以,(如果没有编译器优化)两个字符串a和b的拼接的过程应该是这样的:
这样的话,多个字符串的拼接就是一场灾难,比如这样的for循环代码:
String[] students = { "王新", "陈元", "彭志强" }; for (int i = 1; i < students.length; i++) { students[0] += students[i]; }比如说100次的循环,就会产生99次没有必要的字符串长度计算,99次没有必要的内存划分(需要new一个新的char[]),99(?)次没有必要的字符串复制……
为了节省掉这些不必要的性能损耗,StringBuilder被引入,其工作原理是:
对比如下图所示:
String slagon = "源栈" + "," + "欢迎您!";
StringBuilder sb = new StringBuilder(); for (int i = 0; i < students.length; i++) { sb.append(students[i]); sb.append('-'); } System.out.println(sb.toString());
转到定义演示:
public StringBuilder(int capacity);
所以,如果是大规模的字符串拼接,使用StringBuilder确实有性能上的优势。
那么为什么加号拼接仍然这么常见呢?
但是,你也要明白一点:懒,并不一定是坏事。尤其是对于程序员而言。
凡是涉及性能的问题,我们这已经是老生常谈了,一定要牢记几个原则:
复习:正则表达式
操作字符串最强大的工具!
用字符串存储正则表达式,可以进行:
多快好省!前端后端,线上线下,名师精讲
更多了解 加: