总结了一下Java面试易错的几个题目
一、java中String,StringBuffer与StringBuilder的区别??
1,执行速度
StringBuilder > StringBuffer > String
我们知道String是字符串常量,不可变对象,因此每次对String进行操作的时候实际上是生成了一个新的String对象,然后将指针指向新的String对象上,之前的String对象就没有了指针引用,当内存中无引用的对象多了之后,就会触发JVM的GC操作了。
StringBuilder和StringBuffer是字符串变量,因此当我们对字符串做操作的时候,实际上都是操作的同一个对象,不会创建新的对象。
由此可以得出执行顺序了,但是StringBuilder为什么会比StringBuffer快呢,请继续往下看
2,线程安全
StringBuilder是线程不安全的,而StringBuffer是线程安全的
StringBuffer 上的主要操作是 append 和 insert 方法,可重载这些方法,以接受任意类型的数据。每个方法都能有效地将给定的数据转换成字符串,然后将该字符串的字符追加或插入到字符串缓冲区中。append 方法始终将这些字符添加到缓冲区的末端;而 insert 方法则在指定的点添加字符。
例如,如果 z 引用一个当前内容是“start”的字符串缓冲区对象,则此方法调用 z.append("le") 会使字符串缓冲区包含“startle”,而 z.insert(4, "le") 将更改字符串缓冲区,使之包含“starlet”。
java.lang.StringBuilder一个可变的字符序列是5.0新增的。此类提供一个与 StringBuffer 兼容的 API,但不保证同步。该类被设计用作 StringBuffer 的一个简易替换,用在字符串缓冲区被单个线程使用的时候(这种情况很普遍)。如果可能,建议优先采用该类,因为在大多数实现中,它比 StringBuffer 要快。两者的方法基本相同。
如果一个StringBuffer对象在字符串缓冲区被多个线程使用时,StringBuffer中很多方法可以带有synchronized关键字,所以可以保证线程是安全的,但StringBuilder的方法则没有该关键字,所以不能保证线程安全,有可能会出现一些错误的操作。所以如果要进行的操作是多线程的,那么就要使用StringBuffer,但是在单线程的情况下,还是建议使用速度比较快的StringBuilder。
3,总结
String:适用于少量的字符串操作的情况
StringBuilder:适用于单线程下在字符缓冲区进行大量操作的情况
StringBuffer:适用多线程下在字符缓冲区进行大量操作的情况
二、重写equals方法还需要重写什么方法
答:还需要重写hashcode方法,因为如果是同一个对象,重写了equals方法而没有重写hashcode方法,则会出现equals相等的对象,而hashcode不相等的情况。重写hashcode就是为了避免出现这种情况
hashCode主要用于提升查询效率,来确定在散列结构中对象的存储地址;
重写equals()必须重写hashCode(),二者参与计算的自身属性字段应该相同;
hash类型的存储结构,添加元素重复性校验的标准就是先取hashCode值,后判断equals();
equals()相等的两个对象,hashcode()一定相等;
反过来:hashcode()不等,一定能推出equals()也不等;
hashcode()相等,equals()可能相等,也可能不等。
三、List和Set的区别?
Java中的集合包括三大类,它们是Set(集)、List(列表)和Map(映射),它们都处于java.util包中,Set、List和Map都是接口,它们有各自的实现类。Set的实现类主要有HashSet和TreeSet,List的实现类主要有ArrayList。
Collection是最基本的集合接口,声明了适用于JAVA集合的通用方法,list和set都继承自collection接口。
1、List中元素放入是有顺序的,可以重复。Set去除重复元素,无序的,重复元素会覆盖掉。
2、Set检索效率低,删除和插入效率高,插入和删除不会引起元素位置的改变;而List和数组类似,可以动态增长,查找效率高,插入和删除效率低,会引起元素位置的改变
四、Java中的String类
https://baijiahao.baidu.com/s?id=1640190566307184844&wfr=spider&for=pc
想要完全了解String,在这里我们需要解决以下几个问题
(1)什么是不可变对象?
(2)String如何被设计成不可变对象的?
(3)有什么办法能够改变String?
(4)JAVA语言为什么把String类型设计成不可变?
带着这些问题就可以开始今天的文章了。
一、什么是不可变对象
从字面意思也能够理解,也就是我们的创建的对象不可改变。那什么是不可变呢?为了实现创建的对象不可变,java语言要求我们需要遵守以下5条规则:
(1)类内部所有的字段都是final修饰的。
(2)类内部所有的字段都是私有的,也就是被private修饰。
(3)类不能够被集成和拓展。
(4)类不能够对外提供哪些能够修改内部状态的方法,setter方法也不行。
(5)类内部的字段如果是引用,也就是说可以指向可变对象,那我们程序员不能获取这个应用。
正是由于我们的String类型遵循了上面5条规则,所以才说String对象是不可变的。想要去了解他还是看看String类型内部长什么样子再来看上面5条规则吧。
二、String如何被设计成不可变对象的
1、疑惑一
在看之前,我们先给出一个疑惑问题,我们看下面的代码,
在文章一开始我们就说了,String对象是不可变的,这里a=张三,然后a=李四,这符合String的不可变性嘛?答案是当然符合。
从上面这张图我们可以看到,在第一次String a="张三"的时候,在堆中创建了同一个对象“张三”。后来我们在执行a="李四"的时候再内存中又创建了一个对象“李四”。也就是说我们的a仅仅只是改变了引用a指向的地址而已。
2、源码解释疑惑
既然a指向的引用地址改变了,那么其String内部肯定有一个变量,能够指向不同的实际对象,想要进一步弄清楚我们就进入其String的内部来看看。
我们在这里主要通过String类的源码来分析,看一下Java语言是如何设计,能把String类型设计成不可变的。这里给出的是jdk1.8的一部分源码。
上面最主要的是两个字段:value和hash。我们在这里主要是看value数组,hash和主题无关所以这里不再讲解了,我有专门的文章介绍hash。
我们的String对象其实在内部就是一个个字符然后存储在这个value数组里面的。但是value对外没有setValue的方法,所以整个String对象在外部看起来就是不可变的。我们画一张图解释一下上面的疑惑
现在明白了吧,也就是说真正改变引用的是value,因为value也是一个数组引用。这也可以很方便的解释下一个疑惑问题了。
3、疑惑二
既然我们的String是不可变的,好像内部还有很多substring, replace, replaceAll这些操作的方法。好像都是对String对象改变了,解释起来也很简单,我们每次的replace这些操作,其实就是在堆内存中创建了一个新的对象。然后我们的value指向不同的对象罢了。
面试的时候我们只是解释上面的原因其实不是那么尽善尽美,想要更好的去加薪去装逼,我们还需更进一步回答。
三、有什么办法能够改变String
既然有这个标题。那肯定就是有办法的,别忘了我们的反射机制,在通常情况下,他可以做出一些违反语言设计原则的事情。这也是一个技巧,每当面试官问一些违反语言设计原则的问题,你就可以拿反射来反驳他。下面我们来看一下:
我们可以通过反射来改变String。
现在我们知道它的原理以及用法,也知道可以通过反射来改变String,还有一个问题我们没有弄清楚,面试的时候你也可以反问他,来进一步提升自己的逼格。
四、JAVA语言为什么把String类型设计成不可变
这里有几个特点。
第一:在Java程序中String类型是使用最多的,这就牵扯到大量的增删改查,每次增删改差之前其实jvm需要检查一下这个String对象的安全性,就是通过hashcode,当设计成不可变对象时候,就保证了每次增删改查的hashcode的唯一性,也就可以放心的操作。
第二:网络连接地址URL,文件路径path通常情况下都是以String类型保存, 假若String不是固定不变的,将会引起各种安全隐患。就好比我们的密码不能以String的类型保存,,如果你将密码以明文的形式保存成字符串,那么它将一直留在内存中,直到垃圾收集器把它清除。而由于字符串被放在字符串缓冲池中以方便重复使用,所以它就可能在内存中被保留很长时间,而这将导致安全隐患
第三:字符串值是被保留在常量池中的,也就是说假若字符串对象允许改变,那么将会导致各种逻辑错误
OK,以上就是String类型对象不可变的原因。面试的时候能答出来就好,如有问题还请指正。