目录
1.什么是继承
上面的这个animal就是基类,我们的这个dog和bird都是继承这个基类的特征,使用的是extends这个关键字,表示我们的子类继承父类,父类的这个成员变量和成员方法我们子类都会拥有的;
2.继承的特征
上面的这个代码里面,是构造了一个所谓的base这个基类,根据这个基类引申出来了一个子类,就是我们的derived子类;
但是我们的这个子类可以继承父类的成员变量和成员方法,如果这个子类有的成员变量和成员方法,在我们的父类里面本来就有,这样在进行变量的结果的打印和方法的调用的时候,首先是调用我们的的子类自己的,如果想要调用这个父类的方法,打印父类成员变量,这个时候需要加上这个super进行说明;
3.子类构造方法
下面的这个代码里面,我们是一个父类,两个子类,这个子类继承父类的成员变量和成员方法,子类里面写了对应的构造函数,但是父类里面没有写;
在这个代码里面,如果我们给这个父类加上构造函数,这个时候就会报错;这个究竟是什么原因呢,下面我通过几张图分析一下;
首先,我们需要尝试,发现这个父类加上这个构造函数之后确实会报错:
这个主要是因为我们没有写这个父类的构造函数的时候,这个里面实际上会自动实现没有参数的构造函数,如下图所示(我把这个注释掉了),主要就是因为这个被默认执行,但是当我们的这个父类里面写了构造函数,我们的这个子类里面的super()就找不到父类里面的默认的了,因为我们之前说,这个我们自己实现构造函数之后,默认的就不提供了,我们自己不写构造函数的时候,系统才会为我们提供默认的;
因此这个时候需要我们自己去实现重写这个构造函数里面的super这个部分,才可以实现这个子类的构造;
需要注意的是这个super需要在我们的构造函数的第一行,且这个super()和this()这样的两个构造不可以同时出现;
4.super和this辨析
this使我们当时学习类的构造函数的时候遇到的,这个用来指明我们的类的成员变量(当我们的形参的名字和实参的名字相等的时候,this需要被使用);
以及后来学习的这个this()进行初始化的方法,就是下面的这个例子:我们的this可以间接的进行有参数的构造函数的调用,和有参数的构造函数的作用是一致的,但是这个this必须是我们的这个代码块里面的第一条语句;
java
public class Date {
public int year;
public int month;
public int day;
// 无参构造方法--内部给各个成员赋值初始值,该部分功能与三个参数的构造方法重复
// 此处可以在无参构造方法中通过this调用带有三个参数的构造方法
// 但是this(1900,1,1);必须是构造方法中第一条语句
public Date(){
//System.out.println(year); 注释取消掉,编译会失败
this(1900, 1, 1);
//this.year = 1900;
//this.month = 1;
//this.day = 1;
} /
/ 带有三个参数的构造方法
public Date(int year, int month, int day) {
this.year = year;
this.month = month;
this.day = day;
}
}
而且使用这个this的时候,不可以互相调用,形成一个环,例如下面的这个形成环就会报错,这个是我们之前学习这个初始化的时候就介绍过的,这个地方只是回顾一下;
java
public Date(){
this(1900,1,1);
}
public Date(int year, int month, int day) {
this();
}
而这个super也是一个关键字,我们今天学习这个继承的时候,当我们的父类写出了自己的构造函数的时候(有参数),这个时候我们的子类也是需要有自己的构造函数的,这个时候就有了super进行构造(实际上就是调用的我们的父类的构造方法),还有一个就是我们的这个子类和父类都有相同的成员函数和成员方法的时候,我们的这个一般默认的调用会调用我们的子类自己的,如果我们想要调用父类的,这个时候需要使用这个super.func()之类的写法,super.a=100之类的这样去访问我们的父类的成员变量和成员方法;
还有一点就是我们上面也是演示了的,就是我们的这个默认的构造函数本来就是存在的,只不过是这个super()这样的,没有写出来罢了,但是这个子类也是有自己的,但是这个this如果我们自己不去写,实际上是不存在的;
上面的所有分析总结之后就是下面的这个异同概括:
//相同点
//1. 都是Java中的关键字
//2. 只能在类的非静态方法中使用,用来访问非静态成员方法和字段
//3. 在构造方法中调用时,必须是构造方法中的第一条语句,并且不能同时存在
//不同点
//1. this是当前对象的引用,当前对象即调用实例方法的对象,
super相当于是子类对象中从父类继承下来部分成员的引用
//2. 在非静态成员方法中,this用来访问本类的方法和属性,
super用来访问父类继承下来的方法和属性
//3. 在构造方法中:this(...)用于调用本类构造方法,
super(...)用于调用父类构造方法,两种调用不能同时在构造方法中出现
//4. 构造方法中一定会存在super(...)的调用,用户没有写编译器也会增加,
但是this(...)用户不写则没有
5.再谈初始化
这个时候,再谈初始化,我们来看一下,这个代码的执行顺序,这个里面有我们的静态代码块和实例代码块(不加static修饰的),为什么叫做静态代码块,因为这个静态的是和我们的类存在的,不受对象的创建的影响,是属于我们的这个类的,为什么叫做实例代码块,因为这个代码块只有我们进行这个类实例化成对象的时候才会执行这个实例的代码块;而且我们无论我们怎么进行这个对象的构造,这个静态的static代码块只会执行一次(在我们的类加载的时候执行),但是我们的类实例化几次(就是创建几个对象),我们的这个实例代码块就会执行几次;
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 TestDemo {
public static void main(String[] args) {
Person person1 = new Person("bit",10);
System.out.println("============================");
Person person2 = new Person("gaobo",20);
}
}
上面的这个就是静态,实例,构造方法,实例,构造方法的执行顺序;
这个地方为什么代码块比这个构造方法先执行,这个有很多的解释:首先我们需要了解这个构造代码块是给所有的对象进行初始化的,但是这个构造方法是给对应的对象进行初始化的,其次,我们人可以接受的解释就是:构造代码块本质上就是在这个构造方法里面的第一行,因此这个先于我们的构造函数先执行,可以这么理解;
下面的这个是我们的代码块加上这个继承的语法之后的具体的执行顺序:
java
class Person {
public String name;
public int age;
public Person(String name, int age) {
this.name = name;
this.age = age;
System.out.println("Person:构造方法执行");
}
{
System.out.println("Person:实例代码块执行");
}
static {
System.out.println("Person:静态代码块执行");
}
}
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 TestDemo4 {
public static void main5(String[] args) {
Student student1 = new Student("张三",19);
System.out.println("===========================");
Student student2 = new Student("gaobo",20);
}
public static void main(String[] args) {
Person person1 = new Person("bit",10);
System.out.println("============================");
Person person2 = new Person("gaobo",20);
}
}
其实上面的这个就是学生是子类,我们的person是基类,我们先是创建子类的对象,看看这个代码块的执行顺序:这个也不难理解,我们首先回去调用这个父类的静态代码块,然后是子类的静态代码块,然后是实例代码块,构造方法,只不过这个是先执行基类的实例和构造在,再执行子类的,当我们第二次去创建对象的时候,这个static修饰的静态的就不会再次执行了;
当我们把下面的main1放开的时候,这个时候创建的是父类的对象,这个时候和子类就没有关系了,这个执行的顺序如下图所示;
6.protected关键字用法说明
这个protected就是我们的访问限定符,表示的就是我们的这个类的权限,只不过这个不像public公开访问,private私有的不可以访问那么直截了当,而是需要我们仔细的学习一下,了解一下,但是这个可能用的不是很多,大部分还是用public或者是private两个几极端的吧(成员变量是私有的,成员方法是共有的),我觉得~~
protected用法如下:同一个包里面的同一个类,就是我们的这个包不就是类的集合吗,在一个包里面的某一个类里面,我们可以使用这个protected关键字;
还有就是同一个包的不同的类:
还有就是不同的包的子类:就是我们在test1这个包里面继承了test2包里面的一个类,我们在test1里面创建一个该类的对象,就是不同包的子类(因为我们的创建的对象是实例化的test1里面的类,而这个test1就是子类),如果我们创建的是test2里面的实例化对象吗,这个时候就不可以访问父类的protected变量,但是可以访问pubblic的,行不行我们试一试就知道了;
7.final的用法说明
final的总结如下:
final修饰类:这个类不可以被其他的类继承;我们的String这个类的底层源代码就有体现;
final修饰变量,这个变量的值就不可以被修改了,类似于常量;---这个可以自己试试,一旦加上final之后如果还去修改,这个时候就会报错;
final修饰方法:这个方法不可以被重写(后面学)
下面的这个就是String这个类被final修饰的,这个类不可以被其他的类继承,我们可以在自己的这个idea里面调出来看看,方法如下:
首先在我们的主页面按两下这个shift键,这个时候会弹出来下面的这个页面,我们需要勾选这个图中的框框:
然后直接在这个搜索框里面输入这个String之后,看到这个第一个之后点进去,就可以看到这个String的源代码了;
下面的这个是和我们的final的相关题目,我们的这个final这个地方修饰的是我们的这个int[] array数组,这个时候我们可以修改这个数组在堆区上面的元素,但是这个new的话,相当于是在这个栈区上面改变我们的这个数组的地址,因此这个是不被允许的;