java基础(3)--详解String
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的更多相关文章
- JAVA基础——内部类详解
JAVA内部类详解 在我的另一篇java三大特性的封装中讲到java内部类的简单概要,这里将详细深入了解java内部类的使用和应用. 我们知道内部类可分为以下几种: 成员内部类 静态内部类 方法内部类 ...
- JAVA基础——异常详解
JAVA异常与异常处理详解 一.异常简介 什么是异常? 异常就是有异于常态,和正常情况不一样,有错误出错.在java中,阻止当前方法或作用域的情况,称之为异常. java中异常的体系是怎么样的呢? 1 ...
- Java基础数据类型详解
在Java中的数据类型一共有8种,大致分为整型(4个)浮点型(2个)布尔(1)字符(1个) 分类 类型 默认值 占用字节 范围 整型 byte 0 1 = 8 bit -2^7 - 2^7 short ...
- java基础:数组详解以及应用,评委打分案例实现,数组和随机数综合,附练习案列
1.数组 1.1 数组介绍 数组就是存储数据长度固定的容器,存储多个数据的数据类型要一致. 1.2 数组的定义格式 1.2.1 第一种格式 数据类型[] 数组名 示例: int[] arr; ...
- Java基础 - 异常详解
异常的层次结构 Throwable Throwable 是 Java 语言中所有错误与异常的超类. Throwable 包含两个子类:Error(错误)和 Exception(异常),它们通常用于指示 ...
- 【干货】用大白话聊聊JavaSE — ArrayList 深入剖析和Java基础知识详解(二)
在上一节中,我们简单阐述了Java的一些基础知识,比如多态,接口的实现等. 然后,演示了ArrayList的几个基本方法. ArrayList是一个集合框架,它的底层其实就是一个数组,这一点,官方文档 ...
- java基础(十三)-----详解内部类——Java高级开发必须懂的
可以将一个类的定义放在另一个类的定义内部,这就是内部类. 为什么要使用内部类 为什么要使用内部类?在<Think in java>中有这样一句话:使用内部类最吸引人的原因是:每个内部类都能 ...
- Java基础——枚举详解
前言: 在第一次学习面向对象编程时,我记得最深的一句话就是“万物皆对象”.于是我一直秉承着这个思想努力的学习着JAVA,直到学习到枚举(Enum)时,看着它颇为奇怪的语法……我一直在想,这TM是个什么 ...
- JAVA基础知识详解
1. JVM是什么 JVM是Java Virtual Mechine的缩写.它是一种基于计算设备的规范,是一台虚拟机,即虚构的计算机. JVM屏蔽了具体操作系统平台的信息(显然,就像是我们在电脑上开了 ...
- Java 基础之详解 Java 反射机制
一.什么是 Java 的反射机制? 反射(Reflection)是Java的高级特性之一,是框架实现的基础,定义:JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法: ...
随机推荐
- 安装requests遇到的坑
通过pip install requests命令安装,报错,提示“SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed”.百思不得其解,后 ...
- Birt报表
研究了两天终于发现开始学会了BIRT报表的开发流程. 第一步:到http://www.eclipse.org/downloads/下载 Eclipse IDE for Java and Report ...
- TCP协议--TCP三次握手和四次挥手
TCP三次握手和四次挥手 TCP有6种标示:SYN(建立联机) ACK(确认) PSH(传送) FIN(结束) RST(重置) URG(紧急) 一.TCP三次握手 第一次握手 客户端向服务器发出连 ...
- Python实现获取IP代码
代码如下: # -*- coding: utf-8 -*- #!/usr/bin/env python # @Time : 2018/5/30 11:05 # @Desc : 获取ip # @File ...
- pip的简单用法
pip的用法: 其实跟linux的yum很像,它可以帮我们安装python所需要的环境包,并且可以包解决依赖关系 eg: 列出已安装的包 pip list 安装要安装的包 pip install xx ...
- H5+app -- 关于ajax提交问题
1.前阵子在做系统的h5+ app为满足手机端也能进行业务操作,例如:提货,扫描入库之类的.所以就要将做接口,从手机端调用后台系统的方法. 2.例如这样的请求格式,但是呢,每次请求它都直接跳到erro ...
- 监听器以及在监听类里面获得bean的方法
1实现HttpSessionListener和ServletContextListener,2个接口 2然后在contextInitialized初始化方法里面: ServletContext app ...
- 网站搭建-IIS Windows系统搭建网站 (不小心看到自己的密码 - 怎么找回网站记住的密码)
上一期说到IIS可以用自己喜欢的网站来直接玩,然后得得瑟瑟将自己的博客园账号首页拿过去玩(今天第一天水博客园). 然后自己访问啊,访问啊,然后就一直点啊点的,当然,其实后面的链接都是跳转到博客园里面去 ...
- Cesium坐标系及坐标转换详解
前言 Cesium项目中经常涉及到模型加载.浏览以及不同数据之间的坐标转换,弄明白Cesium中采用的坐标系以及各个坐标系之间的转换,是我们迈向三维GIS大门的前提,本文详细的介绍了Cesium中采用 ...
- hdu 1263 水果 (嵌套 map)
水果Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 65536/32768 K (Java/Others)Total Submissio ...