一、什么是字符串?
一般来说:
我们说字符串一般指用双引号“”括住的。例如:“aasdfasf”,这是字符串 用单引号’ '括住的,例如:‘asdfasf’,‘你好’,这些都是字符
专业来说:
字符串就是一个或多个字符组成的连续序列,程序需要存储的大量文字、字符都使用字符串进行表示、处理。
二、创建字符串
字符串的创建方法有以下几种:
- 使用字符串常量直接初始化
- 使用构造方法创建并初始化
- 串联方式生成字符串
public static void main(String[] args) {
String str1="bingo";
String str2=new String("bingo1");
String str3="bin"+"go";
}
三、字符串的比较
1.==与equals
==完成的是两个对象的堆内存地址相等判断 equals完成的是字符串内容的比较
String str1 = "Hello";
String str2 = "Hello";
String str3 = new String("Hello");
System.out.println(str1 == str2);
System.out.println(str1 == str3);
System.out.println(str1.equals(str2));
System.out.println(str1.equals(str3));
结果如下: 为什么会出现这样的结果呢?在这里我们需要先了解一下什么是字符串常量池。
2.字符串常量池
为了提高性能和减少开销,JVM提供了一个特殊的内存——常量池。 常量池,就是存储常量的内存空间,有String字符串常量池、包装类常量池等。
public static void main(String[] args) {
String str1 = "Hello";
String str2 = "Hello";
String str3 = new String("Hello");
System.out.println(str1 == str2);
System.out.println(str1 == str3);
System.out.println(str1.equals(str2));
System.out.println(str1.equals(str3));
}
在上面创建字符串有两种方式,一是直接赋值,二是采用构造方法。 那么它们二者在创建时,内存是如何分配的呢?
2.1直接赋值内存分配
我们先来看直接赋值: 如果字符串对象使用直接赋值方式完成,那么str1在第一次定义字符串的时候会自动在堆内存之中定义一个新的字符串常量“hello”,如果之后还有其他字符串对象,比如说str2也是采用直接赋值方式实例化,并且此内容已经存在,那么就不会开辟新的字符串常量,而是直接指向已经开辟的空间,也就是同样指向“hello”开辟的空间。 上面的这种模式,我们称为共享设计模式。
【共享设计模式】
在JVM底层,准备出一个对象池(多个对象),如果现在按照某一个特定方式进行对象实例化的操作,那么次对象的内容会保存到对象池之中,而后如果还有其他的对象也采用了固定的方式声明了与之相同的内容,则此时将不会保存新对象到对象池之中,而是从对象池中取出已有的对象内容继续使用。这样一来就可以有效的减少垃圾空间 的产生。
2.2构造方法内存分配
public static void main(String[] args) {
String str1 = "Hello";
String str2 = "Hello";
String str3 = new String("Hello");
String str4 = new String("Hello");
System.out.println(str1 == str2);
System.out.println(str1 == str3);
System.out.println(str1.equals(str2));
System.out.println(str1.equals(str3));
}
通过分析,我们可以看到,两次采用构造方法创建字符串,开辟了两块块新的堆内存,并且内容与已有的堆内存一模一样,这就意味着有一块堆内存将称为垃圾。 在这个时候如果我们想解决这个问题,可以采用String类中的intern()方法手工入池。
在这个时候,内容格局指向变化如下图:
四、字符串不可变
字符串的内容一旦定义,就表示开辟好了指定的空间,那么字符串的内容就不能被改变
我们看到上面这个例子,不禁疑惑:不是说字符串内容不能改变吗?为什么这里改变了?
其实并不是的,我们画图分析: 可以看出,字符串内容并没有改变,改变的知识String类对象的引用,并且会产生大量字符串垃圾。
因此,应该尽量避免出现不断修改字符串内容的现象,以免出现大量垃圾。
五、String类的常见用法
1.字符与字符串
方法名称 | 描述 |
---|
public String(char[] value) | 将接收到的字符数组变为字符串 | public String(char[] value,int offset,int count) | 将部分字符数组变为字符串 | public char charAt(int index) | 返回指定索引位置上的字符内容 | public char[] toCharArray() | 将字符串变为字符数组 |
求字符串中指定位置的字符:
public static void main(String[] args) {
String str="Chinese";
System.out.println(str.charAt(1));
}
再看下一个例子:
public static void main(String[] args) {
String str = "helloworld" ;
char[] data = str.toCharArray() ;
for (int i = 0; i < data.length; i++) {
System.out.print(data[i]+" ");
}
System.out.println(new String(data));
System.out.println(new String(data,5,5));
}
2.字节与字符串
方法名称 | 描述 |
---|
public String(byte[] bytes) | 将全部字节数组变为字符串 | public String(byte[] bytes,int offset,int length) | 将部分字节数组变为字符串 | public byte[] getBytes | 将字符串变为字节数组 | public byte[] getBytes(String charsetName) throws UnsuppotedEncodingException | 将字符串转码 |
public static void main(String[] args) {
String str = "helloworld" ;
byte[] data = str.getBytes() ;
for (int i = 0; i < data.length; i++) {
System.out.print(data[i]+" ");
}
System.out.println(new String(data));
System.out.println(new String(data,4,3));
}
二者使用场景:
- byte[] 是把 String 按照一个字节一个字节的方式处理, 这种适合在网络传输, 数据存储这样的场景下使用. 更适合
针对二进制数据来操作. - char[] 是吧 String 按照一个字符一个字符的方式处理, 更适合针对文本数据来操作, 尤其是包含中文的时候.
3.字符串查找
方法名称 | 描述 |
---|
public boolean contains(String s) | 判断某一个子字符串是否存在,JDK1.5之后提供 | public int indexOf(String str) | 由前向后查找指定子字符串的位置,找不到返回-1 | public int indexOf(String str,int fromIndex) | 由指定位置查找字符串位置,找不到返回-1 | public int lastIndexOf(String str) | 由后向前查找指定字符串位置,找不到返回-1 | public int lastIndexOf(String str,int fromIndex) | 由指定位置从后向前查找 | public boolean startsWith(String prefix) | 判断是否以指定的字符串开头 | public boolean startsWith(String prefix,int toffset) | 从指定位置开始判断是否以指定的字符串开头 | public boolean endsWith(String suffix) | 判断是否以指定的字符串结尾 |
public static void main(String[] args) {
String str="**hello$$world##";
if(str.contains("hello"));
System.out.println("内存存在");
if(str.indexOf("r")!=-1){
System.out.println("内容存在,字符串位置:"+str.indexOf("r"));
}
if(str.indexOf("o",6)!=-1){
System.out.println("内容存在,字符串位置:"+str.indexOf("o",6));
}
if(str.lastIndexOf("w",12)!=-1){
System.out.println("内容存在,字符串位置:"+str.lastIndexOf("w",12));
}
System.out.println(str.startsWith("**"));
System.out.println(str.startsWith("$$",7));
System.out.println(str.endsWith("##"));
}
4.字符串替换
public static void main(String[] args) {
String str="helloworld";
System.out.println(str.replaceAll("o","@@"));
System.out.println(str.replaceFirst("l","***"));
}
【注意】
由于字符串是不可变对象, 替换不修改当前字符串, 而是产生一个新的字符串
5.字符串拆分
public static void main(String[] args) {
String str="hello world welcome";
String data[]=str.split(" ");
for(int i=0;i<data.length;i++){
System.out.println(data[i]);
}
System.out.println("========================");
String data1[]=str.split(" ",2);
for(int i=0;i<data1.length;i++){
System.out.println(data1[i]);
}
}
【注意】
- 字符"|","*","+“都得加上转义字符,前面加上”".
- 而如果是"",那么就得写成"\”
- 如果一个字符串中有多个分隔符,可以用"|"作为连字符.
6.字符串截取
public static void main(String[] args) {
String str="Hellojava!!!";
System.out.println(str.substring(5));
System.out.println(str.substring(0,4));
}
7.常用字符串方法
public static void main(String[] args) {
String str="Hello World ...";
System.out.println("原始字符串内容【"+str+"】"+",长度:"+str.length());
System.out.println("原始字符串内容【"+str.trim()+"】"+",长度:"+str.trim().length());
System.out.println("".isEmpty());
System.out.println(str.isEmpty());
System.out.println("Hello World".toUpperCase());
System.out.println("Hello World".toLowerCase());
System.out.println(str.concat("java"));
}
六、StringBuffer 和 StringBuilder
我们在上面说过,String类对象的内容一旦被初始化就不能再改变,对于String类的每次改变(例如字符串连接等)都会生成一个新的字符串,比较浪费内存。
那么我们如何修改字符串呢?
StringBuffer类 和 StringBuilder类用于内容可以改变的字符串,二者大部分功能相同。 String和StringBuffer最大的区别在于:String的内容无法修改,而StringBuffer的内容可以修改。频繁修改字符串的 情况考虑使用StingBuffer。
public static void main(String[] args) {
StringBuffer sb = new StringBuffer();
sb.append("Hello").append("World");
fun(sb);
System.out.println(sb);
}
public static void fun(StringBuffer temp) {
temp.append("\n").append("........");
}public static void main(String[] args) {
StringBuffer sb = new StringBuffer();
sb.append("Hello").append("World");
fun(sb);
System.out.println(sb);
}
public static void fun(StringBuffer temp) {
temp.append("\n").append("........");
}
StringBuffer 的其他功能:
字符串反转:public synchronized StringBuffer reverse()
public static void main(String[] args) {
StringBuffer sb = new StringBuffer("helloworld");
System.out.println(sb.reverse());
}
删除指定范围的数据:public synchronized StringBuffer delete(int start, int end)
public static void main(String[] args) {
StringBuffer sb = new StringBuffer("helloworld");
System.out.println(sb.delete(5, 10));
}
删除指定范围的数据:public synchronized StringBuffer delete(int start, int end)
public static void main(String[] args) {
StringBuffer sb = new StringBuffer("helloworld");
System.out.println(sb.delete(5,10));
}
插入数据:public synchronized StringBuffer insert(int offset, 各种数据类型 b)
public static void main(String[] args) {
StringBuffer sb = new StringBuffer("helloworld");
System.out.println(sb.delete(5, 10).insert(0, "你好"));
}
String、StringBuffer、StringBuilder的区别
- String的内容不可修改,StringBuffer与StringBuilder的内容可以修改
- StringBuffer与StringBuilder大部分功能是相似的
- StringBuffer采用同步处理,属于线程安全操作;而StringBuilder未采用同步处理,属于线程不安全操作
近来反思
人最大的痛苦便是不自知,面对未来的恐惧而方向尽失,坠入迷途。无法确定自己将要去哪,为何坚持。望得太远,便会丢失当下,沉迷当下,便会囿于自我,万物总是平衡才得恰当,多一点少一点都不行。既然无法预知未来,就先把手上事情搞好!
|