常见类
常见类
Object
Object 方法
全部方法如下:
/**
* native 方法,用于返回当前运行时对象的 Class 对象,使用了 final 关键字修饰,故不允许子类重写。
*/
public final native Class<?> getClass()
/**
* native 方法,用于返回对象的哈希码,主要使用在哈希表中,比如 JDK 中的HashMap。
*/
public native int hashCode()
/**
* 用于比较 2 个对象的内存地址是否相等,String 类对该方法进行了重写以用于比较字符串的值是否相等。
*/
public boolean equals(Object obj)
/**
* naitive 方法,用于创建并返回当前对象的一份拷贝。
*/
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 { }
分类
JUC
notify
notifyAll
wait
,三个
Reflect
getClass
GC:
finalize
:Called by the garbage collector on an object when garbage collection determines that there are no more references to the object. A subclass overrides the {@code finalize} method to dispose of system resources or to perform other cleanup.Hash
hashCode
equals
basic
clone
:需要继承 Cloneable 接口才行,默认 native 的实现是浅拷贝clone 方法是浅拷贝,对象内属性引用的对象只会拷贝引用地址,而不会将引用的对象重新分配内存,相对应的深拷贝则会连引用的对象也重新创建。
toString
== 和 equals() 的区别
==
对于基本类型和引用类型的作用效果是不同的:
- 对于基本数据类型来说,
==
比较的是值。 - 对于引用数据类型来说,
==
比较的是对象的内存地址。
因为 Java 只有值传递,所以,对于 == 来说,不管是比较基本数据类型,还是引用数据类型的变量,其本质比较的都是值,只是引用类型变量存的值是对象的地址。
equals()
不能用于判断基本数据类型的变量,只能用来判断两个对象是否相等。equals()
方法存在于Object
类中,而Object
类是所有类的直接或间接父类,因此所有的类都有equals()
方法。
Object
类 equals()
方法:
public boolean equals(Object obj) {
return (this == obj);
}
equals()
方法存在两种使用情况:
- 类没有重写
equals()
方法 :通过equals()
比较该类的两个对象时,等价于通过“==”比较这两个对象,使用的默认是Object
类equals()
方法。 - 类重写了
equals()
方法 :一般我们都重写equals()
方法来比较两个对象中的属性是否相等;若它们的属性相等,则返回 true(即,认为这两个对象相等)。
举个例子(这里只是为了举例。实际上,你按照下面这种写法的话,像 IDEA 这种比较智能的 IDE 都会提示你将 ==
换成 equals()
):
重写 hashCode() 方法
要重写自己的 hashCode 方法并没有什么绝对正确的答案,但是我们的目标是:不相等的对象尽可能有不同的 hashCode,而且必须满足的一个通用约定是:相等的对象应该具有相同的 hashCode。下面介绍一种 hashCode 的实现方式,这种实现方式对一般的程序来说足够了,至于如何实现更完美的 hashCode 方法就留给数学家或者理论家去讨论吧。
第一步:定义一个初始值,一般来说取 17
int result = 17;
第二步:分别解析自定义类中与 equals 方法相关的字段(假如 hashCode 中考虑的字段在 equals 方法中没有考虑,则两个 equals 的对象就很可能具有不同的 hashCode)
情况一:字段a类型为boolean 则[hashCode] = a ? 1 : 0;
情况二:字段b类型为byte/short/int/char, 则[hashCode] = (int)b;
情况三:字段c类型为long, 则[hashCode] = (int) (c ^ c>>>32);
情况四:字段d类型为float, 则[hashCode] = d.hashCode()(内部调用的是Float.hashCode(d), 而该静态方法内部调用的另一个静态方法是Float.floatToIntBits(d))
情况五:字段e类型为double, 则[hashCode] = e.hashCode()(内部调用的是Double.hashCode(e), 而该静态方法内部调用的另一个静态方法是Double.doubleToLongBits(e),得到一个long类型的值之后,跟情况三进行类似的操作,得到一个int类型的值)
情况六:引用类型,若为null则hashCode为0,否则递归调用该引用类型的hashCode方法。
情况七:数组类型。(要获取数组类型的hashCode,可采用如下方法:s[0]*31 ^ (n-1) + s[1] * 31 ^ (n-2) + ..... + s[n-1], 该方法正是String类的hashCode实现所采用的算法)
第三步:对于涉及到的各个字段,采用第二步中的方式,将其依次应用于下式:
result = result * 31 + [hashCode];
补充说明一点:如果初始值 result 不取 17 而取 0 的话,则对于 hashCode 为 0 的字段来说就没有区分度了,这样更容易产生冲突。比如两个自定义类中,一个类比另一个类多出来一个或者几个字段,其余字段全部一样,分别 new 出来 2 个对象,这 2 个对象共有的字段的值全是一样的,而对于多来的那些字段的值正好都是 0,并且在计算 hashCode 时这些多出来的字段又是最先计算的,这样的话,则这两个对象的 hashCode 就会产生冲突。还是那句话,hashCode 方法的实现没有最好,只有更好。
String
String 为什么是不可变的?
String
类中使用 final
关键字修饰字符数组来保存字符串,所以String
对象是不可变的。
length()
返回值
length()
返回值为字符串中 Unicode(utf-16)代码单元(两字节是一个代码单元)的数量。这个值不同于字符串中实际字符的数量,因为某些 Unicode 字符可能由多个代码单元组成,如表情符号、音乐符号、数学符号、某些语音的字符。
要统计字符数,可以使用codePointCount()
String、StringBuffer、StringBuilder 的区别?
字符串常量池的作用了解吗?
字符串常量池 是 JVM 为了提升性能和减少内存消耗针对字符串(String 类)专门开辟的一块区域,主要目的是为了避免字符串的重复创建。
- java 中的
length
属性是针对数组说的,比如说你声明了一个数组,想知道这个数组的长度则用到了 length 这个属性. - java 中的
length()
方法是针对字符串说的,如果想看这个字符串的长度则用到length()
这个方法. - java 中的
size()
方法是针对泛型集合说的,如果想看这个泛型有多少个元素,就调用此方法来查看!
String 最大长度是多少?
String 类提供了一个 length 方法,返回值为 int 类型,而 int 的取值上限为 2^31 -1。所以理论上 String 的最大长度为 2^31 -1。
达到这个长度的话需要多大的内存吗?
String 内部是使用一个 char 数组来维护字符序列的,一个 char 占用两个字节。如果说 String 最大长度是 2^31 -1 的话,那么最大的字符串占用内存空间约等于 4GB。
也就是说,我们需要有大于 4GB 的 JVM 运行内存才行。
那 String 一般都存储在 JVM 的哪块区域呢?
字符串在 JVM 中的存储分两种情况,一种是 String 对象,存储在 JVM 的堆栈中。一种是字符串常量,存储在常量池里面。
什么情况下字符串会存储在常量池呢?
当通过字面量进行字符串声明时,比如 String s = "程序新大彬";,这个字符串在编译之后会以常量的形式进入到常量池。
那常量池中的字符串最大长度是 2^31-1 吗?
不是的,常量池对 String 的长度是有另外限制的。。Java 中的 UTF-8 编码的 Unicode 字符串在常量池中以 CONSTANT_Utf8 类型表示。
CONSTANT_Utf8_info {
u1 tag;
u2 length;
u1 bytes[length];
}
length 在这里就是代表字符串的长度,length 的类型是 u2,u2 是无符号的 16 位整数,也就是说最大长度可以做到 2^16-1 即 65535。
不过 javac 编译器做了限制,需要 length < 65535。所以字符串常量在常量池中的最大长度是 65535 - 1 = 65534。
最后总结一下:
String 在不同的状态下,具有不同的长度限制。
- 字符串常量长度不能超过 65534
- 堆内字符串的长度不超过 2^31-1