Java.lang.String类
1.String类定义
String 字符串对象本质上是一个 final 修饰的字符串数组对象, java字符串就是Unicode字符序列. 因为被final修饰, 所以字符串是常量,它们的值一旦被创建后不能改变。 Java没有内置的字符串类型, Java内部通过在标准Java类库中提供了一个预定义的String类,实现对字符串类型的支持
通过JDK源码前三行, 可以看出
1. String类被final修饰, 这说明String不可继承
2. String类的主力成员字段value是个final修饰的char[]数组。final修饰的字段创建以后就不可改变。注意, 虽然value是不可变,也只是value这个引用地址不可变。挡不住Array数组是可变的事实。
public static void main(String[] args) {
final char[] value = {1, 2, 3};
int[] another = {4, 5, 6};
//编译器报错,value用final修饰,编译器不允许我把value指向堆区另一个地址
//value = another;
value[0] = 100; //数组变成{100,2,3}
}
Array的数据结构如图所示, 变量value存储的只是对象的引用(指向堆中的地址 0x1000),数组对象的本体结构在heap堆。String类里的value用final修饰,只是说stack里的这个叫value的引用地址0x1000不可变。可没说heap里数组对象本身数据不可变。value[0] = 100时, 变量value引用的值0x1000并没有改变, 改变的是heap堆中对象本身的数据值.
2.字符串的创建
1.String a = "abc";
String b = "abc";
创建了一个对象,
第一行在字符串常量池创建"abc",
第二行检查字符串常量池中有"abc"对象,直接返回
2.String c = new String("abc");
String d = new String("abc");
System.out.println(c==d); // false
System.out.println(c.intern()==d.intern()); //true
总共创建了3个对象
第一行在堆和字符串常量池中各创建一个"abc"
第二行在堆中创建一个"abc", 常量池中已有, 不创建
3.String s = "a" + "b" + "c";
只创建了1个对象
赋值符号右边的"a"、"b"、"c"都是常量,对于常量,编译时就直接存储它们的字面值而不是它们的引用,在编译时就直接将它们连接的结果提取出来变成了"abc",
该语句在class文件中就相当于String s = "abc" ,然后当JVM执行到这一句的时候, 就在String pool里找 ,如果没有这个字符串,就会产生一个
4.String s1 = "abc";
String s2 = "a";
String s3 = "bc";
String s4 = s2 + s3;
System.out.println(s1 == s4); // false
因为s2+s3实际上是使用StringBuilder.append来完成,会生成不同的对象。
s1指向常量池"abc",
s4指向堆中"abc"(append连接而来)
5.String S1 = "abc";
final String S2 = "a";
final String S3 = "bc";
String S4 = S2 + S3;
System.out.println(S1 == S4); // true
因为final变量在编译后会直接替换成对应的值,所以实际上等于s4="a"+"bc",而这种情况下,编译器会直接合并为s4="abc",所以最终s1==s4。
3. String类常量池内存分析,基于JDK1.7 (分析: https://blog.csdn.net/qq_34115899/article/details/86583262#commentsedit)
问题一:
String h = new String("cc"); // 第一行创建2个对象, 堆中对象"cc", 常量池对象"cc"
String intern = h.intern(); // intern 指向常量池中对象0x333
System.out.println(intern == h); // h指向堆中对象0x001, 地址引用不同,所以false
问题二:
String str2 = new String("str") + new String("01"); // 堆中有"str","01","str01" 三个对象; 常量池中有 "str","01" 两个对象
String str1 = "str01"; // 常量池中创建"str01"对象, 变量str1 指向常量池中对象
str2.intern(); // 检查常量池中已经有了"str1"对象, 返回对象引用时,没有变量接收, 相当于啥都没干
System.out.println(str2 == str1); // false
问题三:
String str2 = new String("str") + new String("01");
String str1 = "str01";
String str3 = str2.intern();
System.out.println(str3 == str1); // true
问题四:
String str2 = new String("str") + new String("01");
str2.intern();
String str1 = "str01";
System.out.println(str2 == str1); // true String str3 = new String("str01");
str3.intern();
String str4 = "str01";
System.out.println(str3 == str4); // false
问题五:
4.为什么String类被设计为不可变的,是否真的不可变?
1.如何保证String不可变
1. 对于属性不提供设值方法
2. 所有的属性定义为private final
3. 类声明为final不允许继承
4.Return deep cloned objects with copied content for all mutable fields in class(为类中的所有可变字段返回具有复制内容的深度克隆对象)
注意:不用final关键字也可以实现对象不可变,使用final只是显示的声明,提示开发者和编译器为不可变。
2. 为什么String类被设计为不可变(加final)
1.为了实现字符串池, 只有当字符串是不可变的,字符串常量池才有可能实现。
字符串常量池(String pool, String intern pool, String保留池) 是Java堆内存中一个特殊的存储区域, 当创建一个String对象时,假如此字符串值已经存在于常量池中,则不会创建一个新的对象,而是引用已经存在的对象。假若字符串对象允许改变,那么String interning将不能实现, 比如改变一个对象会影响到另一个独立对象. 严格来说,这种常量池的思想,是一种优化手段.
jdk1.6 以及之前,字符串常量池是存在于永久代中的(方法区),频繁使用,会造成该区域内存占有过大,造成垃圾收集器的gc 从而影响程序的运行
jdk 1.7 常量池移动到了堆空间中,并且常量池中只存储字符串的引用,不再存储字符串值
jdk1.8 又进行改变,字符串常量池已经不存在,又出现了一个metaSpace(元空间) 2.为了线程安全
如果字符串是可变的,那么会引起很严重的安全问题。譬如,数据库的用户名、密码都是以字符串的形式传入来获得数据库的连接,或者在socket编程中,主机名和端口都是以字符串的形式传入。因为字符串是不可变的,所以它的值是不可改变的,否则黑客们可以钻到空子,改变字符串指向的对象的值,造成安全漏洞。
因为字符串是不可变的,所以是多线程安全的,同一个字符串实例可以被多个线程共享。这样便不用因为线程安全问题而使用同步。字符串自己便是线程安全的。 3.为了实现String可以创建HashCode不可变性
因为字符串是不可变的,所以在它创建的时候HashCode就被缓存了,不需要重新计算。这就使得字符串很适合作为Map中的键,字符串的处理速度要快过其它的键对象。这就是HashMap中的键往往都使用字符串。
参考:
String s = a+b+c+d+e;创建了几个对象的详细分析?
String,StringBuilder,StringBuffer 实现原理解析
Java.lang.String类的更多相关文章
- java.lang.String 类的所有方法
java.lang.String 类的所有方法 方法摘要 char charAt(int index) 返回指定索引处的 char 值. int codePointAt(int index) 返回指定 ...
- JDK1.8源码(三)——java.lang.String 类
String 类也是java.lang 包下的一个类,算是日常编码中最常用的一个类了,那么本篇博客就来详细的介绍 String 类. 1.String 类的定义 public final class ...
- java.lang.String 类源码解读
String类定义实现了java.io.Serializable, Comparable<String>, CharSequence 三个接口:并且为final修饰. public fin ...
- JDK1.8源码(三)——java.lang.String类
一.概述 1.介绍 String是一个final类,不可被继承,代表不可变的字符序列,是一个类类型的变量.Java程序中的所有字符串字面量(如"abc")都作为此类的实例实现,&q ...
- 字符串(Java.lang.String类)的使用
java字符串就是Unicode字符序列,例如"Java"就是四个Unicode字符 java没有内置的字符串类型,而是在标准Java类库中提供了一个预定义的类String.每个用 ...
- java.lang.String类compareTo()返回值解析
一.compareTo()的返回值是int,它是先比较对应字符的大小(ASCII码顺序)1.如果字符串相等返回值02.如果第一个字符和参数的第一个字符不等,结束比较,返回他们之间的差值(ascii码值 ...
- 【Java面试题】53 能不能自己写个类,也叫java.lang.String?
可以,但是即使你写了这个类,也没有用. 这个问题涉及到加载器的委托机制,在类加载器的结构图(在下面)中,BootStrap是顶层父类,ExtClassLoader是BootStrap类的子类,ExtC ...
- 能不能自己写个类,也叫java.lang.String?
可以,但在应用的时候,需要用自己的类加载器去加载,否则,系统的类加载器永远只是去加载jre.jar包中的那个java.lang.String.由于在tomcat的web应用程序中,都是由webapp自 ...
- java数组、java.lang.String、java.util.Arrays、java.lang.Object的toString()方法和equals()方法详解
public class Test { public static void main(String[] args) { int[] a = {1, 2, 4, 6}; int[] b = a; in ...
随机推荐
- gcc/g++堆栈保护技术
最近学习内存分布,通过gdb调试发现一些问题,栈空间变量地址应该是从高往低分布的,但是调试发现地址虽然是从高往低分布,但是变量地址的顺序是乱的,请教同事他说可能是gcc/g++默认启用了堆栈保护, ...
- 虚拟机安装windows sever2008
1.打开并进行新建虚拟机 2.默认选择“典型” 3.选择“安装程序盘映像文件”,并‘浏览’选择本地的文件 4. 5.后面的默认选择即可,安装路径可自己修改 6.这一步的磁盘大小可自己修改的,这里先预设 ...
- QMS产品 - MasterControl 质量管理活动
主要质量管理活动如下所示: CAPA 纠正措施/预防措施 Corrective Maintenance 纠正措施 Preventive Maintenance 预防措施 Customs Complai ...
- 【Java】FlowControl 流程控制
FlowControl 流程控制 什么是流程控制? 控制流程(也称为流程控制)是计算机运算领域的用语,意指在程序运行时,个别的指令(或是陈述.子程序)运行或求值的顺序. 不论是在声明式编程语言或是函数 ...
- Linux/UNIX 下 “command not found” 原因分析及解决
在使用 Linux/UNIX 时,会经常遇到 "command not found" 的错误,就如提示的信息,Linux /UNIX 没有找到该命令.原因无外乎你命令拼写错误或 L ...
- 实验一 熟悉IDLE和在线编程平台
实验目的 1.掌握python IDLE集成开发环境的安装与使用 2.熟悉在线编程平台 3.掌握基本的python程序编写.编译与运行程序的方法 实验内容 1.按照实验指导安装IDLE,尝试交互式运行 ...
- Spring Boot 中使用自定义注解,AOP 切面打印出入参日志及Dubbo链路追踪透传traceId
一.使用背景 开发排查系统问题用得最多的手段就是查看系统日志,在分布式环境中一般使用 ELK 来统一收集日志,但是在并发大时使用日志定位问题还是比较麻烦,由于大量的其他用户/其他线程的日志也一起输出穿 ...
- ADO.Net和Entity Framework的区别联系
它们有以下几点区别:1,ADO.Net是开发人员自己select.update等写sql语句,来实现对数据库的增删改查等操作:采用EF进行开发操作数据库的时候,只需要操作对象,这样做使开发更方便,此时 ...
- linux上Docker安装gogs私服
一.背景介绍 Gogs 是一款类似GitHub的开源文件/代码管理系统(基于Git),Gogs 的目标是打造一个最简单.最快速和最轻松的方式搭建自助 Git 服务.使用 Go 语言开发使得 Gogs ...
- 分享一个404页面(猴子动态SVG图)
说明 在CSDN看到的一个404界面,简洁大气非常棒我的个人网站已经用上了! 代码 防止原页面失效,代码 粘贴在下面 <!DOCTYPE html> <html lang=" ...