object 的方法(object类的作用)
object 的方法(object类的作用),新营销网红网本栏目通过数据整理汇集了object 的方法(object类的作用)相关信息,下面一起看看。
一.导言
对象是所有java类的基类,是整个类继承结构的顶层,也是最抽象的类。大家都用toString(),equals(),hashCode(),waite(),notify(),getClass()等。每天都是。可能他们没有意识到自己是属于Object的,也没有去看看Object里面还有什么,去思考为什么要放入Object。这篇文章是关于每一个具体的函数,重写规则和我自己的一些理解。
二、对象详解
Object包含registerNatives()、getClass()、hashCode()、equals()、clone()、toString()、notifyAll()、wait(long)、wait(long,int)、wait()、finalize()这个顺序是按照Object类中定义的顺序列出的,我按照这个顺序依次解释。
1.1、区域化()
public class object { privatestatingnativeoidregisternatives();static { register natives();}}什么鬼?哈哈,刚看到这个,一脸懵。从名字上看,这是注册native (local,JVM实现,底层C/C实现)。它登记在谁的名下?,这是针对JVM的。当程序调用native时,JVM可以找到这些较低层来调用。
对象中的本机,并使用registerNatives()将其注册到JVM。(这属于JNI的范畴。玖龙还不知道。有兴趣的可以自行查阅。)
staticjnitivemethodmodes[]={ { 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},};为什么要用static,放在静态块里?
上一篇文章整理了类的加载过程,我们知道在初始化类的时候,从父类到当前类的类变量和类初始化块中的类变量都会按照定义的顺序放入clinit,以保证父类的类变量和子类的初始化必须先于子类。所以子类调用对应的native,比如计算hashCode,肯定可以调用JVM的native。
1.2、getClass()
Public Native Class GetClass():这是一个公共类,我们可以通过对象直接调用它。
类加载的第一个阶段是将. class文件加载到内存中并生成java.lang.Class对象的过程。GetClass()就是获取这个对象,这个对象就是当前类在运行时的所有信息。这是三种反思方式之一。
1.2.1、反思有三种方式
对象的GetClass();
类名。类;
class . forname();
classextendsObjectTest { privatewidprivatetest(string str){ system . out . println(str);} publicfoidsay(string str){ system . out . println(str);} } publicclassObjectTest { publicstaticvoidmain(String[]args)throws exception { object test=new();//获取对象运行的类对象类?extendsObjectTest aClass=。getClass();system . out . println(a class);//getDeclaredMethod这个可以得到一切,包括private method private test=a class . getDeclaredMethod(private test,string . class);//取消java访问修饰符的限制。private test . set accessible(true);Test.invoke (aclass.newinstance()、privatemethodtest//getmethod只能得到public method say=a class . get method(say,string . class);Say.invoke (aclass.newinstance()、hello world } }//输出结果//classtest。//PrivateMethodTest//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。
publicclassHashCodeTest{privateintage;privateStringname;@OverridepublicinthashCode(){Object[]a=Stream.of(age,name).toArray();intresult=1;for(Objectelement:a){result=31result+(element==null?0:element.hashCode());}returnresult;}}
推荐使用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 ,子类可重写。
publicclassObject{publicbooleanequals(Objectobj){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 。
publicclassEqualsTest{privateintage;privateStringname;//省略get、set、构造函数等@Overridepublicbooleanequals(Objecto){//先判断是否为同一对象if(this==o){returntrue;}//再判断目标对象是否是当前类及子类的实例对象//注意instanceof包括了判断为null的情况,如果o为null,则返回falseif(!(oinstanceof)){returnfalse;}that=()o;returnage==that.age Objects.equals(name,that.name);}publicstaticvoidmain(String[]args)throwsException{EqualsTest1equalsTest1=newEqualsTest1(23, 9龙 EqualsTest1equalsTest12=newEqualsTest1(23, 9龙 EqualsTest1equalsTest13=newEqualsTest1(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。
publicclassCloneTestimplementsCloneable{privateintage;privateStringname;//省略get、set、构造函数等@OverrideprotectedCloneTestclone()throwsCloneNotSupportedException{return(CloneTest)super.clone();}publicstaticvoidmain(String[]args)throwsCloneNotSupportedException{CloneTestcloneTest=newCloneTest(23, 9龙 CloneTestclone=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 对成员变量如果是引用类型,进行是浅拷贝。
那什么是浅拷贝?对应的深拷贝?
浅拷贝拷贝的是引用。
深拷贝新开辟内存空间,进行值拷贝。
那如果我们要进行深拷贝怎么办呢?看下面的例子。
classPersonimplementsCloneable{privateintage;privateStringname;//省略get、set、构造函数等@OverrideprotectedPersonclone()throwsCloneNotSupportedException{Personperson=(Person)super.clone();//name通过new开辟内存空间person.name=newString(name);returnperson;}}publicclassCloneTestimplementsCloneable{privateintage;privateStringname;//增加了person成员变量privatePersonperson;//省略get、set、构造函数等@OverrideprotectedCloneTestclone()throwsCloneNotSupportedException{CloneTestclone=(CloneTest)super.clone();clone.person=person.clone();returnclone;}publicstaticvoidmain(String[]args)throwsCloneNotSupportedException{CloneTestcloneTest=newCloneTest(23, 9龙 Personperson=newPerson(22, 路飞 cloneTest.setPerson(person);CloneTestclone=cloneTest.clone();System.out.println(clone==cloneTest);System.out.println(cloneTest.getAge()==clone.getAge());System.out.println(cloneTest.getName()==clone.getName());PersonclonePerson=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值。
publicclassObject{publicStringtoString(){returngetClass().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作为超时时间;
publicfinalvoidwait()throwsInterruptedException{wait(0);}publicfinalnativevoidwait(longtimeout)throwsInterruptedException;publicfinalvoidwait(longtimeout,intnanos)throwsInterruptedException{if(timeout 0){thrownewIllegalArgumentException( timeoutvalueisnegative }if(nanos 0
nanos 999999){thrownewIllegalArgumentException( nanosecondtimeoutvalueoutofrange }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龙一发工资,就被女朋友给取走了。
//账户publicclassAccount{privateStringaccountNo;privatedoublebalance;privatebooleanflag=false;publicAccount(){}publicAccount(StringaccountNo,doublebalance){this.accountNo=accountNo;this.balance=balance;}/取钱 @paramdrawAmount取款金额/publicsynchronizedvoiddraw(doubledrawAmount){try{if(!flag){//如果flag为false,表明账户还没有存入钱,取钱 阻塞wait();}else{//执行取钱操作System.out.println(Thread.currentThread().getName()+ 取钱 +drawAmount);balance-=drawAmount;//标识账户已没钱flag=false;//唤醒其他线程notify();}}catch(InterruptedExceptione){e.printStackTrace();}}publicsynchronizedvoiddeposit(doubledepositAmount){try{if(flag){//如果flag为true,表明账户已经存入钱,取钱 阻塞wait();}else{//存钱操作System.out.println(Thread.currentThread().getName()+ 存钱 +depositAmount);balance+=depositAmount;//标识账户已存入钱flag=true;//唤醒其他线程notify();}}catch(InterruptedExceptione){e.printStackTrace();}}}//取钱者publicclassDrawThreadextendsThread{privateAccountaccount;privatedoubledrawAmount;publicDrawThread(Stringname,Accountaccount,doubledrawAmount){super(name);this.account=account;this.drawAmount=drawAmount;}@Overridepublicvoidrun(){//循环6次取钱for(inti=0;i 6;i++){account.draw(drawAmount);}}}//存钱者publicclassDepositThreadextendsThread{privateAccountaccount;privatedoubledepositAmount;publicDepositThread(Stringname,Accountaccount,doubledepositAmount){super(name);this.account=account;this.depositAmount=depositAmount;}@Overridepublicvoidrun(){//循环6次存钱操作for(inti=0;i 6;i++){account.deposit(depositAmount);}}}//测试publicclassDrawTest{publicstaticvoidmain(String[]args){Accountbrady=newAccount( 9龙 ,0);newDrawThread( 女票 ,brady,10).start();newDepositThread( 公司 ,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() 时出现异常,垃圾回收机制不会报告异常,程序继续执行。
publicclassFinalizeTest{privatestaticFinalizeTestft=null;publicvoidinfo(){System.out.println( 测试资源清理得finalize }publicstaticvoidmain(String[]args){//创建FinalizeTest对象立即进入可恢复状态newFinalizeTest();//通知系统进行垃圾回收System.gc();//强制回收机制调用可恢复对象的finalize() //Runtime.getRuntime().runFinalization();System.runFinalization();ft.info();}@Overridepublicvoidfinalize(){//让ft引用到试图回收的可恢复对象,即可恢复对象重新变成可达ft=this;thrownewRuntimeException( 出异常了,你管不管啊 }}//输出结果//测试资源清理得finalize
我们看到,finalize() 将可恢复对象置为了可达对象,并且在finalize中抛出异常,都没有任何信息,被忽略了。
1.8.1、对象在内存中的状态
对象在内存中存在三种状态
可达状态有引用指向,这种对象为可达状态;
可恢复状态失去引用,这种对象称为可恢复状态;垃圾回收机制开始回收时,回调用可恢复状态对象的finalize() (如果此 让此对象重新获得引用,就会变为可达状态,否则,会变为不可大状态)。
不可达状态彻底失去引用,这种状态称为不可达状态,如果垃圾回收机制这时开始回收,就会将这种状态的对象回收掉。
1.8.2、垃圾回收机制
垃圾回收机制只负责回收堆内存种的对象,不会回收任何物理资源(例如数据库连接、网络IO等资源);
程序无法精确控制垃圾回收的运行,垃圾回收只会在合适的时候进行。当对象为不可达状态时,系统会在合适的时候回收它的内存。
在垃圾回收机制回收任何对象之前,总会先调用它的finalize() ,该 可能会将对象置为可达状态,导致垃圾回收机制取消回收。
1.8.3、强制垃圾回收
上面我们已经说了,当对象失去引用时,会变为可恢复状态,但垃圾回收机制什么时候运行,什么时候调用finalize 无法知道。虽然垃圾回收机制无法精准控制,但java还是提供了 可以建议JVM进行垃圾回收,至于是否回收,这取决于虚拟机。但似乎可以看到一些效果。
publicclassGcTest{publicstaticvoidmain(String[]args){for(inti=0;i i++){//没有引用指向这些对象,所以为可恢复状态newGcTest();//强制JVM进行垃圾回收(这只是建议JVM)System.gc();//Runtime.getRuntime().gc();}}@Overridepublicvoidfinalize(){System.out.println( 系统正在清理GcTest资源。。。。 }}//输出结果//系统正在清理GcTest资源。。。。//系统正在清理GcTest资源。。。。
System.gc(),Runtime.getRuntime().gc()两个 作用一样的,都是建议JVM垃圾回收,但不一定回收,多运行几次,结果可能都不一致。
三、
本篇举例讲解了Objec中的所有 的作用、意义及使用,从java最基础的类出发,感受java设计之美吧。我是不会高诉大家,这好像面试也会问的【摊手】。
欢迎工作一到五年的Java工程师朋友们加入Java程序员开发 721575865
群内提供免费的Java架构学习资料(里面有高可用、高并发、高性能及分布式、Jvm性能调优、Spring源码,MyBatis,Netty,Redis,Kafka,Mysql,Zookeeper,Tomcat,Docker,Dubbo,Nginx等多个知识点的架构资料)合理利用自己每一分每一秒的时间来学习提升自己,不要再用 没有时间“来掩饰自己思想上的懒惰!趁年轻,使劲拼,给未来的自己一个交代!
objectobject(Object类图文详解)object(Object类有哪些方法?)
江南水乡在哪里(真正的江南水乡在苏南浙北)
阅兵几点开始(俄罗斯举行胜利日阅兵)
台海危机1996(回顾1996年台海危机)
上海世贸(上海世茂成功出售伦敦大楼)
香油是芝麻油吗(香油、芝麻油是同一种物质?)
飞天壁画(五代·平顺大云院壁画《飞天图》)
全球人口总数(世界人口格局如何变化?)
20马赫有多快(20马赫到底有多快?)
日元换算人民币计算器(人民币与俄罗斯卢布 日元 汇率?)
十大品牌机油(德能名列润滑油十大品牌排行榜)
七夕节什么时候(七夕节的来源以及中国传统情人节)
苹果官网序列号查询真伪(iphone13怎么辨别真假是否是正品)
更多object 的方法(object类的作用)相关信息请关注本文章,本文仅仅做为展示!