参考教材:
Java面向对象程序设计(第3版)微课视频版 清华大学出版社
1、定义子类
class 子类名 extends 父类名{
......
}
如:
class Student extends People{
......
}
(1)如果一个类的声明中没有extends关键字,这个类被系统默认为是Object的子类。Object是java.lang包中的类。
(2)除了Object类,任何(子)类有且仅有一个父类。
如 class Student extends People,Person语法错误。
2、子类的继承性
(1)类可以有两种重要的成员:成员变量和成员方法。
子类中的成员变量或方法有一部分是子类自己声明的;另一部分是从父类继承的。
(2)什么叫继承呢?
子类继承父类的成员变量:像在子类中直接声明,可以被子类中自己定义的任何实例方法操作;
子类继承父类的方法:像在子类中直接声明,可以被子类中自己定义的任何实例方法调用;
也就是说,如果子类中定义的实例方法不能操作父类的某个成员变量或方法,那么该成员变量或方法就没有被子类继承。
(3)子类和父类在同一个包中的继承性
继承父类中不是private的成员变量或方法作为自己的成员变量或方法(继承protected、public、友好的变量及方法);继承的成员变量和方法的访问权限保持不变;
(4)子类和父类不在同一个包中的继承性
子类继承父类的protected、public成员变量或方法;继承的成员变量和方法的访问权限保持不变;
子类不能继承父类的友好变量和友好方法。
public class People {
int age;
String country;
String sex;
protected String workType;
private String idcard;
People(){
System.out.println("调用People类的无参构造函数!");
}
}
class Worker extends People{
String position;
public static void main(String[] args) {
Worker one = new Worker();
one.age = 10;
one.country = "China";
one.workType = "worker";
//one.idcard = "123456"; //×,在同一个包中,不继承private变量
}
}
package eight;
public class People1 {
int age;
String country;
public String sex;
protected String workType;
private String idcard;
public People1(){
System.out.println("调用People类的无参构造函数!");
}
}
package object.oriented;
import eight.People1;
class Teacher extends People1{
public static void main(String[] args) {
Teacher two = new Teacher();
two.sex = "male"; //√,子类继承父类的public成员变量
//two.age = 10; //×,不在同一个包中时,不继承友好变量
two.workType = "student"; //√,不在同一个包中时,可继承protected变量
}
}
注意:
无论是否在一个包中,均可继承protected、public变量及方法;
只有在同一个包中,子类才可继承友好变量及友好方法;
3、子类对象的构造过程
构造过程:用子类的构造方法创建一个子类的对象时,子类的构造方法总是先调用父类的某个构造方法,也就是说,如果子类的构造方法中没有明显地指明使用父类的哪个构造方法,子类就调用父类中不带参数的构造方法。(super关键字)
子类对象的特点:
(1)用子类创建对象时,不仅子类中声明的成员变量被分配了内存
(2)父类的成员变量也都分配了内存空间,但只将其中一部分(子类继承的那部分)作为分配给子类对象的变量。
(3)子类中还有一部分方法是从父类继承的,这部分方法却可以操作这部分未继承的变量。
4、成员变量的隐藏和方法重写
成员变量的隐藏
对于子类可以从父类继承的成员变量,只要子类中声明的成员变量和父类中的成员变量同名时,子类就隐藏了继承的成员变量;
需要注意:子类对象可以调用从父类继承的方法操作隐藏的成员变量
package object.oriented;
class People1 {
public double x; // 父类的double类型成员变量x
public void setX(double x) {
this.x = x; // 设置父类的x
}
public double getDoubleX() {
return x; // 返回父类的double x
}
}
class Student1 extends People1 {
int x; // 子类定义int类型x,隐藏父类的double x
public int getX() {
return x; // 返回子类的int x
}
}
public class Example5_3 {
public static void main(String args[]) {
Student1 stu = new Student1();
stu.x = 98; // 合法:操作子类的int x
System.out.println("对象stu的x的值是:" + stu.getX()); // 输出98
// stu.x = 98.98; // 非法:子类x是int,不能赋double值
stu.setX(98.98); // 合法:调用父类的setX方法,设置父类的double x
System.out.println("父类的x的值是:" + stu.getDoubleX()); // 输出98.98
System.out.println("子类的x的值是:" + stu.x); // 输出98(未被修改)
}
}
方法重写
子类通过重写可以隐藏已继承的实例方法(方法重写也被称为方法覆盖)。如果子类可以继承父类的某个实例方法,那么子类就有权利重写这个方法;
重写:子类中定义一个方法,这个方法的类型和父类方法的类型一致,或父类的方法类型的子类型,且方法名、参数个数、参数类型和父类的方法完全相同。
(子类型是指:如果父类的方法类型是“类”,那么允许子类的重写方法类型是“子类”)
重写方法既可以操作继承的成员变量,也可以操作子类新声明的成员变量。(不能操作隐藏的成员变量)
重写父类的方法时,不可以降低方法的访问权限。
class AA{
double f(float x,float y) {
return x+y;
}
public int g(int x,int y) {
return x+y;
}
}
class BB extends AA{
double f(float x,float y) {
return x*y;
}
}
public class Example5_4 {
public static void main(String[] args) {
BB b = new BB();
double result = b.f(5, 6); //b调用重写的方法
System.out.println("调用重写方法得到的结果:"+ result);
int m = b.g(3, 5);
System.out.println("调用继承方法得到的结果:"+ m);
}
}
5、super关键字
(1)在子类中,想使用被子类隐藏的成员变量或方法就可以使用super关键字。
// 定义Animal类
class Animals {
String name = "牧羊犬";
//定义动物叫的方法
void shout() {
System.out.println("动物发出叫声");
}
}
// 定义Dog类继承动物类
class Dogs extends Animals {
//重写父类Animal中的shout()方法,扩大了访问权限
public void shout() {
super.shout(); //调用父类中的shout()方法
System.out.println("汪汪汪……");
}
public void printName(){
//调用父类中的name属性
System.out.println("名字:"+super.name); }
}
public class Example05 {
public static void main(String[] args) {
Dogs dog = new Dogs(); // 创建Dog类的实例对象
dog.shout(); // 调用dog重写的shout()方法
dog.printName(); // 调用Dog类中的printName()方法
}
}
(2)使用super调用父类的构造方法
子类不继承父类的构造方法;
子类如果想使用父类的构造方法,必须在子类的构造方法中使用,并且必须使用super关键字来表示,而且super必须是子类构造方法的第一条语句。
public class Student {
int number;
String name;
Student(){
}
Student(int number,String name){
this.number=number;
this.name=name;
}
public int getNumber() {
return number;
}
public String getName() {
return name;
}
}
public class UniverStudent extends Student{
boolean isMarriage;
UniverStudent(int number,String name,boolean b){
super(number,name); //调用父类的构造方法
}
public boolean getIsMarriage() {
return isMarriage;
}
}
(3)使用super操作被隐藏的成员变量和方法
子类想使用被子类隐藏的成员变量或方法,就可以使用super关键字。
当super调用隐藏的方法时,该方法中出现的成员变量是指被隐藏的成员变量。
6、final关键字
final的英文意思是“最终”。在Java中,可以使用final关键字声明类、属性、方法,在声明时需要注意以下几点:
(1)使用final修饰的类不能有子类,即不能被继承。
(2)使用final修饰的方法不能被子类重写。
(3)使用final修饰的变量(成员变量和局部变量)是常量,常量不可修改。
Java中的类被final关键字修饰后,该类将不可以被继承,即不能够派生子类。
// 使用final关键字修饰Animal类
final class Animal {
}
// Dog类继承Animal类
//class Dog extends Animal //类被final关键字修饰后,该类将不可以被继承
//{
//}
public class Example07 {
public static void main(String[] args) {
}
}
当一个类的方法被final关键字修饰后,这个类的子类将不能重写该方法。
Java中被final修饰的变量是为常量,常量只能在声明时被赋值一次,在后面的程序中,其值不能被改变。如果再次对该常量赋值,则程序会在编译时报错。
在使用final声明变量时,要求全部的字母大写。如果一个程序中的变量使用public static final声明,则此变量将成为全局变量,如下面代码所示。 public static final String NAME = "哈士奇";
public class Example09 {
public static void main(String[] args) {
final int AGE = 18; // 第一次可以赋值
AGE = 20; // 再次赋值会报错
}
}
7、对象的上转型对象
假设,A类是B类的父类,当用子类创建一个对象,并把这个对象的引用放到父类的对象中时,比如:
A a;
a = new B();
//或
A a;
B b = new B();
a = b;
//这时,称对象a是对象b的上转型对象。
class Animal {
}
public class Tiger extends Animal{
public static void main(String[] args) {
Animal a;
Tiger b = new Tiger();
a = b;
}
}
对象的上转型的实体是由子类负责创建的,但上转型对象会失去源对象的一些属性和功能。
(1)上转型对象不能操作子类新增的成员变量(失掉了这部分属性),不能调用子类新增的方法(失掉了一些功能)
(2)上转型对象可以访问子类继承或隐藏的成员变量,也可以调用子类继承的方法或子类的重写方法。
上转型对象操作子类继承的方法或子类重写的方法,其作用等价于子类对象去调用这些方法。
因此,如果子类重写了父类的某个方法后,当对象的上转型对象调用这个方法时一定是调用了子类重写的方法。
class Anthropoid {
double m = 12.58;
void crySpeak(String s) {
System.out.println(s);
}
}
class People extends Anthropoid{
char m = 'A';
int n = 60;
void computer(int a,int b) {
int c = a+b;
System.out.println("a+b的和为"+c);
}
void crySpeak(String s) {
System.out.println(m+"**"+s+"**"+m);
}
}
public class Example5_9{
public static void main(String args[]) {
People person = new People();
Anthropoid monkey = person; //monkey是person对象的上转型对象
monkey.crySpeak("I love this game"); //等同于person调用重写的crySpeak方法
//monkey.n = 100; //非法,因为n是子类新增的成员变量
//monkey.computer(12,19); //非法,因为computer()是子类新增的方法
System.out.println(monkey.m); //操作隐藏的m,不等同于person.m
System.out.println(person.m); //操作子类的m
People zhang = (People)monkey; //将上转型对象强制转化为子类的对象
zhang.computer(55, 33); //zhang是子类的对象
zhang.m = 'T';
System.out.println(zhang.m);
}
}