理解String 类(上)

简介: 理解String 类(上)

引言



我们都知道在C语言中是没有字符串类型的,C语言中,最常用的就是将字符串放进一个数组中,之后,对数组进行一些操作。而在 Java / C++ 中,有直接表示字符串的类型。在Java 中,String 类型就是表示字符串类型,同时它也是引用类型。比方说:


String str = "abcd";


没错,在 Java 中,字符串就是上述这样定义并初始化的,而 abcd 的末尾没有像 C语言那样有 \0,在 Java 中,里面就是 abcd.

在上面的一行代码中,被双引号引起来的 abcd 就叫做字符串,它是有a、b、c、d 这四个字符组成的,而 abcd 又属于字面值常量,其本质是常量,不可以被更改。


一、创建字符串的方式



1. 直接赋值


程序清单1:


public class Test1 {
    public static void main(String[] args) {
        String str1 = "hello";
    System.out.println(str1); 
    //输出结果:hello        
    }
}


2. 通过使用构造方法


程序清单2:


public class Test2 {
    public static void main(String[] args) {
        String str2 = new String("world");
        System.out.println(str2); 
        //输出结果:world
    }
}


3. 通过使用字符数组转换成字符串


程序清单3:


public class Test3 {
    public static void main(String[] args) {
        char[] chars = {'x','y','z'};
        String str3 = new String(chars);      
        System.out.println(str3); 
        //输出结果:xyz
    }
}


二、字符串的引用变量比较相等



1. 情况一


在程序清单4中,例如(str1 == str2)的这种形式,比较的其实是两个引用的地址,而不是字符串的内容 " hello ",这点需要理解,请往下看:


程序清单4:


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


输出结果:


d15961f5dd734f6db8c380290533829f.png


图解上述代码:


165b643e8c9c411e967e3bba437e0a55.png


String 类的设计使用了共享设计模式:

如果现在采用了直接赋值的模式进行String类的对象实例化操作,那么该实例化对象(字符串内容)将自动保存到这个对象池之中。如果下次继续使用直接赋值的模式声明 String 类对象,此时对象池之中如若有指定内容,将直接进行引用。

如若没有,则开辟新的字符串对象而后将其保存在对象池之中以供下次使用。


2. 情况二


程序清单5:


public class Test5 {
    public static void main(String[] args) {
        String str1 = new String("hello");
        String str2 = new String("hello");
        System.out.println(str1 == str2);
    }
}


输出结果:


6abd8debd8bd4f18b99e6a2bcd8b5297.png


图解上述代码:


3d1133069c7b47838d7c78399e0ba2dc.png


3. 情况三


程序清单6:


public class Test6 {
    public static void main(String[] args) {
        String str1 = "hello";
        String str2 = new String("hello");
        System.out.println(str1 == str2);
    }
}


输出结果:


c108169bbe1e405aa566f787805fa5d3.png


图解上述代码:


556cf9df5ac741f5bfda60e4e538f385.png


总结:


  • 直接赋值:只会开辟一块堆内存空间,并且该字符串对象可以自动保存在对象池中以供下次使用。
  • 构造方法:会开辟两块堆内存空间,不会自动保存在对象池中,可以使用intern( ) 方法手工入池。


总结:


  • 直接赋值:只会开辟一块堆内存空间,并且该字符串对象可以自动保存在对象池中以供下次使用。
  • 构造方法:会开辟两块堆内存空间,不会自动保存在对象池中,可以使用intern( ) 方法手工入池。


字符串常量池:主要存放字符串常量,本质上是一个哈希表,StringTable,双引号引起来的字符串常量。


三、理解字符串不可变



String 类的内部实现是基于 char[ ] 来实现的,但是 String 类并没有提供 set 方法之类的来修改内部的字符数组。


程序清单7:


public class Test7 {
    public static void main(String[] args) {
        String str1 = "abc";
        System.out.println(str1);
        str1 = "xyz";
        System.out.println(str1);
    }
}


输出结果:


942dc1e0368f4f8fa0635657c80d61a9.png


注意:在这里,我们并不是通过把字符串的内容修改了,而是将 [ str1 引用 ] 引用了新的对象。


40d7719a6a6645228c39768e1eb3c7df.png


程序清单8:


public class Test8 {
    public static void main(String[] args) {
        String str = "hello" ;
        str = str + " world" ;
        str += " !!!" ;
        System.out.println(str);
    }
}


输出结果:


8870f8b48199492c9b90fbb9697d406b.png


同样地,在程序清单8中,我们并不是将 " hello " 中的内容修改了,而是先通过 " hello " 和 " world " 创建了一个新的对象 " hello world ",之后又通过 " hello world " 和 " !!! " 创建了另一个新的对象 “hello world !!!”。对于这样的操作,相当于每次都要通过关键字 new 实例化一个对象。所以,程序清单8是一个不好的示范,对于程序员来说,不应该这么做。


四、字符、字节、字符串、数组



1. 将字符数组转换成字符串


程序清单9:


public class Test9 {
    public static void main(String[] args) {
        char[] chars = {'a','b','c','d'};
        String str = new String(chars);
        System.out.println(str);
    }
}
//输出结果:abcd


2. 拿到字符串中的连续字符


程序清单10:


public class Test10 {
    public static void main(String[] args) {
        char[] chars = {'a','b','c','d'};
        String str = new String(chars, 1, 2);
        System.out.println(str);
    }
}
//输出结果:bc


String str1 = new String(char[] chars,int offset, int count);

在 String 构造方法中,其中 offset 表示偏移量,count 表示偏移个数,若 offset 为 1,count 为 2,那么,实现 str1 就从数组下标1开始,往后拿2个元素。当然,我们应该注意不能越界哦。


3. charAt( ) 方法 [ 常用 ]


程序清单11:


public class Test11 {
    public static void main(String[] args) {
        String str = "abcde";
        char ch = str.charAt(2);
        System.out.println(ch);
    }
}
//输出结果:c


harAt(int index)


index 表示指定位置的索引,字符串第一个字符索引为 0。

同样地,我们应该注意不能越界哦。


4. 将字符串转变成字符数组 [ 常用 ]


程序清单12:


public class Test12 {
    public static void main(String[] args) {
        String str = "abcde";
        char[] chars = str.toCharArray();
        System.out.println(Arrays.toString(chars));
    }
}
//输出结果:[a, b, c, d, e]


上面四个程序清单说明的是字符数组与字符串之间的一些操作,如果把字符数组换成字节数组,整型数组等等…其对应的思想是一样的。感兴趣的小伙伴可以自己试一下字节与字符串之间的关系。


5. 判断字符串是是由字符构成还是由数字构成


判断一个字符串是否是由字符构成

思路:我们创建一个 judge( ) 方法来判定每个字符是否由字母组成即可,所以我们遍历整个字符串的长度,然后通过下面两行代码来验证每一个字符:


char ch = str.charAt(i);
boolean sign = Character.isLetter(ch);


程序清单13:


public class Test13 {
    public static boolean judge(String str){
        for (int i = 0; i < str.length(); i++) {
            char ch = str.charAt(i);
            boolean sign = Character.isLetter(ch);
            if(sign == false){
                return false;
            }
        }
        return true;
    }
    public static void main(String[] args) {
        String str = "abcde";
        System.out.println(judge(str));
    }
}
//输出结果:true


当然,我们也可以判定是否每个字符是数字,只不过逻辑需要改变一下,代码如下:


char ch = str.charAt(i);
boolean sign = Character.isDigit(ch);


五、字符串比较



1. equals( ) 方法


查看底层代码,通过 equals( ) 方法比较字符串的内容,返回类型是布尔类型。


36962c3fe99c4cb69e9a5eaec1022015.png


程序清单14:


public class Test14 {
    public static void main(String[] args) {
        String str1 = "hello";
        String str2 = new String("hello");
        System.out.println(str1.equals(str2));
        String str3 = null;
        String str4 = "hello";
        //System.out.println(str3.equals(str4)); //空指针异常
        System.out.println(str4.equals(str3));
    }
}


输出结果:


3c14e2603264497e9c9703b022f5d4c0.png


注意,在程序清单14中,str3 和 str4 不能互换位置,因为 str3 这个引用本身不指向任何对象,如果继续使用 str3 的话,会造成空指针异常,我已经通过注释标明出来了。


在程序清单15中,我们对程序清单14做出了一些改变,当我们忽视大小写的时候,可以使用 equalsIgnoreCase( ) 方法。


程序清单15:


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


输出结果:


745bf988cd4840e286d66253bbfe220c.png


2. compareTo( ) 方法


当查看底层代码,我们发现 String 类型实现了 Comparable接口,那么String 类就会重写 compareTo( ) 方法,返回值是整型。


fe671efa4f4a41dfaaa742edc4bc7aac.png


接下来,我们通过程序清单16来演示一下 compareTo( ) 方法是怎么使用的。


程序清单16:


public class Test16 {
    public static void main(String[] args) {
        String str1 = "abc";
        String str2 = "ABC";
        int ret1 = str1.compareTo(str2);
        System.out.println(ret1);
        System.out.println("--------------");
        String str3 = "abc";
        String str4 = "acb";
        int ret2 = str3.compareTo(str4);
        System.out.println(ret2);
        System.out.println("--------------");
        String str5 = "abcde";
        String str6 = "ab";
        int ret3 = str5.compareTo(str6);
        System.out.println(ret3);
    }
}


输出结果:


cc246fa815674f9ebcd2e401e47a86b0.png

在程序清单16中,compareTo( ) 方法是通过对比字符串中的字符一个一个进行比较的,字符对应的 Unicode 编码之差就是返回值( ASCII 码 )。如果两个字符串长度不等,返回的就是字符串长度之差。

目录
相关文章
|
4月前
|
Java 索引
java基础(13)String类
本文介绍了Java中String类的多种操作方法,包括字符串拼接、获取长度、去除空格、替换、截取、分割、比较和查找字符等。
50 0
java基础(13)String类
|
2月前
|
存储 编译器 C语言
【c++丨STL】string类的使用
本文介绍了C++中`string`类的基本概念及其主要接口。`string`类在C++标准库中扮演着重要角色,它提供了比C语言中字符串处理函数更丰富、安全和便捷的功能。文章详细讲解了`string`类的构造函数、赋值运算符、容量管理接口、元素访问及遍历方法、字符串修改操作、字符串运算接口、常量成员和非成员函数等内容。通过实例演示了如何使用这些接口进行字符串的创建、修改、查找和比较等操作,帮助读者更好地理解和掌握`string`类的应用。
66 2
|
3月前
|
Java
【编程基础知识】(讲解+示例实战)方法参数的传递机制(值传递及地址传递)以及String类的对象的不可变性
本文深入探讨了Java中方法参数的传递机制,包括值传递和引用传递的区别,以及String类对象的不可变性。通过详细讲解和示例代码,帮助读者理解参数传递的内部原理,并掌握在实际编程中正确处理参数传递的方法。关键词:Java, 方法参数传递, 值传递, 引用传递, String不可变性。
75 1
【编程基础知识】(讲解+示例实战)方法参数的传递机制(值传递及地址传递)以及String类的对象的不可变性
|
3月前
|
安全 Java 测试技术
Java零基础-StringBuffer 类详解
【10月更文挑战第9天】Java零基础教学篇,手把手实践教学!
70 2
|
4月前
|
安全 Java
String类-知识回顾①
这篇文章回顾了Java中String类的相关知识点,包括`==`操作符和`equals()`方法的区别、String类对象的不可变性及其好处、String常量池的概念,以及String对象的加法操作。文章通过代码示例详细解释了这些概念,并探讨了使用String常量池时的一些行为。
String类-知识回顾①
|
3月前
|
存储 安全 C++
【C++打怪之路Lv8】-- string类
【C++打怪之路Lv8】-- string类
33 1
|
3月前
|
数据可视化 Java
让星星月亮告诉你,通过反射创建类的实例对象,并通过Unsafe theUnsafe来修改实例对象的私有的String类型的成员属性的值
本文介绍了如何使用 Unsafe 类通过反射机制修改对象的私有属性值。主要包括: 1. 获取 Unsafe 的 theUnsafe 属性:通过反射获取 Unsafe类的私有静态属性theUnsafe,并放开其访问权限,以便后续操作 2. 利用反射创建 User 类的实例对象:通过反射创建User类的实例对象,并定义预期值 3. 利用反射获取实例对象的name属性并修改:通过反射获取 User类实例对象的私有属性name,使用 Unsafe`的compareAndSwapObject方法直接在内存地址上修改属性值 核心代码展示了详细的步骤和逻辑,确保了对私有属性的修改不受 JVM 访问权限的限制
72 4
|
3月前
|
存储 安全 Java
【一步一步了解Java系列】:认识String类
【一步一步了解Java系列】:认识String类
35 2
|
3月前
|
安全 C语言 C++
【C++篇】探寻C++ STL之美:从string类的基础到高级操作的全面解析
【C++篇】探寻C++ STL之美:从string类的基础到高级操作的全面解析
58 4
|
4月前
|
存储 安全 Java
Java——String类详解
String 是 Java 中的一个类,用于表示字符串,属于引用数据类型。字符串可以通过多种方式定义,如直接赋值、创建对象、传入 char 或 byte 类型数组。直接赋值会将字符串存储在串池中,复用相同的字符串以节省内存。String 类提供了丰富的方法,如比较(equals() 和 compareTo())、查找(charAt() 和 indexOf())、转换(valueOf() 和 format())、拆分(split())和截取(substring())。此外,还介绍了 StringBuilder 和 StringJoiner 类,前者用于高效拼接字符串,后者用于按指定格式拼接字符串
188 1
Java——String类详解