201871010112-梁丽珍《面向对象程序设计(java)》第六、七周学习总结

项目

内容

这个作业属于哪个课程

           <任课教师博客主页链接>    https://www.cnblogs.com/nwnu-daizh/                                   

这个作业的要求在哪里

<作业链接地址>https://www.cnblogs.com/nwnu-daizh/p/11605051.html

作业学习目标

              深入理解程序设计中算法与程序的关系;

              深入理解java程序设计中类与对象的关系;

              理解OO程序设计的第2个特征:继承、多态;

              学会采用继承定义类设计程序(重点、难点);

              能够分析与设计至少包含3个自定义类的程序;

              掌握利用父类定义子类的语法规则及对象使用要求。

 第一部分:总结第五章理论知识

  本章内容:

  类、超类和子类;

  Object:所有类的超类;

  泛型数组列表;

  对象包装器和自动装箱;

  参数数量可变的方法;

  枚举类;

  反射;

  继承设计的技巧;

1.继承:用已有类来构建新类的一种机制。当定义了一个新类继承了一个类时,这个新类就继承了这个类的方法和域,同时在新类中添   加新的方法和域以适应新情况。

2.继承的特点:具有层次结构 子类继承了父类的域和方法。

3.继承的优点:代码可重用性  父类的域和方法可用于子类  可以轻松定义子类  设计应用程序变得更加简单。

4.反射是指在程序运行期间发现更多的类及其属性的能力。 

(1)类、超类和子类

  1.“is-a”关系是继承的一个明显特征。

  2.在Java中,所有的继承都是公有继承,而没有C++中的私有继承和保护继承.。

  3.类继承的格式:

    Class 新类名 extends 已有类名

  4.已有类名称为: 超类(superclass)、基类(bass class)或父类(parent class)

       来自系统类库

      用户自定义类

  5.新类称作:子类(subclass)、派生类(derived class)或孩子类(child class

  6.一般来说,子类比超类拥有的功能更加丰富

   7.通过扩展超类定义子类时,仅需要指出子类与超类的不同之处。在子类中可以增加域、增加方法或覆盖超类的方法,但绝对不能删除超类的任何域和方法。

  8.关键字this有两个用途:一是引用隐式参数,而是调用该类其他的构造器。同样,super是一个指示编译器调用超类方法的特有关键字,它不是一个对象引用,不能将super赋给另一个对象变量。Super关键字一般有两个用途:以是调用超类的方法

   (格式:super.方法名()),二是调用超类的构造器(格式:super())。

  9.若子类构造器没有显示地调用超类地构造器,则将自动地调用超类默认构造器。如果超类只定义了带参数地构造器,若子类构造器没有显示地调用超类地构造器,则Java编译器将报告错误。

  1)继承层次从一个超类扩展而来地类集合称为继承层次。在继承层次中,从某个类到其祖先地路径被称为该类地继承链。

    Java不支持多继承。

  2)多态性

    概念:多态性泛指在程序中同一个符号在不同地情况下具有不同解释地现象。

    超类中定义地域或方法,被子类继承之后,可以具有不同地数据类型或表现出不同的行为。

    这使得在超类及其各个子类中同名的域或方法具有不同的语义。

    超类中的的方法在子类中可方法重写。

  12.在Java中,不需要将方法声明为虚拟方法。动态绑定是默认的处理方式。如果不希望让一个方法具有虚拟特征,可以将它标记为final。

  3)抽象类

    观察类的继承层次结构,位于上层的类更具通用性,甚至可能更加抽象。从某种角度看,祖先类更加通用,人们只将它作为派生其他类的基类,而不作为特定的实例类。

    为了提高程序清晰度,包含一个或多个抽象方法的类本身必须被声明为抽象类。除了抽象方法之外,抽象类还可以包含具体数据和具体方法。

    抽象方法充当着占位的角色,它们的具体实现在子类中。扩展抽象类可以有两种选择:一种是在子类中实现部分抽象方法,这样就必须将子类也标记为抽象类;另一种是实现全部抽象方法,这样子类就可以不是抽象类。此外,类即使不含抽象方法,也可以将类声明为抽象类。

    抽象类不能被实例化,即不能创建对象,只能产生子类。可以创建抽象类的对象变量,只是这个变量必须指向它的非抽象子类的对象。

  4)动态绑定

    概念:又称为运行时绑定。即程序在运行时会自动选择调用哪个方法。

  调用对象方法的执行过程

    首先,编译器检查对象的声明类型和方法名,搜素相应类(Son)及其父类(father)的方法表,找出所有访问属性为publicmethod方法。

    接下来,编译器检查方法调用中提供的参数类型,找出一个完全匹配的方法,这个过程称为重载解析。

    如果方法时privatestaticfinal修饰的,或者是构造器,那么编译器能准确地判断应该调用哪个方法,这称为静态绑定 

    程序运行时,如果子类son中定义了method()方法,则直接调用子类中地相应方法:如果子类son中没有定义相应地方法,则到其父类中寻找method()方法。

    动态绑定中每次调用方法都要进行搜素,时间开销相当大。因此虚拟机预先为每个类创建了一个方法表,其中列了所有方法地签名和实际调用地方法。

    方法地名称和参数列表称为方法地签名

          例如,f(int)和f(String)是两个具有相同名字,不同签名的方法。如果在子类中定义了一个与超类签名相同的方法,那么子类中的这个方法就覆盖了超类中的这个相同签名的方法。不过,返回类型不是签名的一部分,因此,在覆盖方法时,一定要保证返回类型的兼容性。允许子类将覆盖方法的返回类型定义为原返回类型的子类型。

  5)阻止继承:final类和方法

    不允许继承地类称为final类,在类的定义中用final修饰符加以说明。

    Final class Executive extends Manager

    {   ........ }

     类中的方法可定义为final的。这时子类就不能覆盖该方法。

    如果一个类声明为final,属于它的方法会被自动设为final,但不包括域(如果域定义为final,在对象构造以后,final域就不能再修改了)。

    Private final int Max = 100

    String类是final类的一个例子,不能扩展该类。

  6)强制类型转换

    如果要把一个超类对象赋给一个子类对象变量,就必须进行强制类型转换。其格式为:

    子类  对象 = (子类) (超类对象)

    Manager boss = (Manager) staff[0];   //ok!

    类型转换必须在继承层次内进行;而且在超类转换为子类之前。应必须使用instanceof操作符进行继承链检查。

  7)继承小结

    封装、继承和多态是面对对象的主要特征。

    继承可提高代码重用性,用extends关键字来实现。除构造方法之外,父类的所有方法和属性都被子类继承。

    继承建立了类与类间的关系,同时也是多态特征的前提。

    Java只支持单继承,不直接支持多继承(避免两个父类出现同名方法的调用选择困难)

    Abstract修饰的抽象类不能被实例化为对象,只能扩展子类:抽象类中的抽象方法充当着占位的角色,它们的具体实现在子类中。 

    Final类不允许被继承;类中final方法不允许被子类重写。

  8)受保护访问

    如果希望超类的某些方法或域允许被子类直接访问,就需要在超类定义时,将这些方法或域声明为protected

    Protected违背了OOP提倡的数据封装原则。实际中要谨慎使用protected的访问属性。

    若定义类时要限制类中某个方法的使用,就可以将它声明为protected。这表明子类得到信任,可以使用这个方法,而其他类则不行。

    Java4个访问权限修饰符:

    访问修饰符:private  protected  public  默认

    使用访问修饰符的原因:实现受限信息隐藏

    信息隐藏目的:

      对类中任何实现细节的更改不会影响使用该类的代码

      防止用户意外删除数据

      易于使用类

    访问修饰符:

      Public  该类或非该类均可访问

      Private  只有该类可以访问

      protected  该类及其子类的成员可以访问,同一个包中的类也可以访问

      默认  相同包中的类可以访问

位置

Private

默认

protected

Public

同一个类

同一个包内的类

不同包内的子类

不同包并且不是子类

            注:不写访问修饰符时默认为friendly

 (2)Object :所有类的超类

  1.Object 类是Java中所有类的祖先——每一个类都由它扩展而来。在不给出超类的情况下,Java会自动把Object作为要定义类的超类。

  2.可以使用类型为Object 的变量指向任意类型的对象。但要对它们进行专门的操作都要进行类型转换。

    Object obj = new Employee(“Harry Hacker”35000);

    Employee e = (Employee)obj;

  3.在Java中,只有基本类型(pimitive types)不是对象,例如,数组、字符和布尔类型的值都不是对象。所有的数组类型,不管是对象数组还是基本类型的数组都扩展于Object类。

  1)equals方法

   Object类中的equals方法用于测试某个对象是否同另一个对象相等。它在Object类中的实现是判断两个对象是否具有相同的引用。如果两个对象具有相同的引用,它们一定是相等的。

   如果需要检测两个对象状态的相等性,就需要在新类的定义中需要覆盖equals方法。

   定义子类的equals方法时,可调用超类的equals方法。

    Super.equalsotherObject

   getClass方法将返回一个对象所属的类。在检测中,只有两个对象属于同一个类时,才有可能相等。

  2)hashCode方法

    1.Object类中的hashCode方法导出某个对象的散列码。散列码时任意整数,表示对象的存储地址。

     2.两个相等对象的散列码相等。

     3.字符串的散列值是由内容导出的,s与t的散列值是一样的。字符串缓存sb与tb是不同的。

    4.如果重新定义equals方法,就必须重新定义hashCode方法,以便用户可以将对象插入到散列表中。

    5.hashCode方法应该返回一个整型数值(也可以是负数),并合理地组合实例域的散列码,以便能够让各个不同的对象产生的散列码更加均匀。

    6.需要组合多个散列值时,可以调用Objects.hash并提供多个参数。这个方法会对各个参数调用Objects.hashCode,并组合这些散列值。

     7.Equals与hashCode的定义必须一致:如果x.equals(y)返回true,那么x.hashCode()就必须与y.hashCode()具有相同的值。

     8.如果存在数组类型的域,那么可以使用静态的Ayyas.hashCode方法计算一个散列码,这个散列码由数组元素的散列码组成。

     9.java.lang.Object 1.0

      int hashCode()
      返回对象的散列码。散列码可以是任意的整数,包括正数或负数。两个想等的对象要求返回相等的散列码。

     10.java.lang.Objects 7

      int hash(Object… objects)
      返回一个散列码,由提供的所有对象的散列码组合而得到。

      static int hashCode(Object a)
      如果a为null返回0,否则返回a.hashCode()。

    11.java.util.Arrays 1.2

      static int hashCode(type[] a) 5.0
      计算数组a的散列码。组成这个数组的元素类型可以是object,int,long,short,char,byte,boolean,float或double。

   3)toString方法

    1.Object类的toString方法返回一个代表该对象域值的字符串。

    2.定义子类中的toString方法时,可先调用超类的toString方法。

      super.toString( )

    3.toString方法是非常重要的调试工具。标准类库中,多数类定义了toString方法,以便用户获得对象状态的必要信息。

    4.java.lang.Object 1.0

      Class getClass()
      返回包含对象信息的类对象。

      boolean equals(Object otherObject)

      比较两个对象是否相等,如果两个对象指向同一块存储区域,方法返回true;否则方法返回false。在自定义的类中,应该覆盖这个方法。

      String toString()

      返回描述该对象的字符串。在自定义的类中,应该覆盖这个方法。

    5.java.lang.Class 1.0

      String getName()
      返回这个类的名字。

      Class getSuperclass()
      以Class对象的形式返回这个类的超类信息。

  4)泛型数组列表

    Java中,利用ArrayList类,可允许程序在运行时确定数组的大小。

    ArrayList是一个采用类型参数的泛型类。为指定数组列表保存元素的对象类型,需要用一对尖括号将数组元素的对象类名括号起来加在后面。

    ArrayList<Employee> staff = new ArrayList<Employee>();

    没有< >ArrayList将被认为是一个删去了类型参数的“原始”类型。

      数组列表的操作

        ArrayList定义

        ArrayList <T>对象=new ArrayList<T>();

        例: ArrayList<Employee> staff = new ArrayList<Employee>(;

        API: ArrayList 的构造器

        ArrayList<T>()构造一 个 空数组列表

        - ArrayList<T>(int initialCapacity)构造 -一个具有指定容量的空数组列表

      b.添加新元素

        API: boolean add(T obj)

        把元素obj追加到数组列表的结尾

        例: staff.add(new Empleoy(..);

      c.统计个数

        API: int size()

        返回数组列表中当前元素个数

        例: staff.size();

      d.调整大小

        API: void trimToSize()

        把数组列表的存贮空间调整到当前大小

      e.访问

        API: void set(int index, T obj)

        将obj放入数组列表index位置,将覆盖这个位置的原有内容。

        API: T get(int index)

        获得指定位置index的元素值

        例: Employee harry = new Employee(..);

        staff.set(1, harry);

        Employeee = staff.get(1);

      f.增加与删除

        API: boolean add(int index, T obj)

        向后移动元素,在第n个位置插入obj

        API: T remove(int index);

        将第n个位置存放的对象删除,并将后面的元素前移动

        例:

        staff.add(i,harry);

        Employee e = staff.remove(i);

     5)对象包装器与自动装箱

      所有基本数据类型都有着与之对应的预定义类,它们被称为对象包装器(wrapper)

      以下前6个对象包装器类都是从公共包装器类Number继承而来。

        Integer

        Long

        F loat

        Double

        Short

        Byte

        Character

        Void :

        Boolean

      对象包装器类是不可变的,即一旦构造了包装器,就不允许更改包装在其中的值。且对象包装器类还是final,此不能定义它们的子类。

      使用对象包装器的好处:

        基本类型转化为对象

        定义一些有用的基本方法(static方法)

      在JavaSE5.0中,可以自动的将基本数据类型转换为包装器类的对象,将这种变换称为自动打包(autohoxine)

        ArrayList<Integer> list = new ArrayList<Integer> () ;

        list. add(3);//翻译成

        li st. add(new Integer (3)) ;

      相反地,当对一个包装器类的对象进行赋值或算法运算时,将会自动地拆包。

        intn = list. get(i);

        翻译成

        int n = list. get (i). intValue;

        integer n =3;

        n++;

      打包和拆包是编译器认可的。

     6)参数数量可变的方法

      在Java SE 5. 0以前的版本中,每个Java方法都有固定数量的参数。然而,现在的版本提供了可以用可变的参数数量调用的方法(称为“可变参”方法)

      用户自己可以定义可变参数的方法,并将参数指定为任意类型,甚至是基本类型。 

      public PrintStream printf(String fmt, Object... args)

      {

        return format(fmt, args); }

        System.out.print(%d %s”,n,widgets");

      实际的调用过程为:

        System.out.printf(%d %s",new Object[ ] {new Integer(n),widgets"});

      public static double max(double... values)

      {

        double largest = Double.MIN_ VALUE;

        for (double v: values) if (v > largest) largest = V;

        return largest;

      }

      double m = max(3.1, 40.4, -5);

      编译器将new double[] {3.1, 40.4, -5}传递给max方法。

     7)枚举类

      要定义一个成绩类,成绩的范围只能是ABCDE,接受其它类型的值都是违法的,应该如何定义呢?如何表示成绩呢?整型、字符型?都不合适,因为为没有明确的类型对应,即使是字符型,超出了ABCDE”范围的字符程序需要特别处,以便保证应用安全。

      JavaSE5.0以后版本中提供了一种称为枚举的类型定义方法。

      声明枚举类

        public enum Grade { A, B, C, D, E};

        它包括一~个关键字enum, -一个新枚举类型的名字

        Grade以及为Grade定义的一组值,这里的值既非整型,亦非字符型。

      枚举类说明

        枚举类是一个类,它的隐含超类是java. lang. Enum

        枚举值并不是整数或其它类型,是被声明的枚举类的自身实例,例如AGrade-一个实例。

        枚举类不能有public修饰的构造函数,构造函数都是隐含pr ivate,编译器自动处理。

        枚举值隐含都是由publicstaticfinal修饰的,无须自己添加这些修饰符。

        在比较两个枚举类型的值时,永远不需要调用equals方法,直接使用=="进行相等比较。

      为枚举类增加构造函数

        1.enum Size

        2.

        3.SMALL("S"), MEDIUM("MI"), LARGE("L"),

        EXTRA_ LARGE("XL"); :

        4.

        5.private Size(String abbreviation)

        6.

        7.this. abbreviation = abbreviation;

        8.

        9.public String getAbbreviation(

        10. {

         return abbreviation; }

        1l.

        12.private String abbreviation;

        13. }

    8)反射

      1.反射库(reflection library)提供了一个非常丰富且精心设计的工具集,以便编写能够动态操纵Java代码的程序。特别是在设计或运行中添加新类时,能够快速地应用开发工具动态地查询新添加类的能力。

      2.能够分析类能力的程序称为反射(reflaction)。反射机制的功能及其强大,在下面可以看到,反射机制可以用来:

         在运行中分析类的能力。

         在运行中查看对象。

         实现通用的数组操作代码。

         利用Method对象,这个对象很像C++中的函数指针。

    9)Class类

      1.在程序运行期间,Java运行时系统始终为所有的对象维护一个被称为运行时的类型标识。这个信息跟踪着每个对象所属的类。虚拟机利用运行时类型信息选择相应的方法执行。然而,可以通过专门的Java类访问这些信息。保存这些信息的类被称为Class。Object类中的getClass()方法将会返回一个Class类型的实例。

      2.一个Class对象将表示一个特定类的属性。

      3.可以调用Class类的静态方法forName获得类名对应的Class对象。如果类名保存在字符串中,并可在运行中改变,就可以使用这个方法。当然,这个方法只有在className是类名或接口名时才能执行。否则,forName方法将抛出一个checkedexception(已检查异常)。无论何时使用这个方法,都应该提供一个异常处理器(exception handler)。·

      4.一个Class对象实际上表示的是一个类型,而这个类型未必是一种类。

      5.虚拟机为每个类型管理一个Class对象。因此,可以利用==运算符实现两个类对象比较的操作。

      6.Class类的方法newInstance(),可以用来快速地创建一个类的实例。newInstance方法调用默认的构造器(没有参数的构造器)初始化新创建的对象。如果这个类没有默认的构造器,就会抛出一个异常。

    10)捕获异常

      1.异常有两种类型:未检查异常和已检查异常。对于已检查异常,编译器将会检查是否提供了处理器。未检查异常编译器不会查看是否为这些错误提供了处理器。

      2.java.lang.Class 1.0

        static Class forName(String className)
        返回描述类名为className的Class对象。

        Object newInstance()
        返回这个类的一个新实例。

      3.java.lang.reflect.Constructor 1.1

        Object newInstance(Object[] args)
        构造一个这个构造器所属类的新实例。
        参数:args 这是提供给构造器的参数。

      4.java.lang.Throwable 1.0

        void printStackTrace()
        将Throwable对象和栈的轨迹输出到标准错误流。

    11)利用反射分析类的能力

      1.在java.lang.reflact包中有三个类Field、Method和Constructor分别用于描述类的域、方法和构造器、这三个类都有一各叫做getName的方法,用来返回项目的名称。Field类有一个getType方法,用来返回描述域所属类型的Class对象。Method和Constructor类有能够报告参数类型的方法,它将返回一个整型数值,用不同的位开关描述public和static这样修饰符使用状况。另外,还可以利用java.lang.reflect包中的Modifier类的静态方法分析getModifiers返回的整型数值。还可以利用Modifier.toString方法将修饰符打印出来。

      2.Class类中的getFields、getMethods和getConstructors方法将分别返回类提供的public域、方法和构造器数组,其中包括超类的公有成员。Class类的getDelareFields、getDeclareMethods和getDeclaredConstructors方法将分别返回类中声明的全部域、方法和构造器,其中包括私有和受保护成员,但不包括超类的成员。

    12)在运行时使用反射分析对象

      1.查看对象域的关键方法是Field类中的get方法。

      2.除非拥有访问权限,否则Java安全机制只允许查看任意对象有哪些域,而不允许读取他们的值。

      3.反射机制的默认行为受限于Java的访问机制。然而,如果一个Java程序没有受到安全管理器的控制,就可以覆盖访问控制。

      4.setAccessible方法是AccessibleObject类中的一个方法,它是Field、Method和Constructor类的公共超类。这个特性是为调试、持久存储和相似机制提供的。

      5.调用Field的f.set(obj,value)可以将obj对象的f域设置成新值。

      6.使用Class的getDeclaredFields获取所有的数据域,然后使用setAccessible将所有的域设置为可访问的。对于每个域,获得了名字和值。

    13)使用反射编写泛型数组代码

      1.java.lang.reflect包中的Array类允许动态地创建数组。

      2.可以通过调用Array.getLength(a)获得数组的长度,也可以通过Array类的静态getLength方法的返回值得到任意数组的长度。而要获得新数组元素类型,就需要进行以下工作:
        1)首先获得数组的类对象。
        2)确认它是一个数组。
        3)使用Class类(只能定义表示数组的类对象)的getComponentType方法确定数组对应的类型。

    14)调用任意方法

      1.在Method类中有一个invoke方法,它允许调用包装在当前Method对象中的方法。invoke方法的签名是:Object invoke(Object obj,Object... args),第一个参数是隐式参数,其余的对象提供了显式参数。对于静态方法,第一个参数可以被忽略,即可以将它设置为null。

      2.如何得到Method对象呢?当然,可以通过调用getDeclareMethods方法,然后对返回的Method对象数组进行查找,直到发现想要的方法为止。也可以调用Class类中的getMethod方法得到想要的方法。它与getField方法类似。getField方法根据表示域名的字符串,返回一个Field对象。然而,有可能存在若干个相同名字的方法,因此要格外小心,以确保能够准确地得到想要的那个方法。有鉴于此,还必须提供想要的方法的参数类型。getMethod的签名是:Method getMethod(String name,Class... parameterTypes)

      3.如果在调用方法的时候提供了一个错误的参数,那么invoke方法将会抛出一个异常。

      4.nvoke的参数和返回值必须是Object类型的。这就意味着必须进行多次的类型转换。这样做将会使编译器错过检查代码的机会。因此,等到测试阶段才会发现这些错误,找到并改正它们将会更加困难。不仅如此,使用反射获得方法指针的代码要比仅仅直接调用方法明显慢一些。有鉴于此,建议仅在必要的时候才使用Method对象,而最好使用接口和内部类。特别要重申:建议Java开发者不要使用Method对象的回调功能。使用接口进行回调会使得代码的执行速度更快,更易于维护。

      5.java.lang.reflect.Method 1.1

        public Object invoke(Object implicitParameter,Object[] explicitParamenters)

        调用这个对象所描述的方法,传递给定参数,并返回方法的返回值。对于静态方法,把null作为隐式参数传递。在使用包装器传递基本类型的值时,基本类型的返回值必须是未包装的。

    15)继承设计的技巧

      ①将公共操作和域放在超类。

      ②不要使用受保护的域。

      ③使用继承实现“ is- -a”关系。

      ④除非所有继承的方法都有意义,否则就不要使用继承。

      ⑤在覆盖方法时,不要改变预期的行为。

      ⑥使用多态,而非类型信息。

第二部分:实验部分

1、实验目的与要求

(1) 理解继承的定义;

(2) 掌握子类的定义要求

(3) 掌握多态性的概念及用法;

(4) 掌握抽象类的定义及用途。

2、实验内容和步骤

实验1:导入第5章示例程序,测试并进行代码注释。

测试程序1:

  在elipse IDE中编辑、调试、运行程序5-1 —5-3(教材152-153

  掌握子类的定义及用法;

  结合程序运行结果,理解并总结OO风格程序构造特点,理解EmployeeManager类的关系子类的用途,并在代码中添加注释;

  删除程序中Manager类、ManagerTest类,背录删除类的程序代码,在代码录入中理解父类与子类的关系和使用特点。

子类的定义及用法:一个父类可以有多个子类,但是一个子类只能有一个父类。子类可以通过extends关键字来继承父类。

OO风格程序构造特点:封装,继承,多态,抽象

5-1程序代码:

package inheritance;		//package继承

/**
 * This program demonstrates inheritance.
 * @version 1.21 2004-02-21
 * @author Cay Horstmann
 */
public class ManagerTest
{
   public static void main(String[] args)
   {
      // construct a Manager object			(构造一个管理对象)
      var boss = new Manager("Carl Cracker", 80000, 1987, 12, 15);
      boss.setBonus(5000);				//使用setBonus方法

      var staff = new Employee[3];		//定义一个包含3个雇员的数组

      // fill the staff array with Manager and Employee objects    (用 Manager 和Employee对象填充staff数组)

      staff[0] = boss;
      staff[1] = new Employee("Harry Hacker", 50000, 1989, 10, 1);
      staff[2] = new Employee("Tommy Tester", 40000, 1990, 3, 15);

      // print out information about all Employee objects   (打印出所有Employee对象的信息,输出每个人的薪水)
      for (Employee e : staff)
         System.out.println("name=" + e.getName() + ",salary=" + e.getSalary());
   }
}

5-2程序代码:

package inheritance;	//包继承

import java.time.*;

public class Employee
{
   private String name;
   private double salary;
   private LocalDate hireDay;

   public Employee(String name, double salary, int year, int month, int day)
   {
      this.name = name;
      this.salary = salary;
      hireDay = LocalDate.of(year, month, day);
   }

   public String getName()		//getName方法
   {
      return name;
   }

   public double getSalary()	//getSalary方法
   {
      return salary;
   }

   public LocalDate getHireDay()	//getHireDay方法
   {
      return hireDay;
   }

   public void raiseSalary(double byPercent)
   {
      double raise = salary * byPercent / 100;
      salary += raise;
   }
}

5-3程序代码:

package inheritance;		//包继承

public class Manager extends Employee
{
   private double bonus;

   /**
    * @param name the employee's name
    * @param salary the salary
    * @param year the hire year
    * @param month the hire month
    * @param day the hire day
    */
   public Manager(String name, double salary, int year, int month, int day)		//提供一个子类构造器
   {
      super(name, salary, year, month, day);		//调用超类Employee中含有n,s,year,month和day参数的构造器
      bonus = 0;
   }

   public double getSalary()		// Manager类中的 getSalary方法
   {
      double baseSalary = super.getSalary();	//使用关键字super调用Employee类中的 getSalary方法
      return baseSalary + bonus;
   }

   public void setBonus(double b)
   {
      bonus = b;
   }
}

程序运行结果如下:

删除程序中Manager类、ManagerTest类 的结果及修改:

测试程序2:

   编辑、编译、调试运行教材PersonTest程序(教材163-165)

   掌握超类的定义及其使用要求;

   掌握利用超类扩展子类的要求;

   在程序中相关代码处添加新知识的注释;

   删除程序中Person类、PersonTest类,背录删除类的程序代码,在代码录入中理解抽象类与子类的关系和使用特点。

5-4代码:

package abstractClasses;

/**
 * This program demonstrates abstract classes.
 * @version 1.01 2004-02-21
 * @author Cay Horstmann
 */
public class PersonTest
{
   public static void main(String[] args)
   {
      var people = new Person[2];

      // fill the people array with Student and Employee objects
      //用Student和Employee对象填充people数组
      people[0] = new Employee("Harry Hacker", 50000, 1989, 10, 1);
      people[1] = new Student("Maria Morris", "computer science");

      // print out names and descriptions of all Person objects
      //打印出所有Person对象的名称和描述
      for (Person p : people)
         System.out.println(p.getName() + ", " + p.getDescription());
   }
}

5-5代码:

package abstractClasses;

public abstract class Person		//定义了抽象超类Person
{
   public abstract String getDescription();		//使用abstract关键字
   private String name;

   public Person(String name)			//Person类中保存着姓名和一个返回姓名的具体方法
   {
      this.name = name;
   }

   public String getName()
   {
      return name;
   }
}

5-6代码:

package abstractClasses;

import java.time.*;

public class Employee extends Person		//定义了子类Employee
{
   private double salary;
   private LocalDate hireDay;

   public Employee(String name, double salary, int year, int month, int day)	//提供一个子类构造器
   {
      super(name);
      this.salary = salary;
      hireDay = LocalDate.of(year, month, day);
   }

   public double getSalary()			//getSalary方法
   {
      return salary;
   }

   public LocalDate getHireDay()		// getHireDay方法
   {
      return hireDay;
   }

   public String getDescription()			//getDescription方法
   {
      return String.format("an employee with a salary of $%.2f", salary);
   }

   public void raiseSalary(double byPercent)	//raiseSalary方法
   {
      double raise = salary * byPercent / 100;
      salary += raise;
   }
}

5-7代码:

package abstractClasses;

public class Student extends Person		//定义了子类Student
{
   private String major;

   /**
    * @param name the student's name
    * @param major the student's major
    */
   public Student(String name, String major)
   {
      // pass name to superclass constructor	(将名称传递给超类构造函数)
      super(name);
      this.major = major;
   }

   public String getDescription()		//getDescription方法
   {
      return "a student majoring in " + major;
   }
}

代码运行结果如下:

删除程序中Person类、PersonTest 的结果以及修改:

测试程序3:

  编辑、编译、调试运行教材程序5-85-95-10,结合程序运行结果理解程序(教材174-177页);

  掌握Object类的定义及用法;

  在程序中相关代码处添加新知识的注释。

5-8程序代码:

package equals;

/**
 * This program demonstrates the equals method.
 * @version 1.12 2012-01-26
 * @author Cay Horstmann
 */
public class EqualsTest			//此程序实现了Employee类和Manager类的equals,hashCode,toString方法
{
   public static void main(String[] args)
   {
      var alice1 = new Employee("Alice Adams", 75000, 1987, 12, 15);
      var alice2 = alice1;
      var alice3 = new Employee("Alice Adams", 75000, 1987, 12, 15);
      var bob = new Employee("Bob Brandson", 50000, 1989, 10, 1);

      System.out.println("alice1 == alice2: " + (alice1 == alice2));

      System.out.println("alice1 == alice3: " + (alice1 == alice3));

      System.out.println("alice1.equals(alice3): " + alice1.equals(alice3));

      System.out.println("alice1.equals(bob): " + alice1.equals(bob));

      System.out.println("bob.toString(): " + bob);

      var carl = new Manager("Carl Cracker", 80000, 1987, 12, 15);
      var boss = new Manager("Carl Cracker", 80000, 1987, 12, 15);
      boss.setBonus(5000);
      System.out.println("boss.toString(): " + boss);
      System.out.println("carl.equals(boss): " + carl.equals(boss));
      System.out.println("alice1.hashCode(): " + alice1.hashCode());
      System.out.println("alice3.hashCode(): " + alice3.hashCode());
      System.out.println("bob.hashCode(): " + bob.hashCode());
      System.out.println("carl.hashCode(): " + carl.hashCode());
   }
}

5-9程序代码:

package equals;

import java.time.*;
import java.util.Objects;

public class Employee		
{
   private String name;
   private double salary;
   private LocalDate hireDay;

   public Employee(String name, double salary, int year, int month, int day)	
   {
      this.name = name;
      this.salary = salary;
      hireDay = LocalDate.of(year, month, day);
   }

   public String getName()		// getName方法
   {
      return name;
   }

   public double getSalary()	//getSalary方法
   {
      return salary;
   }

   public LocalDate getHireDay()	//getHireDay方法
   {
      return hireDay;
   }

   public void raiseSalary(double byPercent)	//raiseSalary方法
   {
      double raise = salary * byPercent / 100;
      salary += raise;
   }

   public boolean equals(Object otherObject)
   {
      // a quick test to see if the objects are identical	
      if (this == otherObject) return true;					//检测this与otherObject是否引用同一个对象

      // must return false if the explicit parameter is null	
      if (otherObject == null) return false;				//检测otherObject是否为null,如果为null,返回false

      // if the classes don't match, they can't be equal		
      if (getClass() != otherObject.getClass()) return false;	//比较this与otherObject是否属于同一个类

      // now we know otherObject is a non-null Employee		
      var other = (Employee) otherObject;					//强制类型转换

      // test whether the fields have identical values		(测试字段是否具有相同的值)
      return Objects.equals(name, other.name) 
         && salary == other.salary && Objects.equals(hireDay, other.hireDay);
   }

   public int hashCode()			//hashCode方法
   {
      return Objects.hash(name, salary, hireDay); 
   }

   public String toString()			//toString方法
   {
      return getClass().getName() + "[name=" + name + ",salary=" + salary + ",hireDay=" 
         + hireDay + "]";
   }
}

5-10程序代码:

package equals;

public class Manager extends Employee	
{
   private double bonus;

   public Manager(String name, double salary, int year, int month, int day)		//提供一个子类构造器
   {
      super(name, salary, year, month, day);
      bonus = 0;
   }

   public double getSalary()		//getSalary方法
   {
      double baseSalary = super.getSalary();
      return baseSalary + bonus;
   }

   public void setBonus(double bonus)	//使用setBonus方法
   {
      this.bonus = bonus;
   }

   public boolean equals(Object otherObject)		//equals方法
   {
      if (!super.equals(otherObject)) return false;
      var other = (Manager) otherObject;			//强制类型转换
      // super.equals checked that this and other belong to the same class (检查这个和其他都同属于一个类)
      return bonus == other.bonus;
   }

   public int hashCode()		//hashCode方法
   {
      return java.util.Objects.hash(super.hashCode(), bonus);
   }

   public String toString()		//Manager类中的toString方法
   {
      return super.toString() + "[bonus=" + bonus + "]";
   }
}

代码运行结果如下:

实验2:编程练习

  定义抽象类Shape

  属性不可变常量double PI,值为3.14

  方法public double getPerimeter()public double getArea())

  RectangleCircle继承自Shape类。

  编写double sumAllArea方法输出形状数组中的面积和和double sumAllPerimeter方法输出形状数组中的周长和。

   main方法中

1输入整型值n,然后建立n个不同的形状。如果输入rect,则再输入长和宽。如果输入cir,则再输入半径。
2 然后输出所有的形状的周长之和,面积之和。并将所有的形状信息以样例的格式输出。
3 最后输出每个形状的类型与父类型使用类似shape.getClass()(获得类型)shape.getClass().getSuperclass()(获得父类型);

思考sumAllAreasumAllPerimeter方法放在哪个类中更合适?

输入样例:

输出样例:

 程序代码:

shape.java

package shape;

public abstract class shape {
		double PI = 3.14;
		public abstract double getPerimeter();
		public abstract double getArea();
}

Rectangle.java

package shape;

public class Rectangle extends shape {
	private double width;
	private double length;
	public Rectangle(double w, double l)
	{
		this.width = w;
		this.length = l;
	}

	@Override
	public double getPerimeter() {
		// TODO Auto-generated method stub
		double Perimeter = (width+length)*2;
		return Perimeter;
	}

	@Override
	public double getArea() {
		// TODO Auto-generated method stub
		double Area = width*length;
		return Area;
	}

	@Override
	public String toString() {
		// TODO Auto-generated method stub
		return getClass().getName() + "[ width=" + width + "]" + "[length=" + length + "]";
	}

}

Circle.java

package shape;

public class Circle extends shape {

	private double radius;
	public Circle(double r)
	{
		radius = r;
	}

	@Override
	public double getPerimeter() {
		// TODO Auto-generated method stub
		double Perimeter = 2*PI*radius;
		return Perimeter;
	}

	@Override
	public double getArea() {
		// TODO Auto-generated method stub
		double Area = PI*radius*radius;
		return Area;
	}

	@Override
	public String toString() {
		// TODO Auto-generated method stub
		return getClass().getName() + "[radius=" + radius + "]";
	}
	
}

shapeTest.java

package shape;

import java.util.Scanner;

public class shapeTest {

	public static void main(String[] args) {
		// TODO Auto-generated method stub
		Scanner in = new Scanner(System.in);
		String rect = "rect";
		String cir = "cir";
		System.out.println("请输入形状个数:");
		int n = in.nextInt();
		shape[] score = new shape[n];
		for(int i = 0; i < n; i++)
			{
				System.out.println("(rect or cir):");
				String input = in.next();
			if(input.equals(rect))
				{
					double length = in.nextDouble();
					double width = in.nextDouble();
					System.out.println("Rectangle["+"length:"+length+"width:"+width+"]");
					score[i] = new Rectangle(width,length);
				}
			if(input.equals(cir))
				{
					double radius = in.nextDouble();
					System.out.println("Circle["+"radius:"+radius+"]");
					score[i] = new Circle(radius);
				}
			}
		shapeTest c = new shapeTest();
		System.out.println(c.sumAllPerimeter(score));
		System.out.println(c.sumAllArea(score));
		for(shape s:score)
			{
				System.out.println(s.getClass()+","+s.getClass().getSuperclass());
			}
		in.close();
	}

	private double sumAllArea(shape[] score) {
		// TODO Auto-generated method stub
		double sum = 0;
		for(int i = 0; i < score.length; i++)
			sum+= score[i].getArea();
		return sum;
	}

	private double sumAllPerimeter(shape[] score) {
		// TODO Auto-generated method stub
		double sum = 0;
		for(int i = 0; i < score.length; i++)
			sum+= score[i].getPerimeter();
		return sum;
	}
}

运行结果:

3. 实验总结

  本次学习理解了Java程序设计中类与对象的关系,理解OO程序设计的特征:继承和多态,并学会采用继承定义类设计程序,掌握利用了父类定义子类的语法规则及对象使用要求。在完成实验过程中理解了继承的定义,掌握子类的定义要求,多态性的概念及用法,抽象类的定义及用途。本章知识比较多也比较重要,仍需要多多加强巩固,继续努力。

猜你喜欢

转载自www.cnblogs.com/LZ-728672/p/11607019.html