java 不可变类有哪些_Java不可变类

0. 几个问题

什么是不可变类?

不可变类的优缺点是什么?

常见的不可变类有哪些?String为什么要设计成不可变类?

如何自己设计一个不可变类?

带着这几个问题阅读本文以期能对Java的不可变类有一个全面的了解。

1. 什么是不可变类

不可变类是指类的实例一旦创建后,不能改变其成员变量的值。

与之对应的,可变类的实例创建后可以改变其成员变量的值。

2. 不可变类的优缺点

优点:

效率,例如字符串常量池,字符串常量池可以将一些字符常量放在常量池中重复使用,避免每次都重新创建相同的对象、节省存储空间。但如果字符串是可变的,此时相同内容的String还指向常量池的同一个内存空间,当某个变量改变了该内存的值时,其他遍历的值也会发生改变。所以不符合常量池设计的初衷。

安全性,不可变对象天生是线程安全的,在不同线程共享对象,不需要同步机制,因为对象的值是固定的。

缺点:

资源开销,对象需要频繁的修改属性,则每一次修改都会新创建一个对象,产生大量的资源开销。

3. 常见的不可变类有哪些?String为什么要设计成不可变类?

常见的不可变类:String Integer Long等类型

String设计成不可变类主要是出于效率和安全性考虑。

4. 如何自己设计一个不可变类?

类使用final修饰符修饰,保证类不能被继承。

如果类可以被继承会破坏类的不可变性机制,只要继承类覆盖父类的方法并且继承类可以改变成员变量值,那么一旦子类以父类的形式出现时,不能保证当前类是否可变。

类的成员变量都应该是private final的,保证成员变量不可改变。

任何获取/修改属性的方法都不应作用于属性本身。

不提供修改成员变量的方法,例如setter方法。

getter方法不能返回对象本身,要返回对象的拷贝,防止对象外泄。

修改对象的属性时要返回新对象

对成员变量的初始化通过构造器进行,并进行深拷贝。

如果使用传入的参数直接赋值,则传递的只是引用,仍然可以通过外部变量改变它的值。

5. String类的不可变性分析

public final class String

implements java.io.Serializable, Comparable, CharSequence {

/** The value is used for character storage. */

private final char value[];

...

/**

* Allocates a new {@code String} so that it represents the sequence of

* characters currently contained in the character array argument. The

* contents of the character array are copied; subsequent modification of

* the character array does not affect the newly created string.

*

* @param value

* The initial value of the string

*/

public String(char value[]) {

this.value = Arrays.copyOf(value, value.length);

}

...

/**

* Converts this string to a new character array.

*

* @return a newly allocated character array whose length is the length

* of this string and whose contents are initialized to contain

* the character sequence represented by this string.

*/

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;

}

}

可以看出String类的设计遵循以下原则:

String类被final修饰,不可继承

string内部所有成员都设置为私有变量

不存在value的setter

并将value和offset设置为final。

当传入可变数组value[]时,进行copy而不是直接将value[]复制给内部变量.

获取value时不是直接返回对象引用,而是返回对象的copy.

这都符合上面总结的不变类型的特性,也保证了String类型是不可变的类。

但是,即使是不可变类,通过反射仍然可以改变其属性的值:

String s = "hello world";

System.out.println("s=" + s);

try {

Field v = String.class.getDeclaredField("value");

v.setAccessible(true);

char[] c = (char[]) v.get(s);

c[5] = '_';

System.out.println("s=" + s);

} catch (NoSuchFieldException nfe) {

nfe.printStackTrace();

} catch (IllegalAccessException iae) {

iae.printStackTrace();

}

输出结果为:

s=hello world

s=hello_world

6. 参考

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值