java基础(3)--详解String

其实与八大基本数据类型一样,String也是我们日常中使用非常频繁的对象,但知其然更要知其所以然,现在就去阅读源码深入了解一下String类对象,并解决一些我由来已久的疑问。

为什么String是不可变序列

  • String类使用final修饰的,所以不可以通过继承方式来修改String
  • String类字符串存储是使用final修饰的value数组实现的,所以不可以通过修改value数组来修改String。但是数组其实也是一个引用,那能不能通过修改引用来达到修改String的目的?

    下面来看个例子:
public class Test03 {
public static void main(String[] args) {
char[] arr = new char[]{'a', 'b', 'c', 'd'};
String s = new String(arr);
arr[0] = 'b';
System.out.println(s);
}
}
abcd

那为什么不能通过修改引用来修改String呢,看看源码

 public String(char value[]) {
this.value = Arrays.copyOf(value, value.length);
}

String通过拷贝数组保证了本身不会被修改

  • String类的方法返回的字符串只要不是原字符串就会new一个对象。
public String substring(int beginIndex) {
if (beginIndex < 0) {
throw new StringIndexOutOfBoundsException(beginIndex);
}
int subLen = value.length - beginIndex;
if (subLen < 0) {
throw new StringIndexOutOfBoundsException(subLen);
}
return (beginIndex == 0) ? this : new String(value, beginIndex, subLen);
}
public String substring(int beginIndex, int endIndex) {
if (beginIndex < 0) {
throw new StringIndexOutOfBoundsException(beginIndex);
}
if (endIndex > value.length) {
throw new StringIndexOutOfBoundsException(endIndex);
}
int subLen = endIndex - beginIndex;
if (subLen < 0) {
throw new StringIndexOutOfBoundsException(subLen);
}
return ((beginIndex == 0) && (endIndex == value.length)) ? this
: new String(value, beginIndex, subLen);
}

还有等等......

String类用"=="进行比较的问题

看下面一段代码

public class Test03 {
public static void main(String[] args) {
String s1 = "helloWorld";
String s2 = "hello" + "World";
String s3 = "helloWorld";
String s4 = "hello";
String s5 = "World";
final String s6 = "World";
String s7 = s4 + s5;
String s8 = "hello" + s6; String str1 = new String("helloWorld");
String str2 = new String("helloWorld"); System.out.println(s1 == s2);
System.out.println(s1 == s3);
System.out.println(s1 == s7);
System.out.println(s1 == s8); System.out.println(str1 == str2);
System.out.println(str2 == s1);
}
}
true
true
false
true
false
false

一共进行了5次比较,现在一一道来

  • 第一次和第二次之所以都为true,是因为两者的引用都指向了字符串常量池中的"helloWorld",虚拟机会在编译期间进行优化,字面常量相”+“会直接进行拼接而不是调用其他方法。
  • 当字符串拼接存在不是字符常量而是一个变量的时候,虚拟机在编译期间不会将其进行优化,而是用会在堆上生成一个对象,所以第三个比较是false;但如果String对象使用final修饰,那么他就是不可修改的常量,这时等同于是字符常量拼接,虚拟机才会无顾忌的将其优化,所以第四个比较为true。
  • 最后一部分对象的比较就比较容易了,由于是在堆上分配内存的,所以内容是相等的,但是地址引用并不相等,所以都为false。

StringBuilder和StringBuffer

由于String本身不可修改,所以Java提供了StirngBuiler和StringBuffer来弥补这一空缺。现在String之间使用”+“的使用的时候实际上是调用StringBuilder的append方法完成的。

先看一个例子

public class Test03 {
public static void main(String[] args) {
String s = "hello";
for (int i = 0; i < 100; i++){
s += "hello";
}
}
}

再看看反编译

Compiled from "Test03.java"
public class base.Test03 {
public base.Test03();
Code:
0: aload_0
1: invokespecial #1 // Method java/lang/Object."<init>":()V
4: return public static void main(java.lang.String[]);
Code:
0: ldc #2 // String hello
2: astore_1
3: iconst_0
4: istore_2
5: iload_2
6: bipush 100
8: if_icmpge 37
11: new #3 // class java/lang/StringBuilder
14: dup
15: invokespecial #4 // Method java/lang/StringBuilder."<init>":()V
18: aload_1
19: invokevirtual #5 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
22: ldc #2 // String hello
24: invokevirtual #5 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
27: invokevirtual #6 // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
30: astore_1
31: iinc 2, 1
34: goto 5
37: return
}

可以看出实际上在进行字符串拼接的时候,是调用了append方法进行拼接,再用toString方法返回String对象。

StringBuilder和StringBuffer的不同

StringBuilder是线程不安全的,但是速度快,StringBuffer是线程安全的,他的方法都是用synchronized修饰的,但是速度慢

String,StringBuilder,StringBuffer的效率比较

下面用代码验证一下

String

public class Test03 {
public static void main(String[] args) {
Long start = System.currentTimeMillis();
String s = "hello";
for (int i = 0; i < 10000; i++) {
s += "world";
}
System.out.println(System.currentTimeMillis() - start);
}
}
713

StringBuilder

public class Test03 {
public static void main(String[] args) {
Long start = System.currentTimeMillis();
StringBuilder s = new StringBuilder("hello");
for (int i = 0; i < 10000; i++) {
s = s.append("world");
}
System.out.println(System.currentTimeMillis() - start);
}
}
2

StringBuffer

public class Test03 {
public static void main(String[] args) {
Long start = System.currentTimeMillis();
StringBuffer s = new StringBuffer("hello");
for (int i = 0; i < 10000; i++) {
s = s.append("world");
}
System.out.println(System.currentTimeMillis() - start);
}
}
6

显然StringBuilder > StringBuffer > String;但是在相加字符串较少的时候还是用String效率高点。

java基础(3)--详解String的更多相关文章

  1. JAVA基础——内部类详解

    JAVA内部类详解 在我的另一篇java三大特性的封装中讲到java内部类的简单概要,这里将详细深入了解java内部类的使用和应用. 我们知道内部类可分为以下几种: 成员内部类 静态内部类 方法内部类 ...

  2. JAVA基础——异常详解

    JAVA异常与异常处理详解 一.异常简介 什么是异常? 异常就是有异于常态,和正常情况不一样,有错误出错.在java中,阻止当前方法或作用域的情况,称之为异常. java中异常的体系是怎么样的呢? 1 ...

  3. Java基础数据类型详解

    在Java中的数据类型一共有8种,大致分为整型(4个)浮点型(2个)布尔(1)字符(1个) 分类 类型 默认值 占用字节 范围 整型 byte 0 1 = 8 bit -2^7 - 2^7 short ...

  4. java基础:数组详解以及应用,评委打分案例实现,数组和随机数综合,附练习案列

    1.数组 1.1 数组介绍 数组就是存储数据长度固定的容器,存储多个数据的数据类型要一致. 1.2 数组的定义格式 1.2.1 第一种格式 数据类型[] 数组名 示例: int[] arr;     ...

  5. Java基础 - 异常详解

    异常的层次结构 Throwable Throwable 是 Java 语言中所有错误与异常的超类. Throwable 包含两个子类:Error(错误)和 Exception(异常),它们通常用于指示 ...

  6. 【干货】用大白话聊聊JavaSE — ArrayList 深入剖析和Java基础知识详解(二)

    在上一节中,我们简单阐述了Java的一些基础知识,比如多态,接口的实现等. 然后,演示了ArrayList的几个基本方法. ArrayList是一个集合框架,它的底层其实就是一个数组,这一点,官方文档 ...

  7. java基础(十三)-----详解内部类——Java高级开发必须懂的

    可以将一个类的定义放在另一个类的定义内部,这就是内部类. 为什么要使用内部类 为什么要使用内部类?在<Think in java>中有这样一句话:使用内部类最吸引人的原因是:每个内部类都能 ...

  8. Java基础——枚举详解

    前言: 在第一次学习面向对象编程时,我记得最深的一句话就是“万物皆对象”.于是我一直秉承着这个思想努力的学习着JAVA,直到学习到枚举(Enum)时,看着它颇为奇怪的语法……我一直在想,这TM是个什么 ...

  9. JAVA基础知识详解

    1. JVM是什么 JVM是Java Virtual Mechine的缩写.它是一种基于计算设备的规范,是一台虚拟机,即虚构的计算机. JVM屏蔽了具体操作系统平台的信息(显然,就像是我们在电脑上开了 ...

  10. Java 基础之详解 Java 反射机制

    一.什么是 Java 的反射机制?   反射(Reflection)是Java的高级特性之一,是框架实现的基础,定义:JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法: ...

随机推荐

  1. Django学习day7——简单的使用数据库和模型

    Django支持的数据库 PostgreSQL SQLite 3 MySQL Oracle 其中SQLite 3不需要安装,因为SQLite使用文件系统上的独立文件来存储数据 这里我们用SQLite ...

  2. abp(net core)+easyui+efcore实现仓储管理系统——EasyUI之货物管理八(二十六)

    abp(net core)+easyui+efcore实现仓储管理系统目录 abp(net core)+easyui+efcore实现仓储管理系统——ABP总体介绍(一) abp(net core)+ ...

  3. Wireshark嗅探抓取telnet明文账户密码

    0x00 Wireshark(前称Ethereal)是一个网络封包分析软件.网络封包分析软件的功能是撷取网络封包,并尽可能显示出最为详细的网络封包资料.Wireshark使用WinPCAP作为接口,直 ...

  4. PHP file_get_contents 读取js脚本的问题

    PHP file_get_contents 读取js脚本的问题 如果文件中带有js脚本 会触发 比方说alert 这个时候 你不用去管他

  5. SVN--服务端安装和设置---centos7

    一.安装环境 [root@localhost ~]# getenforce Disabled [root@localhost ~]# systemctl disable firewalld 二.yum ...

  6. 彻底搞懂 netty 线程模型

    编者注:Netty是Java领域有名的开源网络库,特点是高性能和高扩展性,因此很多流行的框架都是基于它来构建的,比如我们熟知的Dubbo.Rocketmq.Hadoop等.本文就netty线程模型展开 ...

  7. Keras 中间层可视化,附代码详解,以Mnist数字为对象

    最近搭建了个Resnet50 的神经网络模型,相看一看中间某一层的输出结果,想感性的感受下逐层提取特征的过程,以数字0为对象,对数字0逐层提取特征,话不多说直接上代码,关于如何搭建Resnet,可以参 ...

  8. 关于Jvm的见解(二)

    栈管运行,堆管存储!!! 栈呢,也叫作栈内存,主要管java程序的运行,在线程创建时创建,生命周期和线程一致,只要线程一结束,该栈就被GC,是线程私有的.基本类型的变量和对象的引用数据类型的变量都在栈 ...

  9. 在Debian/Ubuntu上面安装升级nginx到最新版

    在Debian下面通过 apt-get 可以自动安装 nginx,不过版本一般比较老,如果想要使用nginx的最新特性就需要升级版本.   一般安装可以通过编绎源文件安装,但可能需要安装很多编绎工具, ...

  10. hdu 3342 Legal or Not (topsort)

    Legal or NotTime Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)Total ...