zoukankan      html  css  js  c++  java
  • 继承(一)

    Java中只有共有继承,使用关键字 extends

    class Manager extends Employee

    {

        //添加方法和成员变量

    }

    C++中包含公有,保护,私有继承:

    class A:public B

    {};

    class A:protected B

    {};

    class A:private B

    {};

    Java中超类就是基类、父类。

    Java中的成员变量都是声明为私有的。虽然子类可以继承超类的私有成员变量,但是不能直接访问它们。必须通过超类的公有访问方法。那么如果要重新定义超类的方法(覆盖),并在该方法中调用超类的该方法,应该如何做呢?在C++中,可以使用【基类名::成员方法】,在Java中,使用【super.成员方法】。

    另外,super关键字在构造函数也有作用。无论在C++或者Java中,我们都需要在子类的构造函数中调用父类的构造函数,以初始化从父类继承来的成员变量。在C++中,这一点通过在初始化列表中实现。在Java中,直接在子类的构造函数中第一行调用【super(para1,para2,...)】,它会调用父类含有参数(para1,para2,...)的构造函数。如果没有调用super,则会调用父类的默认构造函数(无参数)。

    在Java中,父类的对象变量可以引用父类或者子类的对象,动态绑定是默认的处理方式,不需要将方法声明为virtual。如果不希望让一个方法具有虚拟特征,可以将它标记为final。

    由于Java中只有共有继承,因此它与C++的public继承一样,表示"is-a"的关系。

    private,static,final或构造函数为静态绑定。【重载】和【覆盖】为动态绑定。

    【重载】的时候,根据名字和参数列表决定该调用哪个函数。方法的【名字】和【参数列表】被成为方法的签名。【返回类型】不是签名的一部分。

    在【覆盖】方法的时候,在JDK5.0以前的版本中,要求返回类型必须是一样的;而现在允许子类将覆盖方法的返回类型定义为原返回类型的子类型。例如:

    class Employee中:public Employee getBuddy(){...}

    子类 class Manager中:public Manager getBuddy(){...}

    虚拟机预先为每个类创建一个方法表,其中列出了所有方法的签名和调用方法。对象变量调用方法时,会根据该变量引用的实际对象类型查找相应的方法表。

    在覆盖一个方法的时候,子类方法的访问权限不能低于超类方法的访问权限。(即不能子类private,超类public)

    动态绑定不需要重新编译,就可实现程序的扩展。

    不允许某个类定义子类,该类称为final类。声明格式如下:

    final class A

    {...}

    类中的方法也可以被声明为final,如果这样做,子类就不能覆盖这个方法。例如:

    class Employee

    {

        ...

        public final String getName()

        {

            return name;

        }

        ...

    }

    前面说过,成员变量也可以声明为final,对于final变量来说,构造对象之后就不允许改变他们的值了(常量)。如果将一个类声明为final,只是将其中的方法自动地成为final,而不包括成员变量。

    如果超类对象变量引用的是一个子类变量,可以将超类对象变量的类型强制转换为子类类型。例如

    Employee e = new Manager;

    Manager boss = (Manager)e;

    如果e不是引用的Manager对象,而是引用的Employee对象,则强制转换会引起运行时的异常。在类型强制转换之前,可以使用【instanceof】运算符检查一下转换能否成功。

    if(e instanceof Manager)

    {

        boss = (Manager)e;

    }

    一般情况下,应该尽量少用类型转换和instanceof运算符。

    包含一个或多个抽象方法的类必须被声明为抽象类。

    abstract class Person

    {

        public abstract String getDescription();

    }

    抽象类也可以不含有抽象方法。

    子类如果没有定义超类的所有抽象方法,那么子类也必须标记为抽象类。

    抽象类不能被实例化,但是可以定义一个抽象类的对象变量,用它引用非抽象子类的对象。

    在C++中,含有纯虚函数的类被称为虚基类,也是抽象类,它不能被实例化。C++没有【abstract】关键字。

    private--仅对本类可见

    public--对所有类都可见

    protected--对本包及所有子类都可见(慎用)

    无修饰符--对本包可见(最好不用)

    Object类是所有类的超类。除了基本类型(数值,字符,boolean)的值不是对象,包括数组在内的其他类型都继承Object类。

    equals方法

    Object类中的equals方法用于检测一个对象是否等于另外一个对象。在Object类中,这个方法将判断两个对象是否具有相同的引用。然而对于多数类来说,这种判断没什么意义。例如判断两个Employee对象是否相等,其实我们想检查两个对象的成员变量【ID】是否相等,如果相等,我们认为两个对象相等。

    因此我们需要覆盖Object类的equals方法:

    public boolean equals(Object otherObject) //由于在Object类中,equals的参数类型是Object,因此在子类中,该参数也必须是Object类型,否则就不是覆盖了
    {
        if(this == otherObject) return true; //Java里this不是个指针,用法像个对象。这里是判断两个对象变量是否引用同一变量
        if(otherObject == null) reurn false;
        if(getClass() != otherObject.getClass()) return false; //getClass()返回实际对象的类型
        Employee other = (Employee) otherObject; //由上面的判断已知otherObject是一个非空的Employee类型的变量,此处做类型转换是为了调用Employee的方法和成员变量。
        return ID.equals(other.ID);
    }

    在Employee的子类Manager中定义equals方法,需要先调用超类的equals方法,以检测超类部分的成员变量是否相等。

    public boolean equals(Object otherObject)
    {
        if(!super.equals(otherObject)) return false;
        Manager other = (Manager)otherObject;
        return (bonus == other.bonus);
    }

    这里,我们除了ID,还比较了bonus。如果我们定义了Manager类的equals方法,一定要在Employee中使用getClass的判断,因为如果使用instanceof,会导致equals的对称性遭到破坏。且这里equals要求两个变量类型都必须是Manager,才可能返回true(因为getClass)。

    实际上,我们真正关心的只有员工的ID是否相等,因此对于Manager类,实际上并不需要单独定义一个equals方法。可以把Employee类型的equals方法声明为final。还要注意将getClass的判断换成【if(!(otherObject instanceof Employee))】。这样,即使m是Manager类型,e是Employee类型,只要ID相等,m.equals(e)和e.equals(m)都为true。

    如果子类能够拥有自己的相等概念,那么对称性要求将强制采用getClass进行检测。

    如果由超类决定相等的概念,那么就可以使用instanceof进行检测,这样可以在不同子类的对象之间进行相等的比较。

    hashCode方法

    hashCode方法定义在Object类中,如果重新定义equals方法,就必须重新定义hashCode方法。如果x.equals(y)返回true,那么x.hashCode()就必须与y.hashCode()具有相同的值。hashCode返回int类型的值。

    toString方法

    toString方法也定义在Object类中,它用于返回表示对象值得字符串。绝大多数的toString方法都遵循这样的格式:

    类的名字[para1=...,para2=...,...]

    例如Employee类中的toString方法的实现:

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

    这里getClass().getName()返回类的名字,这里返回"Employee"。之所以不直接用"Employee",是因为Manager类是Employee的子类,在定义它的toString方法时,只需要

    public String toString()
    {
        return super.toString()
            + "[bonus=" + bonus
            + "]";
    }

    如果x是一个定义了toString方法的类的对象变量,那么+x会自动调用x.toString()

    System.out.println(x)也会打印x.toString()的结果

    泛型数组列表ArrayList

    Java中的ArrayList与C++中的vector十分类似。

    ArrayList<Employee> staff = new ArrayList<Employee>();//声明和构造,如果一开始知道大概的capacity,就可以写在构造中,这样就避免多次分配空间。

    staff.add(new Employee("Harry Hacker",...));//添加到结尾

    staff.size()//返回数组列表的当前元素数量

    trimToSize()//将capacity调整为staff.size(),避免空间浪费,不过如果再add,就得重新分配空间。

    ArrayList没有重载“[]”运算符,因此不能通过下标访问元素。必须通过set和get方法:

    staff.set(i,harry) //等价于staff[i] = harry,注意,添加元素不能用set,set只能用于改变已存在的元素的值。

    Employee e = staff.get(i) //等价于Employee e = staff[i]

    在数组列表的任意地方插入、删除元素:

    staff.add(n,e)//在n之前插于一个新元素,n开始向后的所有元素向后移动一位,效率低。

    staff.remove(n) //删除下标n的元素,n之后的所有元素向前移动一位,效率低。

    Java中,有时需要将诸如int这样的基本类型转换为对象。例如想定义一个整形数组列表,而尖括号中的类型参数不允许是基本类型,即不能写成ArrayList<int>。所有基本类型都有与之对应的类,这些类称为包装器。这些包装器类拥有很鲜明的名字:Integer、Long、Float、Double、Short、Byte、Character、Void和Boolean。对象包装器类时不可变的,即一旦构造了包装器,就不允许更改包装在其中的值。所以整形数组列表只能写成ArrayList<Integer>。注意,ArrayList<Integer>的效率远低于int[]数组。因此,应该用它构造小型集合。

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

    【list.add(3)】将自动变换成【list.add(new Integer(3))】,这种变换称为自动打包。

    【int n=lisg.get(i)】将自动变换成【int n=list.get(i).intValue()】,这种自动变换称为自动拆包。

    注意,要判断两个Integer对象变量是否相等,应该用equals方法,==运算符只是判断两个变量是否引用同一个对象。

    Integer类有一些静态方法,用于字符串和数值类型之间的转换,其他类也有:

    int intValue()//以int的形式返回Integer对象的值

    static String toString(int i)//将int型i转化为十进制字符串,在C语言中,用snprintf实现这个功能。

    static String toString(int i,int radix)//将int型i转化为radix进制字符串,在C语言中,用snprintf实现这个功能。

    static int parseInt(String s)//将字符串s转化为int型,s为十进制,在C语言中,用atoi()实现这个功能。

    static int parseInt(String s,int radix)//将字符串s转化为int型,s为radix进制,在C语言中,用atoi()实现这个功能。

    static Integer valueOf(String s)//将字符串s转化为Integer对象,s为十进制

    static Integer valueOf(String s,int radix)//将字符串s转化为Integer对象,s为radix进制

    注意,除了intValue方法外,其他方法都是static方法。非static方法需要【对象变量.方法名】的方式调用,static方法用【类名.方法名】的方式调用。

    Java的方法是传值的,如果想想修改参数值,就要使用在org.omg.CORBA包中定义的持有者类型,包括IntHolder、BooleanHolder等等。每个持有者类型都包含一个公有成员变量value,通过它可以访问存储在其中的值。

    public static void triple(IntHolder x)

    {

        x.value = 3* x.value;

    }

  • 相关阅读:
    2020-03-12推荐阅读链接
    一问带你区分清楚Authentication,Authorization以及Cookie、Session、Token
    Alibaba-技术专区-开源项目之Nacos源码分析
    Alibaba-技术专区-开源项目之Nacos功能特性
    Alibaba-技术专区-开源项目之Nacos环境准备
    Java-技术专区-javaAgent(插桩,attach)
    SkyWalking 6.x 源码分析 —— 调试环境搭建 (非原创)
    SkyWalking 6.2-Docker-Compose搭建单体
    Zookeeper-技术专区-配置以及学习
    Java-技术专区-设计模式-reactor模式
  • 原文地址:https://www.cnblogs.com/johnsblog/p/4071507.html
Copyright © 2011-2022 走看看