关于Object类下所有方法的简单解析
类Object是类层次结构的根类,是每一个类的父类,所有的对象包括数组,String,Integer等包装类,所以了解Object是很有必要的,话不多说,我们直接来看jdk的源码,开始我们的分析之路
1.hashcode()
public native int hashCode();//native说明跟机器有关,跟对象的地址有关
如果我们新建一个类,而hashcode没有被重写的话,那么hashcode返回的值只于对象的地址有关,如果hashcode被重写了,那么就另当别论了,但是如果我们重写了equals()的话,那么hashcode(),一定要重写,至于为什么要重写的话,请参考我这篇博客:http://www.cnblogs.com/yinbiao/p/8110196.html
现在我们来看一下JDk API文档里面关于hashcode的说明
hashCode
的常规协定是:
- 在 Java 应用程序执行期间,在对同一对象多次调用 hashCode 方法时,必须一致地返回相同的整数,前提是将对象进行 equals 比较时所用的信息没有被修改。从某一应用程序的一次执行到同一应用程序的另一次执行,该整数无需保持一致。
- 如果根据 equals(Object) 方法,两个对象是相等的,那么对这两个对象中的每个对象调用
hashCode
方法都必须生成相同的整数结果。 - 如果根据
equals(java.lang.Object)
方法,两个对象不相等,那么对这两个对象中的任一对象上调用 hashCode 方法不 要求一定生成不同的整数结果。但是,程序员应该意识到,为不相等的对象生成不同整数结果可以提高哈希表的性能。
2.equals()
public boolean equals(Object obj) {
return (this == obj);
}
可以看到如果equals()如果没有被重写的话,比较的是对象的地址,String,Integer等包装类里面都重写了该方法,例如String类的equals()方法是比较对象的内容,也就是字符串的,而不是地址
JDK API文档对equals()说明如下:
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
同时这些性质也是我们在重写equals()需要满足的要求
3.getClass()
我们先来看一下JDK API文档对getclass的说明:
返回此 Object
的运行时类。返回的 Class
对象是由所表示类的 static synchronized
方法锁定的对象。
再看一下源码:
public final Class<?> getClass() {
return shadow$_klass_; }
注意到final关键字,说明这个方法是不能被Object的子类重写的,我们在类中调用的getclass()方法都是调用的Object下面的,且文档对此的说明是返回运行时的对象,举个栗子:
1 import java.util.Date;
2 public class Test extends Date
3 {
4 public void test()
5 {
6 System.out.println(super.getClass().getName());
7 }
8 public static void main(String[] args)
9 {
10 new Test().test();
11 }
12 }
输出:Test
有没有觉得很奇怪?输出的竟然是Test,其实一点也不奇怪,我们调用的getClass()是Object的,且它不能被重写,返回的是类运行时的对象,在代码中运行时的类是Test,虽然Test继承与Date,但是如果我们想返回的是Date类,那么我们运行时的对象应该是Date:Date date = new Date();,现在运行时的类就是Date,返回的就是Date。
那么现在又有一个问题,getClass()和getName(),好像他们的输出值都是一样的,那是不是他们就没有区别呢?不是的,话不多说,直接撸源码:
public
String getName() {
if
(name ==
null
)
name = getName0();
return
name;
}
public
final
native
Class<?> getClass();
getClass()返回的是一个对象,而getName返回的是一个字符串,虽然他们输出都是一样,但是我们必须知道控制台输出任何对象,非基础类型都会转化为字符串输出,所以二者的输出虽然看上去是一样的,但是有很大的区别
4.clone()
JDK API文档对说明:
创建并返回此对象的一个副本。“副本”的准确含义可能依赖于对象的类。这样做的目的是,对于任何对象 x,表达式:
x.clone() != x
为 true,表达式:
x.clone().getClass() == x.getClass()
也为 true,但这些并非必须要满足的要求。一般情况下:
x.clone().equals(x)
为 true,但这并非必须要满足的要求。
我们再来看看源码:
protected native Object clone() throws CloneNotSupportedException;
我的理解是clone()就是将这个对象重新复制一遍,变成两个对象,都有着自己的内存空间,而不是只建立一个引用,指向同一片内存空间,注意到x.clone() != x,说明二者是两个对象,二者的内存空间都是不同的,所以将二者的地址进行比较的时候,肯定是不相等的,注意到x.clone().getClass() == x.getClass(),说明二者运行时的类都是相同的,注意到x.clone().equals(x),虽然不要求一定使这个等式为True,但是一般情况下都是成立的,比如String包装类,就是说明这二者的字符串是一样的,但是如果是自定义类等其他类且equals()没有被重写的话,那么这个等式是不成立的,因为equals()没有被重写,那么使用的就是Object下的equals(),比较的是对象的地址,所以等式不成立
那么为什么要有clone()呢?
我们现在先看一下clone()与=的区别,举个栗子:
有一个Car类
Car c1 = new Car();
Car c2 = c1;
这两句事实上只创建了一个对象。只不过c1和c2指向了同一个对象。
如果上面的两句改为:
Car c1 = new Car();
Car c2 = c1.clone();
那么就有了两个对象,而且这两个对象的内容是一样的。(所有的属性值相同)
假如现在我们有一个类,它有100个属性,那么我们如果这样:a.name=b.name;这样的话我们需要进行100此操作,这样也太不艺术了。。。。,所以我们就有了clone()方法,所有操作一次性完成!
那么我们现在来看一下clone的具体操作:
要知道我们的java语言取消了指针这个东东,导致了很多人都忽略了对象和引用的区别,java不能通过简单的赋值来解决对象的复制问题
看看下面这个栗子也可以解释我们为什么要有clone()
1 A a1=new A();
2 A a2=new A();
3 a1.name="a1";
4 a2=a1;
5 a2.name="a2";
6 System.out.println("a1.name="+a1.name);
7 System.out.println("a2.name="+a2.name);
8 //此时输出的结果是:
9 //a1.name=a2
10 //a2.name=a2
如果我们要用a2保存a1对象的数据,但又不希望a2对象数据被改变时不影响到a1。实现clone()方法是其一种最简单,也是最高效的手段。下面我们来看看clone的分类
浅层clone():用于类属性只有Java基本类型和String类型的时候
深层clone:当类属性有数组和复杂类型的时候
彻底clone:当类属性有更加复杂的类型的时候,比如Vector对象容器
下面我们用栗子来进行说明:
浅层复制
package test; public class A implements Cloneable{
public String name;
public Object clone() {
A obj = null;
try {
obj = (A)super.clone();
} catch (CloneNotSupportedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return obj;
} public static void main(String[] args) {
// TODO Auto-generated method stub
A a1 = new A();
A a2 = new A();
a1.name="a1";
a2 = (A) a1.clone();
a2.name = "a2";
System.out.println("a1.name="+a1.name);
System.out.println("a2.name="+a2.name);
}
/*
* 结果:
* a1.name=a1
* a2.name=a2
*/
}
这就是所谓的浅层复制,我们可以看到我们的name是字符串型的,如果我们的name是字符串数组类型的,仍然用浅层复制,那么又会有什么问题呢?看看下面的栗子:
package test; public class A implements Cloneable{
public String name[];
public A() {
name = new String[2];
}
public Object clone() {
A obj = null;
try {
obj = (A)super.clone();
} catch (CloneNotSupportedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return obj;
} public static void main(String[] args) {
// TODO Auto-generated method stub
A a1 = new A();
A a2 = new A();
a1.name[0]="a";
a1.name[1]="1";
a2=(A)a1.clone();
a2.name[0]="b";
a2.name[1]="1";
System.out.println("a1.name="+a1.name);
System.out.println("a1.name="+a1.name[0]+a1.name[1]);
System.out.println("a2.name="+a2.name);
System.out.println("a2.name="+a2.name[0]+a2.name[1]);
/*
* 结果:
* a1.name=[Ljava.lang.String;@7852e922
* a1.name=a1
* a2.name=[Ljava.lang.String;@4e25154f
* a2.name=a2
*/
/*分析:
* 可以看到我们的a1.name输出的是name数组的地址
* 且输出的a1.name和a2.name都是b1,说明在进行对象复制的时候对name数组只是复制了它的地址,如果要解决这种情况的话,我们就要采用深层复制
*/
}
}
深层clone()
package test; public class A implements Cloneable{
public String name[];
public A() {
name = new String[2];
}
public Object clone() {
A obj = null;
try {
obj = (A) super.clone();
obj.name=(String[])name.clone();//这是实现方式
} catch (CloneNotSupportedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return obj;
} public static void main(String[] args) {
// TODO Auto-generated method stub
A a1 = new A();
A a2 = new A();
a1.name[0]="a";
a1.name[1]="1";
a2=(A)a1.clone();
a2.name[0]="b";
a2.name[1]="1";
System.out.println("a1.name="+a1.name);
System.out.println("a1.name="+a1.name[0]+a1.name[1]);
System.out.println("a2.name="+a2.name);
System.out.println("a2.name="+a2.name[0]+a2.name[1]);
/*
* 结果:
* a1.name=[Ljava.lang.String;@7852e922
* a1.name=a1
* a2.name=[Ljava.lang.String;@4e25154f
* a2.name=b1
*/
/*分析:
* 可以看到我们输出的a1.name=a1b1.name=b1,说明我们在复制对象的时候数组name的负责传递的不是地址,而是开辟了另外的一片储存空间,而不是两个引用指向同一片内存空间
*
*/
}
}
这就是深层复制,可是如果我们类的属性有Vector等储存对象地址的容器的时候,我们就必须进行彻底的clone(),栗子如下:
彻底clone()
package test; public class B implements Cloneable{
public String x="520";
public Object clone() {
B obj = null;
try {
obj = (B) super.clone();
} catch (CloneNotSupportedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return obj;
}
} package test; import java.util.Vector;
public class A implements Cloneable{
public String name[];
public Vector<B> clab;
public A() {
name = new String[2];
clab=new Vector<B>();
}
public Object clone() {
A obj = null;
try {
obj = (A) super.clone();
obj.name=(String[])name.clone();//这是实现方式
obj.clab=new Vector<B>();
for(int i=0;i<clab.size();i++) {//彻底clone
B temp = (B)clab.get(i).clone();//class B 也必须实现clone方法
obj.clab.add(temp);
}
} catch (CloneNotSupportedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return obj;
} public static void main(String[] args) {
// TODO Auto-generated method stub
A a1 = new A();
A a2 = new A();
B b1 = new B();
a1.name[0]="a";
a1.name[1]="1";
a1.clab.add(b1); a2=(A)a1.clone(); System.out.println("a2.name="+a2.name);
System.out.println("a2.name="+a2.name[0]+a2.name[1]);
System.out.println(a2.clab.get(0).x); /*
* 结果:
* a1.name=[Ljava.lang.String;@7852e922
* a1.name=a1
* 520
*/
/*分析:
* 如果类的属性里面有对象容器,那么必须采用彻底负责
*/
}
}
5.toString()
先看看文档对此的说明:
返回该对象的字符串表示。通常,toString
方法会返回一个“以文本方式表示”此对象的字符串。结果应是一个简明但易于读懂的信息表达式。建议所有子类都重写此方法。
总结一下就是返回这个对象的字符串表现形式
下面我们看看源码:
public String toString() {
return getClass().getName() + "@" + Integer.toHexString(hashCode());
}
可以看到返回的字符串是由对象的名称和对象的hashcode组成的
6.finalize()
我们先来看一下文档对此的说明:
当垃圾回收器确定不存在对该对象的更多引用时,由对象的垃圾回收器调用此方法。子类重写 finalize
方法,以配置系统资源或执行其他清除。
finalize 的常规协定是:当 JavaTM 虚拟机已确定尚未终止的任何线程无法再通过任何方法访问此对象时,将调用此方法,除非由于准备终止的其他某个对象或类的终结操作执行了某个操作。finalize 方法可以采取任何操作,其中包括再次使此对象对其他线程可用;不过,finalize 的主要目的是在不可撤消地丢弃对象之前执行清除操作。例如,表示输入/输出连接的对象的 finalize 方法可执行显式 I/O 事务,以便在永久丢弃对象之前中断连接。
Object 类的 finalize 方法执行非特殊性操作;它仅执行一些常规返回。Object 的子类可以重写此定义。
protected void finalize() throws Throwable { }
}
总结一下就是:当某个对象被Java虚拟机回收的时候执行这个方法,如果我们想在这个对象被回收的时候做点什么,比如再次使此对象对其他线程可用,那么就需要我们重写这个方法。
7.notify(),notifyAll(),wait()
这3个方法就是专为线程而生的,要实现线程的安全我们有synchronized,而这3个方法就为线程的同步而准备的
首先我们先来了解一下线程同步和线程互斥
线程同步:就是协调步调,一个任务,多个线程完成,线程的执行有先后顺序,这个就叫线程同步
线程互斥:A线程调用a资源的时候B线程就不能调用a资源,得等到A线程调用该资源结束之后,B线程才能调用资源a,这个就叫线程互斥
wait():如果对象调用了该方法,就会使持有该对象的进程把对象的控制器交出去,然后处于等待状态
notify():如果对象调用了该方法,就会随机通知某个正在等待该对象控制器的线程可以继续运行
notifyAll():如果对象调用了该方法,就会通知所有正在等待该对象控制器的线程可以继续运行
同时wait还可以指定等待的时间长度,如果是默认的话,就一直等待直到被通知
错误的地方,欢迎兄弟们拍砖!!!!!!!
大家一起交流一起进步
关于Object类下所有方法的简单解析的更多相关文章
- [ 转载 ] Java基础10--关于Object类下所有方法的简单解析
关于Object类下所有方法的简单解析 类Object是类层次结构的根类,是每一个类的父类,所有的对象包括数组,String,Integer等包装类,所以了解Object是很有必要的,话不多说,我们直 ...
- Java面试系列第2篇-Object类中的方法
Java的Object是所有引用类型的父类,定义的方法按照用途可以分为以下几种: (1)构造函数 (2)hashCode() 和 equals() 函数用来判断对象是否相同 (3)wait().wai ...
- Object类及其equals()方法
== : 1.基本数据类型:根据基本数据类型的值判断是否相等,相等返回true,反之返回false 注意:两端数据类型可以不同, 在不同的情况下,也可以返回true 2.引用数据类型:Object ...
- Java基础知识强化26:Object类之hashCode()方法、getClass()方法
1. Object类的hashCode()方法,如下: public int hashCode():返回该对象的哈希码值,这个值和地址值有关,但是不是实际地址值(哈希码值是根据实际地址值转化过来的 ...
- object类的equals方法简介 & String类重写equals方法
object类中equals方法源码如下所示 public boolean equals(Object obj) { return this == obj; } Object中的equals方法是直接 ...
- Java中的静态方法和实例方法的调用的理解(不同的类下的方法调用)
public class MethodCall { public static void main(String[] args) { Test.sayStatic(); Test test = new ...
- Java Object类及其equals方法
基本概念: Object类位于java.lang包中,java.lang包包含着Java最基础和核心的类,在编译时会自动导入: Object类是所有Java类的祖先.每个类都使用 Object 作为超 ...
- Object类中通用方法之:toString()方法
1.Java所有的对象都是Object类的实例,都可以直接调用该类中定义的方法,这些方法称为通用方法 2.toString()方法即为Object类中定义的通用方法之一 3.平时我们如果在控制台直接打 ...
- Java中的Object类的toString()方法,equals()方法
Object类是所有类的父类,若没有明确使用extends关键字明确表示该类继承哪个类,那么它就默认继承Object类,也就可以使用Object中的方法: 1.toString 如果输出一个对象的时候 ...
随机推荐
- Django 入门案例开发(下)——创建项目应用及模型类
前面两章是在已经开发好的项目上用来描述环境和业务,这一章创建一个全新的项目来用作开发,你可以跟着我的步骤进行开发,如果有不理解的地方可以给我留言. 今天的任务是创建好项目和用户(users)应用及让它 ...
- HTML5浏览器端图片预览&生成Base64
本文主要介绍如何通过拖拽方式在浏览器端实现图片预览,并生成图片的Base64编码. 工具链接:图片转Base64. 首先介绍一下FileReader, FileReader对象允许浏览器使用File或 ...
- laravel 表单验证 Exists 规则的基本使用方法
public function rules(){ return [ 'm_pushing_frequency_level_id' => 'integer|required|exists:m_pu ...
- States字段的使用规范
背景 为了统一数据库表的状态字段,统一数据库表设计,简化字段在程序开发中的使用方式. 解决方式 States对应位域枚举StatesFlags. /// <summary> /// 数据状 ...
- css选择器的优先级问题
当我们写页面的时候,不知道你会不会产生这样的问题,为什么我给他添加的这条样式分明已经选择到我要给的元素了,但是他的样式并没有生效,那是为什么呢? 定义的属性有冲突时,浏览器会选择用那一套样式呢,下面来 ...
- 【javaFX学习】(二) 面板手册--1
找了好几个资料,没找到自己想要的,自己写个列表吧,方便以后用的时候挑选,边学边记.以学习笔记为主,所以会写的会偏个人记忆性.非教程,有什么问题一起讨论啊. 各个不同的控件放入不同的面板中有不同的效果, ...
- nginx的基础应用
nginx的基础应用 一.简介 今天我们将介绍一些nginx的简单应用,启动.停止nginx,重载nginx的配置,nginx配置文件的格式,如何配置nginx服务静态资源,如何配置nginx作为反向 ...
- centos 下安装pptp (vpn) 的方法
废话少说 01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 3 ...
- 从零开始构建docker基础镜像
段子 今年基本已经结束了,我问了很多朋友今年挣钱了没?大多朋友都有挣,而且挣得五花八门:有挣个屁的,有挣个锤子的,有挣个毛的,更有甚者挣个妹的,奢侈之极!最恐怖的是挣个鬼的!有的还可以,挣个球,下午我 ...
- Android开发之漫漫长途 Ⅷ——Android Binder(也许是最容易理解的)
该文章是一个系列文章,是本人在Android开发的漫漫长途上的一点感想和记录,我会尽量按照先易后难的顺序进行编写该系列.该系列引用了<Android开发艺术探索>以及<深入理解And ...