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
类中声明了以下函数,我会在下文中作详细说明。
- protected Object clone()
- boolean equals(Object obj)
- protected void finalize()
- Class<?> getClass()
- int hashCode()
- void notify()
- void notifyAll()
- String toString()
- void wait()
- void wait(long timeout)
- void wait(long timeout, int nanos)
java的任何类都继承了这些函数,并且可以覆盖不被final
修饰的函数。例如,没有final
修饰的toString()
函数可以被覆盖,但是final wait()
函数就不行。
问:可以声明要“继承Object
类”吗?
答:可以。在代码中明确地写出继承Object
类没有语法错误。参考代码清单1。
代码清单1:明确的继承Object
- import java.lang.Object;
- public class Employee extends Object {
- private String name;
- public Employee(String name) {
- this.name = name;
- }
- public String getName() {
- return name;
- }
- public static void main(String[] args) {
- Employee emp = new Employee("John Doe");
- System.out.println(emp.getName());
- }
- }
你可以试着编译代码1(javac Employee.java
),然后运行Employee.class(java Employee
),可以看到John Doe 成功的输出了。因为编译器会自动引入java.lang
包中的类型,即 import java.lang.Object
; 没必要声明出来。Java也没有强制声明“继承Object
类”。如果这样的话,就不能继承除Object
类之外别的类了,因为java不支持多继承。然而,即使不声明出来,也会默认继承了Object
类,参考代码清单2。
代码清单2:默认继承Object
类
- public class Employee
- {
- private String name;
- public Employee(String name)
- {
- this.name = name;
- }
- public String getName()
- {
- return name;
- }
- public static void main(String[] args)
- {
- Employee emp = new Employee("John Doe");
- System.out.println(emp.getName());
- }
- }
就像代码清单1一样,这里的Employee
类继承了Object
,所以可以使用它的函数。
克隆Object
类
问:clone()
函数是用来做什么的?
答:clone()
可以产生一个相同的类并且返回给调用者。
问:clone()
是如何工作的?
答:Object
将clone()
作为一个本地方法来实现,这意味着它的代码存放在本地的库中。当代码执行的时候,将会检查调用对象的类(或者父类)是否实现了java.lang.Cloneable
接口(Object
类不实现Cloneable
)。如果没有实现这个接口,clone()
将会抛出一个检查异常()——java.lang.CloneNotSupportedException
,如果实现了这个接口,clone()
会创建一个新的对象,并将原来对象的内容复制到新对象,最后返回这个新对象的引用。
问:怎样调用clone()
来克隆一个对象?
答:用想要克隆的对象来调用clone()
,将返回的对象从Object
类转换到克隆的对象所属的类,赋给对象的引用。这里用代码清单3作一个示例。
代码清单3:克隆一个对象
- public class CloneDemo implements Cloneable {
- int x;
- public static void main(String[] args) throws CloneNotSupportedException {
- CloneDemo cd = new CloneDemo();
- cd.x = ;
- System.out.printf("cd.x = %d%n", cd.x);
- CloneDemo cd2 = (CloneDemo) cd.clone();
- System.out.printf("cd2.x = %d%n", cd2.x);
- }
- }
代码清单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()
了。否则,将会看到“clone
在Object
中是被保护的”提示,因为clone()
在Object
中的权限是protected
。(译者注:protected
权限的成员在不同的包中,只有子类对象可以访问。代码清单3的CloneDemo
类和代码清单4的Data
类是Object
类的子类,所以可以调用clone()
,但是代码清单4中的CloneDemo
类就不能直接调用Data
父类的clone()
)。代码清单4在代码清单3上稍作修改来演示覆盖clone()
。
代码清单4:从别的类中克隆对象
- class Data implements Cloneable {
- int x;
- @Override
- public Object clone() throws CloneNotSupportedException {
- return super.clone();
- }
- }
- public class CloneDemo {
- public static void main(String[] args) throws CloneNotSupportedException {
- Data data = new Data();
- data.x = ;
- System.out.printf("data.x = %d%n", data.x);
- Data data2 = (Data) data.clone();
- System.out.printf("data2.x = %d%n", data2.x);
- }
- }
代码清单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:演示浅克隆在复制引用了可变对象的对象时存在的问题
- class Employee implements Cloneable {
- private String name;
- private int age;
- private Address address;
- Employee(String name, int age, Address address) {
- this.name = name;
- this.age = age;
- this.address = address;
- }
- @Override
- public Object clone() throws CloneNotSupportedException {
- return super.clone();
- }
- Address getAddress() {
- return address;
- }
- String getName() {
- return name;
- }
- int getAge() {
- return age;
- }
- }
- class Address {
- private String city;
- Address(String city) {
- this.city = city;
- }
- String getCity() {
- return city;
- }
- void setCity(String city) {
- this.city = city;
- }
- }
- public class CloneDemo {
- public static void main(String[] args) throws CloneNotSupportedException {
- Employee e = new Employee("John Doe", , new Address("Denver"));
- System.out.printf("%s: %d: %s%n", e.getName(), e.getAge(),
- e.getAddress().getCity());
- Employee e2 = (Employee) e.clone();
- System.out.printf("%s: %d: %s%n", e2.getName(), e2.getAge(),
- e2.getAddress().getCity());
- e.getAddress().setCity("Chicago");
- System.out.printf("%s: %d: %s%n", e.getName(), e.getAge(),
- e.getAddress().getCity());
- System.out.printf("%s: %d: %s%n", e2.getName(), e2.getAge(),
- e2.getAddress().getCity());
- }
- }
代码清单5给出了Employee
、Address
和CloneDemo
类。Employee
声明了name
、age
、address
成员变量,是可以被克隆的类;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
- class Employee implements Cloneable
- {
- private String name;
- private int age;
- private Address address;
- Employee(String name, int age, Address address)
- {
- this.name = name;
- this.age = age;
- this.address = address;
- }
- @Override
- public Employee clone() throws CloneNotSupportedException
- {
- Employee e = (Employee) super.clone();
- e.address = address.clone();
- return e;
- }
- Address getAddress()
- {
- return address;
- }
- String getName()
- {
- return name;
- }
- int getAge()
- {
- return age;
- }
- }
- class Address
- {
- private String city;
- Address(String city)
- {
- this.city = city;
- }
- @Override
- public Address clone()
- {
- return new Address(new String(city));
- }
- String getCity()
- {
- return city;
- }
- void setCity(String city)
- {
- this.city = city;
- }
- }
- public class CloneDemo
- {
- public static void main(String[] args) throws CloneNotSupportedException
- {
- Employee e = new Employee("John Doe", , new Address("Denver"));
- System.out.printf("%s: %d: %s%n", e.getName(), e.getAge(),
- e.getAddress().getCity());
- Employee e2 = (Employee) e.clone();
- System.out.printf("%s: %d: %s%n", e2.getName(), e2.getAge(),
- e2.getAddress().getCity());
- e.getAddress().setCity("Chicago");
- System.out.printf("%s: %d: %s%n", e.getName(), e.getAge(),
- e.getAddress().getCity());
- System.out.printf("%s: %d: %s%n", e2.getName(), e2.getAge(),
- e2.getAddress().getCity());
- }
- }
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
。这个检查异常只可能在调用Object
类clone()
的时候抛出。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:对两个数组进行浅克隆
- class City {
- private String name;
- City(String name) {
- this.name = name;
- }
- String getName() {
- return name;
- }
- void setName(String name) {
- this.name = name;
- }
- }
- public class CloneDemo {
- public static void main(String[] args) {
- double[] temps = { 98.6, 32.0, 100.0, 212.0, 53.5 };
- for (double temp : temps)
- System.out.printf("%.1f ", temp);
- System.out.println();
- double[] temps2 = temps.clone();
- for (double temp : temps2)
- System.out.printf("%.1f ", temp);
- System.out.println();
- System.out.println();
- City[] cities = { new City("Denver"), new City("Chicago") };
- for (City city : cities)
- System.out.printf("%s ", city.getName());
- System.out.println();
- City[] cities2 = cities.clone();
- for (City city : cities2)
- System.out.printf("%s ", city.getName());
- System.out.println();
- cities[].setName("Dallas");
- for (City city : cities2)
- System.out.printf("%s ", city.getName());
- System.out.println();
- }
- }
代码清单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 |
Java提高学习之Object类详解(1)的更多相关文章
- Java:Object类详解
Java的一些特性会让初学者感到困惑,但在有经验的开发者眼中,却是合情合理的.例如,新手可能不会理解Object类.这篇文章分成三个部分讲跟Object类及其方法有关的问题. 上帝类 问:什么是Obj ...
- Java常用类(一)之Object类详解
大家都知道Object是所有类的父类,任何类都默认继承Object 理论上Object类是所有类的父类,即直接或间接的继承java.lang.Object类.由于所有的类都继承在Object类,因此省 ...
- JavaScript学习总结(十一)——Object类详解
一.Object类介绍 Object类是所有JavaScript类的基类(父类),提供了一种创建自定义对象的简单方式,不再需要程序员定义构造函数. 二.Object类主要属性 1.constructo ...
- java提高篇(九)-----详解匿名内部类
在java提高篇-----详解内部类中对匿名内部类做了一个简单的介绍,但是内部类还存在很多其他细节问题,所以就衍生出这篇博客.在这篇博客中你可以了解到匿名内部类的使用.匿名内部类要注意的事项.如何初始 ...
- java提高篇(七)-----详解内部类
可以将一个类的定义放在另一个类的定义内部,这就是内部类. 内部类是一个非常有用的特性但又比较难理解使用的特性(鄙人到现在都没有怎么使用过内部类,对内部类也只是略知一二). 第一次见面 内部类我们从外面 ...
- Java提高学习之Object(2)
Equality 问:euqals()函数是用来做什么的? 答:equals()函数可以用来检查一个对象与调用这个equals()的这个对象是否相等. 问:为什么不用“==”运算符来判断两个对象是否相 ...
- java基础学习总结——Object类
一.Object类介绍
- Java提高学习之Object(5)
字符串形式的表现 Q1:toString() 方法实现了什么功能?A1:toString() 方法将根据调用它的对象返回其对象的字符串形式,通常用于debug. Q2:当 toString() 方法没 ...
- Java提高学习之Object(4)
哈希码 问: hashCode()方法是用来做什么的? 答: hashCode()方法返回给调用者此对象的哈希码(其值由一个hash函数计算得来).这个方法通常用在基于hash的集合类中,像java. ...
随机推荐
- UIPageViewController跳跃切换的问题
使用的是XHScrollMenu和UIPageViewController来构建5个页面: ViewController1, ViewController2, ViewController3, Vie ...
- Tcl 简单介绍及特性
[简单介绍|特性] l 简单介绍 Tcl是一门产生于80年代末的语言,和Python一样,她是用c开发出来的.假设说C/Java/C++/C#为编译型语言的话,那么Python.Perl和Tcl就是 ...
- 微信内移动前端开发抓包调试工具fiddler使用教程
在朋友圈看到一款疯转的H5小游戏,想要copy,什么?只能在微信里打开?小样,图样图森破,限制了oauth.微信浏览器内打开,照样能看你源码~ 使用fiddler来抓包 需要先做一些简单的准备工作: ...
- CSS3 背景属性
CSS3 background-size 属性 div {background:url(bg_flower.gif);-moz-background-size:63px 100px; /* 老版本的 ...
- c#简单缓存使用,添加和修改
private void SetCache(string CacheKey, object objObject, DateTime dt) { System.Web.Caching.Cache obj ...
- title:EL表达式获取Map里面的数值失败的问题
在控制器中定义了一个Map<Integer,String>集合,看似没有问题,将这个集合的对象map传递到一个JSP页面中,我们都知道,用EL表达式 ${map[key]}就可以取得key ...
- js常用DOM操作
在博客园看到了苏夏写的常用DOM整理文章,地址:http://www.cnblogs.com/cabbagen/p/4579412.html,然后抽时间都试了一下这些常用的DOM操作.在这里记录一下. ...
- [LeetCode]题解(python):004-Median of Two Sorted Arrays
题目来源: https://leetcode.com/problems/median-of-two-sorted-arrays/ 题意分析: 这道题目是输入两个已经排好序的数组(长度为m,n),将这两 ...
- vb 6.0 msflexgrid 用法详解
问题一 msflexgrid 根据行数或列数的大小,滚动条的滚动块长度怎么改变,发现滚动条很不好用,很小
- tornado 使用过程中提示‘no module name ioloop’
千万不要将程序文件命名为tornado.py 不然永远不会运行,因为运行后会在tornado.py里查找ioloop信息,永远找不到...