本章目录
Java 面向对象三大特性之继承、多态
上篇详细讲述了封装所需要的知识,下面辅以代码,对继承和多态进行详述。
继承详述
1、构造方法
构造方法:是 Java 中一种特殊类型的方法,用于初始化对象,在创建对象时被默认调用。
构造方法的分类:空参构造,有参构造
构造方法的特点:
- 方法名与类名相同。
- 方法声明上没有返回值类型这一项(连 void 也没有,void 表示这个方法不需要返回结果)。
注意事项:
- 在一个类中,如果没写构造方法,系统会默认添加空参构造;如果写了构造方法,系统将不会再添加空参构造。
- 如果只写了有参构造,系统不会默认添加空参构造,这时就无法使用空参构造。
- 建议永远手动写出空参构造。
以学生类为例:
学生类成员变量:name、age
空参构造:
//方法名与类名相同
//没有返回值类型这一项
public Student() {
}
有参构造:
public Student(String name, int age) {
this.name = name;
this.age = age;
}
完整代码:
public class Student {
private String name;
private int age;
//空参构造
public Student() {
}
//有参构造
public Student(String name, int age) {
this.name = name;
this.age = age;
}
}
创建对象时,如果没有顺带传入数据,系统会默认调用空参构造,这时再想赋值,必须使用 set()、get() 方法;
如果顺带传入了数据,系统会调用有参构造,直接给对象的成员变量赋值。
2、static 关键字
static:静态
被 static 关键字修饰的方法或变量不需要依赖对象来访问,只要类加载了,就可以通过类名进行访问。
简单来说就是:方便在没有创建对象的情况下来进行调用(方法/变量)。
被 static 修饰的成员,在类的.class
文件加载进方法区时,会被放入方法区中的静态区。
static 关键字的特点:
- 随着类的加载而加载
- 优先于对象存在
- 被类的所有对象共享
- 可以通过类名调用,也可以通过对象调用,建议通过类名调用
注意事项:
- 在 static 修饰的方法中是没有 this 的,因为静态是随着类的加载而加载,this 是随着对象的创建而存在,静态比对象优先存在。
- 静态方法只能访问静态的成员。
静态变量和成员变量的区别:
- 所属不同。静态变量属于类,成员变量属于对象。
- 内存中的位置不同。静态变量在方法区的静态区,成员变量在堆内存。
- 出现的时间不同。静态变量随着类的加载而加载,成员变量随着对象的创建而存在。
- 调用不同。静态变量可用类名或对象调用,成员变量只能用对象调用。
3、代码块
代码块:使用 {} 括起来的代码。
代码块的分类:普通代码块、构造代码块、静态代码块、同步代码块
普通代码块:方法的方法体。
构造代码块:类中方法外的代码块,在创建对象时被默认调用,每次创建都会调用,优先于构造方法执行。
静态代码块:使用 static 修饰的代码块,随着类的加载而加载,只会执行一次。
同步代码块:使用 synchronized 修饰的方法体,保证多线程下,对共享数据的读写是互斥的。
public class CodeBlock {
//静态代码块
static {
System.out.println("静态代码块");
}
//构造代码块
{
System.out.println("构造代码块");
}
//普通代码块就是方法体
public CodeBlock() {
System.out.println("普通代码块");
}
}
class Test03{
public static void main(String[] args) {
CodeBlock codeBlock = new CodeBlock();
}
}
结果:
静态代码块
构造代码块
普通代码块
主方法进栈后,在创建 CodeBlock 对象前,要先加载 CodeBlock 类,静态代码块随着类的加载而加载,所以先输出:静态代码块,创建对象时,构造代码块优先于构造方法执行,输出:构造代码块,接着调用构造方法,输出:普通代码块。
4、继承
基于已有类派生出新类,已有类称为父类,派生类称为子类,二者的关系称为继承,用关键字 extends 表示。
格式:
class 父类{};
class 子类 extends 父类{};
继承的好处:
- 提高了代码的复用性。
- 提高了代码的维护性。
- 让类与类之间产生了关系,是多态的前提。
继承的弊端:
- 类与类之间的耦合性增强了。开发所遵循的逻辑应该是:高内聚,低耦合。
继承的特点:
- Java 中只支持单继承,不支持多继承。也就是说,每个子类只能有一个父类。
- Java 中支持多层继承。也就是说,父类可能是另一个类的子类。
继承的注意事项:
- 子类只能继承父类非私有的成员。
- 子类不能继承父类的构造方法,但是可以通过 super 关键字去访问父类的构造方法。
- 不要为了部分功能去继承。
继承中成员变量的调用逻辑:
-
当子类和父类的变量名没有重名时,按照变量名调用。
-
当子类和父类的变量名有重名时,按照”就近原则“调用:
- 在子类方法的内部范围寻找,找到优先调用。
- 如果方法内部没有该变量,在子类的成员变量范围内寻找,找到调用。
- 如果子类范围没有该变量,在父类中去寻找,找到调用。
this 和 super 的区别和使用:
this 代表本类的引用;super 代表父类存储空间的标识,可以理解为父类的引用。
this 和 super 的使用:
调用成员变量 | 调用构造方法 | 调用成员方法 | |
---|---|---|---|
this | this.变量名 | this() | this.方法名() |
super | super.变量名 | super() | super.方法名() |
继承中构造方法的关系:
子类中所有构造方法都会默认调用父类的空参构造,因为子类要继承父类中的数据,必须先完成父类的初始化。
实现原理:子类构造方法的第一条语句是隐藏的 super(),系统默认添加的。
注意:当父类没有空参构造时,子类应通过 super() 显示的调用父类的有参构造。
this()、super() 必须出现在构造方法的第一条语句上。
继承中方法的调用逻辑:
当子类方法和父类方法没有重名时,按照方法名调用。
当子类方法和父类方法有重名时,先查找子类中有无调用的方法,有就优先调用,无再去调用父类中的方法。
方法重写:
子类中出现和父类方法名相同的方法,被称为重写,也叫覆盖。
当子类需要父类功能的同时,又想拥有自己独特的内容,就可以重写父类中的方法。这样既沿袭了父类的功能,又定义了子类独特的功能。
注意事项:
- 父类中私有的方法不能被重写,因为子类根本无法继承父类的私有方法。
- 子类重写父类的方法时,访问权限不能变低,最好一致。
- 父类方法是静态方法,子类也必须重写成静态方法。
final 关键字
final:最终的意思,可以修饰类、变量、方法。
final 修饰的特点:
- 修饰类时,该类不能被继承。
- 修饰方法时,该方法不能被重写。
- 修饰变量时,该变量变为常量,不能被重新赋值。
5、继承案例
创建人类:
public class Person {
private String name;
private int age;
//空参构造
public Person() {
}
//有参构造
public Person(String name, int age) {
this.name = name;
this.age = age;
}
//set() get() 方法
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
//成员方法
public void sleep(){
System.out.println("任何人都需要睡觉");
}
}
老师类继承人类:
//继承人类
class Teacher extends Person{
//空参构造
public Teacher() {
//这里有隐藏的 super(); 语句,默认访问父类的空参构造。
}
//重写父类方法
public void sleep() {
System.out.println("各个人的睡眠时间不同");
}
}
测试类:
class Test{
public static void main(String[] args) {
Teacher teacher = new Teacher();
//继承来的 set() 方法
teacher.setName("张三");
teacher.setAge(21);
//调用的是子类重写的 sleep() 方法
teacher.sleep();
System.out.println(teacher.getName());
System.out.println(teacher.getAge());
}
}
多态详述
1、多态实现
多态:同一对象的多种表现形式。
具体来说就是,一个引用变量到底会指向哪个类的实例,该实例发出的方法调用到底调用哪个类的方法,必须在运行期才能决定。
多态的具体实现:父类引用指向子类对象。
父类 对象名 = new 子类();
2、多态中成员的访问特点
成员变量:编译看左边,运行看左边。
构造方法:子类创建时会默认访问父类的构造方法,对父类进行初始化。
成员方法:编译看左边,运行看右边。
静态方法:编译看左边,运行看左边。
3、多态的利弊
利:继承提高了代码的维护性,多态提高了代码的扩展性。
弊:不能使用子类特有的功能。
4、多态中的转型
多态的转型分为两种:向上转型、向下转型。
向上转型:多态本身就是向上转型,子类对象使用父类类型接收。
向下转型:将父类类型对象转为子类引用。
子类类型 对象名 = (子类类型) 父类类型对象;
向下转型后,就可以解决多态不能使用子类特有功能这一问题。
5、多态案例
动物类:
public class Animal {
//成员变量
int age = 1;
//空参构造
public Animal() {
System.out.println("Animal 类的构造方法");
}
//成员方法
public void eat(){
System.out.println("动物吃饭");
}
}
猫类:
继承动物类
class Cat extends Animal{
//同名变量
int age = 2;
//重写父类 eat() 方法
public void eat() {
System.out.println("猫吃猫粮");
}
}
测试类:
class Test02{
public static void main(String[] args) {
//多态创建猫类,用动物类接收,会默认调用动物类的空参构造
Animal animal = new Cat();
//调用成员变量,编译运行看左边
int age = animal.age; //得到的是左边 Animal 类中的 age
System.out.println(age); //打印 1
//调用成员方法,编译看左边,运行看右边
animal.eat(); //调用的是 Cat 类的 eat() 方法
//向下转型
Cat cat1 = (Cat) animal;
//向下转型后,就可以使用子类特有的功能
int age1 = cat1.age; //得到的就是 cat 类的 age
System.out.println(age1);
}
}
你可能会从天而降,也可能从林间降落,但最好的降落方式是,与我坠入爱河.
各位看官,面向对象三大特性结束咯,我们下篇见 (∩_∩)~~
-Czx.