第十一章:继承和多态
一.父类和子类
1.继承可以使你创建一个类(父类),之后可以扩充该类为一个更加特定的类(子类)
public class Circle extends GeometricObject{
}
Circle为子类,也可称为次类、派生类、扩展类
GeometricObject为父类,也成为基类,超类
extends为继承关键字
2.注意
- 子类不是父类的子集,子类通常比父类包含更多的信息和方法
- 父类中的私有成员变量子类不可访问,但是可以访问父类中公共的访问器和修改器
- 不是“是一种”就是继承关系,例如square是矩形的一种,但是不能用square类来扩展矩形类,因为width和height不适合正方形
- 父类和子类之间必须存在“是一种”的关系,但是不能盲目建立继承关系
- java中不予许多继承,一个Java类只能直接继承自一个父类,但是可以通过接口来实现多继承
几个类派生出一个子类称为多继承
二.使用super关键字
关键字super指代父类,可以用于
- 调用父类中的构造方法
super();//调用父类无参构造
super(参数);//调用有参构造
- 调用父类中的普通方法
使用super()或super(参数)必须出现在子类构造方法的第一行,是显式调用父类的构造方法的唯一方式
public class GeometricObject {
//定义父类
private String color;//颜色
private boolean filled;//是否填充
public GeometricObject() {
this("white", false);//调用重载的构造方法
}
public GeometricObject(String color, boolean filled) {
this.color = color;//this调用对象引用的隐藏数据域
this.filled = filled;
}
}
public class Circle extends GeometricObject {
//Circle类继承GeometricObject 类
private double radius;
public static final double PI = 3.14;
public Circle() {
this(1.0, "black", true);
}
public Circle(double radius, String color, boolean filled) {
super(color, filled);//调用父类构造方法,必须放在第一行
this.radius = radius;
}
}
构造一个类的实例时, 将会调用沿着继承链的所有父类的构造方法。 当构造一个子类的对象时, 子类构造方法会在完成自己的任务之前, 首先调用它的父类的构造方法。 如果父类继承自其他类, 那么父类构造方法又会在完成自己的任务之前,调用它自己的父类的构造方法。 这个过程持续到沿着这个继承体系结构的最后一个构造方法被调用为止。
下面这个例子是查资料得来的,更能反映构造方法链
public class Faculty extends Employee{
public Faculty(){
System.out.println("(3)Performs Faculty's tasks") ;
}
public static void main(String[] args){
Faculty fac=new Faculty();
}
}
class Employee extends Person {
public Employee(){
System.out.println("(2)Performs Employee's tasks");
}
}
class Person {
public Person(){
System.out.println("(1)Performs Person's tasks");
}
}
//运行结果
//(1)Performs Person's tasks
//(2)Performs Employee's tasks
//(3)Performs Faculty's tasks
子类中没有显式定义构造方法,默认无参构造被隐式调用,并默认调用父类的无参构造,然而父类中没有定义无参构造,因此无法编译成功
public class GeometricObject {
private String color;
private boolean filled;
public GeometricObject(String color, boolean filled) {
this.color = color;
this.filled = filled;
}
//父类中没有定义无参构造
}
public class Circle extends GeometricObject {
private double radius;
public static final double PI = 3.14;
//默认调用无参构造和父类的无参构造
}
所以:如果要设计一个可以被继承的类,最好提供一个无参构造方法避免程序设计错误
三.方法重写
1.要重写一个方法,就要和父类定义一样的签名和返回值类型来对该方法进行定义
public class GeometricObject {
@Override
public String toString() {
//父类toString方法
return "GeometricObject [color=" + color + ", filled=" + filled + "]";
}
}
public class Circle extends GeometricObject {
@Override//只有父类的实例方法是可访问时才能被覆盖
public String toString() {
//子类重写父类toString方法,方法签名和返回值类型相同
return super.toString()+" radius:"+getRadius();//重写方法必须使用super.方法名 调用父类的方法
}
}
public static void main(String[] args) {
//测试
// TODO Auto-generated method stub
Circle circle2=new Circle(3.0,"black",true);
System.out.println(circle2.toString());//GeometricObject [color=black, filled=true] radius:3.0
}
与实例方法相同,静态方法也能被继承,但是静态方法不能被覆盖,如果父类重定义的静态方法在子类中被重写,那么父类中的静态方法将会被隐藏
2.重写与重载的区别
**重载:**相同的方法名不同的签名定义多个方法
**重写:**在子类中提供一个对方法的新的实现,签名和返回值类型必须相同
方法重写发生在因为继承而相关的不同类中
方法重载可以发生在通一个类中,也可以发生在由于继承而相关的不同类中
重写标注@Override
如果使用了标注但是却没有重写,就会报错
重写方法中必须使用super.function()调用父类的方法
3.Object类
java中所有的类都继承自Object类
四.多态
1.多态意味着父类的变量可以指向父类的对象
2.每个子类的实例都是其父类的实例,反之不行
五.动态绑定
1.声明类型和实际类型
Object o=new GeometricObject();
//Object声明类型 GeometricObject实际类型
2.动态绑定是指可以沿着继承链的多各个中实现
3.引用变量的声明类型决定编译时匹配哪个方法,实际类型决定动态绑定方法的实现
public static void main(String[] args) {
// TODO Auto-generated method stub
printString(new Circle(3.0));//创建Circle的匿名对象
printString(new Rectangle("black", true, 5.0, 6.0));//创建Rectangle的匿名对象
}
public static void printString(GeometricObject go) {
//动态调用toString方法
System.out.println(go.toString());
//运行结果:
//GeometricObject [color=white, filled=false] radius:3.0
//GeometricObject [color=black, filled=true] width:5.0 high:6.0
六.对象转换和instanceof
1.对象的引用可以可以类型转换为另一对象的引用,称为对象转换
Object o=new Student();//向下转换
//但是 Student s=o; 错误,需要显式转换
Student s=(Student)o;//向上转换,显式
尝试显式转换时确保一个对象是另一个对象的实例,利用instancesOf关键字实现
public static void main(String[] args) {
// TODO Auto-generated method stub
GeometricObject[] go = {
new Circle(5.0), new Rectangle("black", true, 5.0, 6.0),
new GeometricObject("blue", true) };//创建一个GeometricObject类型的数组
for (GeometricObject i : go) {
//遍历数组中元素
if (i instanceof Circle) {
//i是否是Circle的实例
System.out.println(((Circle) i).getArea());//因为GeometricObject 类中没有getArea方法,所以要显式转换
} else if (i instanceof Rectangle) {
//i是否是Rectangle的实例
System.out.println(((Rectangle) i).getPrimeter());//因为GeometricObject 类中没有getPrimeter方法,所以要显式转换
} else {
//i是否是GeometricObject的实例
System.out.println(i.toString());
}
}
}
//运行结果:
//78.5
//22.0
//GeometricObject [color=blue, filled=true]
为什么没有一开始就定义为Circle类型或Rectangle类型呢?
是为了能够通用设计,把变量定义为父类,这样就可以接收任何子类型的值
2.对象成员访问运算符(.)优先于类型转换。所以要先转换在打点调用方法
例:((Rectangle) i).getPrimeter()
3.注意
转换基本类型会返回一个新的值,但是转换对象不回创建新的对象
Object o=new Circle();
Circle c=(Circle)o;
//o和c指向同一个对象
七.Object类中equals()方法
1.在子类中,使用签名equals(SomeClassName obj)(例如:equals(Circle c)重写equals方法是错误的,应该使用equals(Object obj))
@Override
public boolean equals(Object obj) {
if(obj instanceof Circle) {
//判断Circle是否是obj的实例
return this.radius==((Circle)obj).getRadius();//比较两个的半径是否相等
}
else
return this==obj;
}
八.ArrayList类
1.Arraylist可以用于存储对象列表
2.可以存储不限个数的对象
3.ArrayList是一个泛型类,具有一个泛型类型E
4.语法
ArrayList<E> list=new ArrayList<E>();
//jdk1.7之后可以去掉这个E
5.方法
ArrayList类中包含添加、清除、是否包含、查找、删除、修改等方法,在
下文中会提及
6.数组和ArrayList的区别
数组 | ArrayList | |
---|---|---|
定义 | int[] array=new array[5] | ArrayList<> list=new ArrayList<>() |
引用 | array[index] | list.get(index) |
更新元素 | array[index]=b | list.set(index,b) |
返回大小 | a.length | list.size() |
添加新元素 | list.add() | |
插入新元素 | list.add(index,b) | |
删除一个元素 | list.remove(index)或list.remove(b) | |
删除所有 | list.clear() | |
排序 | java.util.Arrays.sort(array) | java.util.Collections.sort(list) |
ArrayList中存储的都是对象,所以要用基本类型的包装类型
ArrayList<int> list=new ArrayList<>();//错误
ArrayList<Integer> list=new ArrayList<>();//正确
注意
public static void main(String[] args) {
// TODO Auto-generated method stub
ArrayList<Integer> list = new ArrayList<>();
list.add(1);
list.add(2);
list.remove(1);
System.out.println(list);//[1]删掉的是下标所对应的元素
//如果就想删掉1 用用list.remove((Integer)1);
}
7.数组和数组列表之间的转换
public static void main(String[] args) {
// TODO Auto-generated method stub
String[] str= {
"hello","java","fun"};
ArrayList<String> list = new ArrayList<>(Arrays.asList(str));//将数组转为数组列表
System.out.println(list);//[hello, java, fun]
String[] str2=new String[list.size()];
list.toArray(str2);//将数组列表转为数组
for(String i:str2) {
System.out.print(i+" ");//hello java fun
}
}
8.求列表中最大和最小以及打乱列表
public static void main(String[] args) {
// TODO Auto-generated method stub
ArrayList<Integer> list = new ArrayList<>();
for(int i=0;i<10;i++) {
list.add(i);
}
System.out.println(java.util.Collections.max(list));//获取最大值 9
System.out.println(java.util.Collections.min(list));//获取最小值 0
java.util.Collections.shuffle(list);//打乱数组列表
System.out.println(list);//[5, 7, 4, 3, 8, 9, 1, 2, 0, 6]
}
九.protected数据和方法
1.一个类的受保护成员可以从子类中访问
2.允许子类访问父类的数据域和方法,但不允许非子类访问,使用protected关键字
3.可见性递增:
私有的(private)、默认的(default)、受保护的(protected)、全局的(public)
4.protected允许包内类和包外子类访问
5.子类可以重写父类中protected修饰的方法,但是不能削弱方法的访问性,只能增加。
十.防止扩展和重写
1.一个被final修饰的类和方法都不能扩展
2.只有final可以用在方法的局部变量上
十一.总结
通过对本章的学习,我知道了父类和子类之间关系,学会使用super调用父类的构造方法和方法,一定记得放在第一句,学会了多态的含义(父类的变量可以指向子类的对象),知道了重写和重载的区别,知道了动态加载的原因,懂得了向上显式转换,向下转换和instanceof的用法,学会了ArrayList数组列表的方便,protected修饰符的用法以及使用final关键字修饰类和方法来防止扩展。
加油!第十二章待更……