继前面我们对类和对象进行系统学习之后,我们来继续学习OOP的另一个基本概念:继承。利用继承,可以基于已存在的类构造一个新类。继承已存在的类就是复用(继承)这些类的方法和域,在此基础上,可以添加一些新的方法和域,以满足新的需求。
此外,我们也将介绍反射的概念。反射即在程序运行期间发现更多的类及其属性的能力,不过这个强大的特性更吸引开发软件工具人员的关注,编写应用程序的人员则不太关注,所以我们粗略介绍下。
3、泛型数组列表
ArrayList是一个采用类型参数的泛型类,使用起来有点像数组,但在添加或者删除元素时,具有自动调节数组容量的功能。为了指定数组列表保存的元素对象类型,需要用<>将类名括起来加在后面,如ArrayList< Employee >。
如我们声明并构造一个保存Employee对象的数组列表:
ArrayList<Employee> staff = new ArrayList<>();
(1)使用add方法可以将元素添加到数组列表中
staff.add(new Employee("Harry",···));
数组列表管理着对象引用的一个内部数组,如果调用add且内部数组已经满了,数组列表就将自动的创建一个更大的数组,并将所有的对象从较小的数组中拷贝到较大的数组中。
(2)如果事先能估计出数组可能存储的元素数量,可以利用下述两种方式分配初始容量
staff.ensureCapacity(100);
或者
ArrayList<Employee> staff = new ArrayList<>(100);
(3)size方法可以返回数组列表中的实际元素数目
staff.size();
(4)一旦确认数组列表大小不再发生变化,就可以使用trimTosize方法将存储区域的大小调整为当前元素数量所需要的存储空间数目,从而方便垃圾回收器将多余的存储空间回收。这时,再添加新元素将需要花时间再次移动存储块。
(5)ArrayList类并不是Java程序设计语言的一部分,是由某些人编写且放到标准库中的一个实用类,因此在便利扩展容量同时也增加了访问元素的复杂度。
使用get和set方法实现访问或者改变数组元素的操作,而不是使用[]语法格式。
如
staff.set(i, harry);
(6)注意,下面这个技巧可以一举两得,即可以灵活扩展数组,又可以方便访问数组元素。
首先,创建一个数组,并添加所有元素
再使用toArray方法将数组元素拷贝到一个数组中
4、对象包装器与自动装箱
所有的基本类型都有一个与之对应的类,如基本类型int对应的类为Integer,通常,这些类称为包装器。
对象包装器类是不可变的,即一旦构造,就不允许更改包装在其中的值。同时对象包装器类还是final的,因此不允许定义它们的子类。
(1)因此,可以看到如果想定义一个整型数组列表,但前面介绍中<>中的类型参数并不允许是基本类型,这里就可以用到基本类型相对应的对象包装器类了,如
ArrayList<Integer> List = new ArrayList<>();
不过注意的是,由于每个值分别包装在对象中,所以ArrayList<Integer>
的效率远低于int[],因此,顶多适用于构造小型集合。
(2)为了更加便于添加或获得数组元素,下面调用
list.add(3);
将自动变换成
list.add(Integer.valueOf(3));
这种变化称之为自动装箱。
(3)对应的,将一个Integer对象赋予给一个int值时,将会自动拆箱,即编译器将下列语句
int n = list.get(i);
翻译成
int n = list.get(i).intValue();
(4)==运算符也可以应用于对象包装器对象,只不过检测的是对象是否指向同一个存储区域
5、参数数量可变的方法
目前版本的Java都提供了可以用可变的参数数量调用的方法,也称为变参方法。
如printf方法:
用户也可以自己定义可变参数方法,并将参数指定为任意类型甚至是基本类型,如下例的功能式计算若干个数值的最大值
可以直接调用这个方法
double m = max(3.1, 44.4. -19);
6、枚举类
所有的枚举类型都是Enum类的子类,它们继承了这个类的许多方法。
如之前提到的例子:
实际上,这个声明定义的类型是一个类,恰好有四个实例。因此,在比较两个枚举类型的值时,不是调用equals而是直接使用==。
如果需要,可以在枚举类型中添加一些构造器、方法和域。当然,构造其只是在构造枚举常量的时候被调用。
(1)方法toString可以用来返回枚举常量名,如Size.SMALL.toString()将返回“SMALL”。
(2)方法valueOf是toString的逆方法,如
Size s = Enum.valueOf(Size.class, "SMALL ");
将s设置成Size.SMALL。
(3)每个枚举类型都有一个静态的values方法,用来返回一个包含全部枚举值的数组,如
Size[] values = Size.values();
返回包含元素Size.SMALL,Size.MEDIUM,···,Size.EXTRA_LARGE的数组。
(4)ordinal方法但会enum声明中枚举常量的位置,位置从零开始计数,例如Size.MEDIUM.ordinal()返回1.
7、关于继承设计的技巧
(1)将公共操作和域放在超类
(2)不要使用受保护的域
这是因为,第一,子类集合是无限制的,任何一个人都能由某个类派生一个子类,并编写代码直接访问protected的实例域,从而破坏了封装性。第二,Java中,在同一个包中的所有类都可以访问protected域,而不管它是否为这个类的子类。
(3)使用集成实现“is-a”关系
(4)除非所有继承的方法都有意义,否则不要使用继承
(5)在覆盖方法时,不要改变预期的行为
(6)使用多态,而非类型信息