String的小知识点

String 类

字符串广泛应用在Java 编程中,在 Java 中字符串属于对象,Java 提供了 String 类来创建和操作字符串。

String的两点特殊性:

  • 长度不可变
  • 值不可变

这两点从源码中对String的声明可以体现:

 private final char[] value ;

String 类是final类,不可以被继承。
String 底层用char[] 实现,故String的长度为2^16-1

1.常用方法:
int length()//获取字符串长度 
char charAt(int index);             //获取字符
int indexOf(String str);            //str字符串第一次出现的位置  
boolean equals(str);                //判断字符串内容是否相同
boolean contains(str);              //判断是否包含某一字符串
String trim()//去除两端多余空格     
String substring(begin,end);        //截取一部分,包头不包尾    
String replace(oldstr,newstr);      //替代
String contact(str)//连接字符串  
String toUpperCase(str):            //大写
String toLowerCase(str);            //小写

2.String值的不可变

举个栗子:

String str1 = "Hello";
String str2 = "World";
str1 = str1 + str2;

内存分析如下:

在这里插入图片描述

我们可以看到,初始String值为“hello”,然后在这个字符串后面加上新的字符串“world”,这个过程是需要重新在栈堆内存中开辟内存空间的,最终得到了“hello world”字符串也相应的需要开辟内存空间,String的值是不可变的,这就导致每次对String的操作都会生成新的String对象。而丢弃的对象仍会在内存中滞留,在被垃圾回收之前都有数据泄露的风险。

问:对于敏感信息,为何使用char[]要比String更好?

String是不可变对象,一旦创建,那么整个对象就不可改变,直到垃圾收集器将它回收走。String值的改变,实际上只是指针引用指向了另一个新的对象。而字符数组中的元素是可以更改的,即可以在使用完之后将其更改,而不会保留原始的数据。所以使用字符数组的话,安全保密性高的信息(如密码之类的)将不会存在于系统中被他人看到。

3.String在JVM中内存驻留问题

JVM的常量区(Constant Pool)中维持了大部分创建的string字符串的驻留 ( String Interning ) 。

举例, Sring a=“ABC”;String b=“ABC”;

当 JVM 为 a 赋值时会在常量区生成一个字符串常量(String Constant),当 b 也赋值为“ABC”时,那么会在常量池中查看是否存在值为 “ABC” 的常量,存在的话,则把 b 的指针也指向 “ABC” 的地址,而不是新生成一个字符串常量(String Constant)。
JDK1.6中Interned Strings存储在永久代(Permanent Space)的常量池中;

JDK1.7中Interned Strings已经不再存储在永久代(Permanent Space)中,而是放到了Heap(堆)中;

JDK8中Permanent Space已经被完全移除,Interned Strings也被放到了元空间(MetaSpace)中。

4.字符串和数字互相转换

//字符串转数字
String str = "123";
//方法一
Integer integer = new Integer(str);
System.out.println(integer);
//方法二
Integer i = Integer.parseInt(str);
System.out.println(i);
//方法三
Integer num = Integer.valueOf(str);
System.out.println(num);

//将数字转为字符串
Integer age = 123;
//方法一
String string = String.valueOf(age);
System.out.println(string);
//方法二
System.out.println(Integer.toString(age));

5.如何重复拼接同一字符串?

使用 StringBuilder 构造

String src = "name";
int len = src.length();
int repeat = 5;
StringBuilder builder = new StringBuilder(len * repeat);
for(int i=0; i<repeat; i++){
  builder.append(src);
}
String dst = builder.toString();  

补充 StringBuilder 的小知识:

//StringBuilder中封装的是容量(capacity)
StringBuilder builder = new StringBuilder(int capacity);  //这就是为什么上面用len*repeat

//当封装字符串时,源码中会执行这样的过程: super(str.length() + 16);
StringBuilder builder = new StringBuilder(String str); //将字符串str放入builder

6.如何将String转换为日期?

SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd");
String str = "2013-11-07";
Date date = format.parse(str);
System.out.println(format.format(date));//2013-11-07

7.如何统计某个字符出现的次数?

将该字符用空字符替换,然后用前后的字符串长度求差

String str = "hfghahhdfhkah";
int oldLength = str.length();
String string = str.replace("h","");
int newLength = string.length();
System.out.println(oldLength-newLength);

8.如何通过空白字符拆分字符串

String 的 split()方法接收的字符串会被当做正则表达式解析,
“\s” 代表空白字符,如tab制表符 “\t”,换行"\n",回车"\r"。
而编译器在对源代码解析时,也会进行一次字面量转码,所以需要"\\s".

String[] strArray = aString.split("\\s+");  

例子:

String string = new String("I am a student");
String[] strings = string.trim().split("\\s");
for(String s:strings){
    System.out.println(s);
}

9.拼接字符串

String a = "ab";
String b = "abc";
String c = a +"c";
System.out.println(a==c);  //返回false

java支持可以直接用+号对两个字符串进行拼接。其真正实现的原理是:
中间通过建立临时的StringBuilder对象,然后调用append方法实现,最后调用 toString() 将StringBuilder字符串转化为 String 字符串。

大致内存过程

1)常量池创建“ab”对象,并赋值给st1,所以st1指向了“ab”
2)常量池创建“abc”对象,并赋值给st2,所以st2指向了“abc”
3)由于这里走的+的拼接方法,所以第三步是使用StringBuffer类的append方法,得到了“abc”,这个时候内存0x0011表示的是一个StringBuffer对象,注意不是String对象。
4)调用了Object的 toString() 把StringBuffer对象装换成了String对象。
5)把String对象赋值给st3

> 变式
 
public class Demo2_String {
 
   public static void main(String[] args) {
     String str1 = "a" + "b" + "c";  //常量池中
     String str2 = "abc";   //常量池中
     System.out.println(st1 == st2);  //true
     System.out.println(st1.equals(st2));  //true
   }
}

“a”,”b”,”c”三个本来就是字符串常量,进行+符号拼接之后变成了“abc”,“abc”本身就是字符串常量(Java中有常量优化机制),所以常量池立马会创建一个“abc”的字符串常量对象,在进行st2=”abc”,这个时候,常量池存在“abc”,所以不再创建。所以,不管比较内存地址还是比较字符串序列,都相等。

10.判定定义为String类型的st1和st2是否相等,为什么?

public class Demo2_String {
  public static void main(String[] args) {
    String st1 = "abc";
    String st2 = "abc";
    System.out.println(st1 == st2);  //true
    System.out.println(st1.equals(st2));  //true
  }

}

分析:
第一个打印语句:
在Java中 == 这个符号是比较运算符,它可以基本数据类型和引用数据类型是否相等。

如果是
基本数据类型,==比较的是值是否相等,
如果是
引用数据类型,==比较的是两个对象的内存地址是否相等

字符串不属于基本数据类型,字符串对象属于引用数据类型,在上面把“abc”同时赋值给了st1和st2两个字符串对象,指向的都是同一个地址,所以第一个打印语句中的==比较输出结果是 true

第二个打印语句:
equals是Object这个父类的方法,在String类中重写了这个equals方法。由于st1和st2的值都是“abc”,两者指向同一个对象,当前字符序列相同,所以第二行打印结果也为true。

> 变式:
public class Demo2_String {
   public static void main(String[] args) {
     String st1 = new String("abc");  //指向堆
     String st2 = "abc";  //指向常量池
     System.out.println(st1 == st2);  //false
     System.out.println(st1.equals(st2));  //true
   }
}

11.下面这句话在内存中创建了几个对象?

String str = new String(“abc”);

答案:
在内存中创建两个对象,一个在堆内存,一个在常量池,堆内存对象是常量池对象的一个拷贝副本,str指向堆内存对象。

最后:

String类中需要重点理解的两个方法:

简单理解String的 substring() 方法;
简单理解String的 intern() 方法;

猜你喜欢

转载自blog.csdn.net/weixin_44707004/article/details/107613139