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;
}