Python 官方文档:入门教程 => 点击学习
目录面试题1:Java 中操作字符串都有哪些类?它们之间有什么区别? 正经回答:深入追问:追问1:这三者在效率上怎么说?追问2:那StringBuffer和StringBuffer线
操作字符串的类有:String
、StringBuffer
、StringBuilder
。
String 和 StringBuffer、StringBuilder 的区别在于 String 声明的是不可变的对象,每次操作都会生成新的 String 对象,然后将指针指向新的 String 对象,而 StringBuffer、StringBuilder 可以在原有对象的基础上进行操作,所以在经常改变字符串内容的情况下最好不要使用 String。
而StringBuffer 和 StringBuilder 最大的区别在于,StringBuffer 是线程安全的,而 StringBuilder 是非线程安全的,但 StringBuilder 的性能却高于 StringBuffer,所以在单线程环境下推荐使用 StringBuilder,多线程环境下推荐使用 StringBuffer。
String | StringBuffer | StringBuilder | |
---|---|---|---|
类是否可变 | 不可变(Final) | 可变 | 可变 |
功能介绍 | 每次对String的操作都会在“常量池”中生成新的String对象 | 任何对它指向的字符串的操作都不会产生新的对象。每个StringBuffer对象都有一定的缓冲区容量,字符串大小没有超过容量时,不会分配新的容量,当字符串大小超过容量时,自动扩容 | 功能与StringBuffer相同,相比少了同步锁,执行速度更快 |
线程安全性 | 线程安全 | 线程安全 | 线程不安全 |
使用场景推荐 | 单次操作或循环外操作字符串 | 多线程操作字符串 | 单线程操作字符串 |
StringBulider > StringBuffer > String
String <(StringBuffer,StringBuilder)的原因?
从上面的名字可以看到,String是"字符串常量",也就是不可改变的对象。源码如下:
public final class String{}
对于上面这句话的理解你可能会产生这样一个疑问 ,比如这段代码:
String str = "唐伯虎";
str = str + "点香烟";
System.out.print(str); // result : "唐伯虎点香烟"
我们明明改变了String型的变量str啊,为什么说是没有改变呢?我们来看一下这张对String操作时内存变化的图:
我们可以看到,初始String值为"唐伯虎",然后在这个字符串后面加上新的字符串"点香烟",这个过程是需要重新在栈堆内存中开辟内存空间的,最终得到了"唐伯虎点香烟"字符串也相应的需要开辟内存空间,这样短短的两个字符串,却需要开辟三次内存空间,不得不说这是对内存空间的极大浪费,执行效率同理。
为了应对经常性操作字符串的场景,Java才提供了其他两个操作字符串的类 —— StringBuffer、StringBuilder。
他们俩均属于字符串变量,是可改变的对象,每当我们用它们对字符串做操作时,实际上是在一个对象上操作的,这样就不会像String一样创建一些而外的对象进行操作了,速度自然就相对快了。
我们一般在StringBuffer、StringBuild类上的主要操作是 append 和 insert 方法,这些方法允许被重载,以接受任意类型的数据。每个方法都能有效地将给定的数据转换成字符串,然后将该字符串的字符追加或插入到字符串缓冲区中。append 方法始终将这些字符添加到缓冲区的末端;而 insert 方法则在指定的点(index)添加字符。
String 类型和 StringBuffer、 StringBuild类型的主要性能区别其实在于 String 是不可变的对象(final), 因此在每次对 String 类型进行改变的时候其实都等同于在堆中生成了一个新的 String 对象,然后将指针指向新的 String 对象,这样不仅效率低下,而且大量浪费有限的内存空间,所以经常改变内容的字符串最好不要用 String 。因为每次生成对象都会对系统性能产生影响,特别是当内存中的无引用对象过多了以后, JVM 的 GC 开始工作,那速度是一定会相当慢的。另外当GC清理速度跟不上new String的速度时,还会导致内存溢出Error,会直接kill掉主程序!报错如下:
Caused by: java.lang.OutOfMemoryError: GC overhead limit exceeded
Exception in thread "I/O dispatcher 3797236" java.lang.OutOfMemoryError: GC overhead limit exceeded
StringBuffer和StringBuilder可以算是双胞胎了,这两者的方法没有很大区别。但在线程安全性方面,StringBuffer允许多线程进行字符操作。这是因为在源代码中StringBuffer的很多方法都被关键字synchronized 修饰了,而StringBuilder没有。
synchronized的含义:
每一个类对象都对应一把锁,当某个线程A调用类对象O中的synchronized方法M时,必须获得对象O的锁才能够执行M方法,否则线程A阻塞。一旦线程A开始执行M方法,将独占对象O的锁。使得其它需要调用O对象的M方法的线程阻塞。只有线程A执行完毕,释放锁后。那些阻塞线程才有机会重新调用M方法。这就是解决线程同步问题的锁机制。 > 了解了synchronized的含义以后,大家可能都会有这个感觉。多线程编程中StringBuffer比StringBuilder要安全多了 ,事实确实如此。如果有多个线程需要对同一个字符串缓冲区进行操作的时候,StringBuffer应该是不二选择。
注意
:是不是String也不安全呢?事实上不存在这个问题,String是不可变的。线程对于堆中指定的一个String对象只能读取,无法修改。试问:还有什么不安全的呢?
实际应用场景中:
Error 和 Exception 都是 Throwable 的子类,在Java中只有Throwable类型的实例才可以被抛出或者捕获,它是异常处理机制的基本类型。
基本数据类型 == 比较的是值,引用数据类型 == 比较的是内存地址
)equals()
: 它的作用也是判断两个对象是否相等。但它一般有两种使用情况:情况1
:类没有覆盖 equals() 方法。则通过 equals() 比较该类的两个对象时,等价于调用了Object类的equals() 方法,也就是通过“==”比较这两个对象。
// Object类中的equals() 方法
public boolean equals(Object obj) {
return (this == obj);
}
情况2
:类覆盖了 equals() 方法。一般,我们都会覆盖 equals() 方法来两个对象的内容相等;若它们的内容相等,则返回 true (即,认为这两个对象相等)。
// String类中的equals() 方法,已覆盖,用于比较内容
public boolean equals(Object anObject) {
if (this == anObject) {
return true;
}
if (anObject instanceof String) {
String anotherString = (String)anObject;
int n = value.length;
if (n == anotherString.value.length) {
char v1[] = value;
char v2[] = anotherString.value;
int i = 0;
while (n-- != 0) {
if (v1[i] != v2[i])
return false;
i++;
}
return true;
}
}
return false;
}
举例说明:
重点说明:是否重写Object类中的equals方法,会对结果造成的影响
public static void main(String[] args) {
// 字符串比较
String a = "陈哈哈";
String b = "陈哈哈";
if (a == b) {// true a==b
System.out.println("a==b");
}
if (a.equals(b)) {// true a.equals(b)
System.out.println("a.equals(b)");
}
// StringBuffer 对象比较,由于StringBuffer没有重写Object的equal方法,因此结果出现错误
StringBuffer c = new StringBuffer("陈哈哈");
StringBuffer d = new StringBuffer("陈哈哈");
if (c == d) {// false c != d
System.out.println("c == d");
} else {
System.out.println("c != d");
}
if (c.equals(d)) { // false 调用了Object类的equal方法
System.out.println("StringBuffer equal true");
}else {
System.out.println("StringBuffer equal false");
}
}
object的equals方法是比较的对象的内存地址,而String的equals方法比较的是对象的值。
在重写equals()方法时,也有必要对hashCode()方法进行重写,尤其是当我们自定义一个类,想把该类的实例存储在集合中时。
hashCode方法的常规约定为:值相同的对象必须有相同的hashCode,也就是equals()结果为相同,那么hashcode也要相同,equals()结果为不相同,那么hashcode也不相同;
当我们使用equals方法比较说明对象相同,但hashCode不同时,就会出现两个hashcode值,比如在HashMap中,就会认为这是两个对象,因此会出现矛盾,说明equals方法和hashCode方法应该成对出现,当我们对equals方法进行重写时,也要对hashCode方法进行重写。
可以通过ide快捷键快速生成两个方法,假设现在有一个学生Student类,其中有 age 和 name 两个特征。生成代码如下:
@Override
public boolean equals(Object o){
//首先比较两个的地址值是否相同,如果相同,那内容也一定相同
if(this == o) return true;
//如果o为空值或者两个对象的类型是否相同,如果类型不同或者o为空值则内容一定不同
if(o == null || getClass() != o.getClass()) return false;
//将object类型的实例强转为Student类型
Student student = (Student)o;
//比较两个实例的age是否相同
if(age != student.age) return false;
//在比较name是否相同
return name != null ? name.equals(student.name ) : student.name == null;
}
@Override
public int hashCode() {
int result = age;
result = 31 * result + (name != null ? name.hashCode() : 0);
return result;
}
本篇文章就到这里了,希望能给你带来帮助,也希望您能够多多关注编程网的更多内容!
--结束END--
本文标题: Java面试题冲刺第一天--基础篇1
本文链接: https://lsjlt.com/news/130351.html(转载时请注明来源链接)
有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341
2024-03-01
2024-03-01
2024-03-01
2024-02-29
2024-02-29
2024-02-29
2024-02-29
2024-02-29
2024-02-29
2024-02-29
回答
回答
回答
回答
回答
回答
回答
回答
回答
回答
0