String是个啥?

  字符串?不可变字符串?今天想起来这个又意思的东西,所以来记录一下。我们说String是不可变字符串,那他就真的不可变吗?

public class StringDemo {
public static void main(String[] args) {
String s = " a b ";
s.trim();
System.out.println("-" + s + "-");
}
} 输出:- a b -
空格没有被删除
public class StringDemo {
public static void main(String[] args) {
String s = " a b ";
s = s.trim();
System.out.println("-" + s + "-");
}
} 输出:-a b-
空格被删除
public class StringDemo {
public static void main(String[] args) {
String s = " a b ";
String ss = s.trim();
System.out.println("-" + ss + "-");
}
} 输出:-a b-
空格被删除
public class StringDemo {
public static void main(String[] args) {
String s = " a b ";
String ss = s.trim();
System.out.println("-" + s + "-");
}
} 输出:- a b -
空格没有被删除

  我蒙蔽了,来理一理,先看一看String的源码:

public final class String implements java.io.Serializable, Comparable<String>, CharSequence {
private final char value[]; private int hash; private static final long serialVersionUID=-6849794470754667710L;
......
public String trim() {
int len = value.length;
int st = 0;
char[] val = value; /* avoid getfield opcode */ while ((st < len) && (val[st] <= ' ')) {
st++;
}
while ((st < len) && (val[len - 1] <= ' ')) {
len--;
}
return ((st > 0) || (len < value.length)) ? substring(st, len) : this;
}

 String类为什么被称为不可变字符串呢?是因为类上面加了final还是value[]数组上面加了final呢?还是因为String类没有提供可变的方法呢?我们不妨利用反射拿到这个value数组试一试。

public class StringDemo {
public static void main(String[] args) throws Exception {
String s = " a b ";
Class clazz = s.getClass();
Field f = clazz.getDeclaredField("value");
f.setAccessible(true);
char[] cs = (char[])f.get(s);
cs[1] = 'A';
System.out.println(s);
}
}
输出: A b 

  这说明它是可变的啊,final只不过是约束了变量所指向的地址不能变,并不是说地址段的内容不能变,也就说,虽然value数组使用final修饰,但这只是定义了我value变量指向的地址不能变,但是地址段本身的内容(数组的内容)是可变的。

  String 是final类,也就是说,一旦定义了String类型的变量,那么变量本身所指向的地址就不能变了。但是String变量指向的地址的内容可变吗?这你就得注意点了,String类型变量和普通类型变量以及引用类型变量都有区别,虽然String类型变量是引用类型,但他还有另一个身份,String类型的变量还是个常量!既然是常量,那JVM就会把它区别对待,把它保存在方法区的常量池中。

  当s.trim()时,trim()方法入栈,在本方法的栈针中,定义了一个新的char类型数组val,并指向保存原字符串信息的char数组value,但由于数组是基本类型的,所以并没有传引用,而是直接把数组的内容复制给了新的数组val,经过循环操作完成方法后返回一个字符串,但是这个字符串是个常量,JVM如果发现这个常量在常量池中不存在,就会把他保存在常量池中,注意了:这个新的字符串常量和原字符串常量在不同的内存块。但是由于s是final修饰的,所以s并不能改变地址指向新的字符串常量,所以s的输出的值不变,与s不同的是,String ss = s.trim()刚好站了出来,说:我来指向它!于是,ss的输出的值就成了新的字符串常量。如果没有ss指向那个新的常量,那他就可能过会儿被GC了。

String是个啥?的更多相关文章

  1. 透过WinDBG的视角看String

    摘要 : 最近在博客园里面看到有人在讨论 C# String的一些特性. 大部分情况下是从CODING的角度来讨论String. 本人觉得非常好奇, 在运行时态, String是如何与这些特性联系上的 ...

  2. JavaScript String对象

    本编主要介绍String 字符串对象. 目录 1. 介绍:阐述 String 对象的说明以及定义方式. 2. 实例属性:介绍 String 对象的实例属性: length. 3. 实例方法:介绍 St ...

  3. ElasticSearch 5学习(9)——映射和分析(string类型废弃)

    在ElasticSearch中,存入文档的内容类似于传统数据每个字段一样,都会有一个指定的属性,为了能够把日期字段处理成日期,把数字字段处理成数字,把字符串字段处理成字符串值,Elasticsearc ...

  4. [C#] string 与 String,大 S 与小 S 之间没有什么不可言说的秘密

    string 与 String,大 S 与小 S 之间没有什么不可言说的秘密 目录 小写 string 与大写 String 声明与初始化 string string 的不可变性 正则 string ...

  5. js报错: Uncaught RangeError: Invalid string length

    在ajax请求后得到的json数据,遍历的时候chrome控制台报这个错误:Uncaught RangeError: Invalid string length,在stackoverflow查找答案时 ...

  6. c# 字符串连接使用“+”和string.format格式化两种方式

    参考文章:http://www.liangshunet.com/ca/201303/218815742.htm 字符串之间的连接常用的两种是:“+”连接.string.format格式化连接.Stri ...

  7. 【手记】注意BinaryWriter写string的小坑——会在string前加上长度前缀length-prefixed

    之前以为BinaryWriter写string会严格按构造时指定的编码(不指定则是无BOM的UTF8)写入string的二进制,如下面的代码: //将字符串"a"写入流,再拿到流的 ...

  8. JavaScript中String对象的方法介绍

    1.字符方法 1.1 charAt() 方法,返回字符串中指定位置的字符. var question = "Do you like JavaScript?"; alert(ques ...

  9. 在多线程编程中lock(string){...}隐藏的机关

    常见误用场景:在订单支付环节中,为了防止用户不小心多次点击支付按钮而导致的订单重复支付问题,我们用 lock(订单号) 来保证对该订单的操作同时只允许一个线程执行. 这样的想法很好,至少比 lock( ...

  10. BCL中String.Join的实现

    在开发中,有时候会遇到需要把一个List对象中的某个字段用一个分隔符拼成一个字符串的情况.比如在SQL语句的in条件中,我们通常需要把List<int>这样的对象转换为“1,2,3”这样的 ...

随机推荐

  1. 修改 linux 默认字符集

    [root@eric6 ~]# cat /etc/sysconfig/i18n //查看 linux 默认的字符集,默认是 UTF-8 LANG="zh_CN.UTF-8" cp ...

  2. Taro -- 微信小程序密码弹窗

    记录一个类似支付密码的弹窗写法,实现是否免密功能.如图: index.js   import Taro, { Component } from '@tarojs/taro'   import { Vi ...

  3. MYSQL学习笔记——sql语句优化之索引

    上一篇博客讲了可以使用慢查询日志定位耗时sql,使用explain命令查看mysql的执行计划,以及使用profiling工具查看语句执行真正耗时的地方,当定位了耗时之后怎样优化呢?这篇博客会介绍my ...

  4. iconv 转换文件的编码格式

    1.命令功能 icnov用于转换文件的编码格式 linux默认中没有icnov文件,需要自己安装http://www.gnu.org/software/libiconv/. (1)下载libiconv ...

  5. [Tyvj1423]GF和猫咪的玩具(最短路)

    [Tyvj1423]GF和猫咪的玩具 题目描述 GF同学和猫咪得到了一个特别的玩具,这个玩具由n个金属环(编号为1---n),和m条绳索组成,每条绳索连接两个不同的金属环,并且长度相同.GF左手拿起金 ...

  6. Mongodb副本集实现及读写分离

    前言 地址:https://blog.csdn.net/majinggogogo/article/details/51586409 作者介绍了,mongodb副本集的读写原理,原理是通过代码层来实现. ...

  7. 64bit assembly

    16个通用寄存器: %rax %rbx %rcx %rdx %rsi %rdi %rbp %rsp %r8 %r9 %r10 %r11 %r12 %r13 %r14 %r15

  8. CSS中用 opacity、visibility、display 属性将 元素隐藏 的 对比分析

    说明 opacity 用来设置透明度 display 定义建立布局时元素生成的显示框类型 visibility 用来设置元素是否可见. opacity.visibility.display 这三个属性 ...

  9. YottaChain主网全面上线预示商业应用的落地区块链云存储不一样的云

    Yottachain存储网12月24日平安夜全面启动,意味着全球首个可商用的区块链存储公链全面落地.对于数据存储来说,小到我们个人的照片文档,大到政企机构数据库资料,都是互联网生活中如影随形的标配. ...

  10. Linux内核设计与实现 总结笔记(第五章)系统调用

    系统调用 内核提供了用户进程和内核交互的接口,使得应用程序可以受限制的访问硬件设备. 提供这些接口主要是为了保证系统稳定可靠,避免应用程序恣意妄行. 一.内核通信 系统调用在用户空间进程和硬件设备之间 ...