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. PyCharm的几个使用技巧

      PyCharm是个十分强大的Python编辑器,笔者在日常的工作中学到了很多该IDE的使用技巧,有的是从别人那里学到的,有的是自己学习的.笔者深感自己的开发能力不足,因此希望能够将这些使用技巧记录 ...

  2. 用selenium实现打码平台

    注:本文以  人人网登陆 为例 import time from selenium import webdriver # 准备一个名为yudama的py文件 from yudama import in ...

  3. Python SQLAlchemy入门教程

    本文将以Mysql举例,介绍sqlalchemy的基本用法.其中,Python版本为2.7,sqlalchemy版本为1.1.6. 一. 介绍 SQLAlchemy是Python中最有名的ORM工具. ...

  4. 「Luogu 2367」语文成绩

    更好的阅读体验 Portal Portal1: Luogu Description 语文老师总是写错成绩,所以当她修改成绩的时候,总是累得不行.她总是要一遍遍地给某些同学增加分数,又要注意最低分是多少 ...

  5. Prometheus客户端开发:腾讯云CLB

    一:简介 随着prometheus的使用人群逐渐扩大,官方定义的client exporter虽然能满足我们的大部分需求,但是很多监控还是需要我们自定义开发,以下内容就是基于腾讯云SDK,对腾讯云CL ...

  6. 关于Struts.xml的路径配置以及对应的连接路径问题

    主要记住一点!: 1.   namespace的路径配置例如:/x:   2.  name名自由配置(主要用来是做action跳转方法用的) 3.  form表单中提交action时以这个格式提交(此 ...

  7. Ubuntu18.04.3 LTS初体验

    安装系统 想来虚拟机安装太慢,正好有一台旧电脑,干脆整个乌班图系统. 启动盘工具:UltraISO,老牌工具了. 上官网下载ISO镜像文件: https://cn.ubuntu.com/downloa ...

  8. Kubernetes Horizontal Pod Autoscaling

    HPA介绍 Horizo​​ntal Pod Autoscaler基于观察到的CPU利用率(或借助自定义指标 支持,基于其他一些应用程序提供的指标)自动缩放复制控制器,部署或副本集中的Pod数量 .请 ...

  9. sbt安装

    使用 Scala 编写的程序需要使用 sbt 进行编译打包,官网sbt下载解压 在解压路径下创建脚本: #!/bin/bash SBT_OPTS="-Xms512M -Xmx1536M -X ...

  10. OFFICE 2010 每次打开提示安装的问题

    OFFICE2010 安装后每次打开 文件总是提示配置 解决办法: 前提是office已经激活 注册表找到 HKEY_CURRENT_USER\Software\Microsoft\Office\14 ...