☰
Java类中必须重写的8个核心方法揭秘
2026/9/28 4:44:38 网站建设 项目流程

当我们写一个类的时候,都会对Java.lang.类的一些重要方法进行重写,这些方法包含:(),(),(),(),clone(),wait(),()/() 这八个方法。

一 ()方法:

1.何时需要对代码方法进行重写?

当一个类里面, 它自己有自己特有的那种逻辑相等的概念, 然后这个逻辑相等的概念呢, 是不同于对象身份的概念的。

2.设计()

使用操作符来检查实参是否为正确的类型。

对于该类当中的每一个关键域, 需要针对实里面所提供的对应的那个域的数值情况, 去仔细地与目前所持有的这个对象内部所对应的这些域的数值之间展开检查处理。

对于那些, 不属于float类型以及类型的原语类型字段来说, 应该采用相等符号进行数值上的比对操作, 以确保它们的值是完全一致的。

针对对象引用领域, 将递归调用方法进行处理。

针对float这个类型的字段, 采取办法是先调用Float点括号来把它转换成整数类型, 之后再使用双等号来进行比较操作。

对于域这个概念, 我们是先借助点括号的方法把它转换成为长整型数据, 接着再去执行相等性检查操作来比较它们的值。

针对数组类型这一种数据领域的具体情况, 需要去调用对应的那个方法。

二 ()方法:

1.当进行改写某个内容的操作的时候, 往往总是要针对另一个方面或者内容来进行相应的改写处理工作。

参考改写后的类定义中的方法, 逻辑上可能认为两个完全不同的实例是相等的, 可要是用某个特定的.方法来判断的话, 它们就仅仅是两个分开的对象而已, 这样一来, 就违背了这样的规定, 就是相等的东西, 它们的散列码也得一样才行。

2.设计()

将某个不是零的一个常数数值, 比如说17这一个数字, 给保存到一个int类型的一个变量里面去。

对于该对象里面的每一个关键的域 f 来说, 这里所说的关键的域 f, 意思就是在具体的方法里面会被考虑到的那一个具体的字段。

这个类型的计算方法是将表达式 f?? 0 : 1 进行计算。

对于byte型数据, 对于char型数据, 对于short型数据, 都需要执行转换为int型的计算。

这是一个long类型的数据, 计算的过程是先对它进行强制转换变为int类型, 然后把这个数据和它的右移32位之后的结果按位异或。

这是一个float(浮点数)类型, 在计算的时候需要使用Float方法。

这种类型, 进行计算操作, 最终获取到一个长整型的数值结果, 随后再次执行该步骤。

对象引用, 递归调用该对象的所属类的成员方法。

数组域,对其中每个元素调用它的方法。

把通过上面步骤计算得到的那个散列码, 存到名为c的那个整型变量里面去, 然后接下来去执行等于三十七乘以之前某个值再加上这个c的那一步计算操作。

执行返回这一动作。

三 ()方法:

关于我们所使用的各种方法, 这些方法在我们日常生活的各个领域里面都被普遍应用着, 可以说是占据了一个极其重要位置的小型知识点类别, 这里我就给大家做一个大体上面的讲解和说明。

我们翻阅了API这一文档, 查看那个名称为java.lang.类的这些方法, 它们详细的描述是这样子写着的:

()

这是关于这一事项的一个方面。在里面提到的时候, 有这样一种情况, 那就是“这个”被提及的这个内容。这一个东西本应该是属于某一个特定对象的, 但是大家普遍认为这对于任意一个对象来说都是很容易去阅读的这样一个状态。因此可以说是有必要让所有的相关方都参与到这一件事儿的处理过程中来。

The for class a of the name of the class of which the is an , the at-sign `@', and the of the hash code of the . In other words, this a equal to the value of:

首先(), 然后()接着是('@')最后是。(())。

:

这是一个a的部分的。

我们对上述内容进行大致性的解析与翻译处理。

该函数会返回一个字符串, 这个字符串是能够代表当前对象的。通常来说, 这个方法返回的是对象的“文本表达式”。这种最终获得的结果形式是非常简洁的。但是, 这样并不是那种方便人们去阅读的信息展示方式。因此, 这里郑重建议大家, 在您自己去编写子类的时候, 最好对这一方法进行重新定义与实现。

对于这个类来讲, 方法返回值是由所属类的类的名称、一个符号名为@的字符、还有这个对象的哈希值的没有符号的十六进制的表达形式所共同组成的。换句话说, 这个方法返回来的这些字符串和下面的这个方法所返回的那些数值是完全等同的。

这个表达式看起来是缺失了具体的函数名或者变量名称。

返回:

把那个对象的字符串样子给表现出来。

随后, 我们不妨将目光转向 java.lang. 类中所包含的方法部分, 深入考察一下那些内容在程度上是否具有相同之处呢。

()

This (which is a !) is .

by:

in

:

in class

:

the .

我们依旧还是打算进行一下翻译这件事的吧。

这个对象自己呀, 它已经就是一个字符串了, 那它也就是那个返回值。

说明:

接口中的方法

重写:

重写了中的方法

返回:

那个句子就是它自己本身所包含的那个字符串。

我们可以看到, 这是一个类的实例继承自另一个类, 并且对这个方法进行了重写操作, 也就是说它拥有了属于自己的一套具体的逻辑处理方式, 该方法的作用是把调用者自身转换为一个字符串形式的内容并返回回去, 由于这个对象在被当作字符串使用时本身就已经具备字符串类型的特征或者说是本质上就是字符串类型的一个具体表现, 所以在后续的操作当中或者说在内存的管理环节里头, 就完全没必要去再去生成或者引用一大堆毫无意义的哈希码值了。

为了把这个事情说得更清楚一些, 我们在说明的这段内容中可以看出来, 中方法它就是接口的一个方法, 所以呢, 我们现在就去查看一下 java.lang 这个接口里面的方法的定义情况是怎样的。

()

a the in this in the same order as this . The of the will be the of this .

:

in class

:

这是一个of this of。

咱们还是把它翻译一下。

返回值是一个字符串对象, 该对象里面装着跟这个序列的顺序一样的所有字符, 这个字符串的长度, 就是那个序列的长度。

重写:

的

返回:

这个字符串是由该序列里面的特定的字符所组成的。

第四点, 是关于那个名为clone这个方法的内容。

针对克隆, 也就是那个英文单词叫Clone的事情, Java它有一些那个限制。

1、被克隆的类必须由它来自己去实现那个接口, 这样做是为了表示 .clone() 这个方法能够对这一个类的实例进行字段的复制, 而且这个操作是合法的。这个接口实际上是一个标识用的接口, 它里面没有任何接口方法。

2、那些需要去实现对应的接口的这些类, 它们都应该把自己的.clone这个方法给重新实现一遍, 也就是要进行重写操作, 至于说它原本的那个状态, 是处于受保护的一个权限级别的。

3、在Java.lang.包之中, 那个被叫做克隆的方法, 它的具体定义是这样的:

clone()

请创建并返回那个对象的一个副本, 这是一个受保护的方法, 只有在同一个包里面才可以看到它。

按照大家一直以来的习惯, 返回出来的那个对象, 需要去调用一下 super.clone 这个方法来得到它。

当你在执行浅拷贝操作的时候, 意味着你只是在复制你的该类本身的对象,而你并没有在该类的属性之中所包含的其他类对象的层面上进行拷贝动作。

所谓深拷贝, 这个意思是指对包含类自己本身以及属性类在内的所有类都进行一个完整的拷贝操作。

把话说的再简单一些, 其实就是浅拷贝操作后产生的那两个对象里的属性依然去指向同一个类, 而深拷贝则是让它们全部都变得彼此独立了。也就是说深拷贝这一行为连带着把那些关联关系也给一并拷贝了过来。

我们可以把它简单地理解为在深拷贝里面会存在那些数据类型中包含了类类型的变量这种情况, 这是因为在 java 这门语言里传递参数的形式是基于传值方式来进行操作的。

这五个方面, 其涉及的方法主要是这样的:

大家所知道的是, 这个方法属于垃圾回收器运行机制中一个部分。

这个方法是在垃圾回收器处理它相关联的那些对象的时候被触发的, 如果在执行这个方法的运行期间发生了那些程序本身没有办法进行捕获的异常的情况, 那么垃圾回收机制就会停止对这个特定对象的后续清理工作, 并且这种发生的异常情况将被系统直接忽略掉, 一直到下一次垃圾回收机制启动并对这个仍然存在的对象进行清理操作的时候, 这个方法才会被重新调用一遍。

请您看一下下边给出的这几个小例子。

Java代码

public final class FinallyTest {

// 重写finalize()方法

protected void finalize() throws Throwable {

System.out.println("执行了finalize()方法");

}

public static void main(String[] args) {

FinallyTest ft = new FinallyTest();

ft = null;

System.gc();

}

}

运行结果如下:

• 执行了()方法

在这个程序当中, 调用了属于java.lang.这个类里面的gc()这个方法, 由此导致了垃圾回收(GC)操作被执行。

当垃圾回收器在处理ft这个对象的时候, 调用了该对象的某个方法, 正是由于这个原因, 所以我们才会看到上文提到的输出结果。实际上, 调用.gc()和下面所写的那一行代码在效果上是一样的:

Java代码

.().gc();

调用某个方法的作用, 说白了, 就是给垃圾收集器提个建议。意思是想让垃圾收集器赶紧启动起来, 好把那些没用的对象清理出去, 从而释放出占用的内存空间。不过, 垃圾收集器到底什么时候启动, 这个并不是板上钉钉、必须马上办的事, 而是得由 JAVA虚拟机来拿主意决定。

这就导致了这么一种情况: 在 JAVA虚拟机一直运行着直到它最终停止运行这段时间里, 有些对象的该方法可能连一次都没有被运行过。那么问题来了, 究竟该怎么去保证每个对象的这个方法, 都能在 JAVA虚拟机彻底停止运行之前, 一定被调用成功? 答案就是, 我们可以去调用类的另一个方法。

Java代码

这是一个函数声明, 函数名为 void , 参数为 value , 大括号内有其他代码。

public static void runFinalizersOnExit(boolean value) {

//other code

}

给这个方法传入 true 这个值, 就可以保证当 Java 虚拟机停止运行的时候, 对象的 () 方法一定会被运行。不过遗憾的是, 这个方法是不安全的。它会导致有用的对象 () 被错误地调用。因此, 目前大家已经不赞成使用这种方法了。

由于()这个类属于某种特定的类别, 所以所有的类都拥有这个方法, 并且该类的任意子类都可以对()这一方法进行重写操作, 在这个重写的过程里面可以进行系统资源的释放动作或者是执行其他的一些清理工作, 例如关闭输入流和输出流等。

六 wait()/()/()

在一般情况下, 多个线程彼此之间是需要协调工作的。举个具体的例子来讲, 浏览器里负责进行图片显示的这样一个线程, 它如果想要去执行显示图片的具体任务, 那么就必须等待另一个下载线程, 这个下载线程得先把该张图片给下载完成再说。

倘若此时图片还没有全部都下载完毕的话, 那就可以选择暂时性暂停, 等到相关任务彻底完成了之后, 再去发出“图片已经准备充足, 能够展示”这样的通知, 紧接着再继续往下执行后续的操作。

说白了, 上面的逻辑就是这样的, 如果条件不满足, 那就去等着, 什么时候条件满足了呢, 那个正在等这个条件的线程, 就会被人叫醒。在Java语言里面, 这个法子能够实现, 是靠那个wait方法以及相关的等待机制来完成的, 并且这个等待的法子, 和锁这个东西, 关系是非常非常紧密的。打个比方来说:

(obj) { while(!) { obj.wait(); } obj.(); }

synchronized(obj) {

while(!condition) {

obj.wait();

}

obj.doSomething();

}

当线程A获取了obj锁之后, 它发现当前条件不满足情况, 所以没办法继续执行后续的处理步骤, 紧接着线程A就调用了wait()方法来进行等待操作。

然后来看另一个线程B的情况, 如果在某个时段内, 线程B对某些特定的条件做出了更改动作, 使得原先那个让线程A无法满足的条件现在变得满足了话, 那么这个时候就可以触发机制来唤醒线程A:

(obj)等于true, 然后调用obj的一个方法。

synchronized(obj) {

condition = true;

obj.notify();

}

有一个概念是大家需要加以关注的。

在调用obj.wait()这个方法之前, 是一定要先得到obj这个对象上的锁的, 所以, 相关的操作代码必须是被包裹在针对obj对象的 代码块里面去执行的。

在线程A成功调用obj.wait()这个方法之后, 线程A就把obj的锁给交出来了, 如果没有这一步, 线程B就没法拿到obj的锁, 那么线程B也就没法在obj的大括号所包含的代码段里面把线程A叫醒。

等到obj.wait()这个方法把结果返回回来的时候, 线程A还得再次去把obj的锁给争下来, 拿到了这把锁之后, 它才能接着往下干活。

如果A1以及A2和A3都处在了调用obj.wait()的状态之中, 那么当B去调用()的时候, 它只能把A1、A2或者A3这三个当中的某一个给唤醒掉, 至于具体到底是哪一个被唤醒了, 这个要由JVM来最终决定。

通过调用obj.方法可以一次性唤醒A1、A2和A3这三个线程, 不过若想继续执行紧随obj.wait方法之后的后续代码语句, 线程必须是成功获取到obj对象锁的那个线程, 正因如此, 在A1、A2与A3之中仅有其中任意一个线程才有资格获得该对象锁并把事情做完, 比方说假定是A1抢到了锁, 那么其余的线程就都得等着, 等到A1把对obj对象锁的使用权交出去之后, 它们才又接着往下走。

当程序里的B去访问那个obj对象的时候, 也就是在运行B调用了obj这个相关操作的时候, 此时B手里是正拿着obj这个锁的, 所以, 虽然A1、A2还有A3这三个家伙已经被唤醒弄好了, 可是它们还是没办法拿到obj这把锁来使用, 直到过了一阵子, 到了B退出了它当前占据的那一块代码范围以后, 才松劲了把obj这块锁给放出去了, 这个时候, A1、A2和A3当中挑一个出来才有机会把这个锁给抓住了然后继续跑它的程序。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询