8. 继承和多态
8.1 继承的用法
- 通过extends关键字可以实现类与类的继承
class 子类名 extends 父类名{}
class boys extends person{}
class girls extends person{}
继承使得定义一个通用的类(父类),之后扩充该类为一个更加特定的类(子类)
8.2 继承的利弊
- 提高了代码的复用性:多个相同的成员可以放到同一个类中
- 提高了代码的行为属性:如果功能的代码需要修改,修改一处即可
- 让类与类之间产生了关系,是多态的前提:其实这也是继承的一个弊端, 类的耦合性很强
开发原则:低耦合,高内聚
耦合:类与类的关系
内聚:就是自己完成某件事情的能力
8.3 继承特点
- java只支持单继承,不支持多继承,即一个类只能有一个父类
- 子类只能继承父类所有非私有的成员
- 子类不能继承父类的构造函数
子类函数中访问变量步骤:
子类局部范围->子类成员范围->父类成员范围
8.4 super关键字
关键字super代指所在类的父类,用于调用父类的普通函数和构造函数
作用:
- 调用父类构造方法
public class Test {
public static void main(String[] args) {
//创建子类对象,初始化参数
student s1 = new student(19,"赵文耀",12);
System.out.println(s1.getAge());//父类age的访问器函数也被继承
System.out.println(s1.getName());//父类name的访问器函数也被继承
System.out.println(s1.getGrade());
}
}
//父类
class person{
private int age;
private String name;
public person() {
}
public person(int age, String name) {
this.age = age;
this.name = name;
}
public int getAge() {
return age;
}
public String getName() {
return name;
}
}
//子类
class student extends person{
private int grade;
public student(){
}
public student(int age,String name,int grade) {
super(age,name);//调用父类person的构造函数初始化年龄,姓名
this.grade = grade;
}
public int getGrade() {
return grade;
}
public void setGrade(int grade) {
this.grade = grade;
}
}
注意
- super()调用父类无参构造方法(若上例使用,则结果为0,null,12)
- 要调用父类构造函数必须使用super关键字
- 语句super()必须出现在子类构造函数的第一句
- 调用父类方法
super.方法名(参数)
- 构造方法链
构造一个类的实例时,将会调用沿着继承链的所有父类构造方法
public class boy extends man{
public static void main(String[] args) {
new boy();
}
public boy() {
//super()隐式调用
System.out.println(4);
}
}
class man extends person{
public man(){
//super(),隐式调用
this(2);
System.out.println(3);
}
public man(int a) {
System.out.println(a);
}
}
class person{
public person() {
System.out.println(1);
}
}
输出结果: 1
2
3
4
8.5 方法重写
重写一个方法,要在子类中使用和父类一样的签名以及一样的返回值类型,对该方法进行重新定义
public class Test {
public static void main(String[] args) {
man m = new man();
m.eat();//吃米饭
m.run();//跑马拉松
person.run();//跑步
}
}
class person{
//公有
public void eat() {
System.out.println("吃饭");
}
//静态
public static void run() {
System.out.println("跑步");
}
//私有
private void talk() {
System.out.println("说话");
}
}
class man extends person{
//重写eat()
@Override
public void eat() {
System.out.println("吃米饭");
}
//重写run()
public static void run() {
System.out.println("跑马拉松");
}
}
注意事项:
-
@Override(重写标注)
表示被标注的方法必须重写父类的一个方法
用标注就要重写,否则报错
不用标注也可省略不写 -
父类中私有方法不能被重写
-
静态方法不能被覆盖,若在子类中重新定义,则父类静态方法被隐藏, 用person.run();(父类名.静态方法名)调用
8.6 多态
父类的变量可以指向子类对象
public class Test {
public static void main(String[] args) {
person p1 = new person();//创建父类对象
p1.write(new man());//调用父类write(),函数参数为子类对象
}
}
class person{
private int age;
public person(){
}
public person(int age) {
this.age = age;
}
public int getAge() {
return age;
}
public void write(man m){//父类的变量可以指向子类对象
System.out.println(m.getAge()+m.getName());
}
}
class man extends person{
private String name;
public man(){
}
public man(int age, String name) {
super(age);
this.name = name;
}
public String getName() {
return name;
}
}
8.7 动态绑定
方法沿着继承链在多个类中实现,JVM决定运行时调用那个方法
- 声明类型
(变量p1的声明类型为person类型)
person p1 = new man();
-
实际类型(变量引用的对象的实际类)
变量p1的实际类型为man类型 -
动态绑定机制
public class StringTest {
public static void main(String[] args) {
m(new Object());//调用实际类型Object类的toString()
m(new person());//调用实际类型persont类的toString()
m(new man());//调用实际类型man类的toString()
Object o = new boy();
m(o)//;调用实际类型boy类的toString()(继承自man)
}
public static void m(Object x) {//引用变量的声明类型为Object
System.out.println(x.toString());
}
}
class boy extends man {
}
class man extends person{
@Override
public String toString(){
return "man";
}
}
class person extends Object{
@Override
public String toString(){
return "person";
}
}
结果:
[email protected]
person
man
man
引用变量的声明类型决定编译时匹配那个方法
引用变量的实际类型决定运行时JVM动态绑定方法的实现
8.8 对象转换
-
向上转换
子类实例总是父类实例
Object o = new boy();//隐式转换,正确(boy类是Object的实例)
man m = o;//错误
-
向下转换
父类实例转换为子类变量
man m = (man)o;
注意
必须确保转换的对象是一个子类实例
-
instanceof关键字
判断对象是否是另一个对象的实例
Object O = new person();
if(O instanceof person){//判断父类对象是否是person类
(person)O;//转换为person类
}
注意
- 对象成员访问运算符.优先于类型转换运算符,调用函数时注意加()
((person)O).eat();
- 基本类型转换返回一个新的值
double a = 5.5;
int newa = (int)a;
- 转换一个对象引用使指向同一个对象
person p = new man();
man m = (man)p;//引用变量p,m指向同一个对象
8.9 ==运算符还可以检测两个引用变量是否指向同一 个对象
8.10 方法权限
数据和方法的可见性
类中成员修饰符 | 同类中 | 同类不同包 | 子类中 | 不同包中 |
public | 1 | 1 | 1 | 1 |
protected | 1 | 1 | 1 | 0 |
(default) | 1 | 1 | 0 | 0 |
private | 1 | 0 | 0 | 0 |