当前位置: 首页 > news >正文

JDK源码解析-Object

1. Object类

所有类的基类——java.lang.Object

  • Object 类是所有类的基类,当一个类没有直接继承某个类时,默认继承Object类
  • Object 类属于 java.lang 包,此包下的所有类在使用时无需手动导入,系统会在程序编译期间自动导入。

思考:Object是如何成为默认父类的?

public class JDK8_Test extends Object {public static void main(String[] args) {System.out.println("JDK8源码环境构建...");}
}

推测:

情况1:编译器处理

在编译源代码时,当一个类没有显式标明继承的父类时,编译器会为其指定一个默认的父类(一般为Object),而交给虚拟机处理这个类时,由于这个类已经有一个默认的父类了,因此,VM仍然会按照常规的方法像处理其他类一样来处理这个类。

情况2:虚拟机处理

编译器仍然按照实际代码进行编译,并不会做额外的处理,即如果一个类没有显式地继承于其他类时,编译后的代码仍然没有父类。然后由虚拟机运行二进制代码时,当遇到没有父类的类时,就会自动将这个类看成是Object类的子类

验证:

使用JDK自带的工具(javap)反汇编

在这里插入图片描述

结论:JDK无论版本,都是由编译器在编译阶段就已经织入了Object

1.1 Object类结构图

'xxs'

这里有7个native方法:registerNatives()、getClass()、hashCode()、clone()、notify()、notifyAll()、wait(long)

什么是native方法?官方给的说明是"A native method is a Java method whose implementation is provided by non-java code."

通俗的说,native表示该方法的实现java本身并没有完成,而是有c/c++来完成,放在.dll动态库文件中。

Object源码如下 :

package java.lang;public class Object {/*** 一个本地方法,具体是用C(C++)在DLL中实现的,然后通过JNI调用*/private static native void registerNatives();/*** 对象初始化时自动调用此方法*/static {registerNatives();}/*** 返回此Object的运行时类*/public final native Class<?> getClass();/*** hashCode的常规协定是:* 1.在java应用程序执行期间,在对同一对象多次调用hashCode()方法时,必须一致地返回相同的整数,前提是将对象进行equals比较时所用的信息没有被修改。* 从某一应用程序的一次执行到同一应用程序的另一次执行,该整数无需保持一致。* 2.如果根据equals(object)方法,两个对象是相等的,那么对这两个对象中的每个对象调用hashCode方法都必须生成相同的整数结果。* 3.如果根据equals(java.lang.Object)方法,两个对象不相等,那么对这两个对象中的任一对象上调用hashCode()方法不要求一定生成不同的整数结果。* 但是,应该意识到,为不相等的对象生成不同整数结果可以提高哈希表的性能。*/public native int hashCode();/*** 这里比较的是对象的内存地址*/public boolean equals(Object obj) {return (this == obj);}/*** 本地clone方法,用于对象的复制*/protected native Object clone() throws CloneNotSupportedException;/*** 返回该对象的字符串表示,非常重要的方法* getClass().getName();获取字节码文件的对应全路径名例如java.lang.Object* Integer.toHexString(hashCode());将哈希值转成16进制数格式的字符串。*/public String toString() {return getClass().getName() + "@" + Integer.toHexString(hashCode());}/*** 不能被重写,用于唤醒一个在因等待该对象(调用了wait方法)被处于等待状态(waiting 或 time_wait)的线程,该方法只能同步     * 方法或同步块中调用*/public final native void notify();/*** 不能被重写,用于唤醒所有在因等待该对象(调用wait方法)被处于等待状态(waiting或time_waiting)的线程,该方法只能同步方      * 法或同步块中调用*/public final native void notifyAll();/*** 不能被重写,用于在线程调用中,导致当前线程进入等待状态(time_waiting),timeout单位为毫秒,该方法只能同步方法或同步块中     * 调用,超过设置时间后线程重新进入可运行状态*/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);}/*** 在其他线程调用此对象的notify()方法或notifyAll()方法前,导致当前线程等待。换句话说,此方法的行为就好像它仅执行wait(0)调   用一样。* 当前线程必须拥有此对象监视器。* 该线程发布对此监视器的所有权并等待,直到其他线程通过调用notify方法或notifyAll方法通知在此对象的监视器上等待的线程醒来,* 然后该线程将等到重新获得对监视器的所有权后才能继续执行。*/public final void wait() throws InterruptedException {wait(0);}/*** 这个方法用于当对象被回收时调用,这个由JVM支持,Object的finalize方法默认是什么都没有做,如果子类需要在对象被回收时执行        一些逻辑处理,则可以重写finalize方法。*/protected void finalize() throws Throwable {}
}
补充说明:关键字 Native

问题:为什么要用 native 来修饰方法,这样做有什么用?

JNI:Java Native Interface

一般情况下,我们完全可以使用 Java 语言编写程序,但某些情况下,Java 可能会不满足应用程序的需求,或者是不能更好的满足需求,比如:

①、标准的 Java 类库不支持应用程序平台所需的平台相关功能。

②、我们已经用另一种语言编写了一个类库,如何用Java代码调用?

③、某些运行次数特别多的方法代码,为了加快性能,我们需要用更接近硬件的语言(比如汇编)编写。

上面这三种需求,其实说到底就是如何用 Java 代码调用不同语言编写的代码。那么 JNI 应运而生了。

'xxs'

native 用来修饰方法,用 native 声明的方法表示告知 JVM 调用,该方法在外部定义,可以用任何语言去实现它。 简单地讲,一个native Method就是一个 Java 调用非 Java 代码的接口。

1.2 类构造器

一个类必须要有一个构造器的存在,如果没有显示声明,那么系统会默认创造一个无参构造器,在JDK的Object类源码中,是看不到构造器的,系统会自动添加一个无参构造器。可以通过:

// 构造一个Object类的对象。
Object obj = new Object()

1.3 equals 方法

  • 面试: equals() 方法和 == 运算符的区别?
  • 思考:有没有重写过equals?
  • 思考:为何重写equals() 就得重写hashCode方法?

**源码:**Object 类中的equals 方法:

 public boolean equals(Object obj) {return (this == obj);}

结论:

  • 在 Object 类中,== 运算符和 equals 方法是等价的,都是比较两个对象的引用是否相等,从另一方面来讲,如果两个对象的引用相等,那么这两个对象一定是相等的
  • 对于自定义的一个对象,如果不重写 equals 方法,那么在比较对象的时候就是调用 Object 类的 equals 方法,也就是用 == 运算符比较两个对象

测试程序:

public class JDKTest{public static void main(String[] args) {String test1 = new String("test");String test2 = new String("test");System.out.println(test1.equals(test2)); 
}

String类重写equals方法:

public boolean equals(Object anObject) {//如果内存地址相等,那必须equalif (this == anObject) {return true;}//如果对象是String类型 if (anObject instanceof String) {String anotherString = (String)anObject;// 获取调用方的字符串长度赋值给nint n = value.length;//判断长度相等if (n == anotherString.value.length) {char v1[] = value;char v2[] = anotherString.value;int i = 0;//逐个字符的比较while (n-- != 0) {//从前往后,任意一个字符不匹配,直接返回falseif (v1[i] != v2[i])return false;i++;}//全部匹配结束,返回truereturn true;}}return false;
}

String 是引用类型,比较时不能比较引用是否相等,重点是字符串的内容是否相等。所以 String 类定义两个对象相等的标准是字符串内容都相同

在Java规范中,对 equals 方法的使用必须遵循以下几个原则:

  • ① 自反性:对于任何非空引用值 x,x.equals(x) 都应返回 true。
  • ② 对称性:对于任何非空引用值 x 和 y,当且仅当 y.equals(x) 返回 true 时,x.equals(y) 才应返回 true。
  • ③ 传递性:对于任何非空引用值 x、y 和 z,如果 x.equals(y) 返回 true,并且 y.equals(z) 返回 true,那么 x.equals(z) 应返回 true。
  • ④ 一致性:对于任何非空引用值 x 和 y,多次调用 x.equals(y) 始终返回 true 或始终返回 false,前提是对象上 equals 比较中所用的信息没有被修改
  • ⑤ 对于任何非空引用值 x,x.equals(null) 都应返回 false。

下面自定义一个 Person 类,然后重写其equals 方法,比较两个 Person 对象:

public class Person {private String pname;private int page;public Person(){}public Person(String pname,int page){this.pname = pname;this.page = page;}public int getPage() {return page;}public void setPage(int page) {this.page = page;}public String getPname() {return pname;}public void setPname(String pname) {this.pname = pname;}@Overridepublic boolean equals(Object obj) {if(this == obj){//引用相等那么两个对象当然相等return true;}/*if(obj == null || !(obj instanceof  Person)){//对象为空或者不是Person类的实例return false;}*/if(obj == null || (getClass() != obj.getClass())){//对象为空或者不是Person类的实例return false;}Person otherPerson = (Person)obj;if(otherPerson.getPname().equals(this.getPname()) && otherPerson.getPage()==this.getPage()){return true;}return false;}public static void main(String[] args) {Person p1 = new Person("Tom",21);Person p2 = new Person("Marry",20);System.out.println(p1==p2);//falseSystem.out.println(p1.equals(p2));//falsePerson p3 = new Person("Tom",21);System.out.println(p1.equals(p3));//true}}

通过重写 equals 方法,自定义两个对象相等的标尺为Person对象的两个属性都相等,则对象相等,否则不相等。如果不重写 equals 方法,那么始终是调用 Object 类的equals 方法,也就是用 == 比较两个对象在栈内存中的引用地址是否相等。

重写equals方法总结建议:

@Override
public boolean equals(Object otherObject) {//1、判断比较的两个对象引用是否相等,如果引用相等那么表示是同一个对象,那么当然相等if(this == otherObject){return true;}//2、如果 otherObject 为 null,直接返回false,表示不相等if(otherObject == null ){//对象为空或者不是Person类的实例return false;}//3、比较 this 和 otherObject 是否是同一个类(注意下面两个只能使用一种)//3.1:如果 equals 的语义在每个子类中所有改变,就使用 getClass 检测if(this.getClass() != otherObject.getClass()){return false;}//3.2:如果所有的子类都有统一的定义,那么使用 instanceof 检测if(!(otherObject instanceof Person)){return false;}//4、将 otherObject 转换成对应的类类型变量Person other = (Person) otherObject;//5、最后对对象的属性进行比较。使用 == 比较基本类型,使用 equals 比较对象。如果都相等则返回true,否则返回false//   使用 Objects 工具类的 equals 方法防止比较的两个对象有一个为 null而报错,因为 null.equals() 是会抛异常的return Objects.equals(this.pname,other.pname) && this.page == other.page;//6、注意如果是在子类中定义equals,则要包含 super.equals(other)//return super.equals(other) && Objects.equals(this.pname,other.pname) && this.page == other.page;}

注意,无论何时重写此方法,通常都必须重写hashCode方法,以维护hashCode方法的一般约定,该方法声明相等对象必须具有相同的哈希代码

1.4 hashCode 方法

源码 Object 类中定义如下:

// 是一个用 native 声明的本地方法,作用是返回对象的散列码,是 int 类型的数值。
public native int hashCode();

思考:为什么要有hashCode方法,意义?

思考:hashCode如何被计算出来的?存储在对象内存结构中的那部分中?

作用:

HashCode的存在主要是为了查找的快捷性,HashCode是用来在散列存储结构中确定对象的存储地址

例:比如使用集合 List,Set,还有 Map,List集合一般是存放的元素是有序可重复的,Set 存放的元素则是无序不可重复的,而 Map 集合存放的是键值对。

前面说过判断一个元素是否相等可以通过 equals 方法,没增加一个元素,那么就通过 equals 方法判断集合中的每一个元素是否重复,但是如果集合中有10000个元素了,但新加入一个元素时,那就需要进行10000次equals方法的调用,这显然效率很低。

于是,Java 的集合设计者就采用了 哈希表 来实现。哈希算法也称为散列算法,是将数据依特定算法产生的结果直接指定到一个地址上。这个结果就是由 hashCode 方法产生。这样一来,当集合要添加新的元素时,先调用这个元素的 hashCode 方法,就一下子能定位到它应该放置的物理位置上。

'xxs'

①、如果这个位置上没有元素,它就可以直接存储在这个位置上,不用再进行任何比较了;

②、如果这个位置上已经有元素了,就调用它的equals方法与新元素进行比较,相同的话就不存了;

③、不相同的话,也就是发生了Hash key相同导致冲突的情况,那么就在这个Hash key的地方产生一个链表,将所有产生相同HashCode的对象放到这个单链表

HashCode是如何产生的?

思考:hashCode到底是什么?是不是对象的内存地址?

import java.util.ArrayList;
import java.util.List;public class HashCodeTest {//目标:只要发生重复,说明hashcode不是内存地址,但还需要证明(JVM代码证明)public static void main(String[] args) {List<Integer> integerList = new ArrayList<Integer>();int num = 0;for (int i = 0; i < 150000; i++) {//创建新的对象Object object = new Object();if (integerList.contains(object.hashCode())) {num++;//发生重复(内存地址肯定不会重复)} else {integerList.add(object.hashCode());//没有重复}}System.out.println(num + "个hashcode发生重复");System.out.println("List合计大小" + integerList.size() + "个");}
}

15万个循环,发生了重复,说明hashCode不是内存地址(严格的说,肯定不是直接取的内存地址)

'xxs'

HashCode存储位置

对象内存布局

当一个对象在堆内存中分配好并且初始化完成之后的结构是什么样的呢?

'xxs'

1、添加对求填充是为了保证对象的总大小是8的整数倍个字节。

2、类型指针占4个字节是因为默认开启了指针压缩,如果不开启指针压缩,则占8个字节

hashCode的值存在Java对象头里的,那么什么是Java对象头呢?Hotspot虚拟机的对象头主要包括两部分数据:**Mark Word(标记字段)、**Class Pointer(类型指针)。其中 Class Pointer是对象指向它的类元数据的指针,虚拟机通过这个指针来确定这个对象是哪个类的实例,Mark Word用于存储对象自身的运行时数据,它是实现轻量级锁和偏向锁的关键。

Mark Word用于存储对象自身的运行时数据,如:哈希码(HashCode)、GC分代年龄、锁状态标志、线程持有的锁、偏向线程 ID、偏向时间戳等

'xxs'

'xxs'

HashCode生成时机

@Test
public void test2() {Object_Test object_test = new Object_Test();//jvm的信息System.out.println(VM.current().details());System.out.println("-------------------------");//调用之前打印object_test对象的头信息//以表格的形式打印对象布局System.out.println(ClassLayout.parseInstance(object_test).toPrintable());System.out.println("-------------------------");//调用后再打印object_test对象的hashcode值System.out.println(Integer.toHexString(object_test.hashCode()));System.out.println(ClassLayout.parseInstance(object_test).toPrintable());System.out.println("-------------------------");//有线程加重量级锁的时候,再来看对象头new Thread(()->{try {synchronized (object_test){Thread.sleep(5000);}} catch (InterruptedException e) {e.printStackTrace();}}).start();new Thread(()->{try {synchronized (object_test){Thread.sleep(5000);}} catch (InterruptedException e) {e.printStackTrace();}}).start();System.out.println(Integer.toHexString(object_test.hashCode()));System.out.println(ClassLayout.parseInstance(object_test).toPrintable());
}

结果:

对象的内存结构(无锁状态、未调用HashCode):

在这里插入图片描述

调用对象的HashCode方法后:

在这里插入图片描述

加锁,产生线程竞争后:

在这里插入图片描述

HashCode如何生成的?

源码:

public native int hashCode();

1)先从Object.c开始找hashCode映射

src\share\native\java\lang\Object.c

JNIEXPORT void JNICALL//jni调用
//全路径:java_lang_Object_registerNatives是java对应的包下方法
Java_java_lang_Object_registerNatives(JNIEnv *env, jclass cls)
{//jni环境调用;下面的参数methods对应的java方法(*env)->RegisterNatives(env, cls,methods, sizeof(methods)/sizeof(methods[0]));
}

JAVA--------------------->C++函数对应

//JAVA方法(返回值)----->C++函数对象
static JNINativeMethod methods[] = {//JAVA方法        返回值  (参数)                          c++函数{"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},
};

JVM_IHashCod在哪里呢?

2)全局检索JVM_IHashCode

完全搜不到这个方法名,只有这个还凑合有点像,那这是个啥呢?

'xxs'

src\share\vm\prims\jvm.cpp

/*
JVM_ENTRY is a preprocessor macro that
adds some boilerplate code that is common for all functions of HotSpot JVM API.
This API is a connection layer between the native code of JDK class library and the JVM.JVM_ENTRY是一个预加载宏,增加一些样板代码到jvm的所有function中
这个api是位于本地方法与jdk之间的一个连接层。所以,此处才是生成hashCode的逻辑!
*/
JVM_ENTRY(jint, JVM_IHashCode(JNIEnv* env, jobject handle))JVMWrapper("JVM_IHashCode");//调用了ObjectSynchronizer对象的FastHashCodereturn handle == NULL ? 0 : ObjectSynchronizer::FastHashCode (THREAD, JNIHandles::resolve_non_null(handle)) ;
JVM_END

3)继续,ObjectSynchronizer::FastHashCode

'xxs'

先说生成流程,留个印象:

在这里插入图片描述

intptr_t ObjectSynchronizer::FastHashCode (Thread * Self, oop obj) {//是否开启了偏向锁(Biased:偏向,倾向)if (UseBiasedLocking) {//如果当前对象处于偏向锁状态if (obj->mark()->has_bias_pattern()) {Handle hobj (Self, obj) ;assert (Universe::verify_in_progress() ||!SafepointSynchronize::is_at_safepoint(),"biases should not be seen by VM thread here");//那么就撤销偏向锁(达到无锁状态,revoke:废除)BiasedLocking::revoke_and_rebias(hobj, false, JavaThread::current());obj = hobj() ;//断言下,看看是否撤销成功(撤销后为无锁状态)assert(!obj->mark()->has_bias_pattern(), "biases should be revoked by now");}}// ……ObjectMonitor* monitor = NULL;markOop temp, test;intptr_t hash;//读出一个稳定的mark;防止对象obj处于膨胀状态;//如果正在膨胀,就等他膨胀完毕再读出来markOop mark = ReadStableMark (obj);//是否撤销了偏向锁(也就是无锁状态)(neutral:中立,不偏不斜的)if (mark->is_neutral()) {//从mark头上取hash值hash = mark->hash(); //如果有,直接返回这个hashcode(xor)if (hash) {                       // if it has hash, just return itreturn hash;}//如果没有就新生成一个(get_next_hash)hash = get_next_hash(Self, obj);  // allocate a new hash code//生成后,原子性设置,将hash放在对象头里去,这样下次就可以直接取了temp = mark->copy_set_hash(hash); // merge the hash code into header// use (machine word version) atomic operation to install the hashtest = (markOop) Atomic::cmpxchg_ptr(temp, obj->mark_addr(), mark);if (test == mark) {return hash;}// If atomic operation failed, we must inflate the header// into heavy weight monitor. We could add more code here// for fast path, but it does not worth the complexity.//如果已经升级成了重量级锁,那么找到它的monitor//也就是我们所说的内置锁(objectMonitor),这是c里的数据类型//因为锁升级后,mark里的bit位已经不再存储hashcode,而是指向monitor的地址//而升级的markword呢?被移到了c的monitor里} else if (mark->has_monitor()) {//沿着monitor找header,也就是对象头monitor = mark->monitor();temp = monitor->header();assert (temp->is_neutral(), "invariant") ;//找到header后取hash返回hash = temp->hash();if (hash) {return hash;}// Skip to the following code to reduce code size} else if (Self->is_lock_owned((address)mark->locker())) {//轻量级锁的话,也是从java对象头移到了c里,叫helpertemp = mark->displaced_mark_helper(); // this is a lightweight monitor ownedassert (temp->is_neutral(), "invariant") ;hash = temp->hash();              // by current thread, check if the displaced//找到,返回if (hash) {                       // header contains hash codereturn hash;}}......

问:

为什么要先撤销偏向锁到无锁状态,再来生成hashcode呢?这跟锁有什么关系?

答:

mark word里,hashcode存储的字节位置被偏向锁给占了!偏向锁存储了锁持有者的线程id

(参考上面的markword图)

扩展:关于hashCode的生成算法(了解)

// hashCode() generation :
// 涉及到c++算法领域,感兴趣的同学自行研究
// Possibilities:
// * MD5Digest of {obj,stwRandom}
// * CRC32 of {obj,stwRandom} or any linear-feedback shift register function.
// * A DES- or AES-style SBox[] mechanism
// * One of the Phi-based schemes, such as:
//   2654435761 = 2^32 * Phi (golden ratio)
//   HashCodeValue = ((uintptr_t(obj) >> 3) * 2654435761) ^ GVars.stwRandom ;
// * A variation of Marsaglia's shift-xor RNG scheme.
// * (obj ^ stwRandom) is appealing, but can result
//   in undesirable regularity in the hashCode values of adjacent objects
//   (objects allocated back-to-back, in particular).  This could potentially
//   result in hashtable collisions and reduced hashtable efficiency.
//   There are simple ways to "diffuse" the middle address bits over the
//   generated hashCode values:
//
static inline intptr_t get_next_hash(Thread * Self, oop obj) {intptr_t value = 0 ;if (hashCode == 0) {// This form uses an unguarded global Park-Miller RNG,// so it's possible for two threads to race and generate the same RNG.// On MP system we'll have lots of RW access to a global, so the// mechanism induces lots of coherency traffic.value = os::random() ;//返回随机数} else if (hashCode == 1) {// This variation has the property of being stable (idempotent)// between STW operations.  This can be useful in some of the 1-0// synchronization schemes.//和地址相关,但不是地址;右移+异或算法intptr_t addrBits = cast_from_oop<intptr_t>(obj) >> 3 ;value = addrBits ^ (addrBits >> 5) ^ GVars.stwRandom ;//随机数位移异或计算} else  if (hashCode == 2) {value = 1 ;            // 返回1} else  if (hashCode == 3) {value = ++GVars.hcSequence ;//返回一个Sequence序列号} else  if (hashCode == 4) {value = cast_from_oop<intptr_t>(obj) ;//也不是地址} else {//常用// Marsaglia's xor-shift scheme with thread-specific state// This is probably the best overall implementation -- we'll// likely make this the default in future releases.//马萨利亚教授写的xor-shift 随机数算法(异或随机算法)unsigned t = Self->_hashStateX ;t ^= (t << 11) ;Self->_hashStateX = Self->_hashStateY ;Self->_hashStateY = Self->_hashStateZ ;Self->_hashStateZ = Self->_hashStateW ;unsigned v = Self->_hashStateW ;v = (v ^ (v >> 19)) ^ (t ^ (t >> 8)) ;Self->_hashStateW = v ;value = v ;}

'xxs'

可以通过参数 -XX: hashcode=1进行修改 (JDK8默认使用最后一种)

总结

通过分析虚拟机源码证明了hashCode不是直接用的内存地址,而是采取一定的算法来生成

hashcode值的存储在mark word里,与锁共用一段bit位,这就造成了跟锁状态相关性

  • 如果是偏向锁:

一旦调用hashcode,偏向锁将被撤销,hashcode被保存占位mark word,对象被打回无锁状态

  • 那偏偏这会就是有线程硬性使用对象的锁呢?

对象再也回不到偏向锁状态而是升级为重量级锁。hash code跟随mark word被移动到c的object monitor,从那里取

1.5 getClass 方法

getClass()在 Object 类中如下,作用是返回对象的运行时类。

public final native Class<?> getClass();

这是一个用 native 关键字修饰的方法

native 用来修饰方法,用 native 声明的方法表示告知 JVM 调用,该方法在外部定义,可以用任何语言去实现它。

简单地讲,一个native Method就是一个 Java 调用非 Java 代码的接口。

这里要知道用 native 修饰的方法不用考虑,由操作系统实现,该方法的作用是返回一个对象的运行时类,通过这个类对象可以获取该运行时类的相关属性和方法

1.6 toString 方法

源码:

public String toString() {return getClass().getName() + "@" + Integer.toHexString(hashCode());
}
  • getClass().getName()是返回对象的全类名(包含包名),Integer.toHexString(hashCode()) 是以16进制无符号整数形式返回此哈希码的字符串表示形式。
  • 打印某个对象时,默认是调用 toString 方法,比如 System.out.println(person),等价于 System.out.println(person.toString())

1.7 clone方法

源码:

/*** 本地clone方法,用于对象的复制*/
protected native Object clone() throws CloneNotSupportedException;

保护方法,实现对象的浅拷贝,只有实现了Cloneable接口才可以调用该方法,否则抛出CloneNotSupportedException异常。

1.8 finalize 方法

源码:

protected void finalize() throws Throwable { }

当 GC 确定不再有对该对象的引用时,GC 会调用对象的 finalize() 方法来清除回收。

Java VM 会确保一个对象的 finalize() 方法只被调用一次,而且程序中不能直接调用 finalize() 方法。

finalize() 方法通常也不可预测,而且很危险,一般情况下,不必要覆盖 finalize() 方法。

1.9 registerNatives 方法

源码:

private static native void registerNatives();

这是一个本地方法,要知道一个类定义了本地方法后,想要调用操作系统的实现,必须还要装载本地库

static {registerNatives();}

静态代码块就是一个类在初始化过程中必定会执行的内容,所以在类加载的时候是会执行该方法的,通过该方法来注册本地方法。

相关文章:

JDK源码解析-Object

1. Object类 所有类的基类——java.lang.Object Object 类是所有类的基类&#xff0c;当一个类没有直接继承某个类时&#xff0c;默认继承Object类Object 类属于 java.lang 包&#xff0c;此包下的所有类在使用时无需手动导入&#xff0c;系统会在程序编译期间自动导入。 思…...

pinia——添加插件——基础积累

问题&#xff1a;是否给pinia添加过插件&#xff1f;具体添加的方式是什么&#xff1f; 在pinia中&#xff0c;我们可以为仓库添加插件&#xff0c;通过添加插件能够扩展以下的内容&#xff1a; 为 store 添加新的属性 定义 store 时增加新的选项 为 store 增加新的方法 包装现…...

软件国产化之殇

今天又看到这么一个帖子讨论一款国产化软件&#xff0c;属实给我震撼到了。 对于国产化产品&#xff0c;一直主打的都是”自研“&#xff0c;难道是我对”自研“这个词的理解有误&#xff1f; 做一个产品&#xff0c;别人开源了&#xff0c;你拿过来使用&#xff0c;你可以说…...

SQLyog问题处理集合

sqlyog 问题处理 1. 错误号码:1049错误&#xff1a; 数据库命令参数参考&#xff1a;数据库命令地址 检查数据库是否存在检查创建的数据库名称 与 要进行连接的数据库名称是否一致&#xff1b; 2. 错误号码:1819错误&#xff1a; MySQL授予远程连接权限时出现&#xff1a; …...

JavaSE【继承和多态】(1)(重点:初始化、pretected封装、组合)

一、继承 继承 (inheritance) 机制 &#xff1a;是面向对象程序设计使代码可以复用的最重要的手段&#xff0c;它允许程序员在保持原有类特 性 的基础上进行扩展&#xff0c;增加新功能 &#xff0c;这样产生新的类&#xff0c;称 派生类 。 继承呈现了面向对象程序设计的层次结…...

无涯教程-Android Studio函数

第1步-系统要求 您将很高兴知道您可以在以下两种操作系统之一上开始Android应用程序的开发- MicrosoftWindows10/8/7/Vista/2003(32或64位)MacOSX10.8.5或更高版本,最高10.9(小牛) GNOME或KDE桌面 第二点是,开发Android应用程序所需的所有工具都是开源的,可以从Web上下载。以…...

CentOS8安装mysql8.0.24

一、下载mysql安装包并解压 执行以下命令&#xff1a; # 创建mysql安装目录 mkdir /usr/local/mysql # 进入mysql安装目录 cd /usr/local/mysql/ # 下载mysql-8.0.24 wget https://dev.mysql.com/get/Downloads/MySQL-8.0/mysql-8.0.24-linux-glibc2.12-x86_64.tar.xz # 解压…...

Quasi-eccentricity Error Modeling and Compensation in Vision Metrology

论文&#xff1a;Quasi-eccentricity Error Modeling and Compensation in Vision Metrology 中文&#xff1a;视觉计量中准偏心误差建模与补偿 论文地址&#xff1a;Sci-Hub | Quasi-eccentricity error modeling and compensation in vision metrology. Measurement Scienc…...

ai智能电话机器人是人类的助手和朋友

一直以来&#xff0c;人工智能都是人们关注的热门话题。在以前&#xff0c;说到人工智能&#xff0c;第一想到的是“机器人”&#xff0c;随着人工智能的普及&#xff0c;AI已经渗透到我们生活的每一个角落。现在&#xff0c;说起人工智能&#xff0c;可能会想到“无人驾驶、无…...

应用TortoiseSVN的SubWCRev管理VisualStudio C#项目编译版本号

首先要安装 TortoiseSVN, 并确保TortoiseSVN的bin目录被加入到系统环境变量Path中。 1、拷贝Porperties目录下的文件AssemblyInfo.cs生成副本AssemblyInfo.template, 作为版本管理的模板文件。 2、修改模板文件中的想要管理的版本号信息 // [assembly: AssemblyVersion(&quo…...

【八股】2023秋招八股复习笔记5(计算机网络-CN)

文章目录 八股目录目录1、应用层 & HTTP一些http题HTTPS 加密原理&#xff08;问过&#xff09;HTTP/1.1 新特性HTTP/2.0 与 RPC&#xff08;问过&#xff09;GET 和 POST 比较 2、传输层 & TCPTCP三次握手 & 四次挥手&#xff08;问过&#xff09;为什么每次TCP 连…...

【C++】SLT——Vector详解

本片要分享的是关于STL中Vector的内容&#xff0c;Vector的内容于string非常相似&#xff0c;只要会使用string那么学习Vector时会非常流畅。 目录 1.vector介绍 2.vector的简单实用 2.1.简单的无参构造 ​编辑2.2.简单带参构造 2.3.迭代器区间初始化 2.4.vector的遍历 …...

企业网络安全:威胁情报解决方案

什么是威胁情报 威胁情报是网络安全的关键组成部分&#xff0c;可为潜在的恶意来源提供有价值的见解&#xff0c;这些知识可帮助组织主动识别和防止网络攻击&#xff0c;通过利用 STIX/TAXII 等威胁源&#xff0c;组织可以检测其网络中的潜在攻击&#xff0c;从而促进快速检测…...

为什么2G、3G、4G成功了,5G却?

你可能已经多年来一直听到关于闪电般的5G的炒作。虽然新的无线网络在美国仍然没有普及&#xff0c;但5G正在波士顿和西雅图到达拉斯和堪萨斯城等城市慢慢出现。随着连接速度的加快&#xff0c;用户的安全性和隐私保护将增加&#xff0c;因为无线行业试图改善3G和4G的防御。但是…...

C语言每日一练------Day(10)

本专栏为c语言练习专栏&#xff0c;适合刚刚学完c语言的初学者。本专栏每天会不定时更新&#xff0c;通过每天练习&#xff0c;进一步对c语言的重难点知识进行更深入的学习。 今日练习题关键字&#xff1a;自除数 除自身以外数组的乘积 &#x1f493;博主csdn个人主页&#xff…...

发力服务业务,龙湖集团半程领跑赢在“智慧”

成立三十载&#xff0c;龙湖集团一直是房地产行业“特立独行”的存在。 一方面&#xff0c;龙湖在对外战略方面长期量入为出&#xff0c;从不背上过重的“包袱”。 不久前&#xff0c;一则消息引发市场关注&#xff1a;龙湖集团提前偿还17亿元债务&#xff0c;已基本全部还清…...

Kubernetes(七)修改 pod 网络(flannel 插件)

一、 提示 需要重启服务器 操作之前备份 k8s 中所有资源的 yaml 文件 如下是备份脚本&#xff0c;仅供参考 # 创建备份目录 test -d $3 || mkdir $3 # $1 命名空间 # $2 资源名称&#xff1a; sts deploy configMap svc 等 # $3 资源备份存放的目录名称for app in kubec…...

测试平台metersphere

metersphere可以做接口测试、UI测试、性能测试。 metersphere接口测试底层是jmeter&#xff0c;可以做API管理&#xff0c;快捷调试&#xff0c;接口用例管理&#xff0c;接口自动化场景执行一键选取用例范围&#xff0c;生成测试报告。 会用jmeter&#xff0c;metersphere会…...

论文笔记: One Fits All:Power General Time Series Analysis by Pretrained LM

1 intro 时间序列领域预训练模型/foundation 模型的研究还不是很多 主要挑战是缺乏大量的数据来训练用于时间序列分析的基础模型——>论文利用预训练的语言模型进行通用的时间序列分析 为各种时间序列任务提供了一个统一的框架 论文还调查了为什么从语言领域预训练的Transf…...

记录--怎么实现一个3d翻书效果

这里给大家分享我在网上总结出来的一些知识&#xff0c;希望对大家有所帮助 本篇主要讨论以下两种翻书动画的实现&#xff1a; 第一种是整页翻转的效果&#xff1a; 这种整页翻转的效果主要是做rotateY的动画&#xff0c;并结合一些CSS的3d属性实现。 第二种折线翻转的效果&…...

(数字图像处理MATLAB+Python)第十一章图像描述与分析-第一节、二节:图像描述概述和特征点

文章目录 一&#xff1a;图像描述概述&#xff08;1&#xff09;图像描述&#xff08;2&#xff09;描述子 二&#xff1a;特征点&#xff08;1&#xff09;Moravec角点检测A&#xff1a;原理B&#xff1a;程序 &#xff08;2&#xff09;Harris角点检测A&#xff1a;原理B&…...

SSM框架的学习与应用(Spring + Spring MVC + MyBatis)-Java EE企业级应用开发学习记录(第五天)MyBatis的注解开发

SSM框架的学习与应用(Spring Spring MVC MyBatis)-Java EE企业级应用开发学习记录&#xff08;第五天&#xff09;MyBatis的注解开发 ​ 昨天我们深入学习了MyBatis多表之间的关联映射&#xff0c;了解掌握了一对一关联映射&#xff0c;一对多关联映射&#xff0c;嵌套查询方…...

VBA技术资料MF48:VBA_在Excel中将列号与字母转换

【分享成果&#xff0c;随喜正能量】除非自己的认知获得了改变和刷新&#xff0c;否则&#xff0c;人们常说的“顺应自己的内心”&#xff0c;顺的不过是一颗旧心&#xff0c;一颗惯性的&#xff0c;充满了各种习性的套路之心。与“顺应自己的内心”恰恰相反&#xff0c;人要用…...

LeetCode-160. 相交链表

这是一道真的非常巧妙的题&#xff0c;题解思路如下&#xff1a; 如果让他们尾端队齐&#xff0c;那么从后面遍历就会很快找到第一个相交的点。但是逆序很麻烦。 于是有一个巧妙的思路诞生了&#xff0c;如果让短的先走完自己的再走长的&#xff0c;长的走完走短的&#xff0c;…...

微信小程序如何实现页面传参和页面传递多个参数

前言 只要你的小程序超过一个页面那么可能会需要涉及到页面参数的传递&#xff0c;下面我总结了 4 种页面方法。 下面时多个参数页面传参的方式 let loveJSON.stringify(this.data.totle);let youJSON.stringify(this.data.totleId)let csdnJSON.stringify(this.data.totleP…...

ChatGPT⼊门到精通(3):ChatGPT 原理

OpenAI在2022年11⽉份发布ChatGPT&#xff0c;强⼤的⽂字对话、创意写作能⼒&#xff0c;全球掀起了⼀ 波AI浪潮。本⽂对ChatGPT的技术原理、厉害之处、可能的落地⽅向等⽅⾯进⾏了全⾯ 的解析&#xff0c;看完后会对ChatGPT有更深⼊的了解。 ⼀、前⾔ 2022年11⽉30⽇&#x…...

nginx配置keepalive长连接

nginx之keepalive详解与其配置_keepalive_timeout_恒者走天下的博客-CSDN博客 为什么要有keepalive? 因为每次建立tcp都要建立三次握手&#xff0c;消耗时间较长&#xff0c;所以为了减少tcp建立连接需要的时间&#xff0c;就可以设置keep_alive长连接。 nginx中keep_alive对…...

Thread.enumerate方法

Thread.enumerate方法的作用是将当前线程所对应的的线程组包含的所有线程放入一个数组 参见源码注释 /*** Copies into the specified array every active thread in the current* threads thread group and its subgroups. This method simply* invokes the {@link java.lan…...

* 号靠近数据类型,和靠近变量名, 号靠近数据类型,和靠近变量名,有什么区别

文章目录 一、int* age 和 int *age&#xff0c;* 号靠近数据类型&#xff0c;和靠近变量名&#xff0c;有什么区别&#xff1a;1. int* age:2. int *age: 二、int& age 和 int &age&#xff0c;& 号靠近数据类型&#xff0c;和靠近变量名&#xff0c;有什么区别&a…...

为了做好农业,拼多多请来顶尖农业专家当独立董事

8月29日&#xff0c;拼多多发布截至6月30日的2023年第二季度业绩报告。财报显示&#xff0c;拼多多集团今年第二季度收入为523亿元&#xff0c;同比增长66%&#xff0c;远超市场预期。 财报发布的同时&#xff0c;拼多多还宣布&#xff0c;其董事会已聘任荷兰瓦赫宁根大学终身…...

Linux服务器安装部署MongoDB数据库 – 【无公网IP远程连接】

文章目录 前言1.配置Mongodb源2.安装MongoDB数据库3.局域网连接测试4.安装cpolar内网穿透5.配置公网访问地址6.公网远程连接7.固定连接公网地址8.使用固定公网地址连接 前言 MongoDB是一个基于分布式文件存储的数据库。由 C 语言编写&#xff0c;旨在为 WEB 应用提供可扩展的高…...

Python+PIL+qrcode实现二维码自由—普通二维码+彩色二维码+logo二维码+动态二维码(附完整代码)

有时候我们需要自己制作一个二维码&#xff0c;然后进行打印下来&#xff0c;或者说在二维码中提前写上一段话比如搞笑的话&#xff0c;然后印在衣服上&#xff0c;然后穿出去玩&#xff01;的&#x1f923; 那么今天我们分享一下制作二维码的几种方式&#xff1a; 哎&#x…...

【Spring Data JPA】JPA 常用查询函数

文章目录 前言函数查询表格 前言 函数查询的表格参考了官网的 2.7.3 版本的文档&#xff0c;JPA 的这种函数式查询方法改动不大&#xff0c;如果想知道更多的复杂查询&#xff0c;可以参考这篇文章 【Spring Data JPA】基于 JpaRepository 增删改查 官方文档地址 Spring Data…...

Visual Studio 2022的MFC框架——AfxWinMain全局对象和InitInstance函数

我是荔园微风&#xff0c;作为一名在IT界整整25年的老兵&#xff0c;今天我们来重新审视一下Visual Studio 2022下开发工具的MFC框架知识。 在看这篇帖子前&#xff0c;请先看我的另一篇帖子《Visual Studio 2022的MFC框架——应用程序向导》。 当程序调用了CWinApp类的构造…...

【网络】多路转接——poll | epoll

&#x1f431;作者&#xff1a;一只大喵咪1201 &#x1f431;专栏&#xff1a;《网络》 &#x1f525;格言&#xff1a;你只管努力&#xff0c;剩下的交给时间&#xff01; 书接上文五种IO模型 | select。 poll | epoll &#x1f367;poll&#x1f9c1;认识接口&#x1f9c1;简…...

音视频 ffmpeg命令视频录制(Windows)

先安装dshow软件 Screen Capturer Recorder&#xff0c; 项目地址&#xff1a;https://sourceforge.net/projects/screencapturer/files/ 然后查看可用设备名字&#xff1a;ffmpeg -list_devices true -f dshow -i dummy [dshow 0509d6c0] DirectShow video devices (some ma…...

【拾枝杂谈】从游戏开发的角度来谈谈原神4.0更新

君兮_的个人主页 勤时当勉励 岁月不待人 C/C 游戏开发 Hello,米娜桑们&#xff0c;这里是君兮_&#xff0c;结合最近的学习内容和以后自己的目标&#xff0c;今天又开了杂谈这个新坑&#xff0c;分享一下我在学习游戏开发的成长和自己的游戏理解&#xff0c;当然现在还是一枚…...

QT设置mainwindow的窗口title

QT设置mainwindow的窗口title 在QT程序中&#xff0c;通常会有**aaaa-[bbbbbbb]**这种形式的title&#xff0c;对于刚上手qt的程序员同学&#xff0c;可能会简单的以为修改这种title&#xff0c;就是使用setWindowTitle这个接口&#xff0c;其实只对了一半&#xff0c;这种形式…...

SaaS多租户系统架构设计

前言&#xff1a;多租户是SaaS&#xff08;Software-as-a-Service&#xff09;下的一个概念&#xff0c;意思为软件即服务&#xff0c;即通过网络提供软件服务。SaaS平台供应商将应用软件统一部署在自己的服务器上&#xff0c;客户可以根据工作的实际需求&#xff0c;通过互联网…...

Java自定义捕获异常

需求分析 ElectricalCustomerVO electricalCustomerVO new ElectricalCustomerVO(); electricalCustomerVO.setElcNumber(chatRecordsLog.getDeviceNumber()); List<ElectricalCustomerVO> electricalCustomerlist electricalCustomerMapper.selectElectricalCustomer…...

力扣--数组类题目27. 移除元素

给你一个数组 nums 和一个值 val&#xff0c;你需要 原地 移除所有数值等于 val 的元素&#xff0c;并返回移除后数组的新长度。 示例 1&#xff1a; 输入&#xff1a;nums [3,2,2,3], val 3 输出&#xff1a;2, nums [2,2] 解释&#xff1a;函数应该返回新的长度 2, 并且 n…...

实际并行workers数量不等于postgresql.conf中设置的max_parallel_workers_per_gather数量

1 前言 本文件的源码来自PostgreSQL 14.5&#xff0c;其它版本略有不同 PostgreSQL的并行workers是由compute_parallel_worker函数决定的&#xff0c;compute_parallel_worker是估算扫描所需的并行工作线程数&#xff0c;并不是您在postgresql.conf中设置的max_parallel_work…...

java定位问题工具

一、使用 JDK 自带工具查看 JVM 情况 在我的机器上运行 ls 命令&#xff0c;可以看到 JDK 8 提供了非常多的工具或程序&#xff1a; 接下来&#xff0c;我会与你介绍些常用的监控工具。你也可以先通过下面这张图了解下各种工具的基本作用&#xff1a; 为了测试这些工具&#x…...

【Java】基础入门 (十六)--- 异常

1.异常 1.1 异常概述 异常是指程序在运行过程中出现的非正常的情况&#xff0c;如用户输入错误、除数为零、文件不存在、数组下标越界等。由于异常情况再程序运行过程中是难以避免的&#xff0c;一个良好的应用程序除了满足基本功能要求外&#xff0c;还应具备预见并处理可能发…...

[javaWeb]Socket网络编程

网络编程&#xff1a;写一个应用程序,让这个程序可以使用网络通信。这里就需要调用传输层提供的 api。 Socket套接字 传输层提供协议&#xff0c;主要是两个: UDP和TCP 提供了两套不同的 api&#xff0c;这api也叫做socket api。 UDP和 TCP 特点对比&#xff1a; UDP: 无连…...

<MySon car=“宝马“ :money=“money“></MySon>有没有冒号

为什么car"宝马"没有&#xff1a; 但是 :money"money"就有&#xff1a; <script setup> import {ref} from vue import MySon from /components/MySon.vueconst money ref(100) </script><template><h3>father</h3><My…...

netty(三):NIO——多线程优化

NIO多线程优化 使用Boss线程来处理accepct事件使用Worker线程来处理读写事件&#xff0c;可以创建多个worker线程 package com.review;import lombok.extern.slf4j.Slf4j;import java.io.IOException; import java.net.InetSocketAddress; import java.nio.channels.*; impor…...

Linux操作系统--linux概述

1.Linux概述 Linux&#xff0c;全称GNU/Linux&#xff0c;是一种免费使用和自由传播的类UNIX操作系统&#xff08;OS&#xff09;。简单的说就是一种操作系统。在日常中常见的操作系统有一下三种: 2.linux起源和背景 (1).linux的诞生 linux操作系统是由李纳斯托瓦兹&#xf…...

数组中出现次数超过一半的数字

⭐️ 题目描述 &#x1f31f; OJ链接&#xff1a;数组中出现次数超过一半的数字 思路&#xff1a; 采用投票计数的方式&#xff0c;我们可以把每个数字都看成一次投票并且计数&#xff0c;那么最后剩下来的就是数组中数字出现次数最多的那一个。比如 { 1,2,3,2,2,2,5,4,2 } &a…...

网络优化工程师,你真的了解吗?

一、5G网络优化工程师到底是什么&#xff1f; 5G&#xff0c;就是我们通常所说的第五代移动通信标准&#xff0c;属于目前最热门的新技术趋势。随着2019年5G技术进入正式的商用阶段&#xff0c;拥有广阔的发展前景&#xff0c;备受瞩目。“5G工程师”这个词是一个概念词&#x…...