Object类
1 如何理解根父类
类 java.lang.Object
是类层次结构的根类,即所有类的父类。每个类都使用 Object
作为超类。
- Object类型的变量与除Object以外的任意引用数据类型的对象都多态引用
- 所有对象(包括数组)都实现这个类的方法。
- 如果一个类没有特别指定父类,那么默认则继承自Object类。例如:
public class MyClass /*extends Object*/ {
// ...
}
2、native关键字
-
native的意义
native:本地的,原生的
-
native的语法
native只能修饰方法,表示这个方法的方法体代码不是用Java语言实现的,而是由C/C++语言编写的。但是对于Java程序员来说,可以当做Java的方法一样去正常调用它,或者子类重写它。
JVM内存的管理:
区域名称 | 作用 |
---|---|
程序计数器 | 程序计数器是CPU中的寄存器,它包含每一个线程下一条要执行的指令的地址 |
本地方法栈 | 当程序中调用了native的本地方法时,本地方法执行期间的内存区域 |
方法区 | 存储已被虚拟机加载的类信息、常量、静态变量、即时编译器编译后的代码等数据。 |
堆内存 | 存储对象(包括数组对象),new来创建的,都存储在堆内存。 |
虚拟机栈 | 用于存储正在执行的每个Java方法的局部变量表等。局部变量表存放了编译期可知长度的各种基本数据类型、对象引用,方法执行完,自动释放。 |
3、Object类的其中5个方法
(1)hashCode()
public int hashCode():返回每个对象的hash码值。根据对象内存地址换算得到的一个整数。
hashCode 的常规协定:
- 如果两个对象的hash值是不同的,那么这两个对象一定不相等;
- 如果两个对象的hash值是相同的,那么这两个对象不一定相等。
主要用于后面当对象存储到哈希表等容器中时,为了提高存储和查询性能用的。
public static void main(String[] args) {
System.out.println(“Aa”.hashCode());//2112
System.out.println(“BB”.hashCode());//2112
}
(2)getClass()
public final Class<?> getClass():获取对象的运行时类型
因为Java有多态现象,所以一个引用数据类型的变量的编译时类型与运行时类型可能不一致,因此如果需要查看这个变量实际指向的对象的类型,需要用getClass()方法
public static void main(String[] args) {
Object obj = new String();
System.out.println(obj.getClass().getName());//运行时类型
}
(3)toString()
public String toString()
①默认情况下,toString()返回的是“对象的运行时类型名称 @ 对象的hashCode值的十六进制形式"
②通常是建议重写
③如果我们直接System.out.println(对象),默认会自动调用这个对象的toString()
因为Java的引用数据类型的变量中存储的实际上是对象的内存地址,但是Java对程序员隐藏内存地址信息,所以不能直接将内存地址显示出来,所以当你打印对象时,JVM帮你调用了对象的toString()。
B b = new B();
String s = b.getClass().getName() + "@" + Integer.toHexString(b.hashCode());
System.out.println("s = " + s); //s = day06Inherit.B@1b6d3586
System.out.println(b.toString()); //day06Inherit.B@1b6d3586
System.out.println(b); //day06Inherit.B@1b6d3586
(4)equals()
public boolean equals(Object obj):用于判断当前对象this与指定对象obj是否“相等”
①默认情况下,equals方法的实现等价于与“==”,比较的是对象的地址值
②我们一般选择重写,重写有些要求:
-
如果重写equals,那么一定要一起重写hashCode()方法,因为规定:
- 如果两个对象调用equals返回true,那么要求这两个对象的hashCode值一定是相等的;
- 如果两个对象的hashCode值不同的,那么要求这个两个对象调用equals方法一定是false;
- 如果两个对象的hashCode值相同的,那么这个两个对象调用equals可能是true,也可能是false
-
如果重写equals,那么一定要遵循如下几个原则:
- 自反性:x.equals(x)返回true
- 传递性:x.equals(y)为true, y.equals(z)为true,然后x.equals(z)也应该为true
- 一致性:只要参与equals比较的属性值没有修改,那么无论何时调用结果应该一致
- 对称性:x.equals(y)与y.equals(x)结果应该一样
- 非空对象与null的equals一定是false
(5)finalize()
protected void finalize():用于最终清理内存的方法
演示finalize()方法被调用:
public class TestFinalize {
public static void main(String[] args) {
for (int i = 0; i < 10; i++) {
MyData my = new MyData();
//每一次循环my就会指向新的对象,那么上次的对象就没有变量引用它了,就成垃圾对象
}
//通知垃圾回收器来回收垃圾
System.gc();//为了看到垃圾回收器工作,让main方法不那么快结束,因为main结束就会导致JVM退出,GC也会跟着结束。
try {
Thread.sleep(2000);//等待2秒再结束main,为了看效果
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
class MyData{
@Override
protected void finalize() throws Throwable {
// 正常重写,这里是编写清理系统内存的代码
// 这里写输出语句是为了看到finalize()方法被调用的效果
System.out.println("轻轻的我走了...");
}
}
面试题:对finalize()的理解?
-
当对象被GC确定为要被回收的垃圾,在回收之前由GC帮你调用这个方法,不是由程序员手动调用。
-
finalize方法的调用不一定会销毁当前对象,因为有可能在finalize()中写了让当前对象“复活”的代码
-
每一个对象的finalize方法只会被调用一次。
-
子类可以选择重写,一般用于彻底释放一些资源对象,而且这些资源对象往往时通过C/C++等代码申请的资源内存
4、对象数组
数组是用来存储一组数据的容器,一组基本数据类型的数据可以用数组装,那么一组对象也可以使用数组来装。
即数组的元素可以是基本数据类型,也可以是引用数据类型。当元素是引用数据类型时,我们称为对象数组。
注意:对象数组,首先要创建数组对象本身,即确定数组的长度,然后再创建每一个元素对象,如果不创建,数组的元素的默认值就是null,所以很容易出现空指针异常NullPointerException。
对象数组的内存图分析