`
supportopensource
  • 浏览: 521856 次
  • 性别: Icon_minigender_1
  • 来自: 杭州
社区版块
存档分类
最新评论

Java类的共同父类Object

阅读更多
java.lang.Object类是所有Java类的最高层次父类,该类中没有定义任何属性,方法也只有几个,但正是这些方法提供了面向对象编程技术的基本机制,下面将分别介绍:

1、hashCode()方法

hashCode()方法的格式如下:
public int hashCode()

其功能是返回当前对象的哈希码(HashCode)数值,哈希码可以理解为系统为每个Java对象自动创建的整型编号,任何两个不同的Java对象的哈希码一定不同,而在Java应用程序的一次执行期间,在同一个对象上多次调用hashCode()方法时,必须一致的返回相同的整数。这样哈希码就可以起到标识对象的功能,引用类型变量所记录的对象句柄实际上就包含了该对象的哈希码信息。例如:测试hashCode()方法
源文件:Person.java
public class Person{
	private int age;
	public Person(int age){
		this.age = age;
	}
	public void setAge(int age){
		this.age = age;
	}
	public int getAge(){
		return age;
	}
}

源文件:TestHashCode.java
public class TestHashCode{
	public static void main(String args[]){
		Person p1 = new Person(18);
		Person p2 = new Person(18);
		int handle1 = p1.hashCode();
		System.out.println(handle1);
		System.out.println(Integer.toHexString(handle1));
		System.out.println(p1);
		System.out.println("------------");
		System.out.println(p2.hashCode());
	}
}

程序运行结果如下:
374283533
164f1d0d
Person@164f1d0d
------------
603737068

程序中,变量p1和p2所引用的两个Person类型对象虽然内容相同(属性age的值相同),但其哈希码不同,且保存在不同的空间中。还可看出,使用System.out.println()方法直接打印引用类型数据时输出的信息中包含了对象的哈希码信息,这一点在下面的toString()方法中会做解释。

2、toString()方法

Object类中toString()方法的原始定义如下:
public String toString (){
	return getClass().getName()+”@”+Integer.toHexString(hashCode());
}

该方法以字符串形式返回当前对象的有关信息,从其原始定义可以看出,所返回的是对象所属的类型名称及其哈希码。当使用System.out.println()方法直接打印输出引用类型变量时,println()方法中会自动调用其toString()方法,再将所返回的字符串信息输出到屏幕上。
如:
System.out.println(p1);

等价于:
System.out.println(p1.toString());

由于Java语言中允许子类对父类中继承来的方法进行重写,以改变其实现细节,因此我们也可以根据需要在自己定义的Java类中重写其toString()方法,以提供更适合的说明信息。
例如:重写toString()方法
源文件:Person.java
public class Person{
	private int age;
	public Person(int age){
		this.age = age;
	}
	public void setAge(int age){
		this.age = age;
	}
	public int getAge(){
		return age;
	}
	public String toString(){
		return "This is a instance of Person,age=" + age;
	}
}

源文件:TestOverride.java
public class TestOverride{
	public static void main(String args[]){
		Person p1 = new Person(18);
		System.out.println(p1.hashCode());
		System.out.println(p1);	//等价于
		System.out.println(p1.toString()); 
	}
}

程序运行结果如下:
374283533
This is a instance of Person,age=18
This is a instance of Person,age=18

回想一下,我们以前使用System.out.println()方法直接打印输出java.lang.Stringjava.util.Data等类型数据时输出的也不是对象的哈希码,而是更有意义的字符串信息,原理也是相同的——这些类中也根据需要重写了各自的toString()方法。

3、equals()方法

Object类中equals()方法的原始定义如下:
public boolean equals(Object obj){
	return (this==obj);
}

其功能是比较当前对象和方法参数obj所引用的对象两者的等价性,如果等价则返回值为true,否则返回false。在进一步讲解之前,让我们先明确Java语言中的等价性标准:基本类型数据比较的是数据的值,而引用数据类型比较的则是对象的句柄,即对象的hashCode编码或者说引用类型变量的值,而非对象本身。简单地说,比较的永远是变量的值是否相等。
例如:“==”和equals()方法使用举例1
源文件:Person.java
public class Person{
	private int age;
	public Person(int age){
		this.age = age;
	}
	public void setAge(int age){
		this.age = age;
	}
	public int getAge(){
		return age;
	}
}

源文件:TestEquals1.java
public class TestEquals1{
	public static void main(String args[]){
		int i = 5;
		int j = 5;
		System.out.println(i == j);
		Person p1 = new Person(18);
		Person p2 = new Person(18);
		System.out.println(p1 == p2);
		System.out.println(p1.equals(p2));
		p2 = p1;
		System.out.println(p2 == p1);
		System.out.println(p1.equals(p2));
	}
}

程序运行结果如下:
true
fals
fals
true
true

可以看出比较引用类型数据的等价性时,其标准比较苛刻,只有当两个引用变量的值相等,实际上是指向同一个对象时才算做等价。看起来使用“==”运算符与equals()方法效果似乎相同,而前者还能够判断基本数据类型数据的等价性,那么equals()方法就显得多余了,其实不然,equals()方法在比较一些特定的引用类型(如java.lang.String、java.io.File、java.util.Date以及封装类)数据时,允许改变先前严格的等价性标准——只有两个对象同为上述的特例类型且其内容相同(对象各自封装的属性值对应相同),equals()方法即判为等价,而“==”判断则不存在任何“变通”的可能。
例如:“==”和equals()方法使用举例2
public class TestEquals2{
	public static void main(String args[]){
		String s1 = new String("abc");
		String s2 = new String("abc");
		System.out.println(s1 == s2);
		System.out.println(s1.equals(s2));
		s2 = s1;
		System.out.println(s1 == s2);
		System.out.println(s1.equals(s2));
	}
}

程序运行结果如下:
false
true
true
true

之所以这样处理,是因为在实际应用开发中,人们更关心的常常是两个字符串的内容是否相同,比如身份验证时输入的用户名/密码等是否与数据库中读取出来的注册信息相匹配,而不在乎是否是同一个对象,而文件的名称和存储路径以及时间等信息的性质也是如此。需要特别说明的是,String常量内容相同的话,在内存中将只保存一份
例如:
String s1 = "abc";
String s2 = "abc";
System.out.println(s1 == s2);
System.out.println(s1.equals(s2));

运行结果都为“true”。
其实,要实现上述“特例”并不困难,只需在Object的子类中重写其equals()方法,给出用户定义的等价性标准就是了,我们也可以在应用开发时根据需要进行类似处理。
例如:用户自定义等价性标准
源文件:Person1.java
public class Person1{
	private int age;
	public Person1(int age){
		this.age = age;
	}
	public void setAge(int age){
		this.age = age;
	}
	public int getAge(){
		return age;
	}
	public boolean equals(Object o){
		if(o instanceof Person1){
			Person1 p = (Person1)o;
			if(this.age == p.age){
				return true;
			}
			else
				return false;
		}
		return false;
	}
}

源文件:TestEquals3.java
public class TestEquals3{
	public static void main(String args[]){
		Person1 p1 = new Person1(18);
		Person1 p2 = new Person1(18);
		System.out.println(p1 == p2);
		System.out.println(p1.equals(p2));
	}
}

程序运行结果如下:
false
true

上述程序中Person1类中重写了equals()方法,重新定义了Person1类型数据的等价性判定标准——只要对象均为Person1类型且其age属性值相等,则认为等价,无论是否为同一个对象。其equals()方法体中的代码也可简化为:
if(o instanceof Person1){
			Person1 p = (Person1)o;
			if(this.age == p.age)
				return true;
		}
		return false;

或者:
return (o instanceof Person1)&&(((Person1)o)age == this.age);


4、finalize()方法

Java运行时环境中的垃圾收集器在销毁一个对象之前,会自动调用该对象的finalize()方法,然后才释放对象的内存空间,该方法在Object类中的原始定义如下:
protected void finalize() throws Throwable{}

请注意,这里finalize()方法修饰符是protected,而不是public,这种访问控制等级使得在外界(子类以外的范围)对于该方法是不可见的,相信读者能够理解,要使其发挥作用,应该在子类中重写finalize()方法,而且,重写方法的修饰符应改为public,否则重写仍然没有实际意义。finalize()方法的用途是在子类中重写,以加入所需的逻辑代码来配置系统资源或执行其他清除操作。
例如:
源文件:Person2.java
public class Person2{
	private String name;
	public Person2(String name){
		this.name = name;
		System.out.println("创建Person2对象,name:"+name);
	}
	public void fianlize(){
		System.out.println("销毁Person2对象,name:"+name);
	}
}

源文件:TestFinalize.java
public class TestFinalize{
	public static void main(String args[]){
		for(int i = 0;i < 10;i ++){
			Person2 p = new Person2("Tom" + i);
			for(int j = 0;j < 1000;j ++){
				String[] test = {new String("abc"),new String("def"),new String("ghi")};
			}
		}
	}
}

程序运行的类似结果如下(程序每次运行的结果可能不一样,内存不同的机器运行结果也是不一样的):
创建Person2对象,name:Tom0
创建Person2对象,name:Tom1
创建Person2对象,name:Tom2
创建Person2对象,name:Tom3
创建Person2对象,name:Tom4
创建Person2对象,name:Tom5
创建Person2对象,name:Tom6
创建Person2对象,name:Tom7
销毁Person2对象,name:Tom6
销毁Person2对象,name:Tom5
创建Person2对象,name:Tom8
创建Person2对象,name:Tom9

程序中的内层for循环起到消耗内存空间的作用,读者可能奇怪为什么创建10个Person2类的对象,却只销毁了其中两个,其实Java虚拟机的垃圾回收操作对于应用程序而言是完全透明的——程序无法预料或精确控制某个无用对象何时被销毁,也就无法控制其的finalize()方法的调用时机,而且,除非垃圾回收器认为程序的可用内存空间已经不足,否则它不会试图释放无用对象占用的内存的内存。换句话说,下述情况是完全可能发生的:一个程序只占用了少量的内存,于是垃圾回收器没有在程序运行的过程中销毁无用对象并释放它们所占用的内存,也就没有调用过这些对象的finalize()方法,程序就终止了。不必担心,JVM最终关闭时还是会释放其所占用的所有内存空间。
由于finalize()方法最终是否会执行,以及何时会执行都是不确定的,在应用程序层面无法精确控制和干预,即使在应用程序中显式调用System.gc()或Runtime.gc()方法强制系统清理无用内存空间,也不能保证这一点,因此finalize()方法并不可靠,在应用程序开发中不建议使用。

5、clone()方法

在应用开发过程中,我们可能会需要拷贝(copy,复制)一个现有的对象,即得到一个新对象并希望其与现有对象封装完全相同的信息(属性值),主要是为了此后两者互不相干,修改其中的一个对象不会影响到另一个,我们知道,简单地进行引用变量间的赋值是不能解决问题的,因为并没有创建新对象;而自己编写代码先创建一个新对象,再将原始对象的属性值一一复制过来也比较烦琐,且存在后述的“浅度拷贝”问题;这种情况下,利用clone()方法来实现对象拷贝不失为一种明智的选择。
Object类中的clone()方法专门提供拷贝当前对象的功能,其原型如下:
protected native Object clone() throws CloneNotSupportedException;

其中的修饰符native标明此方法是一个本地方法,即调用了其运行时所在平台/操作系统的底层功能,当然这是早就实现好的,读者不必为此分心。该方法能够创建并返回当前对象的一个副本,可以理解为将当前对象的所有信息(一段连续的内存空间中存储的数据)直接复制一份并单独保存,因此其返回的是已经包含了原有对象信息的一个新对象,而不是原有对象的引用。
和finalize()方法类似,clone()方法在Object类中也被定义为protected的,因此只有在其子类中进行重写才能真正发挥作用,Java语言规定,所有要进行“克隆”的对象所属的类必须实现java.lang.Cloneable接口,这是一种安全性保护。
例如:实现简单的克隆操作
源文件:Person3.java
public class Person3 implements Cloneable{
	private String name;
	private int age;
	public Person3(String name,int age){
		this.name = name;
		this.age = age;
	}
	public void setName(String name){
		this.name = name;
	}
	public void setAge(int age){
		this.age = age;
	}
	public String getName(){
		return name;
	}
	public int getAge(){
		return age;
	}
	public void display(){
		System.out.println("Name:"+name+"\tAge:"+age);
	}
	public Object clone(){
		Person3 p = null;
		try{
			p = (Person3)super.clone();
		}catch(CloneNotSupportedException e){
			e.printStackTrace();
		}
		return p;
	}
}

源文件:TestClone.java
public class TestClone{
	public static void main(String args[]){
		Person3 p1 = new Person3("Tom",18);
		Person3 p2 = (Person3)p1.clone();
		System.out.println(p1 == p2);
		System.out.println(p1.equals(p2));
		p2.setAge(25);
		p2.display();
		p1.display();
	}
}

程序运行结果如下:
false
false
Name:Tom        Age:25
Name:Tom        Age:18

查看java.lang.Cloneable接口大源代码,你会发现该接口中没有任何内容,其源代码如下:
package java.lang;
public interface Cloneable(){
}

这样的接口被称为空接口,实际上只是起到标识的作用——必须是该接口实现类的实例才能进行克隆操作,因此这样的接口也称“标记性接口”。
需要小心的是,使用上述的clone()方法进行对象拷贝可能出现“浅度拷贝”(Low Copy)的问题。
例如:浅度拷贝
源文件:Person3.java
源文件:Book.java
public class Book implements Cloneable{
	String bookName;
	double price;
	Person3 author;
	public Book(String bn,double p,Person3 a){
		bookName = bn;
		price = p;
		author = a;
	}
	public Object clone(){
		Book b = null;
		try{
			b = (Book)super.clone();
		}catch(CloneNotSupportedException e){
			e.printStackTrace();
		}
		return b;
	}
	public void display(){
		System.out.println(bookName+"\t"+price+"\t");
		author.display();
	}
}

源文件:TestLowCopy.java
public class TestLowCopy{
	public static void main(String args[]){
		Book b1 = new Book("Java编程思想",99,new Person3("张三",48));//p0
		Book b2 = (Book)b1.clone();//p1
		b2.bookName = "Java核心技术";//p2
		b2.price = 100;
		b2.author.setName("李四");
		b2.author.setAge(36);
		b1.display();
		b2.display();
	}
}

程序运行结果如下:
Java编程思想    99.0
Name:李四       Age:36
Java核心技术    100.0
Name:李四       Age:36

可以看出克隆后b2对象对其属性author所引用的Person对象的改动影响到了b1,这是由clone()方法的实现机制决定的——clone()方法先在内存中开辟一块目标对象所需的存储空间(只要是同属于一个类型,则对象占有的存储空间大小也一定相同),然后直接将原始对象存储空间中的内容(包括各属性的值)原样拷贝过来。对基本类型的属性,其属性值就是真正要用的信息,这样的操作当然没有问题,但对于引用类型的属性,其值只是所引用的其他对象的句柄,这就导致clone后“副本”对象与原始对象的引用类型属性指向同样的对象,为更直观,现给出上述程序运行到关键点时的内存状态。如图1~3:







这种不够彻底的拷贝也称浅度拷贝,浅度拷贝可能造成“原件”和“副本”对象之间的“藕断丝连”,往往导致我们所不希望的结果。与之相应的彻底拷贝操作被称为“深度拷贝”(Deep Copy),实现起来也不算困难,只需在拷贝目标对象时对与其有关联的对象,比如上例中要拷贝的Book对象通过其属性author所引用的Person3对象,也同时进行显式拷贝处理。
例如:深度拷贝
源文件:Person3.java
源文件:Book1.java
public class Book1 implements Cloneable{
	String bookName;
	double price;
	Person3 author;
	public Book1(String bn,double p,Person3 a){
		bookName = bn;
		price = p;
		author = a;
	}
	public Object clone(){
		Book1 b = null;
		try{
			b = (Book1)super.clone();
		}catch(CloneNotSupportedException e){
			e.printStackTrace();
		}
		b.author = (Person3)author.clone();
		return b;
	}
	public void display(){
		System.out.println(bookName+"\t"+price+"\t");
		author.display();
	}
}

源文件:TestDeepCopy.java
public class TestDeepCopy{
	public static void main(String args[]){
		Book1 b1 = new Book1("Java编程思想",99,new Person3("张三",48));
		Book1 b2 = (Book1)b1.clone();
		b2.bookName = "Java核心技术";
		b2.price = 100;
		b2.author.setName("李四");
		b2.author.setAge(36);
		b1.display();
		b2.display();
	}
}

程序运行结果如下:
Java编程思想    99.0
Name:张三       Age:48
Java核心技术    100.0
Name:李四       Age:36


Object类中的wait()和notify()等方法也很有用,专门用于多线程编程中的线程同步性处理,以后再介绍。
  • 大小: 50.7 KB
  • 大小: 60.4 KB
  • 大小: 61.6 KB
分享到:
评论

相关推荐

    java language and object

    在Java中,抽象类和接口是抽象的表示形式,它们定义了共同的行为规范,但不提供具体实现。 压缩包中的文件名暗示了章节结构,可能对应于教材的不同章节: - **ch1**:通常介绍Java的基础知识,如安装环境、基本...

    Object Oriented Design in Java

    《Object Oriented Design in Java》是Mitchell Waite Signature Series中的一部著作,由Stephen Gilbert和Bill McCarty共同撰写。这本书深入探讨了如何在Java语言中进行面向对象设计(OOD),帮助开发者掌握这一...

    Java Principles of Object Oriented Programming

    Borror共同编著的一本关于Java编程的著作,主要探讨了面向对象编程(Object-Oriented Programming,OOP)在Java语言中的应用。面向对象编程是现代软件开发中的核心思想,Java作为一门强类型、面向对象的语言,其...

    Java的三大特征

    * 如果定义一个类没有 extends 关键字,那么这个类默认继承自 object 类,object 类是 Java 中所有类的直接或间接的父类,是 Java 中所有类的根类 重写(Override) 重写是 Java 中的一种机制,允许子类对从父类...

    Java程序设计:chapter05 类的继承和派生.ppt

    在 Java 中,Object 类定义和实现了 Java 系统所需要的众多类的共同行为,是所有类的根类,所有的类都是由这个类继承、扩充而来的。 派生类的定义 派生类(子类)定义的一般格式为: [ 类修饰符 ] class 子类名 ...

    Java面向对象程序设计

    6. **抽象类与接口**:抽象类是不能实例化的类,用于定义共同接口或者提供部分实现。接口则是一种完全抽象的类型,只包含常量和抽象方法,可以实现多重继承效果。 7. **构造函数**:构造函数是类的一个特殊方法,...

    java基础类与继承.ppt

    Java中的类与继承是面向对象编程(Object-Oriented Programming, OOP)的核心概念,它们在构建复杂的软件系统中起到至关重要的作用。面向对象编程是一种编程范式,它基于对象和类的概念,通过继承、封装和多态等特性...

    07-《Java常用基础类》.rar

    `String`类是处理文本字符串的基石,而`Object`类则是所有Java类的父类,提供了诸如`equals()`、`hashCode()`和`toString()`等基本方法。`System`类提供了与操作系统交互的功能,如获取当前时间、读写标准输入输出等...

    s-13 to 24.rar_Only_java 聊天_object oriented_truthlwe

    面向对象编程(Object-Oriented Programming, OOP)是Java的核心特性,它通过类、对象、继承、封装、多态等概念来组织代码,提高可读性和可维护性。 1. **面向对象编程**: - **类与对象**:类是具有共同属性和...

    JAVA概念题JAVA概念题.doc

    14. **Object类的作用**: `Object`类是所有Java类的根类,提供了一些基础方法,如`equals()`、`hashCode()`、`toString()`等。它的构造方法`Object()`主要用作创建一个对象的默认初始化。 以上是关于Java编程中的...

    java中的Class类和反射.docx

    `Object`类的父类为`null`。 - **`public java.net.URL getResource(String name)`**:根据名称获取资源。 - **其他方法** - `public boolean isEnum()`:判断是否为枚举类型。 - `public native boolean ...

    Java的继承机制详解

    8. `Object`类:所有Java类都隐式地继承自`java.lang.Object`类,这是Java中的顶级父类。通过`Object`数组,可以存储不同类型的对象,体现了多态性。 9. 私有成员的隐藏:父类的私有成员对子类不可见,这意味着子类...

    java所有的API

    `Object`类是所有Java类的父类,而`System`类提供了系统级的服务,例如获取当前时间或打印输出。 2. **java.io**:这个包提供了输入和输出的功能,包括文件操作、流处理和数据转换。例如,`File`类用于处理文件,`...

    关于Java中Object类的几个方法示例

    Java中的Object类作为所有类的共同祖先,其提供的几个基础方法对于Java对象的操作至关重要。理解这些方法的作用和如何适当地重写它们,对于编写可读性好、易于维护的代码十分重要。学会正确地使用getClass()、...

    三月计算机二级java笔试习题及答案.pdf

    13. Java类的父类:所有Java类的父类是`Object`,选项D正确。 14. Java程序段输出:程序段将字符'f'和'k'相减得到5,因此选项D正确。 15. 八进制数:在Java中,以0开头的数字表示八进制,所以026是八进制,但028...

    类、抽象类、接口、继承和对象(java).doc

    在Java中,一个类可以实现多个接口,但只能继承一个父类(除了Object类,它是所有类的最终父类)。 继承是类之间的“is-a”关系,允许一个类(子类)继承另一个类(父类)的属性和方法。子类可以重写父类的方法,以...

    java面向对象编程总结

    `Object` 类是所有 Java 类的顶级父类。类的访问权限修饰符决定了子类能访问父类的哪些成员。方法的重写(覆盖)是在子类中改进或扩展父类方法的功能,以满足特定需求。重写时需注意保持方法签名的一致性,并遵循...

    JAVA继承的相关知识点

    - 每个没有指定父类的类,默认都会继承Java的根类`Object`。 2. **子类继承父类的结果**: - 子类继承了父类的所有非私有属性和方法,包括受保护的和公共的。 - 子类可以在同一包或不同包内调用父类的相应访问...

Global site tag (gtag.js) - Google Analytics