1、== 和equals区别
(1)基本数据类型: == 比较的是值,不能用equals (2)引用数据类型: == 比较的是对象的地址,equals如果没重写本质上就是==;equals如果重写了,则不一定,需要看源码。比如String类先比较地址,地址不一样在比较每个字符的值。Integer类底层先把数值转为int类型再用 == 比较值
//Object类的equals方法
public boolean equals(Object obj) {
return (this == obj);
}
//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;
}
//Integer类的equals方法
public boolean equals(Object obj) {
if (obj instanceof Integer) {
return value == ((Integer)obj).intValue();
}
return false;
}
2、string、stringbuffer、stringbuild
(1)存放string对象的区域:堆区、常量池(在元空间也可以理解为方法区中) A、如果是 =‘’…‘’,则对象存放在常量池。如果常量池中有相同的值则重复利用,否则在池中重新开辟空间
String s1 = "123"; //这种情况生成一个对象在常量池中
String s2 = "123"; //这种情况不生成对象,因为在常量池中有
B、如果是new出的,则对象存放在堆区,但是指向常量池中的对象,至于在常量池中是否生成对象取决于常量池中是否有相同的值。
String s3 = new String("hello"); //这种情况生成两个对象一个在堆中一个在常量池中
String s4 = new String("hello"); //这种情况生成一个对象在堆中,至于hello在常量池中已经生成
关系图如下: (2)String是一个不可变的类,当修改值的时候并不会对原有的对象进行修改,而是在常量池中从新开辟一个空间,然后把这个新对象的地址给原来的引用。但是我们可以用反射暴力修改原对象的值,代码如下
public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException {
String s1 = "123";
String s2 = "123";
//获取String类中的value字段
Field valueFieldOfString = String.class.getDeclaredField("value");
//改变value属性的访问权限
valueFieldOfString.setAccessible(true);
//获取s1对象上的value属性的值
char[] value = (char[]) valueFieldOfString.get(s1);
//把value所引用的数组中的第3个字符改为'e'
value[2] = 'e';
valueFieldOfString.setAccessible(false);
System.err.println(s1);
System.err.println(s2);
}
(3)性能:stringbuild>stringbuffer>string
- string每次改变都会重新开辟空间、赋值,所以是最慢的
- 在多线程的情况下stringbuffer能保证线程是同步的,也就是单位时间内只有一个线程执行,所以比stringbuild慢
(4)线程安全:stringbuffer>stringbuild
(5)使用情况:
- string适合改变少时
- Stringbuild适合多变且无需考虑线程同步时
- Stringbuffer适合多变且需要考虑线程同步时
3、final、finally、finalize区别
(1)final
- 修饰的类不能被继承
- 修饰的方法不能被重写
- 修饰的引用型变量不可指向其它对象,但是该对象的值可以修改
- 修饰的基本类型变量不可改值
(2)finally
- finally块通常放在try、catch的后面,有时可以直接放在try 的后面。
- finally中的语句是正常执行或者处理异常之后必须执行的语句,finally块一般是用来关闭(释放)物理资源(数据库连接,网络连接,磁盘文件等)。无论是否发生异常,资源都必须进行关闭。当没有必要资源需要释放时,可以不用定义finally块。
- finally块中的代码总能执行,这就说明无论try、catch块中执行怎样的代码,是否发生异常,还是正常运行,finally块一定会被执行,如果想要finally块不执行,除非在try、catch块中调用退出虚拟机的方法,否则finally块无论怎么样还是会被执行
注:这里需要说明一下return和finally的执行顺序
- 如果try代码块里有return语句,而finally代码块里没有return语句,程序会先执行finally代码块里的代码然后再执行try代码块里的return语句;
- 如果try代码块和finally代码块里都有return语句,try代码块里的return语句会优先finally代码块里的return语句执行,但不会把返回的结果返回给主函数,而是会把finally代码块里return的结果返回给主函数。
(3)finalize:不推荐使用
-
finalize()是在java.lang.Object里定义的,也就是说每一个对象都有这么个方法。这个方法在gc启动,该对象被回收的时候被调用。其实gc可以回收大部分的对象(凡是new出来的对象,gc都能搞定,一般情况下我们又不会用new以外的方式去创建对象),所以一般是不需要程序员去实现finalize的。
-
特殊情况下,需要程序员实现finalize,比如当对象被回收需要释放一些资源时:一个socket链接,在对象初始化时创建,整个生命周期内有效,那么就需要实现finalize,关闭这个链接。
-
一个对象的finalize()方法只会被调用一次,而且finalize()被调用不意味着gc会立即回收该对象,所以有可能调用finalize()后,该对象又不需要被回收了,然后到了真正要被回收的时候,因为前面调用过一次,所以不会调用finalize(),产生问题。 所以,推荐不要使用finalize()方法。