继承(一)

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;

}

posted @ 2014-11-03 16:29  米其林轮船  阅读(155)  评论(0编辑  收藏  举报