实时搜索: java 不可变类有哪些

java 不可变类有哪些

451条评论 6493人喜欢 5328次阅读 252人点赞
打印结果一次为aaa abc ccc
s1打印aaa 能理解,由于创建了一块新的内存aaa,引用s1指向aaa
那么s2和s3区别在哪,为什么一个改变了,一个没改变 ...

怎么理解,String是不可变类: 1. 不可变类:
所谓的不可变类是指这个类的实例一旦创建完成后,
就不能改变其成员变量值。如JDK内部自带的很多不可变类:I
nterger、Long和String等。
2. 可变类:
相对于不可变类,
可变类创建实例后可以改变其成员变量值
,开发中创建的大部分类都属于可变类。

3.string对象在内存创建后就不可改变(String源码)
public final class String
implements java.io.Serializable, Comparable<String>, CharSequence
{
/** The value is used for character storage. */
private final char value[];
/** The offset is the first index of the storage that is used. */
private final int offset;
/** The count is the number of characters in the String. */
private final int count;
/** Cache the hash code for the string */
private int hash; // Default to 0
....
public String(char value[]) {
this.value = Arrays.copyOf(value, value.length); // deep copy操作
}
...
public char[] toCharArray() {
// Cannot use Arrays.copyOf because of class initialization order issues
char result[] = new char[value.length];
System.arraycopy(value, 0, result, 0, value.length);
return result;
}
...
}
源码发现:
1.String类被final修饰,不可继承
2.string内部所有成员都设置为私有变量
3.不存在value的setter
4.并将value和offset设置为final。
5.当传入可变数组value[]时,进行copy而不是直接将value[]复制给内部变量.
6.获取value时不是直接返回对象引用,而是返回对象的copy.

String类和StringBuffer类的区别: 首先,String和StringBuffer主要有2个区别:
(1)String类对象为不可变对象,一旦你修改了String对象的值,隐性重新创建了一个新的对象,释放原String对象,StringBuffer类对象为可修改对象,可以通过append()方法来修改值
(2)String类对象的性能远不如StringBuffer类。
关于以上具体解释如下:
在java中有3个类来负责字符的操作。
1.Character 是进行单个字符操作的,
2.String 对一串字符进行操作。不可变类。
3.StringBuffer 也是对一串字符进行操作,但是可变类。
String:
是对象不是原始类型.
为不可变对象,一旦被创建,就不能修改它的值.
对于已经存在的String对象的修改都是重新创建一个新的对象,然后把新的值保存进去.
String 是final类,即不能被继承.
StringBuffer:
是一个可变对象,当对他进行修改的时候不会像String那样重新建立对象
它只能通过构造函数来建立,
StringBuffer sb = new StringBuffer();
注意:不能通过赋值符号对他进行赋值.
sb = "welcome to here!";//error
对象被建立以后,在内存中就会分配内存空间,并初始保存一个null.向StringBuffer
中赋值的时候可以通过它的append方法.
sb.append("hello");
字符串连接操作中StringBuffer的效率要比String高:
String str = new String("welcome to ");
str += "here";
的处理步骤实际上是通过建立一个StringBuffer,让侯调用append(),最后
再将StringBuffer toSting();
这样的话String的连接操作就比StringBuffer多出了一些附加操作,当然效率上要打折扣.
并且由于String 对象是不可变对象,每次操作Sting 都会重新建立新的对象来保存新的值.
这样原来的对象就没用了,就要被垃圾回收.这也是要影响性能的.
看看以下代码:
将26个英文字母重复加了5000次,
String tempstr = "abcdefghijklmnopqrstuvwxyz";
int times = 5000;
long lstart1 = System.currentTimeMillis();
String str = "";
for (int i = 0; i < times; i++) {
str += tempstr;
}
long lend1 = System.currentTimeMillis();
long time = (lend1 - lstart1);
System.out.println(time);
得到的结果每次不一定,一般为 1563左右。
我们再看看以下代码
String tempstr = "abcdefghijklmnopqrstuvwxyz";
int times = 5000;
long lstart2 = System.currentTimeMillis();
StringBuffer sb = new StringBuffer();
for (int i = 0; i < times; i++) {
sb.append(tempstr);
}
long lend2 = System.currentTimeMillis();
long time2 = (lend2 - lstart2);
System.out.println(time2);
得到的结果为 16 有时还是 0
所以结论很明显,StringBuffer 的速度几乎是String 上万倍。当然这个数据不是很准确。因为循环的次数在100000次的时候,差异更大。不信你试试。
根据上面所说:
str += "here";
的处理步骤实际上是通过建立一个StringBuffer,让侯调用append(),最后
再将StringBuffer toSting();
所以str += "here";可以等同于
StringBuffer sb = new StringBuffer(str);
sb.append("here");
str = sb.toString();
所以上面直接利用"+"来连接String的代码可以基本等同于以下代码
String tempstr = "abcdefghijklmnopqrstuvwxyz";
int times = 5000;
long lstart2 = System.currentTimeMillis();
String str = "";
for (int i = 0; i < times; i++) {
StringBuffer sb = new StringBuffer(str);
sb.append(tempstr);
str = sb.toString();
}
long lend2 = System.currentTimeMillis();
long time2 = (lend2 - lstart2);
System.out.println(time2);
平均执行时间为1563左右。

什么是不可变对象,它对写并发应用有什么帮助: 面向对象就是你所考虑的对象,比如java就是面向对象编程的。如果想通过编程说明一个人做饭,那么就会面向人这个来写,做饭是他的一种行为,写的时候就抽象为方法。而面向过程就会按他做饭的步骤写,先买菜洗菜炒作煮饭。面向对象思想就是考虑的对象,而不是按过程来的。它会把对象的属性和行为抽象为变量和方法。举例说你一天的生活。你就是一个对象,你有姓名,性别,年龄等属性来表明你的特点,在写的时候就会定义几个变量表示你的姓名性别等属性。你会有做饭,学习,睡觉等行为,写的时候会写做饭,学习,睡觉三个方法对应表示你的行为。你这个具体的人就是一个对象。当然还不止这一点,但是这是最核心和重要的。

JAVA如何实现深拷贝:

下面给你简单介绍protected 域(或方法)实现过程思路:

protected 域(或方法)对本包内的所有类可见(当然包括子类),那么,子类可以获得访超类受保护域(或方法)的权利,但是,若子类和超类不在同一个包下,就不能访问超类对象的这个受保护域(或方法)。

浅拷贝与深拷贝

Object类对自己的具体子类的域一无所知,Object类的clone方法只是将各个域进行拷贝。数值或基本类型不会出现问题,但是,如果在对象中包含了引用对象,这些对象的内容没有被自我复制,拷贝的结果也即是原始对象和拷贝对象引用着同一个引用对象(一般地,动词“引用”可理解为“管理”,就是指向同一内存)。

浅拷贝满足:

x.clone() != x为 true,

x.clone().getClass() == x.getClass()为true,

((x.clone().field1 ) == (x. field1))&& … &&((x.clone().fieldN )==(x. fieldN))也为 true 。

如果原始对象与浅拷贝对象共同引用(管理、指向)的引用对象是不可变的,将不会产生任何问题

如果原始对象管理的引用对象是可变的,就必须需重新定义clone方法,来实现深层次的拷贝。要对涉及的每一个类,判断以下两点:

默认的clone方法是否满足需求。

默认的clone方法是否能通过调用可变引用对象的clone方法得到解决。

对涉及的每一个类,深拷贝要满足:

x.clone() != x为 true,

x.clone().getClass() == x.getClass()为true,

x.clone().equals(x)也为 true ,当然equals方法是如此重写过的。 

Object类中的clone方法被声明为protected,防止出现文章开头所提到的,子类和超类不在同一个包下的情况,要声明clone为public,来实现深拷贝

如何创建不可变的Java类或对象: 不可变对象是指一个对象的状态在对象被创建之后就不再变化。不可变对象对于缓存是非常好的选择,因为你不需要担心它的值会被更改。

创建一个不可变类:
将类声明为final,所以它不能被继承;
将所有的成员声明为私有的,这样就不允许直接访问这些成员;
对变量不要提供setter方法;
将所有可变的成员声明为final,这样只能对它们赋值一次;
通过构造器初始化所有成员,进行深拷贝(deep copy);
在getter方法中,不要直接返回对象本身,而是克隆对象,并返回对象的拷贝;

示例:

public final class Immutable {
private final int id;
private final String name;
private final HashMap map;

public int getId() {
return id;
}

public String getName() {
return name;
}

/**
* 可变对象的访问方法
*/
public HashMap getMap() {
return (HashMap) testMap.clone();
}

/**
* 实现深拷贝的构造器*/
public Immutable(int i, String n, HashMap hm){this.id=i;
this.name=n;
HashMap tempMap=new HashMap();
String key;
Iterator it = hm.keySet().iterator();
while(it.hasNext()){
key=it.next();
tempMap.put(key, hm.get(key));
}
this.map = tempMap;
}

}

不变模式有两种形式:弱不变模式和强不变模式。
弱不变模式,指类实例的状态是不可改变,但是这个类的子类的实例具有可能会变化的状态,要实现弱不变模式,一个类必须满足下面条件:
对象没有任何会修改对象状态的方法 ,这样一来当对象的构造函数将对象的状态初始化之后,对象的状态便不再改变;
属性都是私有的,以防客户端对象直接修改内部状态;
这个对象所引用的其他对象如果是可变的话,必须设法限制外界对这些可变对象的访问,以防止外界修改这些对象,尽量在不可变对象内部初始化这些被引用的对象,而不要在客户端初始化再传入到不可变对象内部来,如果某个可变对象必须在客户端初始化,然后再传入到不变对象里的话,就应当考虑在不可变对象初始化的时候,将这个可变对象进行拷贝。

Java的String类型字符串是不可变序列,求问一题: 因为第一个 S2 调用 test change方法 它的返回值是空的.. 把change方法 改成String类型的就可以了

java数组里都可以放什么类型的数据?Stringbuffer在什么情况下是不可变的?: 数组里的类型,你看声明为什么类型的,譬如如果是String类型的,就是得加双引号,数组可以是对象类型,你看相关的API文档》
要区分清String和StringBuffer,后者相当于一个缓冲区,你可以往里面放东西,通过append进行追加,到最后输出的这个StringBuffer就是缓冲区里你所放的这些所有的内容,String是你设成什么就是什么,可以对其分割split等操作但是操作之后的新字符串是一个新的,但是原来的还不变化,StringBuffer就不一样了。

如何理解python数据类型的不可变特点: 不可变数据类型的就是内存中不管有多少个引用,相同的对象只占用了一块内存,但是它的缺点就是当需要对变量进行运算从而改变变量引用的对象的值时,由于是不可变的数据类型,所以必须创建新的对象,这样就会使得一次次的改变创建了一个个新的对象,不过不再使用的内存会被垃圾回收器回收。
其实其他语言,比如java也有类似的功能,就是一些基础的数字,例如前256个,都是固定在静态区的某个位置的,而不会随着指针变量的变化,而导致所指向的地址变化。

  • 2014年马币贬值多少

    皇室战争ios怎么换号后还是以前的号: 1、部落冲突皇室战争不能直接切换帐号;2、需要更换帐号的玩家只能通过game center中切换登录apple ID来更换游戏帐号;3、或者通过绑定、更换Facebook帐号来切换帐号。4、iOS更换设备之后,只要登...

    244条评论 6334人喜欢 1004次阅读 944人点赞
  • dnf怎么分解

    CF甩狙和CS一样的原理吗。: 我总感觉是有的 但是事实上 真的不是 没bug cf和cs还是有差距的 而且 cf甩狙 一般都是不怎么准确的 我见过有个人用 ak 在那甩 你想想那个人 用 ak 和用狙一样的 ...

    810条评论 3498人喜欢 6072次阅读 583人点赞
  • dnf苦轮哪里爆

    cf审判者踢腿瞬怎么打不准: 踢腿瞬和你普通瞬狙是一样的只是这瞬狙结束后用踢腿才加快下一枪的间隔 所以你打不准不是因为踢腿而是技术问题 你可以用tgp狙击红点提升瞬狙精度 ...

    553条评论 5488人喜欢 2340次阅读 730人点赞
  • 五十以内六和九的公倍数有几个

    春天的礼物 作文 500字 六年级的:   春天来了  我睁开冬天醉睡的眼睛,呀,不经意间,春已在前方微笑。啊,春!她迈着轻盈的步伐而又含着少女的羞涩不敢大步走来,她悄悄撕裂了灰蒙蒙的冬天的外衣,离我们越来越近我漫步在春的田野上,被一层笼罩万物的生灵的薄纱...

    391条评论 5539人喜欢 1465次阅读 236人点赞
  • 15朗逸和13朗逸哪个好

    交通违章代码1613罚款多少: 代码 扣分 处罚 违法行为/依据1613 6 200 上道路行驶的机动车未悬挂机动车号牌/《法》第九十五条第一款、第九十条 罚款200元,扣6分。望采纳~~~~!!! ...

    780条评论 5515人喜欢 3564次阅读 426人点赞