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类结构图
这里有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 应运而生了。
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 方法,就一下子能定位到它应该放置的物理位置上。
①、如果这个位置上没有元素,它就可以直接存储在这个位置上,不用再进行任何比较了;
②、如果这个位置上已经有元素了,就调用它的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不是内存地址(严格的说,肯定不是直接取的内存地址)
HashCode存储位置
对象内存布局
当一个对象在堆内存中分配好并且初始化完成之后的结构是什么样的呢?
1、添加对求填充是为了保证对象的总大小是8的整数倍个字节。
2、类型指针占4个字节是因为默认开启了指针压缩,如果不开启指针压缩,则占8个字节
hashCode的值存在Java对象头里的,那么什么是Java对象头呢?Hotspot虚拟机的对象头主要包括两部分数据:**Mark Word(标记字段)、**Class Pointer(类型指针)。其中 Class Pointer是对象指向它的类元数据的指针,虚拟机通过这个指针来确定这个对象是哪个类的实例,Mark Word用于存储对象自身的运行时数据,它是实现轻量级锁和偏向锁的关键。
Mark Word用于存储对象自身的运行时数据,如:哈希码(HashCode)、GC分代年龄、锁状态标志、线程持有的锁、偏向线程 ID、偏向时间戳等
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
完全搜不到这个方法名,只有这个还凑合有点像,那这是个啥呢?
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
先说生成流程,留个印象:
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 ;}
可以通过参数 -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 类是所有类的基类,当一个类没有直接继承某个类时,默认继承Object类Object 类属于 java.lang 包,此包下的所有类在使用时无需手动导入,系统会在程序编译期间自动导入。 思…...

pinia——添加插件——基础积累
问题:是否给pinia添加过插件?具体添加的方式是什么? 在pinia中,我们可以为仓库添加插件,通过添加插件能够扩展以下的内容: 为 store 添加新的属性 定义 store 时增加新的选项 为 store 增加新的方法 包装现…...
软件国产化之殇
今天又看到这么一个帖子讨论一款国产化软件,属实给我震撼到了。 对于国产化产品,一直主打的都是”自研“,难道是我对”自研“这个词的理解有误? 做一个产品,别人开源了,你拿过来使用,你可以说…...
SQLyog问题处理集合
sqlyog 问题处理 1. 错误号码:1049错误: 数据库命令参数参考:数据库命令地址 检查数据库是否存在检查创建的数据库名称 与 要进行连接的数据库名称是否一致; 2. 错误号码:1819错误: MySQL授予远程连接权限时出现: …...

JavaSE【继承和多态】(1)(重点:初始化、pretected封装、组合)
一、继承 继承 (inheritance) 机制 :是面向对象程序设计使代码可以复用的最重要的手段,它允许程序员在保持原有类特 性 的基础上进行扩展,增加新功能 ,这样产生新的类,称 派生类 。 继承呈现了面向对象程序设计的层次结…...

无涯教程-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安装包并解压 执行以下命令: # 创建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
论文:Quasi-eccentricity Error Modeling and Compensation in Vision Metrology 中文:视觉计量中准偏心误差建模与补偿 论文地址:Sci-Hub | Quasi-eccentricity error modeling and compensation in vision metrology. Measurement Scienc…...
ai智能电话机器人是人类的助手和朋友
一直以来,人工智能都是人们关注的热门话题。在以前,说到人工智能,第一想到的是“机器人”,随着人工智能的普及,AI已经渗透到我们生活的每一个角落。现在,说起人工智能,可能会想到“无人驾驶、无…...

应用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 加密原理(问过)HTTP/1.1 新特性HTTP/2.0 与 RPC(问过)GET 和 POST 比较 2、传输层 & TCPTCP三次握手 & 四次挥手(问过)为什么每次TCP 连…...

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

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

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

C语言每日一练------Day(10)
本专栏为c语言练习专栏,适合刚刚学完c语言的初学者。本专栏每天会不定时更新,通过每天练习,进一步对c语言的重难点知识进行更深入的学习。 今日练习题关键字:自除数 除自身以外数组的乘积 💓博主csdn个人主页ÿ…...
发力服务业务,龙湖集团半程领跑赢在“智慧”
成立三十载,龙湖集团一直是房地产行业“特立独行”的存在。 一方面,龙湖在对外战略方面长期量入为出,从不背上过重的“包袱”。 不久前,一则消息引发市场关注:龙湖集团提前偿还17亿元债务,已基本全部还清…...

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

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

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

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

测试微信模版消息推送
进入“开发接口管理”--“公众平台测试账号”,无需申请公众账号、可在测试账号中体验并测试微信公众平台所有高级接口。 获取access_token: 自定义模版消息: 关注测试号:扫二维码关注测试号。 发送模版消息: import requests da…...

Debian系统简介
目录 Debian系统介绍 Debian版本介绍 Debian软件源介绍 软件包管理工具dpkg dpkg核心指令详解 安装软件包 卸载软件包 查询软件包状态 验证软件包完整性 手动处理依赖关系 dpkg vs apt Debian系统介绍 Debian 和 Ubuntu 都是基于 Debian内核 的 Linux 发行版ÿ…...

使用分级同态加密防御梯度泄漏
抽象 联邦学习 (FL) 支持跨分布式客户端进行协作模型训练,而无需共享原始数据,这使其成为在互联和自动驾驶汽车 (CAV) 等领域保护隐私的机器学习的一种很有前途的方法。然而,最近的研究表明&…...

04-初识css
一、css样式引入 1.1.内部样式 <div style"width: 100px;"></div>1.2.外部样式 1.2.1.外部样式1 <style>.aa {width: 100px;} </style> <div class"aa"></div>1.2.2.外部样式2 <!-- rel内表面引入的是style样…...
【Android】Android 开发 ADB 常用指令
查看当前连接的设备 adb devices 连接设备 adb connect 设备IP 断开已连接的设备 adb disconnect 设备IP 安装应用 adb install 安装包的路径 卸载应用 adb uninstall 应用包名 查看已安装的应用包名 adb shell pm list packages 查看已安装的第三方应用包名 adb shell pm list…...
人工智能--安全大模型训练计划:基于Fine-tuning + LLM Agent
安全大模型训练计划:基于Fine-tuning LLM Agent 1. 构建高质量安全数据集 目标:为安全大模型创建高质量、去偏、符合伦理的训练数据集,涵盖安全相关任务(如有害内容检测、隐私保护、道德推理等)。 1.1 数据收集 描…...

手机平板能效生态设计指令EU 2023/1670标准解读
手机平板能效生态设计指令EU 2023/1670标准解读 以下是针对欧盟《手机和平板电脑生态设计法规》(EU) 2023/1670 的核心解读,综合法规核心要求、最新修正及企业合规要点: 一、法规背景与目标 生效与强制时间 发布于2023年8月31日(OJ公报&…...
c# 局部函数 定义、功能与示例
C# 局部函数:定义、功能与示例 1. 定义与功能 局部函数(Local Function)是嵌套在另一个方法内部的私有方法,仅在包含它的方法内可见。 • 作用:封装仅用于当前方法的逻辑,避免污染类作用域,提升…...
node.js的初步学习
那什么是node.js呢? 和JavaScript又是什么关系呢? node.js 提供了 JavaScript的运行环境。当JavaScript作为后端开发语言来说, 需要在node.js的环境上进行当JavaScript作为前端开发语言来说,需要在浏览器的环境上进行 Node.js 可…...

UE5 音效系统
一.音效管理 音乐一般都是WAV,创建一个背景音乐类SoudClass,一个音效类SoundClass。所有的音乐都分为这两个类。再创建一个总音乐类,将上述两个作为它的子类。 接着我们创建一个音乐混合类SoundMix,将上述三个类翻入其中,通过它管理每个音乐…...