继承
继承在本职上是特殊——一般的关系,即常说的is-a关系。子类继承父类,表明子类是一种特殊的父类,并且具有父类所不具有的 一些属性或方法。
JAVA支持多继承吗?
不支持,java中提供类与类之间提供单继承 。
为什么不支持多继承?
提供多继承会可能出现错误,如:一个类继承了两个父类,而两个父类里面都有show()方法。
为什么接口支持多继承?
接口的方法并没有具体实现,实现需要子类重写接口的方法,所有不存在如上调用的不确定性 。
注意
- 类优先于接口。 如果一个子类继承的父类和接口有相同的方法实现。 那么子类继承父类的方法
- 子类中的方法优先于父类型中的方法。
- 如果以上条件都不满足, 则必须显示覆盖/实现其方法,或者声明成abstract。
继承的好处
- 提高代码复用性
- 提高程序的扩展性(多态里向上转型的前提)
继承的缺陷
- 父类变,子类就必须变。
- 继承破坏了封装,对于父类而言,它的实现细节对与子类来说都是透明的。
- 继承是一种强耦合关系。
Java 继承语法格式
在 Java 中通过 extends 关键字可以申明一个类是从另外一个类继承而来的,一般形式如下:
class 父类 {
}
class 子类 extends 父类 {
}
权限修饰
- 被private修饰的,是不可以被继承的,因为private修饰的只能在本类中可见,子类是不可见的;
- 父类被protected或public修饰的,子类是可以继承的;
- 被默认修饰符修饰的只能在同包下的子类是可以继承的;
构造器
构造器能被继承吗?
我们知道子类可以继承父类的属性和方法,除了那些private的外还有一样是子类继承不了的—构造器。对于构造器而言,它只能够被调用,而不能被继承。 调用父类的构造方法我们使用super()即可。
为什么需要访问父类的构造器?
对于子类而已,其构造器的正确初始化是非常重要的,而且当且仅当只有一个方法可以保证这点:在构造器中调用父类构造器来完成初始化,而父类构造器具有执行父类初始化所需要的所有知识和能力。
怎么调用父类的构造器?
编译器会默认给子类调用父类的构造器。
但是,这个默认调用父类的构造器是有前提的:父类有默认构造器(无参构造 )。如果父类没有默认构造器,我们就要必须显示的使用super()来调用父类构造器,而且必须是在子类构造器中做的第一件事(第一行代码)。 否则编译器会报错:无法找到符合父类形式的构造器。
static关键字
JAVA静态方法是否可以被继承?
结论:java中静态属性和静态方法可以被继承,但是没有被重写(overwrite)而是被隐藏.
如果在子类重写静态方法加上@Override会直接编译报错,不加@Override重写,用谁的对象/类调用就执行方法,静态方法时属于类的;静态属性同上
原因:
1). 静态方法和属性是属于类的,调用的时候直接通过类名.方法名完成对,不需要继承机制及可以调用。如果子类里面定义了静态方法和属性,那么这时候父类的静态方法或属性称之为"隐藏"。如果你想要调用父类的静态方法和属性,直接通过父类名.方法或变量名完成,至于是否继承一说,子类是有继承静态方法和属性,但是跟实例方法和属性不太一样,存在"隐藏"的这种情况。
2). 多态之所以能够实现依赖于继承、接口和重写、重载(继承和重写最为关键)。有了继承和重写就可以实现父类的引用指向不同子类的对象。重写的功能是:"重写"后子类的优先级要高于父类的优先级,但是“隐藏”是没有这个优先级之分的。
3). 静态属性、静态方法和非静态的属性都可以被继承和隐藏而不能被重写,因此不能实现多态,不能实现父类的引用可以指向不同子类的对象。非静态方法可以被继承和重写,因此可以实现多态。
/**
* 父类
*/
public class A {
public static String staticStr = "A静态属性";
public String nonStaticStr = "A非静态属性";
public static void staticMethod() {
System.out.println("A静态方法");
}
public void nonStaticMethod() {
System.out.println("A非静态方法");
}
}
/**
* 子类,重写父类的静态属性和静态方法
*/
public class B extends A {
public static String staticStr = "B改写后的静态属性";
public String nonStaticStr = "B改写后的非静态属性";
public static void staticMethod() {
System.out.println("B改写后的静态方法");
}
}
/**
* 子类,直接继承
*/
public class C extends A{
}
/**
* 测试类
*/
public class Test {
public static void main(String[] args) {
C c = new C();
System.out.println(c.nonStaticStr);
System.out.println(c.staticStr);
c.staticMethod();// 输出的结果都是父类中的非静态属性、静态属性和静态方法,推出静态属性和静态方法可以被继承
System.out.println("-------------------------------");
A c1 = new C();
System.out.println(c1.nonStaticStr);
System.out.println(c1.staticStr);
c1.staticMethod();// 结果同上,输出的结果都是父类中的非静态属性、静态属性和静态方法,推出静态属性和静态方法可以被继承
System.out.println("-------------------------------");
B b = new B();
System.out.println(b.nonStaticStr);
System.out.println(b.staticStr);
b.staticMethod();//(允许在子类中定义同名静态方法,但是没有多态,严格的讲,方法间没有多态就不能称为覆盖)
System.out.println("-------------------------------");
A b1 = new B();
System.out.println(b1.nonStaticStr);
System.out.println(b1.staticStr);
b1.staticMethod();// 结果都是父类的静态方法,说明静态方法不可以被重写,不能实现多态
}
/*
A非静态属性
A静态属性
A静态方法
-------------------------------
A非静态属性
A静态属性
A静态方法
-------------------------------
B改写后的非静态属性
B改写后的静态属性
B改写后的静态方法
-------------------------------
A非静态属性
A静态属性
A静态方法
*/
}
super 与 this 关键字
- super关键字:我们可以通过super关键字来实现对父类成员的访问,用来引用当前对象的父类。
- this关键字:指向自己的引用
class Animal {
void eat() {
System.out.println("animal : eat");
}
}
class Dog extends Animal {
void eat() {
System.out.println("dog : eat");
}
void eatTest() {
this.eat(); // this 调用自己的方法
super.eat(); // super 调用父类方法
}
}
public class Test {
public static void main(String[] args) {
Animal a = new Animal();
a.eat();
Dog d = new Dog();
d.eatTest();
}
}
final关键字
- final 关键字修饰类可以把类定义为不能继承的,即最终类;
- final 关键字修饰修饰方法,该方法不能被子类重写;
方法的重写(覆盖)
两同
即要重写的方法的方法名和参数列表必须相同,可以在要重写的方法的上方添加@Override注解,来判断是否正确的进行了重写;
两小
即子类的返回值类型和抛出的异常类型必须要小于等于父类的类型。
一大
重写的方法的权限修饰符必须要大于等于父类方法的权限
###代码块普通代码块:
在方法或语句中出现的{}就称为普通代码块。
普通代码块和一般的语句执行顺序由他们在代码中出现的次序决定–“先出现先执行”静态代码块:
在java中使用static关键字声明的代码块。
静态块用于初始化类,为类的属性初始化。每个静态代码块只会执行一次。
由于JVM在加载类时会执行静态代码块,所以静态代码块先于主方法执行。
如果类中包含多个静态代码块,那么将按照”先定义的代码先执行,后定义的代码后执行”。
注意:
1 静态代码块不能存在于任何方法体内。
2 静态代码块不能直接访问静态实例变量和实例方法,需要通过类的实例对象来访问。- 构造代码块:
直接在类中定义且没有加static关键字的代码块称为{}构造代码块。
构造代码块在创建对象时被调用,每次创建对象都会被调用,并且构造代码块的执行次序优先于类构造函数。这个构造代码块的执行顺序不会因为方法所在位置而影响,我特意将他放在构造函数之后。
####面试题
java中关于子类和父类的构造函数,静态代码块,普通代码块,静态成员变量和普通成员变量的初始化顺序
1.父类【静态成员】和【静态代码块】,按在代码中出现的顺序依次执行。
2.子类【静态成员】和【静态代码块】,按在代码中出现的顺序依次执行。
3.父类【构造代码块】和子类的构造代码块
3.父类的【普通成员变量被普通成员方法赋值】和【普通代码块】,按在代码中出现的顺序依次执行。
4.执行父类的构造方法。
5.子类的【普通成员变量被普通成员方法赋值】和【普通代码块】,按在代码中出现的顺序依次执行。
https://blog.csdn.net/lilamei170607/article/details/82590287