javaSE基础-字符串

字符串

String类

定义:使用一对“”(引号)引起来表示

public final class String implements java.io.Serializable, 
Comparable<String>, CharSequence {
    private final char value[];
}

//String声明为final的,不可被继承
//String实现了Serializable接口:表示字符串是支持序列化的
        实现了Comparable接口:表示String可以比较大小
//String内部定义了 final char[] value用于存储字符串数据(jdk8,之后新的版本为:byte[] value)

特点

String:代表不可变的字符串序列,即不可变特性

  • 字符串重新赋值时,需要重写指定内存区域赋值,不能使用原有的value进行赋值
  • 当对现有的字符串进行连接操作时,也需要重新指定区域赋值,不能使用原有的value
  • 当调用String的replace()方法修改指定字符串或字符时,需要重新指定内存区域,不能使用原有的value

说明

通过字面量的方式(区别于new)给一个字符串赋值,此时的字符串值声明在字符串常量池中

字符串常量池中是不会存储相同内容(相同内容是指使用String类重写equals()比较,返回true)的字符串

常用创建字符串的方式

方式一:通过字面量定义的方式

//通过字面量定义的方式:此时的s1和s2的数据声明在 方法区中的字符串常量池中
tring s1 = "abc";

方式二:通过new + 构造器的方式

//通过new + 构造器的方式:此时s3和s4保存的地址值,是数据在堆空间中开辟空间以后对应字符串常量池的数据
String s3 = new String("abc");

Stirng创建方式内存示意图

image

注意:new方式创建对象,在内存中创建了几个对象?如:String s = new String("abc");

两个对象:一个是堆空间中new结构,另一个是char[]对应的常量池中的数据:“abc“

示例一

@Test
public void test1(){
    //通过字面量定义的方式
    String s1 = "abc";
    String s2 = "abc";

    //通过new + 构造器的方式
    String s3 = new String("abc");
    String s4 = new String("abc");

    System.out.println(s1 == s2);//true
    System.out.println(s3 == s4);//false
    System.out.println(s1 == s3);//false
    System.out.println(s2 == s4);//false

    System.out.println("*****************");

    Person p1 = new Person("Tom", 19);
    Person p2 = new Person("Tom", 19);

    System.out.println(p1.name.equals(p2.name));//true
    System.out.println(p1.name == p2.name);//true

    p1.name="Mary";
    System.out.println(p2.name);//Tom
}

示例二

/**
 * 结论:
 * 1、常量与常量的拼接结果在常量池,且常量池中不会存在相同内容的常量
 * 2、只要其中一个变量,结果就在堆中
 * 3、如果拼接的结果调用intern()方法,返回值就在常量池中
 */
@Test
public void test2(){
    String s1 = "javaSE";
    String s2 = "SpringBoot";

    String s3 = "javaSESpringBoot";
    String s4 = "javaSE" + "SpringBoot";
    String s5 = s1 + "SpringBoot";
    String s6 = "javaSE" + s2;
    String s7 = s1 + s2;

    System.out.println(s3 == s4);//true
    System.out.println(s3 == s5);//false
    System.out.println(s3 == s6);//false
    System.out.println(s3 == s7);//false
    System.out.println(s6 == s7);//false

    String s8 = s5.intern();//返回值s8使用的是常量池中的javaSESpringBoot
    System.out.println(s3 == s8);//true

    String s9 = "javaEEHello";
    String s10 = "javaEE";
    String s11 = s10 + "Hello";
    System.out.println(s9 == s11); //false

    String s12 = "javaEEHello";
    final String s13 = "javaEE";
    String s14 = s13 + "Hello"; //常量 + 常量  数据存储在常量池中
    System.out.println(s12 == s14);//true

}

案例:常量池中的字符串不可变性

public class StringTest2 {
    String str = new String("hello");
    char[] ch = {'t', 'e', 's', 't'};

    //对str 和 ch 重新赋值操作
    void change(String str, char[] ch){
        str = "hello word";
        ch[0] = 'b';
    }

    public static void main(String[] args) {
        StringTest2 s = new StringTest2();
        s.change(s.str, s.ch);
        System.out.println(s.str);//hello --> String存储在常量池中是不可变的
        System.out.println(s.ch);//'best'
    }
}

String常用方法

常用方法1

int length():返回字符串的长度
char charAt(int index):返回某索引处的字符
boolean isEmpty():判断是否是空字符串
String toLowerCase():使用默认语言环境,将String中的所有字符转换为小写
String toUpperCase():使用默认语言环境,将String中的所有字符转换为大写
String trim():返回字符串的副本,忽略前部空白和尾部空白
boolean equals(Object obj):比较字符串的内容是否相同
boolean equalsIgnoreCase(String anotherString):与equals方法类似,忽略大小写
String concat(String str):指定字符串连接到此字符串的结尾,等价于“+”
int compareTo(String anotherString):比较两个字符串的大小
String substring(int beginIndex):返回一个新的字符串,它是此字符串的从beginIndex开始截取到最后的一个字符串
String substring(int beginIndex, int endIndex):返回一个新的字符串,它是此字符串从beginIndex开始截取到endIndex(不包含)的一个子字符串

示例一

@Test
public void test1() {
    String s1 = "HelloWorld";
    char c = s1.charAt(2);
    System.out.println(c);// l
    String s2 = "";
    System.out.println(s2.isEmpty()); //true
    String s3 = "  hel  le,wor ld  ";
    System.out.println("----" + s3 + "----");//----  hel  le,wor ld  ----
    String s4 = s3.trim();
    System.out.println("----" + s4 + "----");//----hel  le,wor ld----
    String s5 = "helloworld";
    System.out.println(s5.equalsIgnoreCase(s1));//true
    String s6 = s5.concat("你好");
    System.out.println(s6);//helloworld你好
    String s7 = "abc";
    int a = s7.compareTo("abd");
    System.out.println(a);//-1
    String s = s7.substring(2);
    System.out.println(s);//c
    String ss = s1.substring(2, 4);
    System.out.println(ss);//ll
}

常用方法2

boolean endWith(String suffix):测试此字符串是否以指定的后缀结束
boolean startWith(String prefix):测试此字符串是否以指定的前缀结束
boolean startWith(String prefix, int toffset):测试此字符串从指定索引开始的子字符串是否以指定的字符开始boolean contains(CharSequence s):当且仅当此字符串包含指定的char值序列时,返回true
int indexOf(String str):返回指定字符串在此字符中第一次出现处的索引
int indexOf(String str, int fromIndex):返回指定字符串在此字符中第一次出现处的索引,指定开始索引的位置
int lastIndexOf(String str):返回指定子符串在此字符串最右出现处的索引,索引值还是从左边开始计数
int lastIndexOf(String str, int fromIndex):返回指定字符串在此字符串中最后一次出现处,从指定的索引开始反向索引

注:indexOf和lastIndexOf如果没有找到会返回-1

示例二

@Test
public void test2() {
    String str1 = "helloworld";
    boolean b1 = str1.endsWith("rld");
    System.out.println(b1);//true

    boolean b2 = str1.startsWith("he");
    System.out.println(b2);//true

    boolean b3 = str1.startsWith("ll", 2);
    System.out.println(b3);//true

    String str2 = "wor";
    System.out.println(str1.contains(str2));//true

    System.out.println(str1.indexOf("lol"));//-1 没有找到

    System.out.println(str1.indexOf("lo", 5));//-1 没有找到

    String str3 = "hellorworld";
    System.out.println(str3.lastIndexOf("or"));// 7
    System.out.println(str3.lastIndexOf("or", 6));//4
}

常用方法3

替换:
String replace(char oldChar, char newChar):返回一个新的字符串,它是通过用newChar 替换 此字符串中出现的所有oldChar得到的
String replace(CharSequence target, CharSequence replacement):使用指定的字面值替换序列,替换此字符串所有匹配字面值的目标序列的子字符串
String replaceAll(String regex, String replacement):使用给定的replacement 替换此字符串所有匹配给定的正则表达式的子字符串
String replaceFirst(String regex, String replacement):使用给定的replacement 替换此字符串匹配给定的正则表达式的第一个子字符串
匹配:
boolean matches(String regex):告知此字符串是否匹配给定的正则表达式
切片:
String[] split(String regex):根据给定正则表达式的匹配拆分此字符串
String[] split(String regex, int limit):根据匹配给定的正则表达式来拆分此字符串,最多不超过limit个,如果超过了,剩下的全部放到最后一个元素中

示例三

@Test
public void test3(){
    String str1 = "你好,北京欢迎您!";
    String str2 = str1.replace('您', '你');
    System.out.println(str2);
    System.out.println(str1);

    String str3 = str1.replace("您", "大家");
    System.out.println(str3);

    System.out.println("*************************");
    String str = "12hello34world5java7891mysql1456";
    //把字符串中的数字替换成,如果结果中开头和结尾有,的话去掉
    String s = str.replaceAll("\\d+", ",").replaceAll("^,|,$", "");
    System.out.println(s);

    String str4 = "12345";
    //判断str4 字符串中是否全部有数字组成,即有1-n个数字组成
    boolean b1 = str4.matches("\\d+");
    System.out.println(b1);

    String tel = "0518-1234398";
    //判断这是否是一个固定电话
    boolean b2 = tel.matches("0518-\\d{7,8}");
    System.out.println(b2);

    System.out.println("******************");
    String str5 = "hello|world|java";
    String[] strs = str5.split("\\|");
    for (int i = 0; i < strs.length; i++) {
        System.out.println(strs[i]);
    }
}

String类与其它结构之间的转换

String 与 byte[]转换

/**
 * 编码:String --> byte[]:调用String的getBytes()
 * 解码:byte[] --> String:调用String的构造器
 *
 * 说明:解码与编码过程使用的字符集要一致
 */
@Test
public void test3() throws UnsupportedEncodingException {
    String str1 = "abc123北京";
    byte[] bytes = str1.getBytes(); //默认使用系统的编码集
    System.out.println(Arrays.toString(bytes));

    byte[] bytes1 = str1.getBytes("gbk"); //使用指定的编码集
    System.out.println(Arrays.toString(bytes1));

    System.out.println("*********解码***********");
    String str2 = new String(bytes1);
    System.out.println(str2); //出现解码乱码

    String gbk = new String(bytes1, "gbk");
    System.out.println(gbk);

}

String 与 char[] 转换

/**
 * String --> char[] 之间的转换: 调用String的toCharArray()
 * char[] --> String: 调用String的构造器
 */
@Test
public void test2(){
    String str1 = "abc124";
    char[] ch = str1.toCharArray();
    for (int i = 0; i < ch.length; i++) {
        System.out.println(ch[i]);
    }

    char[] arr = new char[]{'1','2','a','b'};
    String str2 = new String(arr);
    System.out.println(str2);
}

String 与 基本数据类型、包装类转换

/**
 * String --> 基本数据类型、包装类,调用包装类的静态方法:parseXxx()
 * 基本数据类型、包装类 --> String: 调用String重载的valueOf(xxx)
 */
@Test
public void test1(){
    String str1 = "123";
    int num = Integer.parseInt(str1);
    System.out.println(num);

    String str2 = String.valueOf(num);
    System.out.println(str2);
}

String 与 StringBuffer / StringBuilder 转换

/**
 * String --> StringBuffer\StringBuilder:调用StringBuffer、StringBuilder构造器
 * StringBuffer\StringBuilder --> String: ①调用String的构造器 ②xxx.toString()
 */
public StringBuffer(String str) {}
public StringBuilder(String str) {}

public String(StringBuffer buffer) {}
public String(StringBuilder builder) {}

StringBuffer 与 StringBuilder

常用方法:

StringBuffer append(xxx):提供了很多的append()的方法,用于进行字符串拼接
StringBuffer delete(int start, int end):删除指定位置的内容
StringBuffer replace(int start, int end, String str):把[start, end)的位置替换为str
StringBuffer insert(int offset, xxx):在指定位置插入xxx
StringBuffer reverse():把当前字符串序列逆转
public int indexOf(String str):查找指定字符串的索引
public String substring(int start, int end): 返回一个从start开始到end索引结束的左闭右开区间的子字符串
public int length():返回字符串长度
public char charAt(int n):返回一个指定位置上的字符
public void setCharAt(int n,char ch):在指定位置上替换一个字符

示例

@Test
public void test2(){
    StringBuffer s1 = new StringBuffer("abc");
    s1.append(1);
    s1.append('1');
    //s1.replace(1, 3, "hello"); //ahello11
    //s1.insert(2,"java"); //abjavac11
    //s1.reverse();//11cba
    //s1.delete(1, 3);//a11

    //char c = s1.charAt(2);
    //System.out.println(c);//c

    int index = s1.indexOf("bc");
    System.out.println(index);//1

    String substring = s1.substring(1, 3);
    System.out.println(substring);//bc

    s1.setCharAt(2, '你');//ab你11

    System.out.println(s1);
    System.out.println(s1.length());
}

注意: append()方法可以添加null,但StringBuilder / StringBuffer 构造器不能添加null对象

@Test
public void test1(){
    String str = null;
    StringBuffer sb = new StringBuffer();

    sb.append(str);
    System.out.println(sb.length());//4
    System.out.println(sb);//null "null"

    StringBuffer sb1 = new StringBuffer(str); //NullPointerException
    System.out.println(sb1);
}

其它拓展

1、String / StringBuffer / StringBuilder 的三者异同

相同:三者底层都是char[] 存储数据(jdk8及以下)

不同:
String:不可变的字符序列,
StringBuffer:可变的字符序列,线程安全的,效率低
StringBuilder:可变的字符序列,线程不安全的,效率高

字符串源码分析

String str = new String(); //char[] value = new char[0]

String str1 = new String("abc"); //char[] value = new char[]{'a','b','c'};

StringBuffer sb1 = new StringBuffer(); // char[] value = new char[16];

​ // 底层创建了一个长度为16字节的字符数组, sb1.length()=0

sb1.append('a'); //value[0] = 'a';

sb1.append('b'); //value[1] = 'b';

StringBuffer sb2 = new StringBuffer("abc"); // char[] value = new char["abc".length() + 16];

//问题1:System.out.println(sb2.length()); //3

//问题2:扩容问题:如果要添加的数据底层数组盛不下了,那就需要扩容底层的数组。

默认情况下,扩容为原来容量的2倍+2,同时将原有数组中的元素复制到新的数组中。

说明:开发中使用:StringBuffer(int capacity)或 StringBuilder(int capacity)

2、将一个字符串中部分的字符进行反转

案例一:比如 "abcdefg" --> "abfedcg"

click me
public class StringExercise {
    //方式一:先将字符串转换为char[],在反转指定位置上的字符,再将char[]转换为字符串
    static String reverse(String str, int startIndex, int endIndex){
        if(str != null && str != ""){
            char[] chars = str.toCharArray();
            for (int i = startIndex, j= endIndex; i < j; i++, j-- ) {
                char temp = chars[i];
                chars[i] = chars[j];
                chars[j] = temp;
            }
            return new String(chars);
        }
        return null;
    }
    //方式二:将字符串使用substring()拼接,反转字符部分使用charAt()索引到字符进行拼接
    static String reverse1(String str, int startIndex, int endIndex){
        if(str != null && str != ""){
            String subStr = str.substring(0, startIndex);
            for (int i = endIndex; startIndex <= i ; i--) {
                subStr += str.charAt(i);
            }
            subStr += str.substring(endIndex + 1);
            return subStr;
        }
        return null;
    }
    //方式三:在方式二的基础上优化字符串拼接,使用StringBuilder更高效的方式拼接反转的字符操作
    static String reverse2(String str, int startIndex, int endIndex){
        if(str != null && str != ""){
            StringBuilder sb = new StringBuilder(str.length());
            String substring = str.substring(0, startIndex);
            sb.append(substring);
            for (int i = endIndex; i >= startIndex  ; i--) {
                sb.append(str.charAt(i));
            }
            sb.append(str.substring(endIndex + 1));
            return new String(sb);
        }
        return null;
    }
    public static void main(String[] args) {
        String str = "abcdefg";
        String reverseStr = reverse1(str, 2, 5);
        System.out.println(reverseStr);
    }
}

3、获取一个字符串在另一个字符串中出现的次数

案例二:比如 获取“ab” 在 “abkkcadkabkbfkaabskabge”中的次数

click me
public class StringExercise2 {

    public static int getCount(String mainStr, String subStr){
        int subLength = subStr.length();
        if(mainStr.length() > subLength){

            int count = 0;
            int index = 0;
            //方式一:查找到subStr之后,截取mainStr后面字符串作为新的mainStr
            //while((index = mainStr.indexOf(subStr)) != -1){
            //    count ++;
            //    mainStr = mainStr.substring(index + subLength);
            //}
            //方式二:
            while ((index = mainStr.indexOf(subStr, index)) != -1){
                count++;
                index += subLength;
            }
            return count;
        }else {
            return 0;
        }
    }

    public static void main(String[] args) {
        String subStr = "ab";
        String mainStr = "abkkcadkabkbfkaabskabge";
        int count = getCount(mainStr, subStr);
        System.out.println(count);
    }
}

4、获取两个字符串中最大相同字串

案例三:比如 str1 = "abcwerthehelloyuiodef" str2 = "cvhellobnm"

click me
public class StringExercise3 {
    //情况一:只有一个最大相同字串
    public String getMaxSameString(String str1, String str2){
        if(str1 != null && str2 != null){
            String maxStr = (str1.length() >= str2.length())?str1:str2;
            String minStr = (str1.length() < str2.length())?str1:str2;
            int length = minStr.length();
            for (int i = 0; i < length; i++) {
                for(int x=0,y = length-i;y<=length;x++,y++){
                    String subStr = minStr.substring(x,y);
                    if(maxStr.contains(subStr)){
                        return subStr;
                    }
                }
            }
        }
        return null;
    }
    //情况二:多个最长字串
    public String[] getMaxSameString1(String str1, String str2){
        if(str1 != null && str2 != null){

            StringBuffer sBuffer = new StringBuffer();
            String maxString = (str1.length() >= str2.length())?str1:str2;
            String minString = (str1.length() < str2.length())?str1:str2;
            int len = minString.length();
            for (int i = 0; i < len; i++) {
                for(int x=0,y=len-i;y<= len; x++,y++){
                    String subStr = minString.substring(x,y);
                    if(maxString.contains(subStr)){
                        sBuffer.append(subStr + ",");
                    }
                }
                if(sBuffer.length() != 0){
                    break;
                }
            }
            String[] split = sBuffer.toString().replaceAll("\\,$", "").split("\\,");
            return split;
        }

        return null;
    }

    @Test
    public void test1(){
        String str1 = "abcwerthehelloyuiodef";
        String str2 = "cvhellobnmiodef";
        String[] maxSameString = getMaxSameString1(str1, str2);
        System.out.println(Arrays.toString(maxSameString));
    }
}
posted @ 2022-10-23 22:07  Bingeone  阅读(35)  评论(0编辑  收藏  举报