1、初步理解继承
1)我们假设有一个Person类和一个Student类。从现实生活的来看,Student是Person的一种,Student有着Person的一些属性和方法。所以我们在已经写好Person类的前提下再写Student类的时候就可以用到继承机制。继承后,Student获得Person的全部功能(属性和方法),我们只需在Student类中写其特有功能。
2)在OOP的术语中,我们把Person称为超类(super class),父类(parent class),基类(base class),把Student称为子类(subclass),扩展类(extended class)。
2、怎么实现继承?
通过extends关键字
class Person {
//属性
//方法
}
class Student extends Person {
//新增的特有属性
//新增特有方法
}
3、继承树
1)我们在定义Person类的时候没有使用extends,是因为在Java中,没有明确写出extends的类,编译器默认加上extends Object。也就是说,Person类是继承自Object类的。除了Object,其他的类都会继承自某个类。这里的继承关系是:Object-->Person-->Student
2)Java中规定一个类只能继承自一个父类,一个类不可以有多个父类。但是可以有多个类继承自同一个类。(就好像一个儿子只能有一个父亲,而一个父亲可以有多个儿子)。只有Object类特殊,它没有父类。
3)在eclipse中,将光标停在想要查看的类名旁边,按ctrl+T,就可以看到类与类的继承关系了。
4、子类中属性的访问、protected
1)子类无法访问父类的private字段和方法。(非private字段是可以访问的)
class Person {
private String name;
}
class Student extends Person {
public String hello() {
return "Hello, " + this.name; //像这样编译时就会报错
}
}
2)要想让子类可以访问父类的字段,只需将父类中private修饰符改为protected。因此,protected可以把访问权限控制在继承树内部,被protected修饰的字段可以被其子类或者子类的子类所访问。
class Person {
protected String name;
protected int age;
}
class Student extends Person {
public String hello() {
return "Hello, " + this.name; //OK
}
}
5、super关键字
1)super关键字表示父类。子类在使用父类字段时使用super.name、this.name、name 效果都是一样的。
2)某些时候必须使用super关键字。例如:
class Person {
protected String name;
protected int age;
public Person(String name, int age) {
this.name = name;
this.age = age;
}
}
class Student extends Person {
protected int score;
public Student(String name, int age, int score) {
this.score = score;
}
}
public class Main {
public static void main(String[] args) {
Student s = new Student("zhangsan", 18, 0001);
}
}
这样写会编译报错。错误提示无法在Studet类中调用Person类的构造方法。
其实,在Java中,任何class的构造方法,第一句必须是调用父类的构造方法。如果没有明确的写出调用,编译器会自动为我们加一句super(); (其实在source里面自动生成构造方法时自带的就有super();这条语句。)所以,实际上Student类是这样的:
class Student extends Person {
protected int score;
public Student(String name, int age, int score) {
super();// 调用父类构造方法
this.score = score;
}
}
这样做就结束了吗?在实际的操作下我们可以看到,在加上super();语句后还是报错的。super();是不带有参数的,它调用的是父类的无参构造方法。而Person类中没有无参构造方法,所以仍会报错。这里有两个解决办法:
①在Student类的构造方法中将属性赋值写全:
class Person {
protected String name;
protected int age;
public Person(String name, int age) {
this.name = name;
this.age = age;
}
}
class Student extends Person {
protected int score;
public Student(String name, int age, int score) {
//super();写或不写都可
this.score = score;
//将其他成员赋值
this.name = name;
this.age = age;
}
}
②在Student类中加入有参super语句:
class Person {
protected String name;
protected int age;
public Person(String name, int age) {
this.name = name;
this.age = age;
}
}
class Student extends Person {
protected int score;
public Student(String name, int age, int score) {
super(name,age);// 添加有参super语句
this.score = score;
}
}
3)通过上面的解读我们认识到:1、super语句可以用来调用父类的构造方法。2、子类不会继承父类的构造方法。
6、向上转型
Person p = new Student();// 这句代码是OK的
1)这句代码与平时所见不同,以往我们见的应该是Person p = new Person();或者Student p = new Student(); 但是这里变量类型和创建的对象的类型不同。这是因为,Student类是从Person类继承下来的,Student类拥有Person类的全部功能,Person类型的变量可以指向Student类的实例。这样把一个子类型安全的转变为父类型的做法叫做向上转型。
2)Object p = new Student();也是可以的。也就是说可以转为更高层次的类型。
3)向上转型实际上是将一个子类型安全的转变为更为抽象的父类型。
4)编译阶段p被认为是Person类型,不可以使用p调用Student类中的方法;运行阶段p被认为是Student类型。
7、向下转型
1)相反的,如果把一个父类型强制转换为子类型就叫做向下转型。
2)怎么实现向下转型?
Person p1 = new Student();
Student s1 = (Student) p1;
Person类型的p1实际指向Student的实例所以在强制类型转换的时候可以成功。
Person p2 = new Person();
Student s2 = (Student) p2;
Person类型的p2实际指向了Person实例,在强制转换时失败。其实例还是个Person类,父类不能变成子类,子类的功能要比父类多,多的部分父类是无法补上的。
8、instanceof操作符
1)instanceof操作符可以判断一个变量是否为指定的类型。例如:
Person p = new Person();
System.out.println(p instanceof Person);// true
我们知道现在Student类是Person类的子类,判断类型的时候会因为继承而有所特殊吗?我们动手试试。
Student s = new Student();
System.out.println(s instanceof Student);// true
System.out.println(s instanceof Person);// true
我们惊奇的发现,s是Person类型。
2)由上面的实验我们可以得知,instanceof的实际作用是:判断一个变量指向的实例是否为指定的类型,或者是否为指定类型的子类。再举个例子说明:
Person p = new Student();
System.out.println(p instanceof Student);// true
//p的实例类型为Student。指定的类型是Student,自然为true
System.out.println(p instanceof Person);// true
//p的实例类型为Student。指定的类型是Person类型,Student是Person类型的子类,故结果也是true。
3)有了instanceof,我们在进行向下转型时就可以先判断变量的实例类型,如果实例类型是子类,那么转型一定成功。
Person p = new Student();
if(p instanceof Student){
Student s = (Student) p;
}
9、继承和组合
假设我们有一个Book类:
public class Book{
protected String name;
public void setName(String name){
this.name = name;
}
public String getName(){
return this.name;
}
}
1)现在我们创建一个Student类,它能否继承Book类呢?答案是可以的。虽然在语法上没有错误,但是这在逻辑上是讲不通的,因为一个学生的父类不能是一本书,而应该是一个学生有一本书。这样的关系叫做组合。
2)继承是is关系,组合是has关系。
正确的写法是:
学生有一本书,就是学生类中可以有Book类的实例。
public class Student{
protected Book book;
protected int age;
}
End...