【项目说明】
最近一直在做将图片在数据库存储的绝对路径改成相对路径,这个时候就需要我们的字符串上场了,需要将url进行重新拆、重组放到如数据库中。
String strImgUrlPath = new StringBuilder(fileServerProperties.getUriPrefix())
.append(productImg.getImgPath())
.toString();
这样就可以从数据库中获取相对路径,然后拼接上服务器的地址,就可以访问到图片了。
【技术点】
(1)String的常用方法
String str = "Hello world";
//获取字符串的长度
int length = str.length();
System.out.println("length-->"+length);
//返回指定字符在此字符串中第一次出现处的索引 --从0开始
Integer index= str.indexOf("1");
System.out.println("index-->"+index);
//返回在此字符串中第一次出现指定字符处的索引,从指定的索引开始搜索
Integer index2 = str.indexOf("1", 5);
System.out.println("index2-->"+index2);
//从指定位置开始,截取到最后的字符串返回
String substr1 = str.substring(3);
System.out.println("substr1-->"+substr1);
//从指定位置开始到指定位置结束,截取字符串返回
String substr2 = str.substring(3, 9);
System.out.println("substr2 -->"+substr2);
//返回字符串前后空格后的字符串
String str2 = str.trim();
System.out.println("str2 -->"+str2 );
//返回指定位置的char字符
char str3 = str.charAt(4);
System.out.println("str3-->"+ str3);
//返回此字符串是否以指定的前缀开始
boolean res1 = str.startsWith("He");
System.out.println("res1 -->"+res1);
//返回此字符串是否以指定的后缀结束
boolean res2 = str.endsWith("!");
System.out.println("res2 -->"+res2 );
//返回字符串从指定的位置开始的字符串是否以指定前缀开始
boolean res3 = str.startsWith("world", 6);
System.out.println("res3 -->"+res3);
//使用默认语言环境的规则将此String中的所有字符都转换为大写
String str4 = str.toUpperCase();
System.out.println("str4 -->"+str4 );
//使用默认语言的规则将此String中的所有字符都转换为小写
String str5 = str.toLowerCase();
System.out.println("str5 -->"+str5);
//返回参数的字符串表示形式 -- 参数可以是其他基本类型、数组、Object
//以指定的字符(或正则表达式)分割字符串,得到的是数组
String regEx = "\\\\d{4}\\\\-\\\\d{1,2}\\\\-\\\\d{1,2}";
boolean res4 = str.matches(regEx);
System.out.println("res4 -->"+res4 );
//以指定的字符(正则表达式)分割字符串,得到的是数组
String[] arr1 = str.split("o");
for (int j = 0; j <arr1 .length ; j++) {
System.out.println(arr1 +":"+ arr1[j]);
}
//以指定的字符(或正则表达式)分割字符串,得到的是数组,后面的参数是对于分割次数的限制,为1将不会分割,得到完整的字符,为2时,得到第一次分割的内容
String [] arr2 = str.split("o",3);
for (int i2 = 0; i2 < arr2.length; i2++) {
System.out.println(arr2+":"+arr2[i2]);
}
//用于根据新的字符串替换字符串中所有匹配的子字符串
String str7 = str.replace("o", "c");
System.out.println("str7-->"+str7);
//用于根据新的字符串替换字符串中所有匹配正则表达式的子字符串
String str8 = str.replaceAll("o", "c");
System.out.println("str8-->"+str8);
//使用给定的新的子字符串替换字符串中给定正则表达式的第一个子字符串
String str9 = str.replaceFirst("o", "c");
System.out.println("str9-->"+str9);
//判断字符串是不是空的
boolean empty = str.isEmpty();
System.out.println(empty);
length-->11
index-->-1
index2-->-1
substr1-->lo world
substr2 -->lo wor
str2 -->Hello world
str3-->o
res1 -->true
res2 -->false
res3 -->true
str4 -->HELLO WORLD
str5 -->hello world
res4 -->false
[Ljava.lang.String;@74a14482:Hell
[Ljava.lang.String;@74a14482: w
[Ljava.lang.String;@74a14482:rld
[Ljava.lang.String;@1540e19d:Hell
[Ljava.lang.String;@1540e19d: w
[Ljava.lang.String;@1540e19d:rld
str7-->Hellc wcrld
str8-->Hellc wcrld
str9-->Hellc world
false
(2)StringBuffer的常用方法
StringBuffer sb = new StringBuffer("hello");
//追加字符串
sb.append("world!");
System.out.println(sb);
//在指定位置开始插入字符串
sb.insert(5," ");
System.out.println(sb);
//替换指定位置的字符串
sb.replace(5,6,"boy,hello");
//删除指定位置的字符串
sb.delete(6,10);
System.out.println(sb);
//反转字符串
sb.reverse();
System.out.println(sb);
helloworld!
hello world!
hellobelloworld!
!dlrowollebolleh
(3)StringBuilder的常用方法
StringBuilder sb = new StringBuilder("hello");
//追加字符串
sb.append("world!");
System.out.println(sb);
//在指定位置开始插入字符串
sb.insert(5," ");
System.out.println(sb);
//删除指定位置的字符串
sb.delete(5,6);
System.out.println(sb);
//替换指定位置的字符串
sb.replace(5,11,"boy!");
System.out.println(sb);
//反转字符串
sb.reverse();
System.out.println(sb);
helloworld!
hello world!
helloworld!
helloboy!
!yobolleh
【三者的区别】
(1)继承结构不同
public final class String
implements java.io.Serializable, Comparable<String>, CharSequence
public final class StringBuilder
extends AbstractStringBuilder
implements java.io.Serializable, CharSequence
public final class StringBuffer
extends AbstractStringBuilder
implements java.io.Serializable, CharSequence
(2)String的值是不可变的,StringBuffer、StringBuilder是可变的
(a)String的值是不可变的,这就导致每次String的操作都会产生一个新的String对象,不仅效率低下,而且大量浪费有限的内存空间。
package com.wlee.test;
public class StringTest {
public static void main(String[] args) {
String str1 = "aaa";
String str2 = "bbb" + "ccc";
String str3 = str1 + "bbb";
String str4 = new String("aaa");
}
}
String str1 = "aaa",str1 创建了几个对象?字符串在创建对象时,会在常量池中看有没有 aaa 这个字符串。如果没有此时还会在常量池中创建一个,如果有则不创建。默认情况下所以会创建一个对象。
String str2 = "bbb" + "ccc",那么这个 str2 会创建几个对象?str2 会直接创建一个 bbbccc 的对象。
str3 先创建了一个 StringBuilder 对象并执行了初始化,然后在 str3 中执行 + 号其实是执行了 StringBuilder 的 append() 操作,最后还调用了一个 StringBuilder 的 toString 方法。说白了,String str3 = str1 + "bbb" 就等价于 String str3 = new StringBuilder().append(str1).append("bbb").toString(),所以 str3 执行结束后,相当于创建了3个对象。
String str4 = new String("aaa"),最后就是 str4 了,这个就很好理解了,在创建这个对象时因为使用了 new 关键字,所以肯定会在堆中创建一个对象。然后会在常量池中看有没有 aaa 这个字符串,如果没有此时还会在常量池中创建一个,如果有则不创建。所以可能是创建一个或者两个对象,但是一定存在两个对象。
(b)StringBuffer是可变类,和线程安全的字符串操作类,任何对它指向的字符串的操作都不会产生新的对象,每次StringBuffer对象都有一定的缓冲区空间,当字符串大小没有超过容量时,不会分配新的容量,当字符串大小超过容量时,会自动增加容量。
StringBuffer buf = new StringBuffer();//分配16字节的字符缓冲区
StringBuffer buf = new StringBuffer();//分配512字节的字符缓冲区
StringBuffer buf = new StringBuffer("this is a test") //在缓冲区中存放了字符串,并在后面预留了16字节的空缓冲区
(c)StringBuilder
StringBuffer和StringBuilder类功能相似,主要区别在于StringBuffer类的方法是多线程、安全的,而StringBuilder不是线程安全的,相比而言,StringBuilder类会略微快一点,对于经常要改变值的字符串应该使用StringBuffer和StringBuilder类。
(3)线程安全
StringBuffer 线程安全
StringBuilder 线程不安全
(4)速度
一般情况下,速度从快到慢:StringBuilder>StringBuffer>String,这种比较是相对的,不是绝对的。
(5)总结
(a).如果要操作少量的数据用 = String
(b).单线程操作字符串缓冲区 下操作大量数据 = StringBuilder
(c).多线程操作字符串缓冲区 下操作大量数据 = StringBuffer
(6)String为什么要设置成final?
主要是从安全和性能考虑(字符串常量池)是String类不可变的主要原因
- 字符串对象被缓存在字符串池中,缓存的字符串被多个客户端共享,这时如果字符串可变。那么某个客户端修改了字符串的值会影响到其它的客户端。字符串的缓存从性能原因来考虑,设计为不可变又是非常重要的。
- 像下面这样字符串one和two都用字面量"something"赋值。它们其实都指向同一个内存地址。
String one = "someString";
String two = "someString";
- 字符串在许多java的类中被广泛使用。在网络连接中,你可以传递主机名和端口号作为字符; 在数据库连接中,你可以传递数据库地址作为字符串; 在File I/O中,你可以通过字符串文件名来打开任何文件。
- 这种情况下,如果字符串不是不可变的,将会导致严重的安全问题。一些人可以访问任何文件,一旦他有了授权,他可以故意的修改文件名,或者无意的获取到另外一个文件的访问权限。
参考文章:
https://www.jianshu.com/p/1589104b6e87?spm=a2c6h.12873639.0.0.28384f27ADwkXA