Java核心技术——第5章

类、超类和自类

定义子类

由继承Employee类来定义manager类

public class Manager extends Employee//使用extends来定义继承
{
	...
}

Employee类称为超类、基类、父类

manager类称为子类、派生类、孩子类

子类可使用超类的方法、域

应该将通用的方法放在超类中,而特殊用途的方法放在子类中

覆盖方法

在子类中添加一个与超类中同名的方法即可覆盖超类的方法

在子类中不能直接访问超类的私有域,需要通过超类中的公有接口,如getSalary()

子类想调用超类中同名的方法时,需要借助super关键字

public double getSalary()
{
	return baseSalary + super.getSalary();
}

:super和this关键字由区别,super不是一个对象的引用,只能访问类中的方法,不能访问对象中的域,不能赋给一个对象变量,而this可以,例如,不能通过super.salary访问超类中的私有域

子类构造器

在子类中想使用超类的构造器,使用super关键字

public Manager(String name,double salary,int year,int month,int day)
{
	super(name,salary,year,month,day);
	bonus=0;
}

super调用超类构造器的语句必须在子类构造器的第一句

子类没有显式调用超类构造器,则自动调用超类默认(没有参数)的构造器

如果超类中没有不带参数的构造器,并且在子类构造器中没有显式调用超类其他构造器,则编译器报错

继承层次

由一个公共超类派生出来的所有类的集合被称为继承层次

多态

定义:一个对象可以指示多种实际类型的现象

置换法则:程序中出现超类对象的任何地方都可以用子类对象置换

对象变量是多态的,一个超类变量可以引用其任何的子类对象,但反过来就不成立,子类变量不能引用超类对象

理解方法调用

动态绑定:在运行时自动选择调用哪个方法的现象

例:调用类C的对象x的方法,x.f(args)

  1. 编译器列出类C中所有名为f的方法,和其超类中访问属性为public且名为f的方法(超类的私有方法不可访问)

  2. 编译器查看调用方法时提供的参数类型

    如果找不到相应参数类型,则找可以类型转换之后的方法

    如果还是找不到,或者转换后的有多个方法,则报错

    方法名和参数类型为方法的签名,返回类型不是签名的一部分。可以允许返回子类型,即必须保证兼容性

  3. 如果是private方法、static方法、final方法或者构造器,则编译器可以准确知道应该调用哪个方法,称为静态绑定

阻止继承:final类和方法

对类使用final修饰符,表明该类不允许拓展

public final class Executive extends Manager
{
	...
}

类中的特定方法被声明为final后,子类不能覆盖这个方法

public class Employee
{
	public final String getName()
	{
		...
	}
}

将类边设置为final之后,其中的方法都变成final,但是域不会变成final,final域表示构造对象之后就不能改变其值

强制类型转换

只能在继承层次内进行类型转换

在将超类转换成子类之前,应该使用instanceof进行检查

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

应尽量避免使用类型转换和instanof运算法,往往强转是为了使用子类的方法,这说明超类设计不合理,应该重新设计超类

抽象类

关键字abstract

为何要设置抽象类:将一些通用的方法放在继承通用超类中

包含抽象方法的类必须被声明为抽象类

抽象方法充当占位的作用,具体的实现放在子类中

抽象类不能创建对象,但可以创建其子类的对象

受保护类

关键字protected

超类中的某些方法或者某个域允许被子类访问

访问修饰符总结

  1. private——仅对本类可见
  2. public——对所有类可见
  3. protected——对本包和所有子类可见
  4. 无修饰符——对本包可见

Object:所有类的超类

可以使用Object类型变量引用任何类型的对象,但是这个变量不能使用其中的域和方法,必须通过类型转换后才能

Java中只有基本类型不是对象,所有数组类型都扩展了Object类

equals方法

Object类的equals方法用于检测两个对象是否具有相同的引用,通常没有意义

相等测试与继承

equals方法应具有的特性:

  1. 自反性:任何非空引用想,x.equals(x)为true
  2. 对称性:任何引用xy,x.equals(y)和y.equals(x)相等
  3. 传递性:任何引用xyz,若x.equals(y)为true,y.equals(z)为true,则x.equals(z)为true
  4. 一致性:如果xy引用的对象没有变化,则反复调用x.equals(y)应返回同样的结果
  5. 对于任意非空引用x,x.equals(null)为false

image-20200414161436855

常见错误:

没有覆盖Object类中的equals方法,显式参数类型必须是Object

@Override public boolean equals(Object other)

可以在方法前添加标记,如果没有重写,则会报错

hashCode方法

散列码(hash code)是由对象导出的一个整数值。

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

toString方法

用于返回对象值的字符串

最好通过getClass().getName()获得类名的字符串,而不要将类名硬加到toString方法中,好处是,它的子类可以通过super来调用这个方法

Object类定义了toString方法,默认打印对象所属的类名和散列码

注:toString方法是一种非常有用的调试工具,每个类都应该增加toString方法

泛型数组列表

数组:

Java中的数组允许在运行时确定数组大小

int actualSize = ...;
Employee[] staff = new Employee[actualSize];

ArrayList:

是一个采用类型参数的泛型类,具有自动调节数组容量的功能

ArrayList<Employee> staff = new ArrayList<Employee>();
ArrayList<Employee> staff = new ArrayList<>();//new后的类型可省略,检查前面的类型,然后放在后面

使用add方法添加元素到数组列表

staff.add(new Employee("Harry Hacker",...));

如果add调用add且内部数组已满,则数组列表自动创建一个更大的数组,并把所有的对象拷到较大的数组中

如果已经清楚数组可能存储的元素数量,则可以用ensureCapacity方法

staff.ensureCapacity(100);

可以在初始化时给出列表容量,并不是只能放这么多的元素,而是超出容量后会重新分配空间

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

size方法返回数组列表包含的实际元素数量,相当于数组的length方法

staff.size()

一旦能确定数组列表大小不变,则使用trimToSize方法整理列表,但是之后再添加就要花时间移动存储块

访问数组列表元素

staff.set(i,harry);//相当于a[i]=harry;
Employuee e = staff.get(i);//相当于Employee e = a[i];

插入元素

int n = staff.size()/2;
staff.add(n,e);//位于n之后的元素都要向后移动一个位置,如果超出容量,则要重新分配空间

可以使用for-each来遍历数组列表

for(Employee e : staff)
{
	...
}
//等价于
for(int i=0;i<staff.size();i++){
	Employee e = staff.get(i);
}

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

public void update(ArrayList list){...}
public ArrayList find(String query){...}

可以将类型化的数组列表传给update,如ArrayList<Employee>

将原始类型的数组列表传给类型化的数组列表得到警告,但是不必做什么,在确定了转换没有问题后,可以用@SuppressWarnings("unchecked")来标注这个类型转换

对象包装器与自动装箱

将基本类型int放入ArrayList中,需要先包装成对象:Integer

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

对象包装器类是不可变的,一旦构造了包装器,则不能改变包装在其中的值

对象包装器是final,不能创建子类

自动装箱:

自动将基本数据类型包装成相应的对象,放入数组列表中

ArrayList<Integer> list = new ArrayList<>();
list.add(3);//自动包装成Integer

自动拆箱:

int n = list.get(i);

算数表达式中也能自动装箱自动拆箱

Integer n = 3;
n++;

如果混合使用Integer和Double类型,Integer会自动拆箱,提升为Double,在装箱为Double

还能将某些基本方法(静态方法)放置在包装器中,例如,将一个数字字符串转换成数值

参数数量可变的方法

printf方法的定义:

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

其中省略号表明该方法可以接收任意数量的对象,相当于Object[] args

posted @ 2020-04-15 11:35  咸鱼不闲咋整啊  阅读(339)  评论(0编辑  收藏  举报