String——字符串
首先看一下string的一部分源码吧
public final class String
private final char value[];
我们暂且只看这两行,
第一行String被final修饰,表示String不可以被继承且它的所有方法都隐式的被final所修饰。(不懂的伙伴可以看以下final,我也写了关于这个关键字的博文)
对于第二行呢,我们可以看到String内部有一个char类型的数组。这个数组被设置为final类型,也就是说这个数组引用被设置为常量,这可以被赋值一次。其次这个value被private修饰,这就保证了我们不能在类的外部获取到这个value,也不能更改value引用(这里指的是不能改变这个引用的赋值,因为是final类型,已经被指定,只能是那个数组,但是可以改变数组的元素。)所以这就保证了我们不可以在String外部对string进行改变。所以说String是一个不可变对象(),String内部的值也是固定的。而我们的引用指向另一个string是指向了另一个对象,而不是最初的这个对象。(但是实际上String内部value指向的数组是可以改变的,但是一般没人动他,因为要通过反射来做这件事。
如果你真的想动它,看我另一篇关于更改string底层数组的博文,不过要知道怎么使用反射。而且,也要懂这篇文章的知识才可以看懂那个代码)
但是我们还是要说,在设计上,我们的string是一个不可变对象。
任何对于String的操作都会使其创建一个新的对象(String)
字符串常量池:
目的是为了,节省我们创建相同字符串的时间与空间。(记住哦,接下来讲一个知识点的时候会用到它)
首先我们需要说明两个概念:
Java中的常量池分为两种:
静态常量池:.class文件中的常量池,存储了类的字符串、数字等常量、还包含类信息和方法信息
运行时常量池:JVM类加载之后会将class文件中的静态常量池加载到方法区中,其中字符串常量、数字常量(int char 这些常量)会保存在运行时常量池里。
运行时常量池包含:字符串常量池和其它的基本数据类型的各自的常量池。
而我们平时说的常量池就是指的运行时常量池。
在这里记住一句话,运行时常量池的所有对象,都是在类加载之后直接完成的。程序运行之后产生的数据类型并不会在这里存储。
当我们在编写代码时出现的所有常量都会被编译器编译成一个常量
比如说 string = “kkk”
这个kkk会被放在字符串常量池中,作为一个字符串常量。
在这里我们只讨论字符串常量池,其它的讨论也没啥意义。
首先我们从代码编写开始讨论,因为在编译时期有一个优化的过程
String str = “kkk” 等价于 string str = “kkk“
string str = "kkk"+"1"等价于string str = "kkk1"
因为在编译时期编译器在这里直接优化了。实际上在最终的字节码文件,这里的kkk不存在
1也不存在,存在的只有一个kkk1
string str = ”kkk“
string str2 = str +”1“
这里就不一样了啊,因为在str2的时候我们使用的是引用类型,所以在编译时期并不能确定你这个引用类型的确定值(你要知道,他是一个变量,即使你给他初始化了,但是,如果采用多线程的模式,是会被改变的,所以不能确定它到底是什么。所以编译器不优化它)
这时候在字符串常量池里只有 kkk和1这两个
string str = ”1“
string str2 = ”kkk“+”mmm“+str+”lll“
等价于 str2 = ”kkkmmm“+str + ”lll“
其实上一部分没有说的很全,说了一部分,因为在编译器编译时期,它会从左边开始优化,但是当它碰到第一个变量之后就停止优化了。(我认为这样顺序举例,大家可以更直观的感受到。不是上面故意不说清楚的,其实这里还没说完。接着看下面吧)
我们在上面的那种情况下其实有一个特例情况
final string str = ”kkk“
string str2 = str +"mmm"
等价为str2 = ”kkkmmm“
在这里因为str是一个被初始化的常量,所以在编译时期,编译器会直接把所有的str都替换成它的具体值。
这里一定要强调在编译之前已经初始化(也就是你代码中明确赋值 str = ‘kkk")但是如果在运行时初始化的话,那就不行了。
那编译器就不会进行优化了,因为编译器没办法替换。(运行时初始化是指的通过方法初始化那种)
类编译之后,会将这些字符串常量放在类的.class文件的静态常量区
好啦,以上我把我能想到的情况都说出来了。以下就该到字符串常量池了。
我们都知道(上面说的)运行时常量池中的字符串常量池是存储类中明确出现的字符串的,所以在类加载的过程中。会将.class文件中的字符串常量都加载到运行时字符串常量池里。所以说类中的所有明确表示的字符串常量都在我们的字符串常量池里(包括final string str)
之后我们该讨论一下,我们程序执行过程中的字符串了。
实际上,在我们程序执行过程中字符串的创建包括了以下几个情况
以下情况在字符串常量池里有(”kkk“,”mmm“,”lll“)这三个字符串的情况下。
首先第一种是
String str = ”kkk“,这种情况并不会创建一个字符串对象,因为我们的字符串常量池里已经有了kkk了。
所以就会直接使栈内的str引用指向常量池里的kkk
string str = new string(”kkk“)这里会创建一个对象,首先kkk已经存在不属于这段代码创建的哈(这里只说运行时)
这里记住一句话,一切的new,都会创建一个对象,绝大部分在堆区,这又匿名类对象在栈内。
上面个的代码就会在堆内创建一个string对象。然后此时的str指向的其实是堆内的string对象,然后堆内的String对象才会去字符串常量池找有没有”kkk“这个字符串,如果有堆内对象的value会等同于字符串常量池里的kkk的value。也就是指向了同一个数组,从而使得string对象和字符串常量池里的string,相等。但是如果string str = new string(s1 +”kkk“),这种传入的参数不是字符串常量池中的,实际上,他的value会指向在堆内的字符串对象的数组,而那个数组的值就是s1+”kkk“。也可以看成它指向了另一个字符串对象。不过那个string是堆内的不是字符串常量池内的。(在这里我们需要说明一下,在编译时期的字符串优化,如果没有完全优化,还存在引用类型,他会在运行时期创建一个stringbuilder来进行+操作,最后使用tostring方法来创建一个string对象,而这个string对象是在堆内的。)
实际上我们说的字符串指向哪个字符串实际上都是把哪个字符串的value拷贝了一份。其实两个字符串对象指向的是同一个字符数组。
补充:所有的字符串的操作,只要不是用反射给人家间接的改,实际上都会创建另一个string对象。因为string是不可变的
被final、private修饰了的value。
向字符串常量池里添加字符串对象:
其实也不是不可以添加,不过不建议吧,如果你的一个字符串是在运行时获得的且用的比较多,可以使用str.intern()方法,可以把它放进去。
关于 string 、stringbuilder 、 stringbuffer的关系
实际上 stringbuilder 、 stringbuffer内部都维护了一个char数组。
通过append向char数组里拼接字符串,最后使用tostring方法来生成返回一个string对象,可以说是为string服务的吧。
在我们的+的时候用的是stringbuilder,
stringbuilder是线程不安全的buffer是安全的。
在需要使用大量字符串拼接的时候单线程建议使用stringbuilder,多线程建议使用stringbuffer。
==和equals
首先说明哈,数组、类、接口、这些都是引用类型哈,不是值类型
在Object中有个方法是equals,所有所有的类都有咯。
==用于值类型,当用于值类型的时候,比较的是值,对吧。
==用于引用类型的时候,比较的是是否是同一个引用。
其实就是比较底层啦。看过我之前那篇值传递和引用传递的同学应该知道他们的底层都是些啥吧。
其实它就是比较底层的01是否相同,相同返回true,不相同返回false。
而equals呢,在object中定义的就是使用 == ,但是可以重写这个方法,定义你自己的规则。
String——字符串的更多相关文章
- Java String字符串/==和equals区别,str。toCharAt(),getBytes,indexOf过滤存在字符,trim()/String与StringBuffer多线程安全/StringBuilder单线程—— 14.0
课程概要 String 字符串 String字符串常用方法 StringBuffer StringBuilder String字符串: 1.实例化String对象 直接赋值 String str=& ...
- [CareerCup] 1.3 Permutation String 字符串的排列
1.3 Given two strings, write a method to decide if one is a permutation of the other. 这道题给定我们两个字符串,让 ...
- 03-Java String字符串详解
1.Java字符串String A.实例化String字符串:直接赋值(更合理一些,使用较多).使用关键字new. B.String内容的比较 // TODO Auto-generated metho ...
- C++学习38 string字符串的增删改查
C++ 提供的 string 类包含了若干实用的成员函数,大大方便了字符串的增加.删除.更改.查询等操作. 插入字符串 insert() 函数可以在 string 字符串中指定的位置插入另一个字符串, ...
- C++学习37 string字符串的访问和拼接
访问字符串中的字符 string 字符串也可以像字符串数组一样按照下标来访问其中的每一个字符.string 字符串的起始下标仍是从 0 开始.请看下面的代码: #include <iostrea ...
- java String字符串——进度1
String字符串 在JAVA中提供了多种创建字符串对象的方法,这里介绍最简单的两种, 第一种是直接赋值, 第二种是使用String类的构造方法: 如下所示: Strin ...
- 关于String字符串反转
这是网上看到的一篇java面试题中的问题: 问题是: 如何将一个String字符串反转. String str = "1234567"; int length = str.leng ...
- JavaScript的内置对象(Date日期+string字符串)基础语法总结
1.Date日期对象可以储存任意一个日期,并且可以精确到毫秒数(1/1000 秒). 1)定义一个时间对象 : var Udate=new Date(); //注意:使用关键字new,Date()的首 ...
- 【转】String字符串相加的问题
String字符串相加的问题 前几天同事跟我说我之前写的代码中在操作字符串时候,使用字符串相加的方式而不是使用StringBuffer或者StringBuilder导致内存开销很大.这个问题一直在困扰 ...
- 从零开始学习前端JAVASCRIPT — 3、JavaScript基础string字符串介绍
1:字符串 JS中的任何数据类型都可以当作对象来看.所以string既是基本数据类型,又是对象. 2:声明字符串 基本数据类型:var sStr = '字符串'; 对象的方法:var oStr = n ...
随机推荐
- CSDN,CNBLOGS博客文章一键转载插件(转载测试)
插件地址: https://greasyfork.org/zh-CN/scripts/381053-csdn%E5%8D%9A%E5%AE%A2%E6%96%87%E7%AB%A0%E8%BD%AC% ...
- 使用ajax的几种方式
1.$.ajax()发送一个get请求,数据返回为json $.ajax({ type: "GET", url: "select", //ajax请求地址 da ...
- 利用Python模拟GitHub登录
最近学习了Fiddler抓包工具的简单使用,通过抓包,我们可以抓取到HTTP请求,并对其进行分析.现在我准备尝试着结合Python来模拟GitHub登录. Fiddler抓包分析 首先,我们想要模拟一 ...
- asp.net core 系列之Configuration
在ASP.NET Core中的App configuration 是通过configuration providers基于key-value对建立的.Configuration providers读取 ...
- Lockey的沙雕低错集锦(未完待续~自己提升用)
(嘿嘿 (^~ ^)) 1. bitset<2100>a[2100] a[x]|=(1<<x) (1<=x<=2100) 使用时忘了x的范围,额,应 ...
- ServiceFabric极简文档-1.2 硬件环境.md
1. 一个C盘,100G,16G内存,2.5HZ2. 官网有推荐配置
- redis集群(单机6节点实现)
Redis集群搭建与简单使用 1.介绍安装环境与版本: 1)Redis使用的是Redis-3.2.8版本. 2)用一台虚拟机模拟6个节点,三个master节点,三个slave节点.虚拟机使用CentO ...
- UVA1103 古代象形符号 Ancient Messages 题解
题目链接: https://www.luogu.org/problemnew/show/UVA1103 题目分析: 我们可以先进行矩阵的还原 for(int k=1;k<=4;k++) { a[ ...
- delegate委托的例子,实现对Form中控件的更新
using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; usin ...
- Vue匿名组件使用keep-alive后动态清除缓存
在使用Vue开发管理系统项目的时候,为了保存页面的浏览状态,我们可以使用内置组件keep-alive来缓存组件内部状态,避免重新渲染. <keep-alive> <router-vi ...