你知道Object中有哪些方法及其作用吗?
一、引言
二、Object方法详解
1.1、registerNatives()
1.2、getClass()
1.2.1、反射三种方式:
1.3、hashCode()
1.4、equals()
1.4、clone()
1.5、toString()
1.6、wait()/ wait(long)/ waite(long,int)
1.7、notify()/notifyAll()
1.8、finalize()
1.8.1、对象在内存中的状态
1.8.2、垃圾回收机制
1.8.3、强制垃圾回收 一、引言
Object是java所有类的基类,是整个类继承结构的顶端,也是最抽象的一个类。大家天天都在使用toString()、equals()、hashCode()、waite()、notify()、getClass()等方法,或许都没有意识到是Object的方法,也没有去看Object还有哪些方法以及思考为什么这些方法要放到Object中。本篇就每个方法具体功能、重写规则以及自己的一些理解。 二、Object方法详解
Object中含有:registerNatives()、getClass()、hashCode()、equals()、clone()、toString()、notify()、notifyAll()、wait(long)、wait(long,int)、wait()、finalize()共十二个方法。这个顺序是按照Object类中定义方法的顺序列举的,下面我也会按照这个顺序依次进行讲解。 1.1、registerNatives()
public class Object {
private static native void registerNatives();
static {
registerNatives();
}
}
什么鬼?哈哈哈,我刚看到这方法,一脸懵逼。从名字上理解,这个方法是注册native方法(本地方法,由JVM实现,底层是C/C++实现的)向谁注册呢?当然是向JVM,当有程序调用到native方法时,JVM才好去找到这些底层的方法进行调用。 Object中的native方法,并使用registerNatives()向JVM进行注册。(这属于JNI的范畴,9龙暂不了解,有兴趣的可自行查阅。) static JNINativeMethod methods[] = {
{"hashCode", "()I", (void *)&JVM_IHashCode},
{"wait", "(J)V", (void *)&JVM_MonitorWait},
{"notify", "()V", (void *)&JVM_MonitorNotify},
{"notifyAll", "()V", (void *)&JVM_MonitorNotifyAll},
{"clone", "()Ljava/lang/Object;", (void *)&JVM_Clone},
};
为什么要使用静态方法,还要放到静态块中呢? 上一篇整理了类加载流程,我们知道了在类初始化的时候,会依次从父类到本类的类变量及类初始化块中的类变量及方法按照定义顺序放到< clinit>方法中,这样可以保证父类的类变量及方法的初始化一定先于子类。所以当子类调用相应native方法,比如计算hashCode时,一定可以保证能够调用到JVM的native方法。 1.2、getClass()
public final native Class getClass():这是一个public的方法,我们可以直接通过对象调用。 类加载的第一阶段类的加载就是将.class文件加载到内存,并生成一个java.lang.Class对象的过程。getClass()方法就是获取这个对象,这是当前类的对象在运行时类的所有信息的集合。这个方法是反射三种方式之一。 1.2.1、反射三种方式:
对象的getClass();
类名.class;
Class.forName();
class extends ObjectTest {
private void privateTest(String str) {
System.out.println(str);
} public void say(String str) {
System.out.println(str);
}
} public class ObjectTest {
public static void main(String[] args) throws Exception {
ObjectTest = new ();
//获取对象运行的Class对象
Class<? extends ObjectTest> aClass = .getClass();
System.out.println(aClass); //getDeclaredMethod这个方法可以获取所有的方法,包括私有方法
Method privateTest = aClass.getDeclaredMethod("privateTest", String.class);
//取消java访问修饰符限制。
privateTest.setAccessible(true);
privateTest.invoke(aClass.newInstance(), "private method test"); //getMethod只能获取public方法
Method say = aClass.getMethod("say", String.class);
say.invoke(aClass.newInstance(), "Hello World");
}
}
//输出结果:
//class test.
//private method test
//Hello World
反射主要用来获取运行时的信息,可以将java这种静态语言动态化,可以在编写代码时将一个子对象赋值给父类的一个引用,在运行时通过反射可以或许运行时对象的所有信息,即多态的体现。对于反射知识还是很多的,这里就不展开讲了。 1.3、hashCode()
public native int hashCode(); 这是一个public的方法,所以子类可以重写它。这个方法返回当前对象的hashCode值,这个值是一个整数范围内的(-2^31 ~ 2^31 - 1)数字。 对于hashCode有以下几点约束 在 Java 应用程序执行期间,在对同一对象多次调用 hashCode 方法时,必须一致地返回相同的整数,前提是将对象进行 equals 比较时所用的信息没有被修改;
如果两个对象 x.equals(y) 方法返回true,则x、y这两个对象的hashCode必须相等。
如果两个对象x.equals(y) 方法返回false,则x、y这两个对象的hashCode可以相等也可以不等。但是,为不相等的对象生成不同整数结果可以提高哈希表的性能。
默认的hashCode是将内存地址转换为的hash值,重写过后就是自定义的计算方式;也可以通过System.identityHashCode(Object)来返回原本的hashCode。
public class HashCodeTest {
private int age;
private String name; @Override
public int hashCode() {
Object[] a = Stream.of(age, name).toArray(); int result = 1;
for (Object element : a) {
result = 31 * result + (element == null ? 0 : element.hashCode());
}
return result;
}
}
推荐使用Objects.hash(Object… values)方法。相信看源码的时候,都看到计算hashCode都使用了31作为基础乘数,为什么使用31呢?我比较赞同与理解result * 31 = (result<<5) - result。JVM底层可以自动做优化为位运算,效率很高;还有因为31计算的hashCode冲突较少,利于hash桶位的分布。 1.4、equals()
public boolean equals(Object obj);用于比较当前对象与目标对象是否相等,默认是比较引用是否指向同一对象。为public方法,子类可重写。 public class Object{
public boolean equals(Object obj) {
return (this == obj);
}
}
为什么需要重写equals方法? 因为如果不重写equals方法,当将自定义对象放到map或者set中时;如果这时两个对象的hashCode相同,就会调用equals方法进行比较,这个时候会调用Object中默认的equals方法,而默认的equals方法只是比较了两个对象的引用是否指向了同一个对象,显然大多数时候都不会指向,这样就会将重复对象存入map或者set中。这就破坏了map与set不能存储重复对象的特性,会造成内存溢出。 重写equals方法的几条约定: 自反性:即x.equals(x)返回true,x不为null;
对称性:即x.equals(y)与y.equals(x)的结果相同,x与y不为null;
传递性:即x.equals(y)结果为true, y.equals(z)结果为true,则x.equals(z)结果也必须为true;
一致性:即x.equals(y)返回true或false,在未更改equals方法使用的参数条件下,多次调用返回的结果也必须一致。x与y不为null。
如果x不为null, x.equals(null)返回false。
我们根据上述规则来重写equals方法。 public class EqualsTest{
private int age;
private String name;
//省略get、set、构造函数等
@Override
public boolean equals(Object o) {
//先判断是否为同一对象
if (this == o) {
return true;
}
//再判断目标对象是否是当前类及子类的实例对象
//注意:instanceof包括了判断为null的情况,如果o为null,则返回false
if (!(o instanceof )) {
return false;
}
that = () o;
return age == that.age &&
Objects.equals(name, that.name);
} public static void main(String[] args) throws Exception {
EqualsTest1 equalsTest1 = new EqualsTest1(23, "9龙");
EqualsTest1 equalsTest12 = new EqualsTest1(23, "9龙");
EqualsTest1 equalsTest13 = new EqualsTest1(23, "9龙"); System.out.println("-----------自反性----------");
System.out.println(equalsTest1.equals(equalsTest1)); System.out.println("-----------对称性----------");
System.out.println(equalsTest12.equals(equalsTest1));
System.out.println(equalsTest1.equals(equalsTest12)); System.out.println("-----------传递性----------");
System.out.println(equalsTest1.equals(equalsTest12));
System.out.println(equalsTest12.equals(equalsTest13));
System.out.println(equalsTest1.equals(equalsTest13)); System.out.println("-----------一致性----------");
System.out.println(equalsTest1.equals(equalsTest12));
System.out.println(equalsTest1.equals(equalsTest12)); System.out.println("-----目标对象为null情况----");
System.out.println(equalsTest1.equals(null));
}
}
//输出结果
//-----------自反性----------
//true
//-----------对称性----------
//true
//true
//-----------传递性----------
//true
//true
//true
//-----------一致性----------
//true
//true
//-----目标对象为null情况----
//false
从以上输出结果验证了我们的重写规定是正确的。 注意:instanceof 关键字已经帮我们做了目标对象为null返回false,我们就不用再去显示判断了。 建议equals及hashCode两个方法,需要重写时,两个都要重写,一般都是将自定义对象放至Set中,或者Map中的key时,需要重写这两个方法。 1.4、clone()
protected native Object clone() throws CloneNotSupportedException; 此方法返回当前对象的一个副本。 这是一个protected方法,提供给子类重写。但需要实现Cloneable接口,这是一个标记接口,如果没有实现,当调用object.clone()方法,会抛出CloneNotSupportedException。 public class CloneTest implements Cloneable {
private int age;
private String name;
//省略get、set、构造函数等 @Override
protected CloneTest clone() throws CloneNotSupportedException {
return (CloneTest) super.clone();
} public static void main(String[] args) throws CloneNotSupportedException {
CloneTest cloneTest = new CloneTest(23, "9龙");
CloneTest clone = cloneTest.clone();
System.out.println(clone == cloneTest);
System.out.println(cloneTest.getAge()==clone.getAge());
System.out.println(cloneTest.getName()==clone.getName());
}
}
//输出结果
//false
//true
//true
从输出我们看见,clone的对象是一个新的对象;但原对象与clone对象的String类型的name却是同一个引用,这表明,super.clone方法对成员变量如果是引用类型,进行是浅拷贝。 那什么是浅拷贝?对应的深拷贝? 浅拷贝:拷贝的是引用。 深拷贝:新开辟内存空间,进行值拷贝。 那如果我们要进行深拷贝怎么办呢?看下面的例子。 class Person implements Cloneable{
private int age;
private String name;
//省略get、set、构造函数等
@Override
protected Person clone() throws CloneNotSupportedException {
Person person = (Person) super.clone();
//name通过new开辟内存空间
person.name = new String(name);
return person;
}
} public class CloneTest implements Cloneable {
private int age;
private String name;
//增加了person成员变量
private Person person; //省略get、set、构造函数等
@Override
protected CloneTest clone() throws CloneNotSupportedException {
CloneTest clone = (CloneTest) super.clone();
clone.person = person.clone();
return clone;
} public static void main(String[] args) throws CloneNotSupportedException {
CloneTest cloneTest = new CloneTest(23, "9龙");
Person person = new Person(22, "路飞");
cloneTest.setPerson(person); CloneTest clone = cloneTest.clone();
System.out.println(clone == cloneTest);
System.out.println(cloneTest.getAge() == clone.getAge());
System.out.println(cloneTest.getName() == clone.getName()); Person clonePerson = clone.getPerson();
System.out.println(person == clonePerson);
System.out.println(person.getName() == clonePerson.getName());
}
}
//输出结果
//false
//true
//true
//false
//false
可以看到,即使成员变量是引用类型,我们也实现了深拷贝。如果成员变量是引用类型,想实现深拷贝,则成员变量也要实现Cloneable接口,重写clone方法。 1.5、toString()
public String toString();这是一个public方法,子类可重写,建议所有子类都重写toString方法,默认的toString方法,只是将当前类的全限定性类名+@+十六进制的hashCode值。 public class Object{
public String toString() {
return getClass().getName() + "@" + Integer.toHexString(hashCode());
}
}
我们思考一下为什么需要toString方法? 我这么理解的,返回当前对象的字符串表示,可以将其打印方便查看对象的信息,方便记录日志信息提供调试。 我们可以选择需要表示的重要信息重写到toString方法中。为什么Object的toString方法只记录类名跟内存地址呢?因为Object没有其他信息了,哈哈哈。 1.6、wait()/ wait(long)/ waite(long,int)
这三个方法是用来线程间通信用的,作用是阻塞当前线程,等待其他线程调用notify()/notifyAll()方法将其唤醒。这些方法都是public final的,不可被重写。 注意: 此方法只能在当前线程获取到对象的锁监视器之后才能调用,否则会抛出IllegalMonitorStateException异常。
调用wait方法,线程会将锁监视器进行释放;而Thread.sleep,Thread.yield()并不会释放锁。
wait方法会一直阻塞,直到其他线程调用当前对象的notify()/notifyAll()方法将其唤醒;而wait(long)是等待给定超时时间内(单位毫秒),如果还没有调用notify()/nofiyAll()会自动唤醒;waite(long,int)如果第二个参数大于0并且小于999999,则第一个参数+1作为超时时间;
public final void wait() throws InterruptedException {
wait(0);
} public final native void wait(long timeout) throws InterruptedException; public final void wait(long timeout, int nanos) throws InterruptedException {
if (timeout < 0) {
throw new IllegalArgumentException("timeout value is negative");
} if (nanos < 0 || nanos > 999999) {
throw new IllegalArgumentException(
"nanosecond timeout value out of range");
} if (nanos > 0) {
timeout++;
} wait(timeout);
}
1.7、notify()/notifyAll()
前面说了,如果当前线程获得了当前对象锁,调用wait方法,将锁释放并阻塞;这时另一个线程获取到了此对象锁,并调用此对象的notify()/notifyAll()方法将之前的线程唤醒。这些方法都是public final的,不可被重写。 public final native void notify(); 随机唤醒之前在当前对象上调用wait方法的一个线程 public final native void notifyAll(); 唤醒所有之前在当前对象上调用wait方法的线程 下面我们使用wait()、notify()展示线程间通信。假设9龙有一个账户,只要9龙一发工资,就被女朋友给取走了。 //账户
public class Account {
private String accountNo;
private double balance;
private boolean flag = false; public Account() {
} public Account(String accountNo, double balance) {
this.accountNo = accountNo;
this.balance = balance;
} /**
* 取钱方法
*
* @param drawAmount 取款金额
*/
public synchronized void draw(double drawAmount) {
try {
if (!flag) {
//如果flag为false,表明账户还没有存入钱,取钱方法阻塞
wait();
} else {
//执行取钱操作
System.out.println(Thread.currentThread().getName() + " 取钱" + drawAmount);
balance -= drawAmount;
//标识账户已没钱
flag = false;
//唤醒其他线程
notify();
}
} catch (InterruptedException e) {
e.printStackTrace();
}
} public synchronized void deposit(double depositAmount) {
try {
if (flag) {
//如果flag为true,表明账户已经存入钱,取钱方法阻塞
wait();
} else {
//存钱操作
System.out.println(Thread.currentThread().getName() + " 存钱" + depositAmount);
balance += depositAmount;
//标识账户已存入钱
flag = true;
//唤醒其他线程
notify();
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
//取钱者
public class DrawThread extends Thread {
private Account account;
private double drawAmount; public DrawThread(String name, Account account, double drawAmount) {
super(name);
this.account = account;
this.drawAmount = drawAmount;
} @Override
public void run() {
//循环6次取钱
for (int i = 0; i < 6; i++) {
account.draw(drawAmount);
}
}
} //存钱者
public class DepositThread extends Thread {
private Account account;
private double depositAmount; public DepositThread(String name, Account account, double depositAmount) {
super(name);
this.account = account;
this.depositAmount = depositAmount;
} @Override
public void run() {
//循环6次存钱操作
for (int i = 0; i < 6; i++) {
account.deposit(depositAmount);
}
}
}
//测试
public class DrawTest {
public static void main(String[] args) {
Account brady = new Account("9龙", 0);
new DrawThread("女票", brady, 10).start();
new DepositThread("公司", brady, 10).start();
}
}
//输出结果
//公司 存钱10.0
//女票 取钱10.0
//公司 存钱10.0
//女票 取钱10.0
//公司 存钱10.0
//女票 取钱10.0
例子中我们通过一个boolean变量来判断账户是否有钱,当取钱线程来判断如果账户没钱,就会调用wait方法将此线程进行阻塞;这时候存钱线程判断到账户没钱, 就会将钱存入账户,并且调用notify()方法通知被阻塞的线程,并更改标志;取钱线程收到通知后,再次获取到cpu的调度就可以进行取钱。反复更改标志,通过调用wait与notify()进行线程间通信。实际中我们会时候生产者消费者队列会更简单。 注意:调用notify()后,阻塞线程被唤醒,可以参与锁的竞争,但可能调用notify()方法的线程还要继续做其他事,锁并未释放,所以我们看到的结果是,无论notify()是在方法一开始调用,还是最后调用,阻塞线程都要等待当前线程结束才能开始。 为什么wait()/notify()方法要放到Object中呢? 因为每个对象都可以成为锁监视器对象,所以放到Object中,可以直接使用。 1.8、finalize()
protected void finalize() throws Throwable ; 此方法是在垃圾回收之前,JVM会调用此方法来清理资源。此方法可能会将对象重新置为可达状态,导致JVM无法进行垃圾回收。 我们知道java相对于C++很大的优势是程序员不用手动管理内存,内存由jvm管理;如果我们的引用对象在堆中没有引用指向他们时,当内存不足时,JVM会自动将这些对象进行回收释放内存,这就是我们常说的垃圾回收。但垃圾回收没有讲述的这么简单。 finalize()方法具有如下4个特点: 永远不要主动调用某个对象的finalize()方法,该方法由垃圾回收机制自己调用;
finalize()何时被调用,是否被调用具有不确定性;
当JVM执行可恢复对象的finalize()可能会将此对象重新变为可达状态;
当JVM执行finalize()方法时出现异常,垃圾回收机制不会报告异常,程序继续执行。
public class FinalizeTest {
private static FinalizeTest ft = null;
public void info(){
System.out.println("测试资源清理得finalize方法");
} public static void main(String[] args) {
//创建FinalizeTest对象立即进入可恢复状态
new FinalizeTest();
//通知系统进行垃圾回收
System.gc();
//强制回收机制调用可恢复对象的finalize()方法
// Runtime.getRuntime().runFinalization();
System.runFinalization();
ft.info();
} @Override
public void finalize(){
//让ft引用到试图回收的可恢复对象,即可恢复对象重新变成可达
ft = this;
throw new RuntimeException("出异常了,你管不管啊");
}
}
//输出结果
//测试资源清理得finalize方法
我们看到,finalize()方法将可恢复对象置为了可达对象,并且在finalize中抛出异常,都没有任何信息,被忽略了。 1.8.1、对象在内存中的状态
对象在内存中存在三种状态: 可达状态:有引用指向,这种对象为可达状态;
可恢复状态:失去引用,这种对象称为可恢复状态;垃圾回收机制开始回收时,回调用可恢复状态对象的finalize()方法(如果此方法让此对象重新获得引用,就会变为可达状态,否则,会变为不可大状态)。
不可达状态:彻底失去引用,这种状态称为不可达状态,如果垃圾回收机制这时开始回收,就会将这种状态的对象回收掉。 1.8.2、垃圾回收机制
垃圾回收机制只负责回收堆内存种的对象,不会回收任何物理资源(例如数据库连接、网络IO等资源);
程序无法精确控制垃圾回收的运行,垃圾回收只会在合适的时候进行。当对象为不可达状态时,系统会在合适的时候回收它的内存。
在垃圾回收机制回收任何对象之前,总会先调用它的finalize()方法,该方法可能会将对象置为可达状态,导致垃圾回收机制取消回收。
1.8.3、强制垃圾回收
上面我们已经说了,当对象失去引用时,会变为可恢复状态,但垃圾回收机制什么时候运行,什么时候调用finalize方法无法知道。虽然垃圾回收机制无法精准控制,但java还是提供了方法可以建议JVM进行垃圾回收,至于是否回收,这取决于虚拟机。但似乎可以看到一些效果。 public class GcTest {
public static void main(String[] args){
for(int i=0;i<4;i++){
//没有引用指向这些对象,所以为可恢复状态
new GcTest();
//强制JVM进行垃圾回收(这只是建议JVM)
System.gc();
//Runtime.getRuntime().gc();
}
} @Override
public void finalize(){
System.out.println("系统正在清理GcTest资源。。。。");
}
}
//输出结果
//系统正在清理GcTest资源。。。。
//系统正在清理GcTest资源。。。。
System.gc(),Runtime.getRuntime().gc()两个方法作用一样的,都是建议JVM垃圾回收,但不一定回收,多运行几次,结果可能都不一致。

Object中有哪些方法及其作用的更多相关文章

  1. 你知道Object中有哪些方法及其作用吗?

    一.引言二.Object方法详解1.1.registerNatives()1.2.getClass()1.2.1.反射三种方式:1.3.hashCode()1.4.equals()1.4.clone( ...

  2. Object中有哪些方法?

    有一种遗憾就是,每天都见到你,但是却不关注你!等到面试的时候才后悔莫及. Object类中有9大public方法: equals:判断两个对象"相等" hashCode:获取对象的 ...

  3. Object中的方法以及对象相等的判定

    看图说话 Object有以下几个方法 getClass() final类型,主要是用来获得运行时的类型 hashCode() 返回该对象的哈希码值,方法是为了提高哈希表(例如 java.util.Ha ...

  4. Object中有哪些公共方法及作用

    大家在学习java的时候,一定遇到过Object类,因为在java单一继承体系中Object类是根类,所有的类都会继承它,并拥有Object的公共方法,意味着在java的面向对象的世界中,所有对象都拥 ...

  5. Java温故而知新(7)Object类及其方法讲解

    一.java.lang.Object java.lang包在使用的时候无需显示导入,编译时由编译器自动导入. Object类是类层次结构的根,Java中所有的类从根本上都继承自这个类. Object类 ...

  6. 方法object面试题分析:7JAVA中Object的clone方法详解-克隆-深克隆

    时间紧张,先记一笔,后续优化与完善.     每日一道理 翻开早已发黄的页张,试着寻找过去所留下的点点滴滴的足迹.多年前的好友似乎现在看来已变得陌生,匆忙之间,让这维持了多年的友谊变淡,找不出什么亲切 ...

  7. List集合去除重复对象及equals()、hashCode()方法的作用

    原文:https://blog.csdn.net/freelander_j/article/details/52211010 在java中,要将一个集合中重复的对象除去,如果这个集合中的数据类型是基本 ...

  8. 自己(转)JAVA中toString方法的作用

    JAVA中toString方法的作用 因为它是Object里面已经有了的方法,而所有类都是继承Object,所以“所有对象都有这个方法”. 它通常只是为了方便输出,比如System.out.print ...

  9. 面向对象编程(四)继承,概念及super关键字,final关键字,Object类常见方法

    继承 概念: ①   继承背后的思想就是基于已存在的类来构建新类; ②   当从已存在类继承时,就重用了它的方法和属性,还可以添加新的方法和属性来定制新类以应对需求; ③   当从其它类导出的类叫作子 ...

随机推荐

  1. SQL进程死锁排查

    --进程执行状态 SELECT der.[session_id],der.[blocking_session_id], sp.lastwaittype,sp.hostname,sp.program_n ...

  2. python 通过序列索引迭代

    另外一种执行循环的遍历方式是通过索引,如下实例: #!/usr/bin/python # -*- coding: UTF-8 -*- fruits = ['banana', 'apple', 'man ...

  3. c++回溯法求组合问题(取数,选取问题)从n个元素中选出m个的回溯算法

    假如现在有n个数,分别从里面选择m个出来,那么一共有多少种不同的组合呢,分别是哪些呢? 利用计算机的计算力,采用回溯算法很容易求解 程序源代码如下: #include<iostream># ...

  4. CCPC-Wannafly & Comet OJ 夏季欢乐赛(2019)D

    题面 一开始想到一个 O(N^2) 做法,先把x排序,然后顺次枚举x最大的点,看向前最多可以保留多少点 (也就是先不管正方形的上下长度限制,先考虑左右的限制).然后再对这些点做一遍类似的..(等等这么 ...

  5. Battle ships HDU - 5093二分匹配

    Battle shipsHDU - 5093 题目大意:n*m的地图,*代表海洋,#代表冰山,o代表浮冰,海洋上可以放置船舰,但是每一行每一列只能有一个船舰(类似象棋的車),除非同行或者同列的船舰中间 ...

  6. TensorFlow使用记录 (六): 优化器

    0. tf.train.Optimizer tensorflow 里提供了丰富的优化器,这些优化器都继承与 Optimizer 这个类.class Optimizer 有一些方法,这里简单介绍下: 0 ...

  7. Spring——顾问封装通知

    通知(advice)是Spring中的一种比较简单的切面,只能将切面织入到目标类的所有方法中,而无法对指定方法进行增强 顾问(advisor)是Spring提供的另外一种切面,可以织入到指定的方法中  ...

  8. http状态码301和302详解及区别——辛酸的探索之路

    原文链接:https://blog.csdn.net/grandPang/article/details/47448395 一直对http状态码301和302的理解比较模糊,在遇到实际的问题和翻阅各种 ...

  9. 消费端ACK和重回队列

    使用场景 消费端ACK和重回队列 消费端ACK使用场景: 1.消费端进行消费的时候,如果由于业务异常我们可以进行日志记录,然后进行补偿. 2.由于服务器宕机等严重问题,那我们就需要手工进行ACK保障消 ...

  10. 【面试】Spring 执行流程

    Spring Aop的实现原理: AOP 的全称是  Aspect Orient Programming  ,即面向切面编程.是对 OOP (Object Orient Programming) 的一 ...