继承
在现实生活中,继承是什么意思?我们知道继承可以是孩子继承了家族企业和家族的财产。
那么在Java中什么是继承:我们来看一个代码,对猫和狗这两个类进行定义,我们会用如下的定义方式:
class Cat{
public String name;
public int age;
public void eat(){
System.out.println(this.name + "正在吃猫粮");
}
public void sleep(){
System.out.println(this.name + "正在睡觉");
}
}
class Dog{
public String name;
public int age;
public void eat(){
System.out.println(this.name + "正在吃狗粮");
}
public void sleep(){
System.out.println(this.name + "正在睡觉");
}
}
有没有发现这两个类有很多相似之处,这时候我们能不能只写一次呢?在Java中确实可以,我们可以使用Java中的继承思想,通过把共性的地方进行抽取,新建一个类用来给猫和狗这两个类继承这些共性的地方,这样就实现了代码的复用~~
继承(inheritance)机制:是面向对象程序设计使代码可以复用的最重要的手段,它允许程序员在保持原有类特 性的基础上进行扩展,增加新功能,这样产生新的类,称派生类。继承呈现了面向对象程序设计的层次结构, 体现了由简单到复杂的认知过程。
继承主要解决的问题是:共性的抽取,实现代码复用。
根据继承的思想,我们来抽取猫和狗的共性的地方:
public String name;
public int age;
public void sleep(){
System.out.println(this.name + "正在睡觉");
}
这时候我们再来定义一个新的类,叫做Animal:
class Animal{
public String name;
public int age;
public void sleep(){
System.out.println(this.name + "正在睡觉");
}
}
extends
我们会使用extends来实现继承~~
现在我们将猫和狗这两个类继承Animal这个类~
public class Dog extends Animal {
public void eat() {
System.out.println(this.name + "正在吃狗粮");
}
}
public class Cat extends Animal {
public void eat(){
System.out.println(this.name + "正在吃猫粮");
}
}
public class Animal {
public String name;
public int age;
public void sleep(){
System.out.println(this.name + "正在睡觉");
}
}
让Dog和Cat继承Animal这个类,然后分别对Dog和Cat这两个类进行拓展,这样我们就实现了继承~~
访问成员变量和成员方法
成员变量访问遵循就近原则,子类有优先访问子类自己的,如果没有则向父类中找。
子类和父类的成员变量同名时
通过子类的引用会优先访问子类的成员变量!!!
class A{
public int age = 10;
}
class B extends A{
public int age = 66;
}
public class Test {
public static void main(String[] args) {
B test = new B();
System.out.println(test.age);
}
}
子类和父类的成员变量不重名
优先在子类中找,然后再向父类中找,如果都没有则发生编译报错~~
class A{
public int age = 10;
public int year = 2024;
}
class B extends A{
public int age = 66;
public static void main(String[] args) {
B b = new B();
System.out.println(b.age);
System.out.println(b.year);
}
}
访问成员方法
优先访问子类的成员方法,还是一样子类有的先访问子类自己的,子类没有的则在父类找,如果都没有,则编译报错~~
class A{
public int age = 10;
public int year = 2024;
public void test(){
System.out.println("A-test()...");
}
public void outPut(){
System.out.println("A-outPut()...");
}
}
class B extends A{
public int age = 66;
public void test(){
System.out.println("B-test()...");
}
public static void main(String[] args) {
B b = new B();
b.test();
b.outPut();
}
}
super
如果无法通过子类来直接调用父类的成员变量时,我们可以使用super关键字,super 关键字可以用来访问直接父类的成员方法和成员变量的~~
直接父类就是如果存在多层继承,super只能访问到子类的直接父类,不能访问到子类的父类的父类,不存在super.super的东西~~
class A{
public int age = 10;
public int year = 2024;
}
class B extends A{
public int age = 66;
public void print(){
System.out.println(super.age);
}
}
public class Test {
public static void main(String[] args) {
B test = new B();
test.print();
}
}
super 和 this 的异同
super是用来访问父类的成员方法和成员变量的,而 this 是用来访问当前类的成员方法和成员变量的~~
suoer() 和 this() 都是可以用来调用构造方法的~~
并且这两个都是要放在第一行的,所以super() 和 this() 是不能共存的~~
当你没有写构造方法的时候Java会默认提供一个不带参数的构造方法
所以实际上如果你没有写子类的和父类的构造方法时;Java会默认提供如下的代码:
class A{
public A(){
}
}
class B extends A{
public B(){
super();
}
}
当父类有无参的构造方法或者没有构造方法的时候,在子类构造方法第一行默认有隐含的super()调用,即调用父类构造方法
当父类有有参的构造方法是,子类的所有构造方法必须先调用父类的构造方法,否则编译报错
class A{
public A(int a){
}
}
class B extends A{
public B(){
super(10);//必须提供
System.out.println("haha");
}
public B(int a){
super(10);//必须提供
System.out.println("哈哈");
}
}
只能在类的非静态方法中使用,因为 super 和 this 都是依赖对象的
子类和父类的代码块执行顺序
结论:先执行父类的静态代码块,再执行子类的静态代码块,接着分别执行父类的实例化代码块和构造方法,最后分别执行子类的实例化代码块和构造方法
并且父类和子类的静态代码块只会随着类的加载而执行,总体来说就是只会执行一次~~
class A{
{
System.out.println("父类实例化代码块");
}
static{
System.out.println("父类静态代码块");
}
public A(){
System.out.println("父类构造方法");
}
}
class B extends A{
{
System.out.println("子类实例化代码块");
}
static{
System.out.println("子类静态代码块");
}
public B(){
System.out.println("子类构造方法");
}
}
public class Test {
public static void main(String[] args) {
B b = new B();
System.out.println("==============");
B b2 = new B();
}
}
小结
父子静(一次),父实例化+构造,子实例化+构造
protected
注意子类访问父类的成员变量或者成员方法也是有访问权限的,如果父类的成员被private修饰,子类是无法访问到该成员的
当父类的成员是默认权限(default),如果父类和子类不在一个包下,子类也是访问不到该成员的
如果全用public的话,就是滥用权限了,这时候就有了protected关键字,即使子类和父类不在同一个包下,子类能访问父类的被protected修饰的成员~~
我们需要根据具体的情况来指定成员的访问权限,也就需要大家写代码的时候考虑这个成员会被哪里使用到~
继承方式
不建议太多层的继承,一般继承三次就可以了,继承多了,就很难找到之间的关系了~~也是为了代码的可读性
Java是不支持多继承的,一个子类有且仅有一个父类
final
如果某个类不想被继承,可以加 final 进行修饰
final class A{
public A(int a){
}
}
class B extends A{ // err 无法继承A
}
修饰变量的时候,可以让变量获得常属性,意味着变量不能被修改
public final int a = 10;
a = 1;//err 不能改变具有常属性的a
修饰方法的时候,表示该方法不能被重写(多态会讲到)
class A{
public final void print(){
System.out.println("密封方法");
}
}
class B extends A{
public void print(){ //err 不能进行重写
}
}
继承和组合
继承是is-a 关系(狗是动物,猫是动物),组合是has - a关系(汽车是由轮胎和发动组成的)
和继承类似, 组合也是一种表达类之间关系的方式, 也是能够达到代码重用的效果。组合并没有涉及到特殊的语法(诸如 extends 这样的关键字), 仅仅是将一个类的实例作为另外一个类的字段。
class Tire{
//轮胎的属性和方法
}
class Engine{
//发动机的属性和方法
}
class Car{
// has a 关系 ( a part of )
private Tire tire; // 可以复用轮胎中的属性和方法
private Engine engine; // 可以复用发动机中的属性和方法
}
// 奔驰是汽车
class Benz extends Car{
// is a 关系
// 将汽车中包含的:轮胎、发送机全部继承下来
}