关于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 如果输出一个对象的时候 ...
随机推荐
- Shiro SpringMVC 非maven HelloWorld
项目用到Shiro就从网上找一些案例看看吧,结果看了很多都是maven的,没有办法就自己弄了一个.废话不多说,原理自己找开始上菜. 配置web.xml <?xml version="1 ...
- Oracle漏洞分析(tns_auth_sesskey)
p216 Oracle漏洞分析: 开启oracle: C:\oracle\product\\db_1\BIN\sqlplus.exe /nolog conn sys/mima1234 as sysdb ...
- MyBatis《2》
MyBatis入参考文档:http://mybatis.org/mybatis-3/zh/ 1.properties 属性 1.在MyBatis配置文件中引用属性文件 MyBatis允许在 ...
- CSS实现模糊效果
HTML代码如下: <body> <h1>body设置了模糊效果</h1> <div id="aa"></div> &l ...
- 《Linux命令行与shell脚本编程大全》第十八章 图形化桌面环境中的脚本编程
18.1 创建文本菜单 直接上例子吧: 1 #!/bin/bash 2 function menu 3 { 4 clear 5 echo 6 ...
- php简单实现发微博动态
首先,肯定是注册成为开发者新浪微博开放平台 选择网站应用,填写一些基本信息 填完后在'我的应用'中,会看到刚创建的应用信息,我们只是简单的测试一下,所以其他复杂的注册信息都不用填写,有这些就够了 很重 ...
- webMagic+RabbitMQ+ES爬取京东建材数据
本次爬虫所要爬取的数据为京东建材数据,在爬取京东的过程中,发现京东并没有做反爬虫动作,所以爬取的过程还是比较顺利的. 为什么要用WebMagic: WebMagic作为一款轻量级的Java爬虫框架,可 ...
- jq获取今天、昨天、一周时间
不少后台文章管理系统就有今天.明天.一周内.全部的分类展示,用Jquery获取今天.明天.一周内的时间节点(如下图) html页面代码: <div class="date-list-c ...
- 运行期以索引获取tuple元素-C++11之2
//运行期以索引获取tuple元素-C++11之2 //需支持C++11及以上标准的编译器,VS2017 15.5.x.CodeBlocks 16.01 gcc 7.2 //参见<深入应用C++ ...
- 剑指Offer_4_二维数组中的查找
题目描述 在一个二维数组中,每一行都按照从左到右递增的顺序排序,每一列都按照从上到下递增的顺序排序.请完成一个函数,输入这样的一个二维数组和一个整数,判断数组中是否含有该整数. ...