String考点

String 字符串常量
StringBuffer 字符串变量(线程安全)
StringBuilder 字符串变量(非线程安全)

性能上通常StringBuilder > StringBuffer > String。

String是不可变对象,每次对String类型进行改变的时候都等同于生成了一个新的String对象,然后将指针指向新的String对象,所以性能最差,对于要经常改变内容的字符串不用String。

StringBuffer是字符串变量,对它操作时,并不会生成新的对象,而是直接对该对象进行更改,所以性能较好。
StringBuilder和StringBuffer一样,是字符串变量,但是他不带有synchronized关键字,不保证线程安全,所以性能最好。在单线程的情况下,建议使用StringBuilder。

总体来说:
String:适用于少量的字符串操作的情况。
StringBuilder:适用于单线程下在字符缓冲区进行大量操作的情况。
StringBuffer:适用多线程下在字符缓冲区进行大量操作的情况。

== vs equals

下面这段代码的输出结果是什么?

String a = "helloworld";
String b = "hello" + "world";
System.out.println((a == b));

输出结果为:True。
原因是String对字符串的直接相加,会在编译期进行优化。即hello+world在编译时期,被优化为helloworld,所以在运行时期,他们指向了同一个对象。我们也可以推理,对于直接字符串的相加,String不一定比其余两个慢。

下面这段代码的输出结果是什么?

String a = "helloworld";
String b = "hello";       
String c = b + "world";       
System.out.println((a == c));

输出结果为:False。
原因是c并非两个字符串直接相加,包含了一个字符串引用,这时不会做编译期的优化。所以a、c最终生成了两个对象,这时他的效率低。

转自Java基础面试题及答案解析(9)

== 解读

对于基本类型和引用类型 == 的作用效果是不同的,如下所示:

基本类型:比较的是是否相同;
引用类型:比较的是引用是否相同(即内存地址);
代码示例:

String x = "string";
String y = "string";
String z = new String("string"); //(“string”)会被首先创建,放入字符串常量池中,然后new出的对象z放在堆中
System.out.println(x==y); // true
System.out.println(x==z); // false
System.out.println(x.equals(y)); // true
System.out.println(x.equals(z)); // true

代码解读:因为 x 和 y 指向的是同一个引用,所以 == 也是 true,而 new String()方法则重写开辟了内存空间,所以 == 结果为 false,而 equals 比较的一直是值,所以结果都为 true。

equals 解读

equals 本质上就是 ==,只不过 String 和 Integer 等重写了 equals 方法,把引用比较改成了值比较

String 的 equals 方法:

public boolean equals(Object anObject) {   	
if (this == anObject) {
    return true;
}
if (anObject instanceof String) {
    String anotherString = (String)anObject;
    int n = count;
    if (n == anotherString.count) {
	char v1[] = value;
	char v2[] = anotherString.value;
	int i = offset;
	int j = anotherString.offset;
	while (n-- != 0) {
	    if (v1[i++] != v2[j++])
		return false;
	}
	return true;
    }
}
return false;

首先来看默认情况下 equals 比较一个有相同值的对象,代码如下:

class Cat {
    public Cat(String name) {
		this.name = name;
    }
    
    private String name;
    
    public String getName() {
		return name;
    }
    
    public void setName(String name) {
		this.name = name;
    }
}

Cat c1 = new Cat("王磊");
Cat c2 = new Cat("王磊");
System.out.println(c1.equals(c2)); // false

输出结果出乎我们的意料,竟然是 false?这是怎么回事,看了 equals 源码就知道了,源码如下:

public boolean equals(Object obj) {
	return (this == obj);
}

原来 equals 本质上就是 ==

总结 :== 对于基本类型来说是值比较,对于引用类型来说是比较的是引用;而 equals 默认情况下是引用比较,只是很多类重新了 equals 方法,比如 String、Integer 等把它变成了值比较,所以一般情况下 equals 比较的是值是否相等。

Dog dog = new Dog()
一般情况下比较两个对象是比较他的值是否一致,所以要进行重写
Dog 类中的age属性

@override
public boolean equals(Object obj) {
		if (this == obj)//引用的值(地址)是否相同【相等表示指向同一个对象】
			return true;
		if (obj == null)//不会比较2个null对象的 一个为null一个不为null肯定不相等
			return false;
		if (getClass() != obj.getClass())//判断2个对象的类型
			return false;
		Dog other = (Dog) obj;
		if (age != other.age)//属性值比较
			return false;
		return true;
	}

类的对象:关心对象属性值的信息-----数据信息 。
类对象:类的代码信息(属性,变量名,方法)
类对象与类的对象

JDK6与JDK6+ 中String的intern()方法区别

JDK6:当调用intern()方法时,如果字符串常量池先前已创建出该字符串对象,则返回池中的该字符串的引用。否则,将此字符串对象添加到字符串常量池中,并且返回该字符串的引用。

JDK6+:当调用intern()方法时,如果字符串常量池先前已创建出该字符串对象,则返回池中的该字符串的引用。否则,如果该字符串对象已经存在于Java堆中,则将堆中此对象的引用添加到字符串常量池中,并且返回该引用;如果堆中不存在,则在池中创建该字符串并返回其引用。

注:在JDK1.6的时候,字符串常量池是存放在Perm Space中的(Perm Space和堆是相隔而开的),在1.6+的时候,移到了堆内存中

String为什么是Final的?

安全和性能考虑(字符串常量池)是String类不可变的主要原因。

  1. 字符串对象被缓存在字符串池中,缓存的字符串被多个客户端共享,这时如果字符串可变。那么某个客户端修改了字符串的值会影响到其它的客户端。字符串的缓存从性能原因来考虑,设计为不可变又是非常重要的。
  2. 字符串在许多java的类中被广泛使用。在网络连接中,你可以传递主机名和端口号作为字符; 在数据库连接中,你可以传递数据库地址作为字符串; 在File I/O中,你可以通过字符串文件名来打开任何文件。
    这种情况下,如果字符串不是不可变的,将会导致严重的安全问题。一些人可以访问任何文件,一旦他有了授权,他可以故意的修改文件名,或者无意的获取到另外一个文件的访问权限。
  3. 多线程编程中,因为String是不可变的,你就无需考虑字符串的同步问题。不可变性让字符串变的线程安全。

注意:SubString导致的内存泄漏,虽然不是线程问题,但是也要注意

猜你喜欢

转载自blog.csdn.net/eluanshi12/article/details/95633951