java string 不可改变为什么不可变

Java(31)
interview(1)
对象不可变定义
不可变对象是指对象的状态在被初始化以后,在整个对象的生命周期内,不可改变。
如何不可变
通常情况下,在java中通过以下步骤实现不可变
对于属性不提供设值方法
所有的属性定义为private final
类声明为final不允许继承
Return deep cloned objects with copied content for all mutable fields in class
注意:不用final关键字也可以实现对象不可变,使用final只是显示的声明,提示开发者和编译器为不可变。
Java中典型的不可变类为String类
为什么String被设计为不可变?
安全首要原因是安全,不仅仅体现在你的应用中,而且在JDK中,Java的类装载机制通过传递的参数(通常是类名)加载类,这些类名在类路径下,想象一下,假设String是可变的,一些人通过自定义类装载机制分分钟黑掉应用。如果没有了安全,Java不会走到今天
string不可变的设计出于性能考虑,当然背后的原理是string pool,当然string pool不可能使string类不可变,不可变的string更好的提高性能。
线程安全 当多线程访问时,不可变对象是线程安全的,不需要什么高深的逻辑解释,如果对象不可变,线程也不能改变它。
以上内容应该会使面试官满意
参考知识库
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
访问:581885次
积分:5289
积分:5289
排名:第3494名
原创:124篇
译文:10篇
评论:43条
(2)(3)(2)(2)(2)(7)(2)(1)(3)(2)(2)(1)(2)(16)(26)(4)(26)(2)(6)(5)(1)(11)(4)(2)(4)(1)(1)<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
您的访问请求被拒绝 403 Forbidden - ITeye技术社区
您的访问请求被拒绝
亲爱的会员,您的IP地址所在网段被ITeye拒绝服务,这可能是以下两种情况导致:
一、您所在的网段内有网络爬虫大量抓取ITeye网页,为保证其他人流畅的访问ITeye,该网段被ITeye拒绝
二、您通过某个代理服务器访问ITeye网站,该代理服务器被网络爬虫利用,大量抓取ITeye网页
请您点击按钮解除封锁&Java中String类型的不可变性和驻留池_Linux编程_Linux公社-Linux系统门户网站
你好,游客
Java中String类型的不可变性和驻留池
来源:Linux社区&
作者:woshixuye
一 基本概念
可变类和不可变类(Mutable and Immutable Objects)的初步定义:
可变类:当获得这个类的一个实例引用时,可以改变这个实例的内容。
不可变类:不可变类的实例一但创建,其内在成员变量的值就不能被修改。其中String类就是不可变类的经典应用。
package cn.xy.
public class StringTest{
&/**& * a的值在编译时就被确定下来,故其值"xy"被放入String的驻留池(驻留池在堆中)并被a指向。& * b的值在编译时也被确定,那么b的值在String的驻留池中找是否有等于"xy"的值,有的话也被b指向。& * 故两个对象地址一致& * @return true& */&public static Boolean testString1()&{& String a = "xy";& String b = "xy";& return a ==&}&&/**& * b的值在是两个常量相加,编译时也被确定。& * @return true& */&public static Boolean testString2()&{& String a = "xyy";& String b = "xy" + "y";& return a ==&}
&/**& * b的值为一个变量和一个常量相加,无法编译时被确定,而是会在堆里新生成一个值为"abc"的对象& * @return false& */&public static Boolean testString3()&{& String a = "xyy";& String b = "xy";& b = b + "y";& return a ==&}&
&/**& * b的值都无法编译时被确定,而是会在堆里分别新生成一个对象叫"xyy"。& * @return false& */&public static Boolean testString4()&{& String a = "xyy";& String b = "xy".concat("y");& return a ==&}
&&/**& * new String()创建的字符串不是常量,不能在编译期就确定,所以new String() 创建的字符串不放入常量池中,它们有自己的地址空间。 & * a,b的值都无法编译时被确定,会在堆里分别新生成一个值为"xy"的对象。& * @return fasle& */&public static Boolean testString5()&{& String a = new String("xy");& String b = new String("xy");& return a ==&}
&/**& * intern()把驻留池中"xy"的引用赋给b。& * @return true& */&public static Boolean testString6()&{& String a = "xy";& String b = new String("xy");& b = b.intern();& return a == b.intern();&}
/**& * String不可变性的体现& */&String str = "xy";
&public String chage(String str)&{& str = "xyy";&&}
&/**& * 一般引用类型的可变性(传值的时候把地址传过去,相当于把仓库的要是交给方法,方法拿到钥匙去移动仓库里的东西)& */&Person p = new Person("xy");
&public String changePerson(Person p)&{& p.setName("xyy");& return p.toString();&}
&public static void main(String[] args)&{& print(testString1()); // true& print(testString2()); // true& print(testString3()); // fasle& print(testString4()); // false& print(testString5()); // false& print(testString6()); // true
& StringTest t = new StringTest();& print(t.str); // xy& print(t.chage(t.str)); // xxy& print(t.str); // xy
& print(t.p.toString()); //xy& print(t.changePerson(t.p)); //xyy& print(t.p.toString()); //xyy&}
&&public static void print(Object o)&{& System.out.println(o);&}
相关资讯 & & &
& (12/05/:48)
& (04/10/:47)
& (02/28/:54)
& (03/16/:14)
& (04/04/:28)
& (02/03/:55)
   同意评论声明
   发表
尊重网上道德,遵守中华人民共和国的各项有关法律法规
承担一切因您的行为而直接或间接导致的民事或刑事法律责任
本站管理人员有权保留或删除其管辖留言中的任意内容
本站有权在网站内转载或引用您的评论
参与本评论即表明您已经阅读并接受上述条款Java中的String为什么是不可变的? -- String源码分析
什么是不可变对象?
众所周知, 在Java中, String类是不可变的。那么到底什么是不可变的对象呢? 可以这样认为:如果一个对象,在它创建完成之后,不能再改变它的状态,那么这个对象就是不可变的。不能改变状
什么是不可变对象?
众所周知, 在Java中, String类是不可变的。那么到底什么是不可变的对象呢? 可以这样认为:如果一个对象,在它创建完成之后,不能再改变它的状态,那么这个对象就是不可变的。不能改变状态的意思是,不能改变对象内的成员变量,包括基本数据类型的&#20540;不能改变,引用类型的变量不能指向其他的对象,引用类型指向的对象的状态也不能改变。
区分对象和对象的引用
对于Java初学者, 对于String是不可变对象总是存有疑惑。看下面代码:
String s = "ABCabc";
System.out.println("s = " + s);
s = "123456";
System.out.println("s = " + s);
打印结果为:
s = ABCabc
s = 123456
首先创建一个String对象s,然后让s的&#20540;为“ABCabc”, 然后又让s的&#20540;为“123456”。 从打印结果可以看出,s的&#20540;确实改变了。那么怎么还说String对象是不可变的呢? 其实这里存在一个误区: s只是一个String对象的引用,并不是对象本身。对象在内存中是一块内存区,成员变量越多,这块内存区占的空间越大。引用只是一个4字节的数据,里面存放了它所指向的对象的地址,通过这个地址可以访问对象。
也就是说,s只是一个引用,它指向了一个具体的对象,当s=“123456”; 这句代码执行过之后,又创建了一个新的对象“123456”, 而引用s重新指向了这个心的对象,原来的对象“ABCabc”还在内存中存在,并没有改变。内存结构如下图所示:
Java和C&#43;&#43;的一个不同点是, 在Java中不可能直接操作对象本身,所有的对象都由一个引用指向,必须通过这个引用才能访问对象本身,包括获取成员变量的&#20540;,改变对象的成员变量,调用对象的方法等。而在C&#43;&#43;中存在引用,对象和指针三个东西,这三个东西都可以访问对象。其实,Java中的引用和C&#43;&#43;中的指针在概念上是相&#20284;的,他们都是存放的对象在内存中的地址&#20540;,只是在Java中,引用丧失了部分灵活性,比如Java中的引用不能像C&#43;&#43;中的指针那样进行加减运算。
为什么String对象是不可变的?
要理解String的不可变性,首先看一下String类中都有哪些成员变量。 在JDK1.6中,String的成员变量有以下几个:
public final class String
implements java.io.Serializable, Comparable, CharSequence
/** The value is used for character storage. */
private final char value[];
/** The offset is the first index of the storage that is used. */
/** The count is the number of characters in the String. */
/** Cache the hash code for the string */
// Default to 0
在JDK1.7中,String类做了一些改动,主要是改变了substring方法执行时的行为,这和本文的主题不相关。JDK1.7中String类的主要成员变量就剩下了两个:
public final class String
implements java.io.Serializable, Comparable, CharSequence {
/** The value is used for character storage. */
private final char value[];
/** Cache the hash code for the string */
// Default to 0
由以上的代码可以看出, 在Java中String类其实就是对字符数组的封装。JDK6中, value是String封装的数组,offset是String在这个value数组中的起始位置,count是String所占的字符的个数。在JDK7中,只有一个value变量,也就是value中的所有字符都是属于String这个对象的。这个改变不影响本文的讨论。 除此之外还有一个hash成员变量,是该String对象的哈希&#20540;的缓存,这个成员变量也和本文的讨论无关。在Java中,数组也是对象(可以参考我之前的文章java中数组的特性)。
所以value也只是一个引用,它指向一个真正的数组对象。其实执行了String s = “ABCabc”; 这句代码之后,真正的内存布局应该是这样的:
value,offset和count这三个变量都是private的,并且没有提供setValue, setOffset和setCount等公共方法来修改这些&#20540;,所以在String类的外部无法修改String。也就是说一旦初始化就不能修改, 并且在String类的外部不能访问这三个成员。此外,value,offset和count这三个变量都是final的, 也就是说在String类内部,一旦这三个&#20540;初始化了,
也不能被改变。所以可以认为String对象是不可变的了。
那么在String中,明明存在一些方法,调用他们可以得到改变后的&#20540;。这些方法包括substring, replace, replaceAll, toLowerCase等。例如如下代码:
String a = "ABCabc";
System.out.println("a = " + a);
a = a.replace('A', 'a');
System.out.println("a = " + a);
打印结果为:
a = ABCabc
a = aBCabc
那么a的&#20540;看&#20284;改变了,其实也是同样的误区。再次说明, a只是一个引用, 不是真正的字符串对象,在调用a.replace('A', 'a')时, 方法内部创建了一个新的String对象,并把这个心的对象重新赋给了引用a。String中replace方法的可以说明问题:
读者可以自己查看其他方法,都是在方法内部重新创建新的String对象,并且返回这个新的对象,原来的对象是不会被改变的。这也是为什么像replace, substring,toLowerCase等方法都存在返回&#20540;的原因。也是为什么像下面这样调用不会改变对象的&#20540;:
String ss = "123456";
System.out.println("ss = " + ss);
ss.replace('1', '0');
System.out.println("ss = " + ss);
打印结果:
ss = 123456
ss = 123456
String对象真的不可变吗?
从上文可知String的成员变量是private final 的,也就是初始化之后不可改变。那么在这几个成员中, value比较特殊,因为他是一个引用变量,而不是真正的对象。value是final修饰的,也就是说final不能再指向其他数组对象,那么我能改变value指向的数组吗? 比如将数组中的某个位置上的字符变为下划线“_”。 至少在我们自己写的普通代码中不能够做到,因为我们根本不能够访问到这个value引用,更不能通过这个引用去修改数组。
那么用什么方式可以访问私有成员呢? 没错,用反射, 可以反射出String对象中的value属性, 进而改变通过获得的value引用改变数组的结构。下面是实例代码:
public static void testReflection() throws Exception {
//创建字符串"Hello World", 并赋给引用s
String s = "Hello World";
System.out.println("s = " + s); //Hello World
//获取String类中的value字段
Field valueFieldOfString = String.class.getDeclaredField("value");
//改变value属性的访问权限
valueFieldOfString.setAccessible(true);
//获取s对象上的value属性的值
char[] value = (char[]) valueFieldOfString.get(s);
//改变value所引用的数组中的第5个字符
value[5] = '_';
System.out.println("s = " + s);
//Hello_World
打印结果为:
s = Hello World
s = Hello_World
在这个过程中,s始终引用的同一个String对象,但是再反射前后,这个String对象发生了变化, 也就是说,通过反射是可以修改所谓的“不可变”对象的。但是一般我们不这么做。这个反射的实例还可以说明一个问题:如果一个对象,他组合的其他对象的状态是可以改变的,那么这个对象很可能不是不可变对象。例如一个Car对象,它组合了一个Wheel对象,虽然这个Wheel对象声明成了private final 的,但是这个Wheel对象内部的状态可以改变,
那么就不能很好的保证Car对象不可变。

我要回帖

更多关于 int变成string 的文章

 

随机推荐