您的位置:首页 > 编程语言 > Java开发

Java核心技术(第8版)学习笔记_继承(1)

2016-02-12 11:49 489 查看

第5章 继承

__5_1 类,超类和子类

1)子类不能直接访问超类的私有域,使用
super.get...()
,调用超类的共有方法访问超类的私有域。

2)构造器

调用超类的构造器——在一个构造器的第一条语句调用super(),与this()类似,前者调用超类构造器,后者调用子类自身的其他构造器。


3)Java中,对象变量是多态的,Employee变量既可以引用一个Employee对象,也可以引用Employee类的任何一个子类的对象。

Manager boss = new Manager(...);
Employee staff = new Employee[3];
staff[0] = boss;


staff[0] 和 boss 引用同一个对象,但编译器将staff[0]看做Employee对象,这意味着可以调用

boss.setBonus(5000);


而不可以调用


staff[0].setBonus(5000);


另外,不可以将超类的引用赋给子类变量。

在Java中,子类数组的引用可以转换成超类数组的引用,而不需要强制类型转换。

Manager[] managers = new Manager[10];


将它转换为Employee[]是完全合法的:

Employee[] staff = managers;


此时执行

staff[0] = new Employee(...);


是合法的。而后再调用

managers[0].setBonus(1000);


将发生错误,确保数组元素类型以避免此错误。

4)动态绑定

a)如果private,static,final方法,编译器将可以准确知道应该调用哪个方法,这种调用方式称为“静态绑定”(static binding)

b)调用的方法依赖于隐式参数的实际类型,并在运行时实现动态绑定

c)Java SE 5.0之后的版本允许子类将覆盖方法的返回类型定义为原返回类型的子类型

d)在覆盖一个方法时,子类方法不能低于超类方法的可见性

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

不允许继承的类称为final类,final类中的方法自动为final,而不包括域
类中的方法被声明为final,子类就不能覆盖这个方法


6)强制类型转换

在继承链上进行向下的类型转换,即超类向子类的转换前,应先查看一下是否能够成功地转换


if(staff[1] instanceof Manager)
boss = (Manager) staff[1];


对象变量x为null时

x instanceof c


不会产生异常,而是返回false

7)抽象类

包含一个或多个抽象方法的类本身必须被声明为抽象的。
抽象类黑可以包含具体数据和具体方法。
抽象方法可使用abstract,这样就不必实现这个方法。


public abstract String getDescription();


定义全部抽象方法的子类就不是抽象的了。

即使类不包含抽象方法也可以将类声明为抽象类。

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

8)受保护访问

将方法或域声明为protected可以使子类访问超类的方法和域,但是Manager类中的方法只能访问Manager类中的域,而不能访问Employee对象中的这个域。
慎用protected属性,假设设计了带有受保护域的类,因其他程序员可能由此类派生出新类,并访问受保护域,在对这个类修改时就必须通知所有使用这个类的程序员,这违背了数据封装原则。


9)访问修饰符

访问修饰符可见性
private仅对本类可见
public对所有类可见
protected对本包和所有子类可见
默认对本包可见

__5_2 Object:所有类的超类

没有明确的指出超类时,Object就被认为是这个类的超类,Object类型的变量可以引用任何类型的对象,但是在具体操作前需要进行类型转换。

Java中只有基本类型(primitive types)不是对象。

1)Equals方法

Object类中的equals方法判断两个对象是否具有相同的引用。
Employee覆盖equals方法:


public boolean equals(Objec otherObject)
{
if(this == otherObject)
return true;
if(otherObject == null)
return false;
if(getClass() != otherObject.getClass())
return false;
Employee other = (Employee) otherObject;
return .......;
}


Manager覆盖equals方法:

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


2) 相等测试与继承 使用getClass还是instanceof

1)如果子类能够拥有自己的相等概念,则对称性需求将强制采用getClass进行检测
2)如果超类决定相等的概念,可以使用instanceof
3)数组类型可用Array.equals方法检测相应数组元素是否相等


3)HashCode方法

1)String类的散列码计算


int hash = 0;
for(int i=0; i < length(); i++)
hash = 31 * hash + charAt(i);


相同的字符串有相同的散列码,因字符串的散列码是由内容导出的

2)hashCode方法定义在Object类中,每个对象都有一个默认的散列码,其值为对象的存储地址。

3)如果重新定义equals方法,就必须重新定义hashCode方法,以便用户可以将对象插入散列表中。Equals和hashCode的定义必须一致,如果
x.equals(y)
返回true,那么
x.hashCode()
就必须与
y.hashCode()
具有相同的值。

4)数组类型的域可用静态的Arrays.hashCode方法计算一个散列码,这个散列码由数组元素的散列码组成。

5)hashCode 方法示例

class Employee
{
public int hashCode()
{
return 7 * name.hashCode()
+ 11 * new Double(salary).hashCode()
+ 13 * hireDay.hashCode();
}
}


4)ToString方法

1)Object类定义的toString方法,返回类名和散列码,如
`System.out.println(System.out);`
将输出
java.io.PrintStream@2f6684
2)数组继承了object类的toString方法,使用Arrays.toString,打印多维数组使用Arrays.deepToString


__5_3 泛型数组列表

1)ArrayList是一个采用类型参数(type parameter)的泛型类(generic class)。

2)使用add方法时内部数组已经满了,数组列表将自动创建一个更大的数组并把所有对象拷贝,可以在填充前使用ensureCapacity方法避免重新分配空间。

staff.ensureCapacity(100);


3)在确认数组列表大小不再变化后,使用trimToSize方法,回收多余的存储空间。

4)使用toArray方法将数组元素拷贝到一个数组中

X[] a = new X[list.size()];
list.toArray(a);


5)类型化与原始数组列表的兼容性

Java SE 5.0以后的版本使用类型参数的数组列表,如ArrayList<Employee>,当发生与原始ArrayList类型交叉操作时


public class EmployeeDB
{
public void update(ArrayList list){...}
public ArrayList finde(String query){...}
}


可以将类型化的数组列表传递给update方法,而不需任何类型转换

将原始的ArrayList赋给一个类型化的ArrayList会得到一个警告

ArrayList<Employee> result = employeeDB.find(query);    //yields warning, yields may means output


使用类型转换不能避免出现警告

ArrayList<Employee> result = (ArrayList<Employee>) employeeDB.find(query);  //yields another warning


在程序运行时,所有数组列表都是一样的,即没有虚拟机中的类型参数。

__5_4 对象包装器和自动打包

Integer类对应基本类型int,通常,这些类被称为包装器(wrapper)。

对象包装器类是不可变的,一旦构造了包装器,就不允许更改包装在其中的值,同时,对象包装器类是final。

数组列表中尖括号中的类型参数不允许是基本类型,可以声明一个Integer对象的数组列表,在调用

list.add(3);


时自动变换成

list.add(new Ineger(3));


这种变换称为自动打包(autoboxing / autowrapping)

相反的,当一个Interger对象赋值给int值时,会自动拆包

int n = list.get(i);
<–>
int n = list.get(i).intValue();


自动打包和拆包在算数表达式中也适用,但是在比较两个包装器对象时应适用equals方法。

自动打包规范要求boolean,byte,char<=127,介于-128~127之间的short和int被包装到固定的对象中。

自动打包和拆包是编译器认可的而不是虚拟机

public static void triple(Integer x)    //won't work
{
x = 3*x;
}


想要编写修改数值参数值的方法,需要适用在org.omg.CORBA包中定义的持有者(holder)类型,包括IntHolder,BooleanHolder等,每个持有者有一个公有域值,可以通过它访问存储其中的值。

public static void triple(IntHolder x)
{
x.value = 3 * x.value;
}
Integer.toString(int i, int radix)
Integer.parseInt(String s, int radix)
Integer.valueOf(String s, int radix)
//radix 表示基于给定radix参数进制的表示。


__5_5 参数数量可变的方法

public class PrintStream
{
public PrintStream printf(String fmt, Object... args){ return format(fmt, args);}
}


省略号…是Java代码中的一部分,表明这个方法可以接受任意数量的对象,实际上printf接收String和Object[]两个参数

public static double max(double... values)
{
doublc largest = Double.MIN_VALUE;
for(double v : values)
if(v > largest)
largest = v;
return largest;
}


允许将一个数组传递给可变参数方法的最后一个参数,如

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


因此可以将已经存在且最后一个参数是数组的方法重新定义为可变参数的方法,而不会破坏任何已经存在的代码。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: