为什么 java 中的 string 是不可变的

作者&投稿:洪腾 (若有异议请与网页底部的电邮联系)
java中string为什么不可变~

为什么不可变,回答这个问题,你就要理解把它设计为不可变的好处,String作为java中最常用的一种类,提供了很多操作方法。把它设计为final有以下好处:
1:稳定性和节省内存开销
final型会在jvm进行类加载的时候就直接进行实例化,这样就节省以后去不断new带来的内存开辟成本了。实例化后固定不可变了,这样它就是很稳定的。java程序员每天都在使用String,如果String不稳定,你想java最基本的操作还能实现吗?
2:防篡改
final是无法被继承的,这样它是独立存在的,自身封装的很好,就不会有子类去修改它本身的方法,不会有外界来打扰它,自身结构也不会被篡改了。
3:安全性:
这和它不能被继承这个息息相关,没有任何类可以继承它,就不会暴露给外部访问它内部方法的机会,这样它自身就比较安全了。
4:方便jvm优化
比如:String str1=“a"+"b"+1; String str2="ab1";为final的话,jvm就可以很方便的把str1的内容优化为str2,并指向同一个引用。这样就不用再去内存中new了。
当然还有很多好处了,java设计者这样做的根本目的就是为了保证java体系基本类的稳固和安全。

ng对象的数据类型

1. 首先String不属于8种基本数据类型,String是一个对象。

因为对象的默认值是null,所以String的默认值也是null;但它又是一种特殊的对象,有其它对象没有的一些特性。

2. new String()和new String(“”)都是申明一个新的空字符串,是空串不是null;

3. String str=”kvill”;

String str=new String (“kvill”);的区别:

在这里,我们不谈堆,也不谈栈,只先简单引入常量池这个简单的概念。

常量池(constant pool)指的是在编译期被确定,并被保存在已编译的.class文件中的一些数据。它包括了关于类、方法、接口等中的常量,也包括字符串常量。

看例1:

String s0=”kvill”;

String s1=”kvill”;

String s2=”kv” + “ill”;

System.out.println( s0==s1 );

System.out.println( s0==s2 );

结果为:

true

true

首先,我们要知结果为道Java会确保一个字符串常量只有一个拷贝。

因为例子中的s0和s1中的”kvill”都是字符串常量,它们在编译期就被确定了,所以s0==s1为true;而”kv”和”ill”也都是字符串常量,当一个字符串由多个字符串常量连接而成时,它自己肯定也是字符串常量,所以s2也同样在编译期就被解析为一个字符串常量,所以s2也是常量池中”kvill”的一个引用。

所以我们得出s0==s1==s2;

用new String() 创建的字符串不是常量,不能在编译期就确定,所以new String() 创建的字符串不放入常量池中,它们有自己的地址空间。

看例2:

String s0=”kvill”;

String s1=new String(”kvill”);

String s2=”kv” + new String(“ill”);

System.out.println( s0==s1 );

System.out.println( s0==s2 );

System.out.println( s1==s2 );

结果为:

false

false

false

例2中s0还是常量池中”kvill”的应用,s1因为无法在编译期确定,所以是运行时创建的新对象”kvill”的引用,s2因为有后半部分new String(“ill”)所以也无法在编译期确定,所以也是一个新创建对象”kvill”的应用;明白了这些也就知道为何得出此结果了。

4. String.intern():

再补充介绍一点:存在于.class文件中的常量池,在运行期被JVM装载,并且可以扩充。String的intern()方法就是扩充常量池的一个方法;当一个String实例str调用intern()方法时,Java查找常量池中是否有相同Unicode的字符串常量,如果有,则返回其的引用,如果没有,则在常量池中增加一个Unicode等于str的字符串并返回它的引用;看例3就清楚了

例3:

String s0= “kvill”;

String s1=new String(”kvill”);

String s2=new String(“kvill”);

System.out.println( s0==s1 );

System.out.println( “**********” );

s1.intern();

s2=s2.intern(); //把常量池中“kvill”的引用赋给s2

System.out.println( s0==s1);

System.out.println( s0==s1.intern() );

System.out.println( s0==s2 );

结果为:

false

**********

false //虽然执行了s1.intern(),但它的返回值没有赋给s1

true //说明s1.intern()返回的是常量池中”kvill”的引用

true

最后我再破除一个错误的理解:

有人说,“使用String.intern()方法则可以将一个String类的保存到一个全局String表中,如果具有相同值的 Unicode字符串已经在这个表中,那么该方法返回表中已有字符串的地址,如果在表中没有相同值的字符串,则将自己的地址注册到表中“如果我把他说的这个全局的String表理解为常量池的话,他的最后一句话,“如果在表中没有相同值的字符串,则将自己的地址注册到表中”是错的:

看例4:

String s1=new String("kvill");

String s2=s1.intern();

System.out.println( s1==s1.intern() );

System.out.println( s1+" "+s2 );

System.out.println( s2==s1.intern() );

结果:

false

kvill kvill

true

在这个类中我们没有声名一个”kvill”常量,所以常量池中一开始是没有”kvill”的,当我们调用s1.intern()后就在常量池中新添加了一个”kvill”常量,原来的不在常量池中的”kvill”仍然存在,也就不是“将自己的地址注册到常量池中”了。

s1==s1.intern()为false说明原来的“kvill”仍然存在;

s2现在为常量池中“kvill”的地址,所以有s2==s1.intern()为true。

5. 关于equals()和==:

这个对于String简单来说就是比较两字符串的Unicode序列是否相当,如果相等返回true;而==是比较两字符串的地址是否相同,也就是是否是同一个字符串的引用。

6. 关于String是不可变的

这一说又要说很多,大家只要知道String的实例一旦生成就不会再改变了,比如说:String str=”kv”+”ill”+” “+”ans”;

就是有4个字符串常量,首先”kv”和”ill”生成了”kvill”存在内存中,然后”kvill”又和” “ 生成 ”kvill “存在内存中,最后又和生成了”kvill ans”;并把这个字符串的地址赋给了str,就是因为String的“不可变”产生了很多临时变量,这也就是为什么建议用StringBuffer的原因了,因为StringBuffer是可改变的。
另外,团IDC网上有许多产品团购,便宜有口碑

个人理解:因为string在java的编程当中比较普遍,以下的回答比较全面一些:
Java中String为什么是不可变的
1、在Java中,String类是不可变类,一个不可变类是一个简单的类,并且这个的实例也不能被修改,
这个类的实例创建的时候初始化所有的信息,并且这些信息不能够被修改
2、字符串常量池
字符串常量池是方法区中一块特殊的存储区域,当创建一个字符串常量的时候,判断该字符串字在符串字符串常量池中是否已经存在
如果存在,返回已经存在的字符串的引用;如果不存在,则创建一个新的字符串常量,并返回其引用
String string1 = "abcd";
String string2 = "abcd";
变量string1,string2指向常量池中的同一个字符串常量对象;如果String是可变的,给一个变量重新赋值一个引用,将会指向错误的值
3、缓存哈希值
在Java中字符串的哈希值会经常被使用到。例如在HashMap中,String的不可变总能保证哈希值总是相等的,并且缓存起来,不用担心会改变,
那意味着不需要每次都计算哈希值,这样会提高效率。在String类中有以下的代码:
private int hash; //用来缓存哈希值
3、促进其他对象的使用
HahSet<String> set = new HashSet<String>();
set.add(new String("a"));
set.add(new String("b"));
set.add(new String("c"));


沧源佤族自治县19374813277: JAVA中的static -
肇洪糖尿: Java是个面向对象编程的语言,一般而言,方法需要通过实例对象来调用.但是有一些方法不需要实例对象(或者说没有这样的对象)来调用,那么就可以在方式前面加上static修饰符,表示这个方法是静态方法,不需要实例对象就可以调用....

沧源佤族自治县19374813277: Strinput strin=new Strinput();在java中是什么意思 谢谢 -
肇洪糖尿: 这么说把,假如你有个类: public class Strinput{ //这里面有你的方法,以及属性 public static void main(String[] args) { Strinput strin=new Strinput();//那么这句话就是实例化你的类Strinput //new Strinput() 就是实例化一个Strinput的对像 //哪么strin就是这个对象的引用 } }

沧源佤族自治县19374813277: 为什么java中的string不可变 -
肇洪糖尿: 一: 原因分析: 因为String在源代码使用了final修饰, 所以不可变. //部分源代码 public final class String implements java.io.Serializable, Comparable, CharSequence { //..... }String 不可变的好处, 可以解决同步安全等问题. 二: 解决办法 StringBuffer 和 StringBuilder 是可变的字符串变量 , 可以提高效率 两者区别如下 StringBuffer 字符串变量(线程安全) StringBuilder 字符串变量(非线程安全)

沧源佤族自治县19374813277: java 里面for (String str : s){}怎么理解? -
肇洪糖尿: 这种写法是增强for循环,for(int i = 0;i < s.length(); i++){ String str = s[i]; //当成数组的写法 } 编译器会认为:1.创建名称为str 的String变量. 2.将s的第一个元素赋给str . 3.执行重复的内容. 4.赋值给下一个元素str . 5.重复执行至所有的元素都...

沧源佤族自治县19374813277: java为什么不能用string声明 -
肇洪糖尿: java里面没有string,只有String,String和基本类型不同,它是个类.

沧源佤族自治县19374813277: JAVA的递归方法中的函数为什么要STATIC? -
肇洪糖尿: 表示 静态 不依赖于类对象的方法定义为静态方法

沧源佤族自治县19374813277: 初学java的小白问大神们个问题java中为什么要把int转换为String呢?强制类型转换是什么这样做有什么好处 -
肇洪糖尿: 如果是在字符串连接中就不用把int转String了, 如是是一个函数的参数,它如果需要String类型的参数,你就得给它一个String类型的参数,简单的,1 +"" 就默认被转成了String类型. 类型转换又分为隐式转换和显示转换,像上面的就是隐式转换,而String.valueOf(1)或者Integer.toString(1)都是把1强制转换成了字符串.

沧源佤族自治县19374813277: java 新手 为什么 java类中 要有 static静态方法??? -
肇洪糖尿: 1.静态方法的特点: 直接调用类名+方法名,不需要实例化类对象. 如: Hello.P(); 非静态方法则必须实例化一个对象出来,再通过对象调用该方法如: Hello hello=new Hello(参数1~n); hello.P();2.程序被打包成.jar文件后(相当于.exe文件),给外界唯一的接口就是main方法.使用者双击.jar文件,其实就是让虚拟机执行main方法.3.main方法不是提供给程序员的,而是提供给虚拟机和使用客户的. 一个软件你没法让客户知道你内部的详情,当然客户也就没办法知道怎么去实例化对象,更不知道实例化对象时需要输入什么参数了.所以只能采用静态方法.

沧源佤族自治县19374813277: java string是引用传递还是值传递 -
肇洪糖尿: String是引用类型,不过java有字符串常量池机制,声明字符串常量或者字符串常量连接都要用到字符串常量池.如果常量池中有该字符串序列则返回,所以str创建了一个字符串并放入常量池中,str2内的字符序列已在常量池中所以str2直接指向...

沧源佤族自治县19374813277: Java中的主方法public static void main(String args[])为什么要用static来修饰
肇洪糖尿: 因为java是完全面向对象语言,这个主函数其实是一个类的方法,这个方法在类没有生成对象的时候就必须被JVM调用,所以它必须是静态的成员函数.

本站内容来自于网友发表,不代表本站立场,仅表示其个人看法,不对其真实性、正确性、有效性作任何的担保
相关事宜请发邮件给我们
© 星空见康网