likes
comments
collection
share

Java 核心知识总结 常用类

作者站长头像
站长
· 阅读数 25

Object

🔥Object 类的常见方法有哪些?

Object 类是一个特殊的类,是所有类的父类。它主要提供了以下 11 个方法:

// native 方法,用于返回当前运行时对象的 Class 对象,使用了 final 关键字修饰,故不允许子类重写
public final native Class<?> getClass()

// native 方法,用于返回对象的哈希码,主要使用在哈希表中,比如 JDK 中的 HashMap
public native int hashCode()

// 用于比较 2 个对象的内存地址是否相等,String 类对该方法进行了重写以用于比较字符串的值是否相等
public boolean equals(Object obj)

// native 方法,用于创建并返回当前对象的一份拷贝
protected native Object clone() throws CloneNotSupportedException

// 返回类的名字实例的哈希码的 16 进制的字符串。建议 Object 所有的子类都重写这个方法
public String toString()

// native 方法,并且不能重写。唤醒一个在此对象监视器上等待的线程(监视器相当于就是锁的概念)。如果有多个线程在等待只会任意唤醒一个
public final native void notify()

// native 方法,并且不能重写。跟 notify 一样,唯一的区别就是会唤醒在此对象监视器上等待的所有线程,而不是一个线程
public final native void notifyAll()

// native方法,并且不能重写。暂停线程的执行。注意:sleep 方法没有释放锁,而 wait 方法释放了锁 ,timeout 是等待时间
public final native void wait(long timeout) throws InterruptedException

// 多了 nanos 参数,这个参数表示额外时间(以纳秒为单位,范围是 0-999999)。 所以超时的时间还需要加上 nanos 纳秒
public final void wait(long timeout, int nanos) throws InterruptedException

// 跟之前的2个wait方法一样,只不过该方法一直等待,没有超时时间这个概念
public final void wait() throws InterruptedException

// 实例被垃圾回收器回收的时候触发的操作
protected void finalize() throws Throwable { }

🔥== 和 equals() 的区别

  • == 对于基本类型和引用类型的作用效果是不同的:

    • 对于基本数据类型来说,== 比较的是值。
    • 对于引用数据类型来说,== 比较的是对象的内存地址。 Java 只有值传递,所以对于 == 来说,不管是比较基本数据类型,还是引用数据类型的变量,其本质比较的都是值,只是引用类型变量存的值是对象的地址。
  • equals() 不能用于判断基本数据类型的变量,只能用来判断两个对象是否相等。equals()方法存在于Object类中,而Object类是所有类的直接或间接父类,因此所有的类都有equals()方法。equals() 方法存在两种使用情况:

    • 类没有重写 equals()方法:通过equals()比较该类的两个对象时,等价于通过“==”比较这两个对象,使用的默认是 Object类equals()方法。
    • 类重写了 equals()方法:一般我们都重写 equals()方法来比较两个对象中的属性是否相等;若它们的属性相等,则返回 true,即认为这两个对象相等。String 中的 equals 方法就是被重写过的。

🔥hashCode() 是什么?返回值是内存地址吗?

🔥为什么要有 hashCode()?

Java的集合有两类,一类是 List,还有一类是 Set。前者有序可重复,后者无序不重复。当我们在Set中插入的时候怎么判断是否已经存在该元素呢,可以通过equals方法。但是如果元素太多,用这样的方法就会比较慢。

于是有人发明了哈希算法来提高集合中查找元素的效率。 这种方式将集合分成若干个存储区域,每个对象可以计算出一个哈希码,可以将哈希码分组,每组分别对应某个存储区域,根据一个对象的哈希码就可以确定该对象应该存储的那个区域。

hashCode 方法返回的就是一个哈希码值,当集合要添加新的元素时,先调用这个元素的 hashCode 方法,就一下子能定位到它应该放置的位置上。如果这个位置上没有元素,它就可以直接存储在这个位置上,不用再进行任何比较了;如果这个位置上已经有元素了,就调用它的 equals 方法与新元素进行比较,相同的话就不存了,不相同就散列其它的地址。这样一来实际调用 equals 方法的次数就大大降低了,相应就大大提高了执行速度。

hashCode() 和 equals() 都是用于比较两个对象是否相等。

  • 那为什么 JDK 还要同时提供这两个方法呢?

    这是因为在一些容器(比如 HashMap、HashSet)中,有了 hashCode() 之后,判断元素是否在对应容器中的效率会更高。我们在前面也提到了添加元素进HashSet的过程,如果 HashSet 在对比的时候,同样的 hashCode 有多个对象,它会继续使用 equals() 来判断是否真的相同。也就是说 hashCode 帮助我们大大缩小了查找成本。

  • 那为什么不只提供 hashCode() 方法呢?

    这是因为两个对象的hashCode 值相等并不代表两个对象就相等。

  • 那为什么两个对象有相同的 hashCode 值,它们也不一定是相等的?

    因为 hashCode() 所使用的哈希算法也许刚好会让多个对象传回相同的哈希值。越糟糕的哈希算法越容易碰撞,但这也与数据值域分布的特性有关(所谓哈希碰撞也就是指的是不同的对象得到相同的 hashCode)。

🔥为什么重写 equals() 时必须重写 hashCode() 方法?

之前在 HashSet 检查元素重复的过程中,判断的过程是:

  • 如果两个对象的hashCode 值相等,那这两个对象不一定相等(哈希碰撞)。
  • 如果两个对象的hashCode 值相等并且equals()方法也返回 true,我们才认为这两个对象相等。
  • 如果两个对象的hashCode 值不相等,我们就可以直接认为这两个对象不相等。

如果重写 equals() 时没有重写 hashCode() 方法的话,就可能会导致 hashCode 值却不相等被判断为不同的对象,但是事实上两个对象是重复的(equals 为 true)。

String

为什么 String 是不可变的?

保存字符串的数组被 final 修饰且为私有的,并且 String 类没有提供修改这个字符串的方法。因此,只要修改了字符串的内容,都会将结果用一个新的 String 类对象返回。

字符串常量池的作用了解吗?

字符串常量池 是 JVM 为了提升性能和减少内存消耗针对字符串(String 类)专门开辟的一块区域,主要目的是为了避免字符串的重复创建。

那么哪些字符串对象是放在常量池中的呢?

  1. 直接"…"(字面量)得到的字符串对象放在常量池。
  2. 直接"…"+"…"拼接的字符串对象放在常量池。
  3. 两个指向"…"的final常量拼接结果放在常量池。
  4. 所有字符串对象.intern( )方法得到的结果放在常量池。

除以上四种方式,其他方式得到的字符串结果都在堆中。

Java 核心知识总结 常用类

上面代码中分割线以上都返回 true,分割线以下都返回 false

字符串拼接用 + 还是 StringBuilder ?

Java 语言只支持 + 和 += 这两个专门为 String 类重载过的运算符。

在 Java8 中,字符串对象通过“+”的字符串拼接方式,实际上是通过 StringBuilder 调用 append() 方法实现的,拼接完成之后调用 toString() 得到一个 String 对象 。不过,在循环内使用“+”进行字符串的拼接的话,存在比较明显的缺陷:编译器不会创建单个 StringBuilder 以复用,会导致创建过多的 StringBuilder 对象。如果直接使用 StringBuilder 对象进行字符串拼接的话,就不会存在这个问题了。

使用 “+” 进行字符串拼接会产生大量的临时对象的问题在 JDK9 中得到了解决。在 JDK9(JEP 280)中,字符串相加 “+” 改为了用动态方法 makeConcatWithConstants() 来实现,而不是大量的 StringBuilder,因此放心使用 “+” 进行字符串拼接。

String、StringBuffer、StringBuilder 的区别?

  • 可变性
    • String 是不可变的。
    • StringBuilder 与 StringBuffer 都继承自 AbstractStringBuilder 类,在 AbstractStringBuilder 中也是使用字符数组(JDK 8)保存字符串,不过没有使用 final 和 private 关键字修饰,并且 AbstractStringBuilder 类提供了很多修改字符串的方法比如 append 方法。
  • 线程安全性
    • String 中的对象是不可变的,也就可以理解为常量,线程安全。
    • AbstractStringBuilder 是 StringBuilder 与 StringBuffer 的公共父类,定义了一些字符串的基本操作,如 expandCapacity、append、insert、indexOf 等公共方法。
      • StringBuffer 对方法加了同步锁或者对调用的方法加了同步锁,所以是线程安全的。
      • StringBuilder 并没有对方法进行加同步锁,所以是非线程安全的。
  • 性能
    • 每次对 String 类型进行改变的时候,都会生成一个新的 String 对象,然后将指针指向新的 String 对象。
    • StringBuffer 每次都会对 StringBuffer 对象本身进行操作,而不是生成新的对象并改变对象引用。
    • 相同情况下使用 StringBuilder 相比使用 StringBuffer 仅能获得 10%~15% 左右的性能提升,但却要冒多线程不安全的风险。
  • 使用总结
    • 操作少量的数据,使用 String
    • 多线程操作字符串缓冲区下操作大量数据,使用 StringBuffer
    • 单线程操作字符串缓冲区下操作大量数据,使用 StringBuilder

String s1 = new String("xyz") 创建了几个字符串对象?

一个或两个。如果字符串常量池已经有“xyz”,则是一个;否则,两个。

当字符创常量池没有 “xyz”,此时会创建如下两个对象:

  • 一个是字符串字面量 "xyz" 所对应的、驻留(intern)在一个全局共享的字符串常量池中的实例,此时该实例也是在堆中,字符串常量池只放引用。
  • 另一个是通过 new String() 创建并初始化的,内容与"xyz"相同的实例,也是在堆中。

参考:

流程图详解 new String("abc") 创建了几个字符串对象 - 知乎

流程图详解 new String("abc") 创建了几个字符串对象 哔哩哔哩 bilibili

String.intern() 方法有什么作用?

String.intern() 是一个 native(本地)方法,其作用是将指定的字符串对象的引用保存在字符串常量池中,可以简单分为两种情况:

  • 如果字符串常量池中保存了对应的字符串对象的引用,就直接返回该引用。
  • 如果字符串常量池中没有保存了对应的字符串对象的引用,那就在常量池中创建一个指向该字符串对象的引用并返回。
String s1 = "Java";
String s2 = s1.intern();
String s3 = new String("Java");
String s4 = s3.intern();
System.out.println(s1 == s2); // true
System.out.println(s3 == s4); // false
System.out.println(s1 == s4); //true

String 类型的变量和常量做 “+” 运算时发生了什么?

对于编译期可以确定值的字符串,也就是常量字符串 ,JVM 会将其存入字符串常量池。并且,字符串常量拼接得到的字符串常量在编译阶段就已经被存放字符串常量池。

在编译过程中,编译器会进行常量折叠

  • 常量折叠会把常量表达式的值求出来作为常量嵌在最终生成的代码中,对于 String str3 = "str" + "ing"; 编译器会优化成 String str3 = "string"; 。

  • 并不是所有的常量都会进行折叠,只有编译器在程序编译期就可以确定值的常量才可以:

    • 基本数据类型(byte、boolean、short、char、int、float、long、double)以及字符串常量。
    • final 修饰的基本数据类型和字符串变量
    • 字符串通过 “+” 拼接得到的字符串、基本数据类型之间算数运算(加减乘除)、基本数据类型的位运算(<<、>>、>>>)
  • 引用的值在程序编译期是无法确定的,编译器无法对其进行优化。

    对象引用和 + 的字符串拼接方式,实际上是通过 StringBuilder 调用 append() 方法实现的,拼接完成之后调用 toString() 得到一个 String 对象。因此如果需要使用 + 得到字符串常量,被拼接的两个字符串需要被 final 修饰。

转载自:https://juejin.cn/post/7377546788020371496
评论
请登录