目录
1.继承
2.子类访问父类的成员变量
3.子类访问父类的成员方法
4.super关键字
5.初始化顺序
6.final关键字
引言
面向对象三大特性:封装,继承和多态。今天我们就来聊聊继承。
1.继承
1.1为什么需要继承
因为我们在创建类时可能会大量用到重复的字段和方法,为了解决这个问题,面向对象思想中提出了继承的概念,专门用来进行共性抽取,实现代码复用。
1.2继承的语法
继承需要使用到extends关键字
java
修饰符 class 子类 extends 父类{
// 主体
}
2.子类访问父类的成员变量
子类继承了父类的方法和字段,子类在方法中就可以直接访问父类的成员吗?
2.1子类和父类不存在同名成员变量
java
class Base {
public int a = 1;
public int b = 2;
}
class Derievd extends Base{
public int c = 3;
public void func() {
System.out.println(this.a);// 访问从父类中继承下来的a
System.out.println(this.b);// 访问从父类中继承下来的b
}
}
public class Test {
public static void main(String[] args) {
Derievd derievd = new Derievd();
derievd.func();
}
}
运行结果:

2.2子类和父类成员变量同名
代码如下:
java
class Base {
public int a = 1;
public int b = 2;
}
class Derievd extends Base{
public int c = 3;
public int a = 100;
public void func() {
System.out.println(this.a);//访问子类自己新增加的a
System.out.println(this.b);//访问从父类中继承下来的b
}
}
public class Test {
public static void main(String[] args) {
Derievd derievd = new Derievd();
derievd.func();
}
}
运行结果:

得出结论:
当子类和父类成员同名时,优先访问子类自己的成员。
特性:遵循就近原则,子类有的优先使用自己的,没有再去父类中找。
2.3如果一定要访问父类的成员怎么做?
方法1:使用super关键字
方法2:初始化一个父类对象,用父类对象的引用访问父类的成员变量。
java
class Base {
public int a = 1;
public int b = 2;
}
class Derievd extends Base{
public int c = 3;
public int a = 100;
//子类当中 如何访问父类的成员
public void func() {
//方法1
System.out.println("父类的a: "+super.a);
System.out.println(this.a);
//方法2
Base base = new Base();
System.out.println(base.a);
}
}
public class Test {
public static void main(String[] args) {
Derievd derievd = new Derievd();
derievd.func();
}
}
执行结果:

3子类中访问父类的成员方法
3.1成员方法名字不同
java
class Base {
public void methodA(){
System.out.println("Base中的methodA()");
}
}
class Derived extends Base{
public void methodB(){
System.out.println("Derived中的methodB()方法");
}
public void func(){
this.methodB(); // 访问子类自己的methodB()
this.methodA(); // 访问父类继承的methodA()
}
}
public class Test {
public static void main(String[] args) {
Derived derived = new Derived();
derived.func();
}
}
执行结果:

3.2成员方法名字相同
java
class Base {
public void methodA(){
System.out.println("Base中的methodA()");
}
}
class Derived extends Base{
public void methodA(){
System.out.println("Derived中的methodA()方法");
}
public void func(){
this.methodA(); // 访问子类自己的methodA()
}
}
public class Test {
public static void main(String[] args) {
Derived derived = new Derived();
derived.func();
}
}
执行结果:

结论:【和成员变量同名时相同】
当子类和父类成员同名时,优先访问子类自己的成员。
特性:遵循就近原则,子类有的优先使用自己的,没有再去父类中找。
3.3如果一定要访问父类的成员怎么做?
【和上面的方法相同】
方法1:使用super关键字
方法2:初始化一个父类对象,用父类对象的引用访问父类的成员变量。
java
class Base {
public void methodA(){
System.out.println("Base中的methodA()");
}
}
class Derived extends Base{
public void methodA(){
System.out.println("Derived中的methodA()方法");
}
public void func(){
//方法1
super.methodA(); // 访问子类自己的methodA()
//方法2
Base base = new Base();
base.methodA();
}
}
public class Test {
public static void main(String[] args) {
Derived derived = new Derived();
derived.func();
}
}
执行结果:

4.super关键字
因为子类可能会和父类成员同名,如果明确要访问父类的成员,就需要使用super关键字。
java
public void testExtends(){
super.name = "zhangsan";//访问从父类继承下来的成员变量
super.age = 18;//访问从父类继承下来的成员变量
super.eat();///访问从父类继承下来的成员方法
color = "黄色";//访问子类自己的成员变量
run();//访问子类自己的成员方法
}
4.1子类构造方法
在继承性关系下,构造子类对象时,需要先调用基类构造方法,然后执行子类的构造方法。
即先要把继承于父类的成员初始化完,才能初始化子类自己的成员。
java
class Animal {
String name;
int age;
public Animal(String name, int age) {
this.name = name;
this.age = age;
}
}
public class Dog extends Animal{
String color;
public Dog(String name, int age,String color) {
super(name, age);
this.color = color;
}
}
注意事项:
- 若父类无显示构造方法(没写构造方法)或默认构造方法(即无参数构造方法),子类构造方法第一行默认有隐藏的super()调用,即调用子类构造方法时,先调用基类的构造方法。
- 若父类构造方法有带参数,子类构造方法需要自己定义super()。
- 在子类构造方法中,super()必须是第一条语句。
- 在子类构造方法中,super()只能出现一次,并且不能和this()同时出现。
第四点解释,当我们重载子类构造方法时,this()调用,会调用重载的子类构造方法。而this()和super()在构造方法中,都必须是第一条语句,所有不能同时出现。
代码如下:
java
public class Dog extends Animal{
String color;
//方法的重载
public Dog() {
this("green");//调用带有一个参数的构造方法
System.out.println("使用了不带参数的构造方法");
}
public Dog(String color) {
this.color = color;
System.out.println("使用了带有一个参数的构造方法");
}
public static void main(String[] args) {
Dog dog = new Dog();//调用无参构造方法
System.out.println(dog.color);
System.out.println("==============");
Dog dog1 = new Dog("red");//调用带有一个参数的构造方法
System.out.println(dog.color);
}
}
运行结果:

4.2super和this
super和tthis都可以在成员方法中用来访问:成员变量和调用其他成员方法,都可以作为构造方法的第一条语句。
【相同点】
1.都只能在类的非静态方法中使用,用来访问非静态成员变量和调用其他成员方法。
2.在构造方法中调用时,必须是构造方法的第一条语句,并且不能同时存在。
【不同点】
1,this是当前对象的引用,当前对象即调用实例方法的对象,super相当于子类对象从父类继承下来的部分成员的引用。
2.在非静态成员方法中,this用来访问本类的方法和属性,super用来访问从父类继承下来的方法和属性。
3.在构造方法中:this()用于调用本类的构造方法,super()用于调用父类的构造方法,两者在构造方法中不能同时存在。
4.在继承体系下,构造方法中一定会存在super()调用,用户没有写编译器也会自动增加,但this()用户不写则没有。
5.初始化顺序
5.1没有继承关系时的执行顺序
我们再来回顾下初始化执行顺序,没有继承关系时的执行顺序:
java
class Person {
public String name;
public int age;
public Person(String name, int age) {
this.name = name;
this.age = age;
System.out.println("构造方法执行");
}
{
System.out.println("实例代码块执行");
}
static {
System.out.println("静态代码块执行");
}
}
public class Test {
public static void main(String[] args) {
Person person1 = new Person("bit",10);
System.out.println("============================");
Person person2 = new Person("gaobo",20);
}
}
运行结果:

结论分析:
1.被static修饰的静态代码块先执行,并且只执行一次,在类的加载阶段执行。
2.当有对象创建时,才会执行实例代码块,执行完实例代码块,最后执行构造方法。
5.1继承关系下的执行顺序
java
class Person {
public String name;
public int age;
public Person(String name, int age) {
this.name = name;
this.age = age;
System.out.println("构造方法执行");
}
{
System.out.println("实例代码块执行");
}
static {
System.out.println("静态代码块执行");
}
}
class Student extends Person{
public Student(String name,int age) {
super(name,age);
System.out.println("Student:构造方法执行");
}
{
System.out.println("Student:实例代码块执行");
}
static {
System.out.println("Student:静态代码块执行");
}
}
public class Test {
public static void main(String[] args) {
Student student1 = new Student("张三",19);
System.out.println("===========================");
Student student2 = new Student("gaobo",20);
}
}
执行结果:

结论分析:
1.父类静态代码块先执行,执行完再执行子类静态代码块。
2.父类实例代码块和构造方法先执行。
3.再执行子类实例代码块和构造方法。
4.静态代码块只执行一次。
6.final关键字
final可以用来修饰变量、成员方法和类。
1.被final修饰的变量或字段,表示常量,不能再被改变。
java
final int a = 10;
a = 20; // 编译出错
2.被final修饰的类,不能被继承
java
final class Person {
}
class Student extends Person{
}

3.被final修饰的方法,不能重写
java
class Person {
public final void eat(){
System.out.println("正在吃饭。。。");
}
}
class Student extends Person{
public void eat(){
System.out.println("zhangsan正在吃饭。。。");
}
}
