类的继承格式
class 父类 {
}
class 子类 extends 父类 {
}
通过extends关键字来实现
一、为什么需要继承
有一些类有共同的地方,容易出现代码冗余,所以我们需要抽取一下,封装继承多态嘛。
子类通过复写方法等方式可以对父类做一些拓展,比如:
笔都有产地和颜色,笔就是父类,钢笔继承自笔,钢笔的产地和颜色是什么,蜡笔呢,毛笔也有,但是毛笔没有笔帽,特殊一点,这就可以做拓展了,为什么没有笔帽,要注意什么。
二、继承到底继承了什么?有什么特点?
一个类里面有4种东西
- 属性(包括类属性和实例属性)、
- 方法(包括类方法和实例方法)、
- 构造器/构造方法
- 初始化块(包括类的初始化块和实例的初始化块)。
子类继承父类的时候,到底继承了什么?
- 1、子类继承父类所有的属性(除了private)
- 2、子类继承父类(除private)所有的方法,(子类方法如果不调用 super.所复写方法名称 ,那么对应父类方法将不会执行)
- final方法不可以被继承
- static方法不可以被继承,随着类的加载而加载,继承毛线。但是如果权限允许子类还是可以用。
- 3、子类可以通过super,表示父类的引用,调用父类的属性或者方法。
(构造函数和代码块是无法被继承)
对于private这点非常好理解,因为private的访问权限是本类中嘛,就算通过super也不能访问private的private属性。但是可以通过对应的get方法获取,get是public嘛。
如果子类非要访问父类的私有域,那么就反射吧
继承有什么特点:
Java只可以单继承,不能多继承
继承的好处是是可以提高效率,抽取封装,缺点是提高了代码耦合。
Object是所有类的直接或者间接父类,Object是万类之祖。
-
继承中的this和super
super关键字:
我们可以通过super关键字来实现对父类成员的访问,用来引用当前对象的父类。this关键字:
指向自己的引用。
-
继承与final
-
final 类
final class 类名 {//类体}
不能继承的,即最终类
被声明为 final 类的方法自动地声明为 final,但是实例变量并不是 final -
final方法
修饰符(public/private/default/protected) final 返回值类型 方法名(){//方法体}
该方法不能被子类重写
-
-
final实例变量
final修饰的变量称为常量,这些变量只能赋值一次,final 的变量不能被修改
final修饰的引用类型变量,表示该变量的引用不能变,而不是该变量的值不能变; 如果子类方法签名和父类签名完全一致,那就必须写上@Override的注解,也就是必须复写,不然的话,报错。
调用被覆盖的父类方法:使用super.方法名(实参);
三、子类的初始化
类的初始化分为 类初始化 和 对象初始化。
三、1.类初始化
先执行父类的 类成员(static成员) 和 类代码块(static代码块),后执行子类的类成员(static成员) 和 类代码块(static代码块)。
static成员和static代码块仅会在类加载的时候加载一次。
三、2.对象初始化
1、先初始化父类的属性和代码块,后初始化子类的属性和代码块。
2、接着初始化父类的构造函数,然后是子类
关于子类能从父类继承到什么和继承中的类初始化,我们看一下下面的代码就清楚了:
AClass
public class AClass {
int normalVar = 10;
// 父类私有变量,子类无法继承,但可以get到
private int priVar = 20;
{
System.out.println("AClass 普通代码块");
}
static{
System.out.println("AClass static代码块");
}
public AClass(){
System.out.println("AClass 无参构造方法");
}
public AClass(String str){
System.out.println("AClass 带参构造方法!");
}
public void aMethodOne(){
normalVar = 55;
System.out.println("aMethodOne");
}
public void aMethodTwo(){
normalVar = 66;
System.out.println("aMethodTwo");
}
public int getPriVar() {
return priVar;
}
public void setNormalVar(int normalVar) {
this.normalVar = normalVar;
}
}
.
.
BClass
public class BClass extends AClass{
{
System.out.println("BClass 普通代码块");
}
static{
System.out.println("BClass static代码块");
}
public BClass(){
System.out.println("BClass 无参构造方法");
}
public BClass(String str){
System.out.println("BClass 带参构造方法!");
}
@Override
public void aMethodOne() {
super.aMethodOne();
System.out.println("子类Override的aMethodOne方法 并且调用super.aMethodOne();");
}
@Override
public void aMethodTwo() {
//super.aMethodTwo();
System.out.println("子类Override的aMethodTwo方法 不调用super.aMethodTwo();");
}
public void getFatherVar(){
System.out.println("调用父类的非私有变量:"+normalVar);
//int a = priVar; // 父类的私有变量子类无法继承,本句会报错
}
}
.
.
TrtExt
public class TrtExt{
public static void main(String[] args) {
BClass bClass1 = new BClass();
System.out.println("=========");
BClass bClassZs = new BClass("张三");
bClassZs.getFatherVar();
bClassZs.setNormalVar(88); // 子类bClass2对象改变父类的变量,不会影响其他子类
System.out.println("====================");
BClass bClassLs = new BClass("李四");
bClassLs.getFatherVar();//
System.out.println("====================");
BClass bClassWw = new BClass("王五");
bClassWw.getFatherVar();// 父类的原始值
bClassWw.aMethodOne(); // BClass的aMethodOne方法有super.aMethodOne();所以会调父类的aMethodOne()
bClassWw.getFatherVar();
System.out.println("====================");
BClass bClassAl = new BClass("阿六");
bClassAl.getFatherVar();// 父类的原始值
bClassAl.aMethodTwo();// BClass的aMethodTwo方法 没有super.aMethodOne();所以不调父类aMethodTwo
bClassAl.getFatherVar();
}
}
.
.
输出:
AClass static代码块
BClass static代码块
AClass 普通代码块
AClass 无参构造方法
BClass 普通代码块
BClass 无参构造方法
=========
AClass 普通代码块
AClass 无参构造方法
BClass 普通代码块
BClass 带参构造方法!
调用父类的非私有变量:10
====================
AClass 普通代码块
AClass 无参构造方法
BClass 普通代码块
BClass 带参构造方法!
调用父类的非私有变量:10
====================
AClass 普通代码块
AClass 无参构造方法
BClass 普通代码块
BClass 带参构造方法!
调用父类的非私有变量:10
aMethodOne
子类Override的aMethodOne方法 并且调用super.aMethodOne();
调用父类的非私有变量:55
====================
AClass 普通代码块
AClass 无参构造方法
BClass 普通代码块
BClass 带参构造方法!
调用父类的非私有变量:10
子类Override的aMethodTwo方法 不调用super.aMethodTwo();
调用父类的非私有变量:10
通过这个例子,我们可以清楚地看到
1、静态代码块只会随着类的加载加载一次,优先于对象
2、子类只可以继承父类的非private的
变量
和方法
,无法继承代码块
和构造方法
3、当子类被实例化时,会先调用父类的无参构造方法。
子类从父类继承的变量和方法是独立的,不会受到其他子类的影响。
(bClass2改变父类的变量不会影响bClass3继承到的变量)4、子类复写父类的方法,如果不调用 super.所复写方法名称 那么对应父类方法将不会执行,也就是说,这个方法就相当一个普通的方法,没有从父类继承到什么。
(绝大多数情况下是需要调用 super.所复写方法名称 的,但是有时候我们个别子类不想要父类方法的初始化逻辑,我们可以灵活选择是否保留 )
四、overload和override
方法重载(overload)
1.必须是同一个类
2方法名一样
3参数类型不一样或参数数量不一样
方法的重写/复写(override)
子类对父类方法的复写
两同两小一大原则:
两同:
方法名相同,参数类型相同
两小:
子类返回类型小于等于父类方法返回类型,若为基本类型或者void则必须相同
子类抛出异常小于等于父类方法抛出异常,
一大:
子类访问权限大于等于父类方法访问权限。
实例一下:
AClass
public class AClass {
public int number = 10;
protected void changeNumber(int i){
setNumber(100);
System.out.println("父类 changeNumber: "+number);
}
public int setNumber(int i) {
number = i;
System.out.println("父类 number: "+number);
return number;
}
}
.
.
BClass
public class BClass extends AClass{
// 这个复写方法的访问权限比父类大,也就是两者访问权限不一致,但是也是对那个方法的复写
@Override
public void changeNumber(int i) {
super.changeNumber(i);
setNumber(i);
}
}
.
.
TrtExt
public class TrtExt{
public static void main(String[] args) {
BClass bClass2 = new BClass();
bClass2.changeNumber(70);
}
}
输出:
父类 number: 100
父类 changeNumber: 100
父类 number: 70
我们看到,其实并不是复写的方法就需要和父类方法完全一致的。