Java基础知识004

1. 不可变类

不可变类是指当创建了这个类的实例后,就不允许修改它的值了。
所有基本类型的包装类都是不可变类,String也是不可变类。

public class Test {
	public static void main(String[] args) {
		String s="hello";
		s+="world";
		System.out.println(s);
	}
}

表面上看好像是修改String类型对象s的值,其实不是。
String s="hello"语句声明了一个可以指向String类型对象的引用,这个引用的名字为s,它指向了一个字符串常量“hello”。s+="world"并没有改变s所指向的对象(“hello”是String类型的对象,而String又是不可变量),这句代码运行后,s指向了另外一个String类型的对象,该对象的内容为“hello world”。原来的那个字符串常量“hello”还存在与内存中,并没有被改变。
创建一个不可变类

  • 类中所有成员变量被private修饰。
  • 类中没有写或者修改成员变量的方法如setXX,只提供构造函数,一次生成,永不改变。
  • 确保类中所有方法不被子类覆盖,可以把类定义为final或把方法定义为final。
  • 如果一个类成员不是不可变量,那么在成员初始化或者使用get方法获取该成员变量时,需要通过clone方法来确保类的不可变性。
  • 如果有必要,可使用覆盖Object类的equals方法和hashCode方法。在equals方法中,根据对象的属性值来比较两个对象是否相等,并且保证用equals方法判断为相等的两个对象其hashCode方法的返回值也相等,这可以保证这些对象能被正确的放到HashMap或HashSet集合中。
错误实现:
class ImmutableClass{
	private Date d;
	public ImmutableClass(Date d) {
		this.d=d;
	}
	public void printState() {
		System.out.println(d);
	}
}
public class Test {
	public static void main(String[] args) {
		Date d=new Date();
		ImmutableClass ic=new ImmutableClass(d);
		ic.printState();
		d.setMonth(5);
		ic.printState();
	}
}
输出
Mon Apr 08 21:39:35 CST 2019
Sat Jun 08 21:39:35 CST 2019
由于Date对象状态是可以被改变的,而ImmutableClass保存了Date类型对象的引用,当被引用的对象的状态改变时会导致ImmutableClass对象状态的改变。
正确实现:
class ImmutableClass{
	private Date d;
	public ImmutableClass(Date d) {
		this.d=(Date)d.clone();
	}
	public void printState() {
		System.out.println(d);
	}
	public Date getDate() {
		return (Date)d.clone();
	}
}

不可变类的对象会因为值的不同而产生新的对象,从而导致无法预料的问题。

2. 值传递、引用传递

  • 值传递:在方法调用中实参会把它的值传递给形参,形参只是用实参的值初始化一个临时的存储单元,因此形参与实参虽然有着相同的值,但是却有着不同的存储单元,因此对形参的改变不会影响实参的值。
  • 引用传递:在方法调用中传递的是对象的地址,这时形参与实参的对象指向同一块存储单元,因此对形参的修改就会影响实参的值。
public class Test {
	public static void changeStringBuffer(StringBuffer ss1,StringBuffer ss2) {
		ss1.append("world");
		ss2=ss1;
	}
	public static void main(String[] args) {
		Integer a=1,b=a;
		System.out.println(System.identityHashCode(a)); 2018699554
		System.out.println(System.identityHashCode(b)); 2018699554
		b++;
		System.out.println(System.identityHashCode(b)); 1311053135
		StringBuffer s1=new StringBuffer("hello");
		StringBuffer s2=new StringBuffer("hello");
		System.out.println(System.identityHashCode(s1)); 118352462
		System.out.println(System.identityHashCode(s2)); 1550089733
		changeStringBuffer(s1, s2);
		System.out.println(System.identityHashCode(s1)); 118352462
		System.out.println(s1);//hello world
		System.out.println(s2);//hello
	}
}

调用change函数时分别传递了s1和s2的地址赋给ss1和ss2,所以当ss1修改地址的内容时,s1的内容也发生了改变,并且将s1的地址赋给ss2,但s2地址中的内容并未发生改变。

字符串创建与存储机制

String s1="abc";//把abc放到常量区中,在编译时产生
System.out.println(System.identityHashCode(s1)); //输出2018699554
String s2="ab"+"c";s2引用常量区中的对象,因此不会创建新的对象
System.out.println(System.identityHashCode(s2)); //输出2018699554
String s3=new String("abc");在运行时把abc放到堆里面 在堆中创建新的对象
System.out.println(System.identityHashCode(s3)); //输出1311053135
String s4=new String("abc");在堆中又创建一个新的对象
System.out.println(System.identityHashCode(s4)); //输出118352462
  • 对于String s3=new String(“abc”);和String s3=4=new String(“abc”);,存在两个引用对象s3和s4,两个内容相同的字符串对象“abc”,他们在内存中的地址是不同的。只要用到new总会生成新对象。
  • 对于String s1=“abc”;和String s2=“abc”;。在JVM中存在一个字符串池,其中保存着很多String对象,并且可以被共享使用,s1和s2引用的是同一个常量池中的对象。当创建一个字符串常量时,比如String s=“abc”;会首先在字符串常量池中查找是否已经有相同的字符串被定义,其判断依据是String类的equals(Object obj)方法的返回值。若已经定义,则直接获取对其的引用,此时不需要创建新的对象;若没有定义,则首先创建这个对象,然后把它加到字符串池中,再将他的引用 返回。
  • String s=new String(“abc”)为了便于理解可以认为的分解成两个过程。第一个过程是新建对象的过程,即new String(“abc”);第二个过程是赋值的过程即String s=。由于第二个过程只是定义了一个名为s的String类型的变量,将一个String类型对象的引用赋值给s,因此在这个过程中不会创建新的对象。第一个过程new String(“abc”)等价于“abc”和new String()两个操作。若在字符串池中不存在abc则会创建一个字符串常量“abc”,并将其添加到字符串池中;若存在则不创建,然后new String()会在堆中创建一个新的对象,所以s3和s4指向的是堆中不同的String对象,地址自然也不同了。

new String(abc“”)创建了几个对象?
一个或两个。如果常量池中原来有“abc”,那么只创建一个对象;如果常量池中原来没有字符串“abc”,那么就会创建两个对象。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值