Java常规【待完善】

==与equals()的区别

==: 作用是判断两个对象的地址是不是相等,即判断两个对象是否是同一个对象。
这边要注意一下,基本数据类型==比较的是值,引用数据类型比较的是内存地址。
java是值传递,所以对于==而言,不管比较的是基本数据类型还是引用数据类型,本质比的还是值,只是引用类型变量存的值是对象的地址而已。

equals():作用也是比较两个对象是否相等,但是无法用于比较基本数据类型。equals方法是Object类方法之一,所以所有的类由于都是Object的子类,所有都拥有equals方法。
对于equals的用法:
如果类没有重写equals,那么这个方法等同于==
如果重写了,那么一般我们都覆盖equals来判断内从是否相等,如果内容相等,我们就认为等同。

值得一提的是String类的equals方法进行了重写,Object中的equals比较的还是对象的内存地址等同于==,
但是String的equals比较的是对象的值。
当创建String类型对象的时候,虚拟机会在常量池中查找是否有已经存在的值和将要创建值相同的对象,如果有就把它赋给当前引用。
如果没有的话就在常量池中重新创建一个String对象。

hashCode()与 equals() 区别?为什么重写 equals 时必须重写 hashCode 方法?

本质上他们都是用来比较对象是否相等。
简单来说,equals相同的对象,hashcode一定相同。
hashcode相同的,equals不一定相同。
首先hashcode是获取哈希码,即散列码。返回的是一个int整数,这个哈希码的作用是确定该对象在哈希表的索引位置。hashcode方法同样是Object方法之一,所以java任何类都有这个方法。Object中的hashcode方法是本地方法,用c或者c语言实现,通常是把对象的内存地址转化为整数返回。
散列表存储的是键值对,所以可以根据键快速查到对应的值,这其中就利用到得了哈希码,

hashCode存在的作用?
对于需要大量并且快速的对比的话如果都用equal()去做显然效率太低,所以解决方式是,每当需要对比的时候,首先用hashCode()去对比,如果hashCode()不一样,则表示这两个对象肯定不相等(也就是不必再用equal()去再对比了),如果hashCode()相同,此时再对比他们的equal(),如果equal()也相同,则表示这两个对象是真的相同了,这样既能大大提高了效率也保证了对比的绝对正确性!这也是HashSet检查重复的原理,减少equals大大提高了执行效率。
为什么重写 equals 时必须重写 hashCode 方法?
如果两个对象相等,则 hashcode 一定也是相同的。两个对象相等,对两个对象分别调用 equals 方法都返回 true。但是,两个对象有相同的 hashcode 值,它们也不一定是相等的 。因此,equals 方法被覆盖过,则 hashCode 方法也必须被覆盖。
为什么两个对象有相同的 hashcode 值,它们也不一定是相等的?
因为 hashCode() 所使用的杂凑算法也许刚好会让多个对象传回相同的杂凑值。越糟糕的杂凑算法越容易碰撞,但这也与数据值域分布的特性有关(所谓碰撞也就是指的是不同的对象得到相同的 hashCode。
我们刚刚也提到了 HashSet,如果 HashSet 在对比的时候,同样的 hashcode 有多个对象,它会使用 equals() 来判断是否真的相同。也就是说 hashcode 只是用来缩小查找成本。

深拷贝和浅拷贝

浅拷贝:对基本数据类型进行值传递,对引用数据类型进行引用传递般的拷贝,此为浅拷贝。
深拷贝:对基本数据类型进行值传递,对引用数据类型,创建一个新的对象,并复制其内容,此为深拷贝。

我们通常可以利用序列化和反序列化来实现深克隆

public Object deapClone(){
        ByteArrayOutputStream os=new ByteArrayOutputStream();
        ObjectOutputStream oos=null;
        ByteArrayInputStream is=null;
        ObjectInputStream ois = null;
        try {
            //序列化
            oos=new ObjectOutputStream(os);
            oos.writeObject(this);
            //反序列化
            is=new ByteArrayInputStream(os.toByteArray());
            ois=new ObjectInputStream(is);
            try {
                return ois.readObject();
            } catch (ClassNotFoundException e) {
                e.printStackTrace();
            }
        } catch (IOException e) {
            e.printStackTrace();
        }finally {
            try {
                ois.close();
                is.close();
                oos.close();
                os.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        return null;
    }

String StringBuffer 和 StringBuilder 的区别是什么? String 为什么是不可变的?

String 类中使用 final 关键字修饰字符数组来保存字符串,private final char value[],所以 String 对象是不可变的。
StringBuilder 与 StringBuffer 都继承自 AbstractStringBuilder 类,在 AbstractStringBuilder 中也是使用字符数组保存字符串char[]value 但是没有用 final 关键字修饰,所以这两种对象都是可变的。
StringBuilder 与 StringBuffer 的构造方法都是调用父类构造方法也就是AbstractStringBuilder 实现的。

线程安全性
String 中的对象是不可变的,也就可以理解为常量,线程安全。AbstractStringBuilder 是 StringBuilder 与 StringBuffer 的公共父类,定义了一些字符串的基本操作,如 expandCapacity、append、insert、indexOf 等公共方法。StringBuffer 对方法加了同步锁或者对调用的方法加了同步锁,所以是线程安全的。StringBuilder 并没有对方法进行加同步锁,所以是非线程安全的。

性能
每次对 String 类型进行改变的时候,都会生成一个新的 String 对象,然后将指针指向新的 String 对象。StringBuffer 每次都会对 StringBuffer 对象本身进行操作,而不是生成新的对象并改变对象引用。相同情况下使用 StringBuilder 相比使用 StringBuffer 仅能获得 10%~15% 左右的性能提升,但却要冒多线程不安全的风险。

对于三者使用的总结
操作少量的数据: 适用 String
单线程操作字符串缓冲区下操作大量数据: 适用 StringBuilder
多线程操作字符串缓冲区下操作大量数据: 适用 StringBuffer

Java 序列化中如果有些字段不想进行序列化,怎么办?

对于不想进行序列化的变量,使用 transient 关键字修饰。

transient 关键字的作用是:阻止实例中那些用此关键字修饰的的变量序列化;当对象被反序列化时,被 transient 修饰的变量值不会被持久化和恢复。transient 只能修饰变量,不能修饰类和方法。

全部评论

相关推荐

点赞 收藏 评论
分享
牛客网
牛客企业服务