java对象继承对象
Java提供了一个由数千个类和其他引用类型组成的标准类库。 尽管它们的功能存在差异,但这些类型通过直接或间接扩展Object
类来形成一个庞大的继承层次结构。 对于您创建的任何类和其他引用类型,也是如此。
关于Java继承的本教程的前半部分向您展示了继承的基础知识,特别是如何使用Java的extends
和super
关键字从父类派生子类,调用父类的构造函数和方法,覆盖方法等等。 现在,我们将焦点转向Java类继承层次结构java.lang.Object
。
学习Object
及其方法将帮助您更全面地了解继承及其在Java程序中的工作方式。 通常,熟悉这些方法将有助于您更好地理解Java程序。
对象:Java的超类
Object
是所有其他Java类的根类或最终超类。 存储在java.lang
包中的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()
方法。
我们将研究每种方法,以及它们如何使您能够在Java类的上下文中执行特殊任务。 首先,让我们考虑Object
继承的基本规则和机制。
通用类型
在上面的列表中,您可能已经注意到getClass()
,其Class<?>
返回类型是泛型类型的示例。 我将在以后的文章中讨论泛型类型。
扩展对象:一个例子
一个类可以显式扩展Object
,如清单1所示。
清单1.显式扩展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部分中可以回顾到Java不支持基于类的多重继承),所以您不必强制显式扩展Object
; 否则,您将无法扩展任何其他类。 因此,您将隐式扩展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或清单2:
javac Employee.java
运行生成的应用程序:
java Employee
您应该观察以下输出:
John Doe
查找类:getClass()
getClass()
方法返回调用它的任何对象的运行时类。 运行时类由Class
对象表示,可以在java.lang
包中找到。 Class
是Java Reflection API的切入点,当我们进入Java编程的更高级主题时,您将学到。 现在,知道Java应用程序使用Class
和Java Reflection API的其余部分来了解其自身的结构。
类对象和静态同步方法
返回的Class
对象是被表示的类的static synchronized
方法锁定的对象; 例如, static synchronized void foo() {}
。 (我将在以后的教程中介绍Java同步。)
复制对象:clone()
clone()
方法创建并返回对其进行调用的对象的副本。 由于clone()
的返回类型为Object
,因此必须将clone()
返回的对象引用强制转换为对象的实际类型,然后再将该引用分配给该对象类型的变量。 清单3展示了一个演示克隆的应用程序。
清单3.克隆一个对象
class CloneDemo implements Cloneable
{
int x;
public static void main(String[] args) throws CloneNotSupportedException
{
CloneDemo cd = new CloneDemo();
cd.x = 5;
System.out.println("cd.x = " + cd.x);
CloneDemo cd2 = (CloneDemo) cd.clone();
System.out.println("cd2.x = " + cd2.x);
}
}
清单3的CloneDemo
类实现了Cloneable
接口,该接口位于java.lang
包中。 Cloneable
由该类实现(通过implements
关键字),以防止Object
的clone()
方法抛出CloneNotSupportedException
类的实例(也在java.lang
找到)。
CloneDemo
声明了一个名为x
基于int
的实例实例字段,以及一个执行该类的main()
方法。 main()
是通过throws
子句声明的,该子句将CloneNotSupportedException
传递给方法调用堆栈。
main()
首先实例化CloneDemo
,并将生成的实例的x
副本初始化为5
。 然后,它输出实例的x
值,并在此实例上调用clone()
,将返回的对象强制转换为CloneDemo
然后再存储其引用。 最后,它输出克隆的x
字段值。
编译清单3( javac CloneDemo.java
)并运行应用程序( java CloneDemo
)。 您应该观察以下输出:
cd.x = 5
cd2.x = 5
覆盖clone()
前面的示例不需要覆盖clone()
因为调用clone()
的代码位于要克隆的类( CloneDemo
)中。 但是,如果对clone()
的调用位于不同的类中,那么您将需要覆盖clone()
。 因为clone()
被声明为protected
,如果您在编译类之前未覆盖它,则您将收到“ Object中的克隆具有受保护的访问 ”消息。 清单4显示了重构的清单3,该清单演示了重写clone()
。
清单4.从另一个类克隆一个对象
class Data implements Cloneable
{
int x;
@Override
public Object clone() throws CloneNotSupportedException
{
return super.clone();
}
}
class CloneDemo
{
public static void main(String[] args) throws CloneNotSupportedException
{
Data data = new Data();
data.x = 5;
System.out.println("data.x = " + data.x);
Data data2 = (Data) data.clone();
System.out.println("data2.x = " + data2.x);
}
}
清单4声明了要复制其实例的Data
类。 Data
实现Cloneable
接口,以防止在调用clone()
方法时引发CloneNotSupportedException
。 然后,它声明基于int
的实例字段x
,并覆盖clone()
方法。 clone()
方法执行super.clone()
来调用其超类的(即Object
的) clone()
方法。 覆盖的clone()
方法在其throws
子句中标识CloneNotSupportedException
。
清单4还声明了一个CloneDemo
类:实例化Data
,初始化其实例字段,输出实例字段的值,克隆Data
对象并输出其实例字段的值。
编译清单4( javac CloneDemo.java
)并运行应用程序( java CloneDemo
)。 您应该观察以下输出:
data.x = 5
data2.x = 5
浅克隆
浅克隆 (也称为浅复制 )是指复制对象的字段,而不复制从该对象的参考字段(如果有任何参考字段)引用的任何对象。 清单3和清单4实际上展示了浅克隆。 每个的cd
- , cd2
- , data
-和data2
参考的字段标识都有自己的拷贝对象int
基于x
场。
当所有字段都是原始类型并且(在许多情况下)任何引用字段都引用不可变 ( 不可更改)对象时,浅克隆效果很好。 但是,如果任何引用的对象是可变的,则原始对象及其克隆可以看到对这些对象中任何一个所做的更改。 清单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;
}
}
class CloneDemo
{
public static void main(String[] args) throws CloneNotSupportedException
{
Employee e = new Employee("John Doe", 49, new Address("Denver"));
System.out.println(e.getName() + ": " + e.getAge() + ": " +
e.getAddress().getCity());
Employee e2 = (Employee) e.clone();
System.out.println(e2.getName() + ": " + e2.getAge() + ": " +
e2.getAddress().getCity());
e.getAddress().setCity("Chicago");
System.out.println(e.getName() + ": " + e.getAge() + ": " +
e.getAddress().getCity());
System.out.println(e2.getName() + ": " + e2.getAge() + ": " +
e2.getAddress().getCity());
}
}
清单5给出了Employee
, Address
和CloneDemo
类。 Employee
声明name
, age
和address
字段; 并且是可克隆的。 Address
声明一个由城市组成的地址,其实例是可变的。 CloneDemo
驱动该应用程序。
CloneDemo
的main()
方法创建一个Employee
对象并克隆该对象。 然后,它在原始Employee
对象的address
字段中更改城市的名称。 由于两个Employee
对象都引用同一个Address
对象,因此两个对象都可以看到更改后的城市。
编译清单5( javac CloneDemo.java
)并运行此应用程序( java CloneDemo
)。 您应该观察以下输出:
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 Object clone() throws CloneNotSupportedException
{
Employee e = (Employee) super.clone();
e.address = (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 Object clone()
{
return new Address(new String(city));
}
String getCity()
{
return city;
}
void setCity(String city)
{
this.city = city;
}
}
class CloneDemo
{
public static void main(String[] args) throws CloneNotSupportedException
{
Employee e = new Employee("John Doe", 49, new Address("Denver"));
System.out.println(e.getName() + ": " + e.getAge() + ": " +
e.getAddress().getCity());
Employee e2 = (Employee) e.clone();
System.out.println(e2.getName() + ": " + e2.getAge() + ": " +
e2.getAddress().getCity());
e.getAddress().setCity("Chicago");
System.out.println(e.getName() + ": " + e.getAge() + ": " +
e.getAddress().getCity());
System.out.println(e2.getName() + ": " + e2.getAge() + ": " +
e2.getAddress().getCity());
}
}
清单6显示了Employee
的clone()
方法首先调用super.clone()
,该方法super.clone()
复制了name
, age
和address
字段。 然后,它在address
字段上调用clone()
以复制所引用的Address
对象。 Address
覆盖clone()
方法,并显示出与覆盖该方法的先前类的一些区别:
-
Address
未实现Cloneable
。 没必要,因为只有Object
的clone()
方法要求一个类实现此接口,并且不会调用此clone()
方法。 - 覆盖的
clone()
方法不会引发CloneNotSupportedException
。 仅从Object
的clone()
方法抛出此异常,该方法未调用。 因此,不必通过throws子句处理异常或将异常传递到方法调用堆栈。 - 不调用
Object
的clone()
方法(没有super.clone()
调用),因为Address
类不需要浅表复制-仅复制一个字段。
翻译自: https://www.infoworld.com/article/2987584/java-101-inheritance-in-java-part-2.html
java对象继承对象