java对象继承对象_Java继承,第2部分:对象及其方法

java对象继承对象

Java提供了一个由数千个类和其他引用类型组成的标准类库。 尽管它们的功能存在差异,但这些类型通过直接或间接扩展Object类来形成一个庞大的继承层次结构。 对于您创建的任何类和其他引用类型,也是如此。

关于Java继承的本教程的前半部分向您展示了继承的基础知识,特别是如何使用Java的extendssuper关键字从父类派生子类,调用父类的构造函数和方法,覆盖方法等等。 现在,我们将焦点转向Java类继承层次结构java.lang.Object

学习Object及其方法将帮助您更全面地了解继承及其在Java程序中的工作方式。 通常,熟悉这些方法将有助于您更好地理解Java程序。

下载
下载本教程中示例应用程序的源代码。 由Jeff Friesen为JavaWorld创建。

对象: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应用程序使用ClassJava 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关键字),以防止Objectclone()方法抛出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给出了EmployeeAddressCloneDemo类。 Employee声明nameageaddress字段; 并且是可克隆的。 Address声明一个由城市组成的地址,其实例是可变的。 CloneDemo驱动该应用程序。

CloneDemomain()方法创建一个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显示了Employeeclone()方法首先调用super.clone() ,该方法super.clone()复制了nameageaddress字段。 然后,它在address字段上调用clone()以复制所引用的Address对象。 Address覆盖clone()方法,并显示出与覆盖该方法的先前类的一些区别:

  • Address未实现Cloneable 。 没必要,因为只有Objectclone()方法要求一个类实现此接口,并且不会调用此clone()方法。
  • 覆盖的clone()方法不会引发CloneNotSupportedException 。 仅从Objectclone()方法抛出此异常,该方法未调用。 因此,不必通过throws子句处理异常或将异常传递到方法调用堆栈。
  • 不调用Objectclone()方法(没有super.clone()调用),因为Address类不需要浅表复制-仅复制一个字段。

翻译自: https://www.infoworld.com/article/2987584/java-101-inheritance-in-java-part-2.html

java对象继承对象

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值