Java提高学习之Object类详解(1)

转自:http://www.importnew.com/10304.html

问:什么是Object类?

答:Object类存储在java.lang包中,是所有java类(Object类除外)的终极父类。当然,数组也继承了Object类。然而,接口是不继承Object类的,原因在这里指出:Section 9.6.3.4 of the Java Language Specification:“Object类不作为接口的父类”。
Object类中声明了以下函数,我会在下文中作详细说明。

 1 protected Object clone()
 2 boolean equals(Object obj)
 3 protected void finalize()
 4 Class<?> getClass()
 5 int hashCode()
 6 void notify()
 7 void notifyAll()
 8 String toString()
 9 void wait()
10 void wait(long timeout)
11 void wait(long timeout, int nanos)

java的任何类都继承了这些函数,并且可以覆盖不被final修饰的函数。例如,没有final修饰的toString()函数可以被覆盖,但是final wait()函数就不行。

问:可以声明要“继承Object类”吗?

答:可以。在代码中明确地写出继承Object类没有语法错误。参考代码清单1。

代码清单1:明确的继承Object

 1 import java.lang.Object;
 2 public class Employee extends Object {
 3     private String name;
 4
 5     public Employee(String name) {
 6         this.name = name;
 7     }
 8     public String getName() {
 9         return name;
10     }
11     public static void main(String[] args) {
12         Employee emp = new Employee("John Doe");
13         System.out.println(emp.getName());
14     }
15 }

你可以试着编译代码1(javac Employee.java),然后运行Employee.class(java Employee),可以看到John Doe 成功的输出了。因为编译器会自动引入java.lang包中的类型,即 import java.lang.Object; 没必要声明出来。Java也没有强制声明“继承Object类”。如果这样的话,就不能继承除Object类之外别的类了,因为java不支持多继承。然而,即使不声明出来,也会默认继承了Object类,参考代码清单2。

代码清单2:默认继承Object

 1 public class Employee
 2 {
 3    private String name;
 4
 5    public Employee(String name)
 6    {
 7       this.name = name;
 8    }
 9
10    public String getName()
11    {
12       return name;
13    }
14
15    public static void main(String[] args)
16    {
17       Employee emp = new Employee("John Doe");
18       System.out.println(emp.getName());
19    }
20 }

就像代码清单1一样,这里的Employee类继承了Object,所以可以使用它的函数。

克隆Object

问:clone()函数是用来做什么的?

答:clone()可以产生一个相同的类并且返回给调用者。

问:clone()是如何工作的?

答:Objectclone()作为一个本地方法来实现,这意味着它的代码存放在本地的库中。当代码执行的时候,将会检查调用对象的类(或者父类)是否实现了java.lang.Cloneable接口(Object类不实现Cloneable)。如果没有实现这个接口,clone()将会抛出一个检查异常()——java.lang.CloneNotSupportedException,如果实现了这个接口,clone()会创建一个新的对象,并将原来对象的内容复制到新对象,最后返回这个新对象的引用。

问:怎样调用clone()来克隆一个对象?

答:用想要克隆的对象来调用clone(),将返回的对象从Object类转换到克隆的对象所属的类,赋给对象的引用。这里用代码清单3作一个示例。

代码清单3:克隆一个对象

 1 public class CloneDemo implements Cloneable {
 2     int x;
 3
 4     public static void main(String[] args) throws CloneNotSupportedException {
 5         CloneDemo cd = new CloneDemo();
 6         cd.x = 5;
 7         System.out.printf("cd.x = %d%n", cd.x);
 8         CloneDemo cd2 = (CloneDemo) cd.clone();
 9         System.out.printf("cd2.x = %d%n", cd2.x);
10     }
11 }

代码清单3声明了一个继承Cloneable接口的CloneDemo类。这个接口必须实现,否则,调用Object的clone()时将会导致抛出异常CloneNotSupportedException

CloneDemo声明了一个int型变量x和主函数main()来演示这个类。其中,main()声明可能会向外抛出CloneNotSupportedException异常。

Main()先实例化CloneDemo并将x的值初始化为5。然后输出x的值,紧接着调用clone() ,将克隆的对象传回CloneDemo。最后,输出了克隆的x的值。

编译代码清单3(javac CloneDemo.java)然后运行(java CloneDemo)。你可以看到以下运行结果:


1

2

cd.x = 5

cd2.x = 5

问:什么情况下需要覆盖clone()方法呢?

答:上面的例子中,调用clone()的代码是位于被克隆的类(即CloneDemo类)里面的,所以就不需要覆盖clone()了。但是,如果调用别的类中的clone(),就需要覆盖clone()了。否则,将会看到“cloneObject中是被保护的”提示,因为clone()Object中的权限是protected。(译者注:protected权限的成员在不同的包中,只有子类对象可以访问。代码清单3的CloneDemo类和代码清单4的Data类是Object类的子类,所以可以调用clone(),但是代码清单4中的CloneDemo类就不能直接调用Data父类的clone())。代码清单4在代码清单3上稍作修改来演示覆盖clone()

代码清单4:从别的类中克隆对象

 1 class Data implements Cloneable {
 2     int x;
 3
 4     @Override
 5     public Object clone() throws CloneNotSupportedException {
 6         return super.clone();
 7     }
 8 }
 9
10 public class CloneDemo {
11     public static void main(String[] args) throws CloneNotSupportedException {
12         Data data = new Data();
13         data.x = 5;
14         System.out.printf("data.x = %d%n", data.x);
15         Data data2 = (Data) data.clone();
16         System.out.printf("data2.x = %d%n", data2.x);
17     }
18 }

代码清单4声明了一个待克隆的Data类。这个类实现了Cloneable接口来防止调用clone()的时候抛出异常CloneNotSupportedException,声明了int型变量x,覆盖了clone()方法。这个方法通过执行super.clone()来调用父类的clone()(这个例子中是Object的)。通过覆盖来避免抛出CloneNotSupportedException异常。代码清单4也声明了一个CloneDemo类来实例化Data,并将其初始化,输出示例的值。然后克隆Data的对象,同样将其值输出。编译代码清单4(javac CloneDemo.java)并运行(java CloneDemo),你将看到以下运行结果:


1

2

data.x = 5

data2.x = 5

问:什么是浅克隆?

A:浅克隆(也叫做浅拷贝)仅仅复制了这个对象本身的成员变量该对象如果引用了其他对象的话,也不对其复制。代码清单3和代码清单4演示了浅克隆。新的对象中的数据包含在了这个对象本身中,不涉及对别的对象的引用。如果一个对象中的所有成员变量都是原始类型,并且其引用了的对象都是不可改变的(大多情况下都是)时,使用浅克隆效果很好!但是,如果其引用了可变的对象,那么这些变化将会影响到该对象和它克隆出的所有对象!代码清单5给出一个示例。

代码清单5:演示浅克隆在复制引用了可变对象的对象时存在的问题

 1 class Employee implements Cloneable {
 2     private String name;
 3     private int age;
 4     private Address address;
 5
 6     Employee(String name, int age, Address address) {
 7         this.name = name;
 8         this.age = age;
 9         this.address = address;
10     }
11
12     @Override
13     public Object clone() throws CloneNotSupportedException {
14         return super.clone();
15     }
16
17     Address getAddress() {
18         return address;
19     }
20
21     String getName() {
22         return name;
23     }
24
25     int getAge() {
26         return age;
27     }
28 }
29
30 class Address {
31     private String city;
32
33     Address(String city) {
34         this.city = city;
35     }
36
37     String getCity() {
38         return city;
39     }
40
41     void setCity(String city) {
42         this.city = city;
43     }
44 }
45
46 public class CloneDemo {
47     public static void main(String[] args) throws CloneNotSupportedException {
48         Employee e = new Employee("John Doe", 49, new Address("Denver"));
49         System.out.printf("%s: %d: %s%n", e.getName(), e.getAge(),
50                           e.getAddress().getCity());
51         Employee e2 = (Employee) e.clone();
52         System.out.printf("%s: %d: %s%n", e2.getName(), e2.getAge(),
53                           e2.getAddress().getCity());
54         e.getAddress().setCity("Chicago");
55         System.out.printf("%s: %d: %s%n", e.getName(), e.getAge(),
56                           e.getAddress().getCity());
57         System.out.printf("%s: %d: %s%n", e2.getName(), e2.getAge(),
58                           e2.getAddress().getCity());
59     }
60 }

代码清单5给出了EmployeeAddressCloneDemo类。Employee声明了nameageaddress成员变量,是可以被克隆的类;Address声明了一个城市的地址并且其值是可变的。CloneDemo类驱动这个程序。CloneDemo的主函数main()创建了一个Employee对象并且对其进行克隆,然后,改变了原来的Employee对象中address值城市的名字。因为原来的Employee对象和其克隆出来的对象引用了相同的Address对象,所以两者都会提现出这个变化。编译 (javac CloneDemo.java) 并运行 (java CloneDemo)代码清单5,你将会看到如下输出结果:


1

2

3

4

John Doe: 49: Denver

John Doe: 49: Denver

John Doe: 49: Chicago

John Doe: 49: Chicago

问:什么是深克隆?

答:深克隆(也叫做深复制)会复制这个对象和它所引用的对象的成员变量,如果该对象引用了其他对象,深克隆也会对其复制。例如,代码清单6在代码清单5上稍作修改演示深克隆。同时,这段代码也演示了协变返回类型和一种更为灵活的克隆方式。

代码清单6:深克隆成员变量address

 1 class Employee implements Cloneable
 2 {
 3    private String name;
 4    private int age;
 5    private Address address;
 6
 7    Employee(String name, int age, Address address)
 8    {
 9       this.name = name;
10       this.age = age;
11       this.address = address;
12    }
13
14    @Override
15    public Employee clone() throws CloneNotSupportedException
16    {
17       Employee e = (Employee) super.clone();
18       e.address = address.clone();
19       return e;
20    }
21
22    Address getAddress()
23    {
24       return address;
25    }
26
27    String getName()
28    {
29       return name;
30    }
31
32    int getAge()
33    {
34       return age;
35    }
36 }
37
38 class Address
39 {
40    private String city;
41
42    Address(String city)
43    {
44       this.city = city;
45    }
46
47    @Override
48    public Address clone()
49    {
50       return new Address(new String(city));
51    }
52
53    String getCity()
54    {
55       return city;
56    }
57
58    void setCity(String city)
59    {
60       this.city = city;
61    }
62 }
63
64 public class CloneDemo
65 {
66    public static void main(String[] args) throws CloneNotSupportedException
67    {
68       Employee e = new Employee("John Doe", 49, new Address("Denver"));
69       System.out.printf("%s: %d: %s%n", e.getName(), e.getAge(),
70                         e.getAddress().getCity());
71       Employee e2 = (Employee) e.clone();
72       System.out.printf("%s: %d: %s%n", e2.getName(), e2.getAge(),
73                         e2.getAddress().getCity());
74       e.getAddress().setCity("Chicago");
75       System.out.printf("%s: %d: %s%n", e.getName(), e.getAge(),
76                         e.getAddress().getCity());
77       System.out.printf("%s: %d: %s%n", e2.getName(), e2.getAge(),
78                         e2.getAddress().getCity());
79    }
80 }

Java支持协变返回类型,代码清单6利用这个特性,在Employee类中覆盖父类clone()方法时,将返回类型从Object类的对象改为Employee类型。这样做的好处就是,Employee类之外的代码可以不用将这个类转换为Employee类型就可以对其进行复制。Employee类的clone()方法首先调用super().clone(),对name,age,address这些成员变量进行浅克隆。然后,调用成员变量Address对象的clone()来对其引用Address对象进行克隆。从Address类中的clone()函数可以看出,这个clone()和我们之前写的clone()有些不同:

  • Address类没有实现Cloneable接口。因为只有在Object类中的clone()被调用时才需要实现,而Address是不会调用clone()的,所以没有实现Cloneable()的必要。
  • 这个clone()函数没有声明抛出CloneNotSupportedException。这个检查异常只可能在调用Objectclone()的时候抛出。clone()是不会被调用的,因此这个异常也就没有被处理或者传回调用处的必要了。
  • Object类的clone()没有被调用(这里没有调用super.clone())。因为这不是对Address的对象进行浅克隆——只是一个成员变量复制而已。

为了克隆Address的对象,需要创建一个新的Address对象并对其成员进行初始化操作。最后将新创建的Address对象返回。

编译(javac CloneDemo.java)代码清单6并且运行这个程序,你将会看到如下输出结果(java CloneDemo):


1

2

3

4

John Doe: 49: Denver

John Doe: 49: Denver

John Doe: 49: Chicago

John Doe: 49: Denver

Q:如何克隆一个数组?

A:对数组类型进行浅克隆可以利用clone()方法。对数组使用clone()时,不必将clone()的返回值类型转换为数组类型,代码清单7示范了数组克隆。

代码清单7:对两个数组进行浅克隆

 1 class City {
 2     private String name;
 3
 4     City(String name) {
 5         this.name = name;
 6     }
 7
 8     String getName() {
 9         return name;
10     }
11
12     void setName(String name) {
13         this.name = name;
14     }
15 }
16
17 public class CloneDemo {
18     public static void main(String[] args) {
19         double[] temps = { 98.6, 32.0, 100.0, 212.0, 53.5 };
20         for (double temp : temps)
21             System.out.printf("%.1f ", temp);
22         System.out.println();
23         double[] temps2 = temps.clone();
24         for (double temp : temps2)
25             System.out.printf("%.1f ", temp);
26         System.out.println();
27
28         System.out.println();
29
30         City[] cities = { new City("Denver"), new City("Chicago") };
31         for (City city : cities)
32             System.out.printf("%s ", city.getName());
33         System.out.println();
34         City[] cities2 = cities.clone();
35         for (City city : cities2)
36             System.out.printf("%s ", city.getName());
37         System.out.println();
38
39         cities[0].setName("Dallas");
40         for (City city : cities2)
41             System.out.printf("%s ", city.getName());
42         System.out.println();
43     }
44 }

代码清单7声明了一个City类存储名字,还有一些有关城市的数据(比如人口)。CloneDemo类提供了主函数main()来演示数组克隆。

main()函数首先声明了一个双精度浮点型数组来表示温度。在输出数组的值之后,克隆这个数组——注意没有运算符。之后,输出克隆的完全相同的数据。

紧接着,main()声明了一个City对象的数组,输出城市的名字,克隆这个数组,输出克隆的这个数组中城市的名字。为了证明浅克隆完成(比如,这两个数组引用了相同的City对象),main()最后改变了原来的数组中第一个城市的名字,输出第二个数组中所有城市的名字。我们马上就可以看到,第二个数组中的名字也改变了。

编译 (javac CloneDemo.java)并运行 (java CloneDemo)代码清单7,你将会看到如下输出结果:


1

2

3

4

5

6

98.6 32.0 100.0 212.0 53.5

98.6 32.0 100.0 212.0 53.5

Denver Chicago

Denver Chicago

Dallas Chicago

时间: 2024-11-04 08:53:07

Java提高学习之Object类详解(1)的相关文章

JavaScript学习总结(十一)——Object类详解

一.Object类介绍 Object类是所有JavaScript类的基类(父类),提供了一种创建自定义对象的简单方式,不再需要程序员定义构造函数. 二.Object类主要属性 1.constructor:对象的构造函数. 2.prototype:获得类的prototype对象,static性质. 三.Object类主要方法 1.hasOwnProperty(propertyName) 判断对象是否有某个特定的属性.必须用字符串指定该属性,例如,obj.hasOwnProperty("name&q

java提高篇(九)-----详解匿名内部类

摘自http://blog.csdn.net/chenssy/article/details/13170015 java提高篇(九)-----详解匿名内部类 在Java提高篇-----详解内部类中对匿名内部类做了一个简单的介绍,但是内部类还存在很多其他细节问题,所以就衍生出这篇博客.在这篇博客中你可以了解到匿名内部类的使用.匿名内部类要注意的事项.如何初始化匿名内部类.匿名内部类使用的形参为何要为final. 一.使用匿名内部类内部类 匿名内部类由于没有名字,所以它的创建方式有点儿奇怪.创建格式

java基础学习总结——Object类

永不放弃,一切皆有可能!!! 只为成功找方法,不为失败找借口! java基础学习总结——Object类 一.Object类介绍 Object类在JAVA里面是一个比较特殊的类,JAVA只支持单继承,子类只能从一个父类来继承,如果父类又是从另外一个父类继承过来,那他也只能有一个父类,父类再有父类,那也只能有一个,JAVA为了组织这个类组织得比较方便,它提供了一个最根上的类,相当于所有的类都是从这个类继承,这个类就叫Object.所以Object类是所有JAVA类的根基类,是所有JAVA类的老祖宗.

Java:Object类详解

Java的一些特性会让初学者感到困惑,但在有经验的开发者眼中,却是合情合理的.例如,新手可能不会理解Object类.这篇文章分成三个部分讲跟Object类及其方法有关的问题. 上帝类 问:什么是Object类? 答:Object类存储在java.lang包中,是所有java类(Object类除外)的终极父类.当然,数组也继承了Object类.然而,接口是不继承Object类的,原因在这里指出:Section 9.6.3.4 of the Java Language Specification:"

Java之Object类详解

相等性的比较: 1.对于原生数据类型来说,比较的是左右两边的值是否相等 2.对于引用类型来说,比较左右两边的引用是否指向同一个对象,或者说左右两边的引用地址是否相同 java.lang.Object类.java.lang包在使用的时候无需显示导入,编译时由编译器自动帮助我们导入 API(application Programming interface),应用编程接口. package com.yuanzijian01; public class ObjectTest { public stat

java基础学习——4、集合类详解

一.Java集合框架图 上述类图中,实线边框的是实现类,比如ArrayList,LinkedList,HashMap等,折线边框的是抽象类,比如AbstractCollection,AbstractList,AbstractMap等,而点线边框的是接口,比如Collection,Iterator,List等. 发现一个特点,上述所有的集合类,都实现了Iterator接口,这是一个用于遍历集合中元素的接口,主要包含hashNext(),next(),remove()三种方法. 它的一个子接口Lin

Java提高学习之Object(2)

Equality 问:euqals()函数是用来做什么的? 答:equals()函数可以用来检查一个对象与调用这个equals()的这个对象是否相等. 问:为什么不用“==”运算符来判断两个对象是否相等呢? 答:虽然“==”运算符可以比较两个数据是否相等,但是要来比较对象的话,恐怕达不到预期的结果.就是说,“==”通过是否引用了同一个对象来判断两个对象是否相等,这被称为“引用相等”.这个运算符不能通过比较两个对象的内容来判断它们是不是逻辑上的相等. 问:使用Object类的equals()方法可

Object类详解

相等性比较 == 1)对于原生数据类型来说,比较的是左右两边的值是否相等 2)对于引用类型来说,比较左后两边的引用是否指向同一个对象,或者说左右两边的引用地址是否相同. java.lang.Object类.java.lang包在使用时无需显式导入,在编译时由编译器自动帮助我们导入. API(Application programming interface),应用程序接口. 当打印引用时,实际上会打印出所指对象的toString()方法的返回值,因为每个类都是直接或间接的继承自Object,而O

Java提高学习之Object(5)

字符串形式的表现 Q1:toString() 方法实现了什么功能?A1:toString() 方法将根据调用它的对象返回其对象的字符串形式,通常用于debug. Q2:当 toString() 方法没有被覆盖的时候,返回的字符串通常是什么样子的?A2:当 toString() 没有被覆盖的时候,返回的字符串格式是 类名@哈希值,哈希值是十六进制的.举例说,假设有一个 Employee 类,toString() 方法返回的结果可能是[email protected]. Q3:能提供一个正确覆盖 t