JavaSE之认识String类

目录

一、创建字符串

 二、字符串比较相等

三、理解字符串的不可变

四、字符, 字节与字符串

1.字符与字符串

2.字节与字符串 

 五、字符串常见操作

1.字符串的比较

 2.字符串的查找

 3.字符串的替换

4.字符串的拆分

 5.字符串的截取

 6.其他操作方法

六、StringBuffer 和 StringBuilder


一、创建字符串

1、字符串是不能被继承的;String是一个引用类型

注意:java中的字符串和C语言不一样,java中的字符串没有所谓的‘\0’结尾

2.创建方式:

// 方式一
String str = "Hello Bit";
// 方式二  调用构造方法进行构造对象
String str2 = new String("Hello Bit");
// 方式三
char[] array = {'a', 'b', 'c'};
String str3 = new String(array);

其实不止以上三种创建方式,其他的创建方式我们在用到的时候再去查就好了

3.不是说传引用就能改变实参的值,我们要看这个引用干了什么事情,我们来看一下这个代码:

import java.util.Arrays;

public class TestDemo {
    public static void func(String s, char[] array){
        s = "love";//修改了s的指向
        array[0] = 'l';
    }
    public static void main(String[] args) {
        String str = "abcdef";
        char[] chars = {'d','o','g'};
        func(str,chars);
        System.out.println(str);
        System.out.println(Arrays.toString(chars));
    }
}

编译并运行该程序,输出如下:

abcdef

['l','o','g']

str并没有按我们的预期被改为"love",我们一起来看一下这个代码的内存布局图:

 二、字符串比较相等

1.

String str1 = "hello";//产生一个String对象
String str2 = new String("hello");//产生一个String对象,自己又new了一个对象,一共2个对象
System.out.println(str1 == str2);//这里比较的是两个字符串的地址,而不是内容

编译并运行该程序,输出如下:

false 

分析如下:

1.一些概念:

Class文件常量池:如:int a = 10;此时10就放在这里

运行时常量池:当程序把编译好的字节码文件,加载到JVM当中后,会生成一个运行时常量池(方法区),实际上是Class文件常量池

字符串常量池 :主要存放字符串常量,本质上是一个哈希表(StringTable),JDK1.8开始,放在堆里面

"池" 是编程中的一种常见的, 重要的提升效率的方式
2.

 3.我们现在来分析一下它的内存布局:

注意:如果是简单类型的比较,则比较的是它们的值

int x = 10 ;
int y = 10 ;
System.out.println(x == y);

 编译并运行该程序,输出如下:

true

2. 

String str1 = "hello";
String str2 = new String("hello");
System.out.println(str1 == str2);

编译并运行该程序,输出如下:

true

 字符串常量池不会再存"hello",因为第一次已经存过了,我们现在来分析一下它的内存布局:

 3.

String str1 = "hello";
String str2 = "he" + "llo";
System.out.println(str1 == str2);

编译并运行该程序,输出如下:

true

 此时"he" + "llo"是两个常量,编译的时候,就已经确定好了"hello"

4.

String str1 = "hello";
String str2 = "he";
String str3 = str2 + "llo";
System.out.println(str1 == str3);

编译并运行该程序,输出如下:

false

 String str3 = str2 + "llo";此时的str2是变量,编译的时候不知道它是什么

5.

String str1 = "11";
String str2 = new String("1") + new String("1");
System.out.println(str1 == str2);

编译并运行该程序,输出如下:

false

 我们现在来分析一下它的内存布局:

 其实不画图也可以得到答案,因为new String("1") + new String("1")是一个新的对象

6.

String str1 = new String("1") + new String("1");
str1.intern();//手动入池
String str2 = "11";
System.out.println(str1 == str2);

编译并运行该程序,输出如下:

true

 我们现在来分析一下它的内存布局:

 7.

String str1 = "11";
String str2 = new String("1") + new String("1");
str2.intern();//手动入池
System.out.println(str1 == str2);

编译并运行该程序,输出如下:

false

str1的11已经入池了,str2的11不会再入池,内存分布图和5的一样

 8.

String str1 = "hello";
String str2 = new String("hello");
System.out.println(str1.equals.(str2));

编译并运行该程序,输出如下:

true

此时比较的是内容,所以为true 

注意:

String str = new String("Hello");
// 方式一
System.out.println(str.equals("haha"));
// 方式二
System.out.println("haha".equals(str));

我们推荐方式二写代码,一旦 str null, 方式一的代码会抛出异常, 而方式二不会,会显示false

三、理解字符串的不可变

String str = "hello" ; 
str = str + " world" ; 
str += "!!!" ; 
System.out.println(str);

这里一共创建了5个对象,"hello","world","helloworld","!!!","helloworld!!!",,它并没有修改字符串,所以说字符串的拼接是一个非常废效率的问题

如果我们非要改变字符串,可以用到反射,"反射" 这样的操作可以破坏封装, 访问一个类内部的 private 成员

String str = "Hello";
// 获取 String 类中的 value 字段. 这个 value 和 String 源码中的 value 是匹配的. 
Field valueField = String.class.getDeclaredField("value");
// 将这个字段的访问属性设为 true
valueField.setAccessible(true);
// 把 str 中的 value 属性获取到. 
char[] value = (char[]) valueField.get(str);
// 修改 value 的值
value[0] = 'h';
System.out.println(str);

现在"Hello"就改变为"hello"啦

法二:

String str = "Hello";
str = "h" + str.substring(1);
System.out.println(str);

 现在"Hello"也改变为"hello"啦

那么为什么String要不可变呢?

(1)方便实现字符串对象池. 如果 String 可变, 那么对象池就需要考虑何时深拷贝字符串的问题了.
(2)不可变对象是线程安全的.
(3)不可变对象更方便缓存 hash code, 作为 key 时可以更高效的保存到 HashMap 中

四、字符, 字节与字符串

1.字符与字符串

字符串内部包含一个字符数组, String 可以和 char[] 相互转换
No方法名称类型描述
1public String(char value[])构造将字符数组中的所有内容变为字符串
2public String(char value[],int offset,int count)构造将部分字符数组中的内容变为字符串
3public char charAt(int index)普通取得指定索引位置的字符,索引从0开始
4public char[] tocharArray()普通将字符串变为字符数组返回

分别举个例子:

public class TestDemo {
    public static void main(String[] args) {
        char[] val = {'a','b','c'};
        String str = new String(val);
        System.out.println(str);
        String str2 = new String(val,1,2);//注意不要越界
        System.out.println(str2);
    }
}

编译并运行该程序,输出如下:

abc

bc

public class TestDemo {
    public static void main(String[] args) {
        String str = "hello";
        char ch = str.charAt(1);//获取1下标的字符
        System.out.println(ch);
    }
}

 编译并运行该程序,输出如下:

e

import java.util.Arrays;
public class TestDemo {
    public static void main(String[] args) {
        String str = "hello";
        char[] chars = str.toCharArray();
        System.out.println(Arrays.toString(chars));
    }
}

  编译并运行该程序,输出如下:

[h, e, l, l, o]

我们来看一条题:给定字符串一个字符串, 判断其是否全部由数字所组成 

思路 : 将字符串变为字符数组而后判断每一位字符是否是 " 0 "~"'9'" 之间的内容
public class TestDemo {
    public static boolean isNumberChar(String s){
        for(int i = 0; i < s.length(); i++){
            char c = s.charAt(i);
            /*if(c < '1' || c > '9'){
                return false;
            }*/
            //或者
            boolean flg = Character.isDigit(c);//判断某个字符是不是数字
            if(flg == false){
               return false;
            }
        }
        return true;
    }
    public static void main(String[] args) {
        String str = "12a456";
        System.out.println(isNumberChar(str));
    }
}

编译并运行该程序,输出如下:

false

2.字节与字符串 

 字节常用于数据传输以及编码转换的处理之中,String 也能方便的和 byte[] 相互转换

No方法名称类型描述
1public String(byte byres[])构造将字节数组变为字符串
2public String(byte byres[],int offset,int length)构造将部分字节数组的内容变为字符串
3public byte[] getBytes()普通将字符串以字节数组的形式返回
4public byte[] getBytes(String charSetName)throws UnspportedEncodingException普通编码变换处理

分别举个例子:

(1)(2)

public class TestDemo {
    public static void main(String[] args) {
        byte[] bytes = {97,98,99,100};
        String str = new String(bytes);
        System.out.println(str);
        String str2 = new String(bytes,2,2);
        System.out.println(str2);
    }
}

编译并运行该代码,输出如下:

abcd

cd

注意: 

(3) 

import java.util.Arrays;

public class TestDemo {
    public static void main(String[] args) {
        String str = "hello";
        byte[] bytes = str.getBytes();
        System.out.println(Arrays.toString(bytes));
    }
}

 编译并运行该代码,输出如下:

[104, 101, 108, 108, 111]

(4) 

 编译并运行该代码,输出如下:

[-24, -117, -71, -26, -98, -100]

//java的编码是JDK编码

小结:么何时使用 byte[], 何时使用 char[] ?

(1)byte[] 是把 String 按照一个字节一个字节的方式处理 , 这种适合在网络传输 , 数据存储这样的场景下使用 . 更适合 针对二进制数据来操作.
(2)char[] 是吧 String 按照一个字符一个字符的方式处理 , 更适合针对文本数据来操作 , 尤其是包含中文的时候

 五、字符串常见操作

1.字符串的比较

No方法名称类型描述
1public boolean equals(Object anObject)普通区分大小写的比较
2public boolean equalsIgnoreCase(String anotherString)普通不区分大小写的比较
3public int compareTo(String anotherString)普通比较两个对字符串的大小关系

分别举个例子: 

(1)(2)

public class TestDemo {
    public static void main(String[] args) {
        String str1 = "hello" ;
        String str2 = "Hello" ;
        System.out.println(str1.equals(str2)); 
        System.out.println(str1.equalsIgnoreCase(str2));
    }
}

编译并运行该代码,输出如下:

false

true

我们来看一下equals的底层源码:

 (3)String类中compareTo()方法是一个非常重要的方法,该方法返回一个整型

相等:返回0.              小于:返回内容小于0.           大于:返回内容大于0

public class TestDemo {
    public static void main(String[] args) {
        String str1 = "hello" ;
        String str2 = "Hello" ;
        int ret = str1.compareTo(str2);
        System.out.println(ret);
    }
}

编译并运行该代码,输出如下:

32

我们来看一下它的底层源码:

 2.字符串的查找

No方法名称类型描述
1public boolean contains(CharSequence s)普通判断一个子字符串是否存在
2public int indexOf(String str)普通从头开始查找指定字符串的位置,查找了返回位置的开始索引,如果查找不到返回-1
3public int indexOf(String str,int fromIndex)普通从指定位置开始查找子字符串
4public int lastIndexOf(String str)普通由后向前查找字符串位置
5public int lastIndexOf(String str,int fromIndex)普通从指定位置由后向前查找
6public boolean startsWith(String prefix)普通判断是否以指定字符串开头
7public boolean startsWith(String prefix,int offset)普通从指定位置开始判断是否以指定字符串开头
8public boolean endsWith(String suffix)普通判断是否以指定字符串结尾

分别举个例子:

public class TestDemo {
    public static void main(String[] args) {
        String str = "ababcabcd";
        String tmp = "abc";
        boolean flg = str.contains(tmp);
        System.out.println(flg);
        int index = str.indexOf(tmp);
        System.out.println(index);
        int index2 = str.indexOf(tmp,3);//从str的3位置开始从前往后找tmp第一次出现的位置
        System.out.println(index2);
        int index3 = str.lastIndexOf(tmp);
        System.out.println(index3);
        int index4 = str.lastIndexOf(tmp,6);//从str的6位置开始从后往前找tmp第一次出现的位置
        System.out.println(index4);
        System.out.println(str.startsWith("ab"));//判断str是不是以ab开头
        System.out.println(str.startsWith("c", 4));//在str偏移量为4的地方判断是否以c开头
        System.out.println(str.endsWith("cd"));//判断str是不是以cd结尾
    }
}

编译并运行该代码,输出如下:

为什么tmp是String类型,但是可以作为contains的参数,这是因为String实现了CharSequence的接口

 3.字符串的替换

No方法名称类型描述
1public String replace(char oldChar, char newChar)普通用 newChar 字符替换字符串中出现的所有 oldChar 字符
2public String replace(CharSequence target, CharSequence replacement)普通用 newChar 字符串替换字符串中出现的所有 oldChar 字符串
3public String replaceAll(String regex, String replacement)普通替换所有的指定内容
4public String replaceFirst(String regex, String replacement)普通替换首个内容

分别举个例子:

public class TestDemo {
    public static void main(String[] args) {
        String str = "ababdfaafvabadab";
        String ret = str.replace('a','z');
        System.out.println(ret);
        String ret2 = str.replace("ab","oo");
        System.out.println(ret2);
        String ret3 = str.replaceAll("ab","oo");
        System.out.println(ret3);
        String ret4 = str.replaceFirst("ab","oo");
        System.out.println(ret4);
    }
}

 编译并运行,输出如下:

 注意: 由于字符串是不可变对象, 替换不修改当前字符串, 而是产生一个新的字符串】

4.字符串的拆分

No方法名称类型描述
1public String[] split(String regex)普通将字符串全部拆分
2public String[] split(String regex,int limit)普通将字符串部分拆分,该数组长度就是limit极限

分别举个例子:

(1)

public class TestDemo {
    public static void main(String[] args) {
        String str = "name=zhangsa&age=19";
        String[] strings = str.split("&");
        for(String s:strings){
            //System.out.println(s);//name=zhangsa
                                    //age=19
            //进一步拆分
            String[] ss = s.split("=");
            for(String tmp:ss){
                System.out.println(tmp);
            }
        }
    }
}

编译并运行该代码,输出如下:

 注意:

  • 字符"|","*","+"都得加上转义字符,前面加上"\".
  • 而如果是"\",那么就得写成"\\\\".
  • 如果一个字符串中有多个分隔符,可以用"|"作为连字符.

如:

public class TestDemo {
    public static void main(String[] args) {
        String str = "192.168.1.1" ;
        String[] result = str.split("\\.") ;//转义.要加上\,但是\本身又是一个特殊符号,需要被再次转义
        for(String s: result) {
            System.out.println(s);
        }
    }
}
public class TestDemo {
    public static void main(String[] args) {
        String str = "Java66 12&99#hello";
        String[] strings = str.split(" |&|#");
        for(String s: strings) {
            System.out.println(s);
        }
    }
}

 编译并运行该程序,输出如下:

(2)

public class TestDemo {
    public static void main(String[] args) {
        String str = "192.168.1.1" ;
        String[] result = str.split("\\.",2) ;
        for(String s: result) {
            System.out.println(s);
        }
        System.out.println("---------------------------");
        String[] result2 = str.split("\\.",6) ;//做多6组,不一定要6组
        for(String s: result2) {
            System.out.println(s);
        }
    }
}

编译并运行该代码,输出如下:

 5.字符串的截取

No方法名称类型描述
1public String substring(int beginIndex)普通从指定索引截取到结尾
2public String substring(int beginIndex,int endIndex)普通截取部分内容

分别举个例子;

public class TestDemo {
    public static void main(String[] args) {
        String str = "helloworld" ;
        System.out.println(str.substring(5));
        System.out.println(str.substring(2, 5));//区间为左闭右开
    }
}

编译并运行该程序,输出如下:

world

llo

注意 :

 6.其他操作方法

No方法名称类型描述
1public String trim()普通去掉字符串中的左右空格
2public String toUpperCase()普通字符串转大写
3public String toLowerCase()普通字符串转小写
4public native String intern()普通字符串入池操作
5public String concat(String str)普通字符串拼接,等同于“+”,拼接后的字符串不入池
6public int length()普通取得字符串长度
7public boolean isEmpty()普通判断是否为空字符串,但不是null,而是长度为0

分别举一些例子:

(1)

public class TestDemo {
    public static void main(String[] args) {
        String str ="  abc  abc  ";
        System.out.print(str.trim());
        System.out.println("===");
    }
}

编译并运行该程序,输出如下:

abc  abc===

(2)(3)

public class TestDemo {
    public static void main(String[] args) {
        String str = "abcABC";
        String ret1 = str.toUpperCase();
        String ret2 = str.toLowerCase();
        System.out.println(ret1);
        System.out.println(ret2);
    }
}

 编译并运行该代码,输出如下;

ABCABC

abcabc

 (5)

public class TestDemo {
    public static void main(String[] args) {
        String str = "hello";
        String ret = str.concat("world");
        System.out.println(ret);
    }
}

 编译并运行该代码,输出如下;

helloworld

六、StringBuffer StringBuilder

1.任何的字符串常量都是 String 对象,而且 String 的常量一旦声明不可改变,如果改变对象内容,改变的是其引用的指向而已。 通常来讲String 的操作比较简单,但是由于 String 的不可更改特性,为了方便字符串的修改,提供 StringBuffer 和StringBuilder类。StringBuffer 和 StringBuilder 大部分功能是相同的,我们主要总结  StringBuilder。在String 中使用 "+" 来进行字符串连接,但是这个操作在 StringBuilder 类中需要更改为append()方法。
public class TestDemo {
    public static void main(String[] args) {
        /*//法一:
        StringBuilder sb = new StringBuilder("abcdef");
        System.out.println(sb);//默认调用sb的toString方法,StringBuilder里面有toString方法*/
        //法二:
        StringBuilder sb = new StringBuilder();
        sb.append("abcdef");
        System.out.println(sb);
        //继续append
        sb.append("123");
        System.out.println(sb);//注意:此时没有创建新对象
        //append可以连用
        System.out.println(sb.append("abcdef").append("123"));
    }
}

 编译并运行该代码,输出如下;

abcdef

abcdef123

abcdef123abcdef123

 

其实由此可见,StringBuilder是一个可变的对象,因为它没有像String那种不可变对象一样不断创建新对象 

注意:普通的String方法拼接底层会被优化为StringBuilder
public class TestDemo {
    public static void main(String[] args) {
        /*String str = "abcdef";
        for(int i = 0; i < 10; i++){
            str += i;
        }
        System.out.println(str);*/

        //底层是这样做的
        /*String str = "abcdef";
        for(int i = 0; i < 10; i++){
            StringBuilder sb = new StringBuilder();
            sb.append(str).append(i);
            str = sb.toString();
        }
        System.out.println(str);*/
        //或者这样写:
        String str = "abcdef";
        StringBuilder sb = new StringBuilder();
        sb.append(str);
        for(int i = 0; i < 10; i++){
            sb.append(i);
            str = sb.toString();
        }
        System.out.println(str);
    }
}

小结论:如果是在循环里面,进行字符串的拼接,尽量不要使用String,优先使用StringBuffer StringBuilder

2.除了 append() 方法外, StringBuffer 也有一些 String 类没有的方法,如图:

 比如:

public class TestDemo {
    public static void main(String[] args) {
        StringBuilder sb = new StringBuilder("abcdef");
        sb.reverse();//字符串的逆置
        System.out.println(sb);
        
        StringBuilder sb2 = new StringBuilder("helloworld");
        System.out.println(sb2.delete(5, 10));//删除数据
        
        StringBuilder sb3 = new StringBuilder("helloworld");
        System.out.println(sb3.delete(5, 10).insert(0, "你好"));//插入数据
    }
}
 编译并运行该代码,输出如下;
fedcba
hello
你好hello
3. String StringBuffer StringBuilder 的区别 :
String 的内容不可修改, StringBuffer StringBuilder 的内容可以修改 .
StringBuffer StringBuilder 大部分功能是相似的
StringBuffer 采用同步处理,属于线程安全操作;而 StringBuilder 未采用同步处理,属于线程不安全操作

 4.String和StringBuilder或StringBuffer类不能直接转换。如果要想互相转换,可以采用如下原则:

  • String变为StringBuilder或StringBuffer:利用StringBuilder或StringBuffer的构造方法或append()方法
  • StringBuilder或StringBuffer变为String:调用toString()方法
//String变StringBuilder(法一) 使用构造方法
public static StringBuilder func1() {
    String str = "abcdef";
    return new StringBuilder(str);
}
//String变StringBuilder(法二) 使用构造方法
public static StringBuilder func(){
    String str = "abcdef";
    StringBuilder sb = new StringBuilder();
    sb.append(str);
    return sb;
}
//StringBuilder变String
public static StringBuilder func1() {
   StringBuilder sb = new StringBuilder();
   return sb.toString();
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值