Java从入门到精通六(java中的字符串变量String,StringBuilder,StringBuffer)

简介: 一: String1:String的数据类型首先我们认识到java中的数据类型分为基本数据类型和引用数据类型。基本数据类型分为数值,字符,布尔,而引用数据类型分为类,接口,数组。String是属于引用数据类型的。因为String本身就是一个类需要了解基本数据类型和引用数据类型的区别。基本数据类型是直接存储在内存的栈上的,引用数据类型继承自Object类,按照对象的内存模式进行存储。我们的引用存放在内存的栈上,而对于对象本身的值存放在内存的堆上。我们java中通过new出来的对象就会存放在堆中。

一: String


1:String的数据类型


首先我们认识到java中的数据类型分为基本数据类型和引用数据类型。基本数据类型分为数值,字符,布尔,而引用数据类型分为类,接口,数组。


String是属于引用数据类型的。因为String本身就是一个类


需要了解基本数据类型和引用数据类型的区别。基本数据类型是直接存储在内存的栈上的,引用数据类型继承自Object类,按照对象的内存模式进行存储。我们的引用存放在内存的栈上,而对于对象本身的值存放在内存的堆上。我们java中通过new出来的对象就会存放在堆中。


关于堆栈的一点点普及,可以参考java中的堆与栈


2:String 在静态数据区和堆区(动态存储区之一)创建对象的区别

String 在创建对象如果是在静态存储区,如果两次创建的对象的值是一样的,那么地址是一样的,如果是通过new的话,地址绝对是不一样的。String的一个特点就是如果静态数据区存在,那么不会创建新的对象,而是指向这个对象。在比较的时候==是比较的地址,而提供的equals()方法,比较的是内容。

在代码中举例


package java_practice;
public class StringDemo_1 {
    public static void main(String args[])
    {
        String str1 = "jgdabc";
        String str2 = "jgdabc";
        System.out.println(str1 == str2);
        String str3 = new String("jgdabc");
        String str4 = new String("jgdabc");
        System.out.println(str2==str3);
        System.out.println(str3 == str4);
        System.out.println(str1.equals(str2));
        System.out.println(str3.equals(str4));
    }
}


3:String 的相关方法

下面展示一些 内联代码片。


package java_practice;
import java.io.UnsupportedEncodingException;
public class StringDemo2 {
    public static void main(String args[]) throws UnsupportedEncodingException{
        System.out.println("Java String 类方法");
        String s = "abc";
        //1:截取字符(按照索引)
        char c = s.charAt(1);
        System.out.println("截取到的字符:"+c);
        String s1 = "def";
        //2:进行字符串大小比较
        int s2 = s1.compareTo(s);
        System.out.println("比较结果为:"+s2);
        ;//3:将字符串进行连接
        String s3 = s1.concat(s);
        System.out.println("连接后的字符串;"+s3);
        //4判断是否以某字符串开始
        boolean b =s.startsWith("c"); 
        System.out.println("输出判断结果;"+b); 
        //5:判断是否以某字符串结尾
        boolean b1 = s.endsWith("a");
        System.out.println("输出判断结果;"+b1);
        //6:比较字符串的内容
        boolean b2 = s.equals(s1);
        System.out.println("输出内容比较结果:"+b2);
        //7:将字符串转换为byte数组
        byte[] b3 = s.getBytes("utf-8"); 
        System.out.println("转换结果为:"+b3);
        //8:找出字符或者字符串在当前字符串中的起始位置
        int index = s.indexOf("a");
        System.out.println("起始位置为;"+index);   
         //9:从后向前查找指定字符或者字符串在字符串中第一次出现的位置 
        int last_index = s.lastIndexOf('a');
        System.out.println("从后向前查找到字符首次出现的位置为:"+last_index);
        //10:返回字符串的长度
        int length = s.length();
        System.out.println("找到字符串的长度为:"+length);
        //11:替换指定的字符,生成新的字符串
        String ss = s.replace('a','1');
        System.out.println("替换后的字符串为:"+ss);
        //12:根据指针的正则表达式拆分字符
        String s_1 = "jgdabc";
        String[] s_2 = s_1.split("a");//从a处分割
        System.out.println("内部含分割出的字符串长度为:"+s_2.length);
        System.out.println(s_2[0]);
        System.out.println(s_2[1]);
        //13:截取指定索引后的索引字符串
        String ss_3 = ss.substring(2);
        System.out.println("截取到的字符串为:"+ss_3);
    }
}



4:String基本类型的替换操作


我们可以通过调用包装类的静态方法进行转换。比如parseIInt()转换字符String类型为Integer包装类型。以及String.valueof()转换为String类型。


//String-->基本数据类型、包装类
        String s1 = "123";
        int i = Integer.parseInt(s1);
        System.out.println(i + 123);//123
        //基本数据类型、包装类-->String
        String s2 = 222 + "";
        System.out.println(s2 + 111);//222111
        String s3 = String.valueOf(12345);
        System.out.println(s3 + 222);//12345222


5:String与char[]之间的转换


既然是转换为数组,那就是toCharArray()


//String --> char[]
        String str1 = "abc123";
        char[] charArray = str1.toCharArray();
        for (int i = 0; i < charArray.length; i++) {
            System.out.println(charArray[i]);//abc123
        }
        //char[] --> String
        char[] arr = new char[]{
    'h', 'e', 'l', 'l', 'o'};
        String str2 = new String(arr);
        System.out.println(str2);//hello


二:StringBuilder


StringBuilder是类,固然也是引用数据类型。


1:有关StringBuilder的一些说明


通过在帮助文档中查看,我们可以了解到一些简要的说明。



2:StringBuilder的常用方法


StringBuilder sb = new StringBuilder();
        // 1:对象名.length() 序列长度
        System.out.println(sb.length());  // 0
        // 2:对象名.append() 追加到序列
        sb.append("hello");
        System.out.println(sb);  // hello
        //3:97代表的是是'a'
        sb.appendCodePoint(97);
        System.out.println(sb);  // helloa
        // 4:链式编程
        sb.append(1).append("world").append(2);
        System.out.println(sb);  // helloa1world2
        // 5:索引是5的位置替换成空格
        sb.setCharAt(5, ' ');
        System.out.println(sb);  // hello 1world2
        // 6:指定位置0前插入0
        sb.insert(0, 0);
        System.out.println(sb);  // 0hello 1world2
        // 7:删除索引6(包含)至索引14(不包含)的字符串
        sb.delete(6, 14);
        System.out.println(sb);  // 0hello
        // 8:StringBuilder类型转换成String类型
        String s__ = sb.toString();
        System.out.println(s__);  // 0hello
        // 9:进行反转
        StringBuilder sb__ = sb.reverse();
        System.out.println(sb__);


三: StringBuffer


1:有关StringBuffer的一些说明



2:有关StringBuffer的常用方法


StringBuffer append(boolean b)
将boolean参数的字符串表示形式追加到序列中。
StringBuffer append(char c)
将char参数的字符串表示形式追加到此序列。
StringBuffer append(char[] str)
将char数组参数的字符串表示形式追加到此序列。
StringBuffer append(char[] str, int offset, int len)
将char数组参数的子数组的字符串表示形式追加到此序列。
StringBuffer append(CharSequence s)
将指定的内容附加CharSequence到此序列。
StringBuffer append(CharSequence s, int start, int end)
将指定的子CharSequence序列追加到此序列。
StringBuffer append(double d)
将double 参数的字符串表示形式追加到此序列。
StringBuffer append(float f)
将float 参数的字符串表示形式追加到此序列。
StringBuffer append(int i)
将int 参数的字符串表示形式追加到此序列。
StringBuffer append(long lng)
将long 参数的字符串表示形式追加到此序列。
StringBuffer append(Object obj)
附加Object参数的字符串表示形式。
StringBuffer append(String str)
将指定的字符串追加到此字符序列。
StringBuffer append(StringBuffer sb)
将指定的内容附加StringBuffer到此序列。
StringBuffer appendCodePoint(int codePoint)
将codePoint参数的字符串表示形式追加到此序列。
int capacity()
返回当前容量。
char charAt(int index)
返回char指定索引处的此序列中的值。
int codePointAt(int index)
返回指定索引处的字符(Unicode代码点)。
int codePointBefore(int index)
返回指定索引之前的字符(Unicode代码点)。
int codePointCount(int beginIndex, int endIndex)
返回此序列的指定文本范围内的Unicode代码点数。
StringBuffer delete(int start, int end)
删除此序列的子字符串中的字符。
StringBuffer deleteCharAt(int index)
char按此顺序删除指定位置。
void ensureCapacity(int minimumCapacity)
确保容量至少等于指定的最小值。
void getChars(int srcBegin, int srcEnd, char[] dst, int dstBegin)
字符从此序列复制到目标字符数组中dst。
int indexOf(String str)
返回指定子字符串第一次出现的字符串中的索引。
int indexOf(String str, int fromIndex)
从指定的索引处开始,返回指定子字符串第一次出现的字符串中的索引。
StringBuffer insert(int offset, boolean b)
将boolea参数的字符串表示形式插入此序列中。
StringBuffer insert(int offset, char c)
将char参数的字符串表示形式插入此序列中。
StringBuffer insert(int offset, char[] str)
将char数组参数的字符串表示形式插入此序列中。
StringBuffer insert(int index, char[] str, int offset, int len)
将str数组参数的子数组的字符串表示形式插入到此序列中。
StringBuffer insert(int dstOffset, CharSequence s)
将指定的内容CharSequence插入此序列中。
StringBuffer insert(int dstOffset, CharSequence s, int start, int end)
将指定的子CharSequence序列插入此序列中。
StringBuffer insert(int offset, double d)
将double参数的字符串表示形式插入此序列中。
StringBuffer insert(int offset, float f)
将float参数的字符串表示形式插入此序列中。
StringBuffer insert(int offset, int i)
将第二个int 参数的字符串表示形式插入到此序列中。
StringBuffer insert(int offset, long l)
将long参数的字符串表示形式插入此序列中。
StringBuffer insert(int offset, Object obj)
将Object 参数的字符串表示形式插入此字符序列。
StringBuffer insert(int offset, String str)
将字符串插入此字符序列。
int lastIndexOf(String str)
返回指定子字符串最后一次出现在字符串中的索引。
int lastIndexOf(String str, int fromIndex)
返回指定子字符串最后一次出现在字符串中的索引。
int length()
返回该字符串的长度(字符数)。
int offsetByCodePoints(int index, int codePointOffset)
返回此序列中与代码点给定index的偏移量的索引codePointOffset。
StringBuffer replace(int start, int end, String str)
用指定的字符替换此序列的子字符串中的字符String。
StringBuffer reverse()
返回此字符序列的反向替换。
void setCharAt(int index, char ch)
指定索引处的字符设置为ch。
void setLength(int newLength)
设置字符序列的长度。
CharSequence subSequence(int start, int end)
返回一个新的字符序列,它是该序列的子序列。
String substring(int start)
返回一个新的String,包含此字符序列中当前包含的字符的子序列。
String substring(int start, int end)
返回一个新的String,包含此序列中当前包含的字符的子序列。
String toString()
返回表示此序列中数据的字符串。
void trimToSize()


四:String,StringBuilder,StringBuffer三者的区别(重点 )


1:定义声明上的区别


我们从定义上看



可以看到,只有String类型才可以直接声明创建,在静态数据存储区,而StringBuffer与StringBuilder只能通过new对象。


下面是一个继承关系图



2:结构上的区别

String是不可变字符串,而StringBuilder和StringBuffer是可变的。

去稍微看一下Stringl类的源码



摘录部分



public final class String
    implements java.io.Serializable, Comparable, CharSequence {
    /** The value is used for character storage. */
    private final char value[];}


类整体是被final关键字修饰的,这在一定程度上说明了该类是不可变的,是最终类,final修饰的方法同样是不可以改变的。private修饰的value更说明了value是不可以被访问到的。value只会在构造方法初始化,但是也没有提供可供修改value的方法,所以String类在结构上是不可变字符串。


在者,我们查看StringBuilder的相关源代码方法,StringBuilder可以动态变化的理由。


@Override
    public StringBuilder append(Object obj) {
        return append(String.valueOf(obj));
    }
    @Override
    @HotSpotIntrinsicCandidate
    public StringBuilder append(String str) {
        super.append(str);
        return this;
    }
    /**
     * Appends the specified {@code StringBuffer} to this sequence.
     * <p>
     * The characters of the {@code StringBuffer} argument are appended,
     * in order, to this sequence, increasing the
     * length of this sequence by the length of the argument.
     * If {@code sb} is {@code null}, then the four characters
     * {@code "null"} are appended to this sequence.
     * <p>
     * Let <i>n</i> be the length of this character sequence just prior to
     * execution of the {@code append} method. Then the character at index
     * <i>k</i> in the new character sequence is equal to the character at
     * index <i>k</i> in the old character sequence, if <i>k</i> is less than
     * <i>n</i>; otherwise, it is equal to the character at index <i>k-n</i>
     * in the argument {@code sb}.
     *
     * @param   sb   the {@code StringBuffer} to append.
     * @return  a reference to this object.
     */
    public StringBuilder append(StringBuffer sb) {
        super.append(sb);
        return this;
    }
    @Override
    public StringBuilder append(CharSequence s) {
        super.append(s);
        return this;
    }
    /**
     * @throws     IndexOutOfBoundsException {@inheritDoc}
     */
    @Override
    public StringBuilder append(CharSequence s, int start, int end) {
        super.append(s, start, end);
        return this;
    }
    @Override
    public StringBuilder append(char[] str) {
        super.append(str);
        return this;
    }
    /**
     * @throws IndexOutOfBoundsException {@inheritDoc}
     */
    @Override
    public StringBuilder append(char[] str, int offset, int len) {
        super.append(str, offset, len);
        return this;
    }
    @Override
    public StringBuilder append(boolean b) {
        super.append(b);
        return this;
    }
    @Override
    @HotSpotIntrinsicCandidate
    public StringBuilder append(char c) {
        super.append(c);
        return this;
    }
    @Override
    @HotSpotIntrinsicCandidate
    public StringBuilder append(int i) {
        super.append(i);
        return this;
    }
    @Override
    public StringBuilder append(long lng) {
        super.append(lng);
        return this;
    }
    @Override
    public StringBuilder append(float f) {
        super.append(f);
        return this;
    }
    @Override
    public StringBuilder append(double d) {
        super.append(d);
        return this;
    }
    /**
     * @since 1.5
     */
    @Override
    public StringBuilder appendCodePoint(int codePoint) {
        super.appendCodePoint(codePoint);
        return this;
    }
    /**


同理啊,我们也可以去研究查看StringBuffer的源代码


@Override
    public synchronized int codePointCount(int beginIndex, int endIndex) {
        return super.codePointCount(beginIndex, endIndex);
    }
    /**
     * @throws IndexOutOfBoundsException {@inheritDoc}
     * @since     1.5
     */
    @Override
    public synchronized int offsetByCodePoints(int index, int codePointOffset) {
        return super.offsetByCodePoints(index, codePointOffset);
    }
    /**
     * @throws IndexOutOfBoundsException {@inheritDoc}
     */
    @Override
    public synchronized void getChars(int srcBegin, int srcEnd, char[] dst,
                                      int dstBegin)
    {
        super.getChars(srcBegin, srcEnd, dst, dstBegin);
    }
    /**
     * @throws IndexOutOfBoundsException {@inheritDoc}
     * @see        #length()
     */
    @Override
    public synchronized void setCharAt(int index, char ch) {
        toStringCache = null;
        super.setCharAt(index, ch);
    }
    @Override
    public synchronized StringBuffer append(Object obj) {
        toStringCache = null;
        super.append(String.valueOf(obj));
        return this;
    }
    @Override
    @HotSpotIntrinsicCandidate
    public synchronized StringBuffer append(String str) {
        toStringCache = null;
        super.append(str);
        return this;
    }
    /**
     * Appends the specified {@code StringBuffer} to this sequence.
     * <p>
     * The characters of the {@code StringBuffer} argument are appended,
     * in order, to the contents of this {@code StringBuffer}, increasing the
     * length of this {@code StringBuffer} by the length of the argument.
     * If {@code sb} is {@code null}, then the four characters
     * {@code "null"} are appended to this {@code StringBuffer}.
     * <p>
     * Let <i>n</i> be the length of the old character sequence, the one
     * contained in the {@code StringBuffer} just prior to execution of the
     * {@code append} method. Then the character at index <i>k</i> in
     * the new character sequence is equal to the character at index <i>k</i>
     * in the old character sequence, if <i>k</i> is less than <i>n</i>;
     * otherwise, it is equal to the character at index <i>k-n</i> in the
     * argument {@code sb}.
     * <p>
     * This method synchronizes on {@code this}, the destination
     * object, but does not synchronize on the source ({@code sb}).
     *
     * @param   sb   the {@code StringBuffer} to append.
     * @return  a reference to this object.
     * @since 1.4
     */
    public synchronized StringBuffer append(StringBuffer sb) {
        toStringCache = null;
        super.append(sb);
        return this;
    }


synchronized 是锁我们后文继续研究。


3:线程安全上和性能上的区别

String是不可变的,char[]数组被final修饰,是不可变的。所以也就不存在线程不安全的问题。因为其不可变,所以当我们需要添加或者进行一系列相关的操作的时候就会显得很不方便

StringBuffer也是线程安全的,我们参考上述源码可以了解到,虽然SringBuffer是可以动态改变的,但是其内部添加了synchronized 。这就为其加了锁,这个锁的作用就是当一个线程在对StringBuffer进行操作的时候,另外一个线程是没有权力去操作的。一直到锁被释放掉才可以被使用。所以这个StringBuffer比较适合在多线程下使用。

所以



总结一下,String执行速度是最慢的,因为操作不是很方便,常常需要多次定义,但是线程比较安全,所以对于字符串比较少的,变化改动不是很大的进行操作。


StringBuffer执行速度比String要快,但是加了锁,加锁不可同时被多个线程操作,所以是比较安全,在多线程下操作比较适用。


StringBuilder是不安全的,没有加锁,对字符串的操作比较灵活。但是在多线程下是必定不安全的,所以适合在单线程下进行大量操作。


4:equals()方法上实现以及hashcode()的区别


String是实现了equals()和hashcode()方法的,StringBuilder和StringBuffer并没有实现。


equals()方法本来是用来比较地址的,但是String重写来该方法,使得可以进行对内容进行比较。


所以如果想要比较StringBuffer(),StringBuilder()的内容,可以先转换为String类型。

比如StringBuffer可以使用toString()方法进行转换为字符串String类型。


相关文章
|
2月前
|
存储 缓存 安全
除了变量,final还能修饰哪些Java元素
在Java中,final关键字不仅可以修饰变量,还可以用于修饰类、方法和参数。修饰类时,该类不能被继承;修饰方法时,方法不能被重写;修饰参数时,参数在方法体内不能被修改。
34 2
|
6天前
|
自然语言处理 Java
Java中的字符集编码入门-增补字符(转载)
本文探讨Java对Unicode的支持及其发展历程。文章详细解析了Unicode字符集的结构,包括基本多语言面(BMP)和增补字符的表示方法,以及UTF-16编码中surrogate pair的使用。同时介绍了代码点和代码单元的概念,并解释了UTF-8的编码规则及其兼容性。
78 60
|
1月前
|
Java 开发者 微服务
Spring Boot 入门:简化 Java Web 开发的强大工具
Spring Boot 是一个开源的 Java 基础框架,用于创建独立、生产级别的基于Spring框架的应用程序。它旨在简化Spring应用的初始搭建以及开发过程。
61 6
Spring Boot 入门:简化 Java Web 开发的强大工具
|
1月前
|
监控 架构师 Java
Java虚拟机调优的艺术:从入门到精通####
本文作为一篇深入浅出的技术指南,旨在为Java开发者揭示JVM调优的神秘面纱,通过剖析其背后的原理、分享实战经验与最佳实践,引领读者踏上从调优新手到高手的进阶之路。不同于传统的摘要概述,本文将以一场虚拟的对话形式,模拟一位经验丰富的架构师向初学者传授JVM调优的心法,激发学习兴趣,同时概括性地介绍文章将探讨的核心议题——性能监控、垃圾回收优化、内存管理及常见问题解决策略。 ####
|
2月前
|
监控 安全 Java
Java中的多线程编程:从入门到实践####
本文将深入浅出地探讨Java多线程编程的核心概念、应用场景及实践技巧。不同于传统的摘要形式,本文将以一个简短的代码示例作为开篇,直接展示多线程的魅力,随后再详细解析其背后的原理与实现方式,旨在帮助读者快速理解并掌握Java多线程编程的基本技能。 ```java // 简单的多线程示例:创建两个线程,分别打印不同的消息 public class SimpleMultithreading { public static void main(String[] args) { Thread thread1 = new Thread(() -> System.out.prin
|
2月前
|
Java 程序员 数据库连接
Java中的异常处理:从入门到精通
在Java编程的海洋中,异常处理是一艘不可或缺的救生艇。它不仅保护你的代码免受错误数据的侵袭,还能确保用户体验的平稳航行。本文将带你领略异常处理的风浪,让你学会如何在Java中捕捉、处理和预防异常,从而成为一名真正的Java航海家。
|
2天前
|
监控 Java
java异步判断线程池所有任务是否执行完
通过上述步骤,您可以在Java中实现异步判断线程池所有任务是否执行完毕。这种方法使用了 `CompletionService`来监控任务的完成情况,并通过一个独立线程异步检查所有任务的执行状态。这种设计不仅简洁高效,还能确保在大量任务处理时程序的稳定性和可维护性。希望本文能为您的开发工作提供实用的指导和帮助。
33 17
|
13天前
|
Java
Java—多线程实现生产消费者
本文介绍了多线程实现生产消费者模式的三个版本。Version1包含四个类:`Producer`(生产者)、`Consumer`(消费者)、`Resource`(公共资源)和`TestMain`(测试类)。通过`synchronized`和`wait/notify`机制控制线程同步,但存在多个生产者或消费者时可能出现多次生产和消费的问题。 Version2将`if`改为`while`,解决了多次生产和消费的问题,但仍可能因`notify()`随机唤醒线程而导致死锁。因此,引入了`notifyAll()`来唤醒所有等待线程,但这会带来性能问题。
Java—多线程实现生产消费者
|
15天前
|
安全 Java Kotlin
Java多线程——synchronized、volatile 保障可见性
Java多线程中,`synchronized` 和 `volatile` 关键字用于保障可见性。`synchronized` 保证原子性、可见性和有序性,通过锁机制确保线程安全;`volatile` 仅保证可见性和有序性,不保证原子性。代码示例展示了如何使用 `synchronized` 和 `volatile` 解决主线程无法感知子线程修改共享变量的问题。总结:`volatile` 确保不同线程对共享变量操作的可见性,使一个线程修改后,其他线程能立即看到最新值。