继承

1、关键字:extends

2、格式

//定义父类的方式:(一个普通的类的定义)

public class 父类名{

  //....

 }


//定义子类的格式:

public class 子类名 extends 父类名{

 //...

}

3、继承中变量的重名

在父子继承关系中,如果成员变量重名,则创建子类对象时,访问有两种方式:

1、直接通过子类对象访问:

(该对象被创建时) 等号左边是谁,就优先用谁,没有则向上找

2、间接通过成员方法访问成员变量

该方法属于谁,就优先用谁,没有则向上找

【例】:

public class Fu {
    int numFu = 1;
    int num = 3;

    public int getNum(){
        return num;
    }
}

public class Zi extends Fu {
    int numZi = 2;
    int num = 4;
    
    public int getNumZi(){
        return num;
    }
}

public class Text{
	public static void main(String[] args) {
        Fu fu = new Fu();//创建一个父类对象
		//1、直接通过对象访问:
		//等号左边是谁,就优先用谁,没有则向上找  (Fu fu =   等号左边为Fu类)
        System.out.println("fu.num "+fu.num);

		//2、间接通过成员方法访问成员变量
		//  该方法属于谁,就优先用谁,没有则向上找 (getNum()方法属于父类)
        System.out.println("fu.getNum() "+fu.getNum());

		Zi zi = new Zi();
		System.out.println("zi.numFu "+zi.numFu+" zi.numZi "+zi.numZi);

		//1、直接通过对象访问:
		//等号左边是谁,就优先用谁,没有则向上找  ( Zi zi =   等号左边为Zi类)
		System.out.println("zi.num "+zi.num);

		//2、间接通过成员方法访问成员变量
		//  该方法属于谁,就优先用谁,没有则向上找 (getNumZi()方法属于子类)
		System.out.println("zi.getNumZi() "+zi.getNumZi());
	}
}

4、继承关系中,父类构造方法特点

以下为子类中的几种情况:

情况1,子类中无构造

如果子类中没写构造方法,则会自动赠送一个无参构造

(无参构造中又会隐式的调用父类中的无参构造,详情见【情况2】)

class Fu {
    //无参构造
    public Fu(){
        System.out.println("父类无参构造执行!");
    }
    
    //带参构造
    public Fu(int num){
        System.out.println("父类有参构造执行 "+num);
    }
}

class Zi extends Fu{
}

public class Main {
    public static void main(String[] args) {
        Zi zi = new Zi();
    }
}

运行结果

父类无参构造执行!

可以理解为:子类的系统默认构造函数,默认调用了super();

所以,如果父类中有 无参构造,子类就不用写构造方法

情况2,子类中的构造隐式调用父类构造

如果子类的构造方法中没有出现自定义super语句(没有显式的调用父类构造),则编译器会“赠送”一个super() 作为子类构造方法的第一条语句,从而调用到父类的无参构造

(super只能有一个,还必须是第一句)

a).子类无参构造中的隐式调用

class Fu {
    //无参构造
    public Fu(){
        System.out.println("父类无参构造执行!");
    }
    
    //带参构造
    public Fu(int num){
        System.out.println("父类有参构造执行 "+num);
    }
}
class Zi extends Fu{
    public Zi(){
        //这里会有一个隐藏的super()
         System.out.println("子类无参构造执行!");
    }
}

public class Main {
    public static void main(String[] args) {
        Zi zi = new Zi();
    }
}

运行结果:

父类无参构造执行!
子类无参构造执行!

b).子类带参构造中的隐式调用

class Fu {
    int nun;
    public Fu(){
        System.out.println("父类中的无参构造执行");
    }

    public Fu(int a,int b){
        System.out.println("父类中的带参构造执行,参数:"+a+","+b);
    }
}

class Zi extends Fu {
    public Zi(int a,int b){
        //这里也有个隐藏的编译器赠送的super()
        /*
        	这里可以通过super(9,12)语句显式的调用父类中的带参构造
       		这时的输出结果就为:
                        父类中的带参构造执行,参数:9,12
                        子类的带参构造:参数:3,6
                        
     		【注意】,如果这样使用后,编译器便不会赠送super()语句
     	*/
        System.out.println("子类的带参构造:参数:"+a+","+b);
    }
}


public class Main {
    public static void main(String[] args) {
        Zi zi =new Zi(3,6);
    }
}

运行结果:

父类中的无参构造执行
子类的带参构造:参数:3,6

情况3,private修饰不可以被继承

如果父类的构造方法只有一个,且修饰符是private,则该方法不可以被继承。

【延申】:

防止类的扩展:使用final修饰符表明一个类值终极的,不可被继承的。

【注意】:

修饰符可以用在类和成员上,只有final还可以用在方法中的局部变量上(常量)。

小结:子类中如果没写构造方法,则编译器会自动赠送一个

如果子类中使用无参构造且其中没有出现自定义super语句,则编译器会“赠送”一个super(),(super只能有一个,还必须是第一句)

子类构造可以通过super关键字来调用父类带参构造

如果父类的构造函数只有一个,且修饰符是private,则不可以被继承。

5、覆盖重写(override)

在父子类的关系继承当中,创建子类对象,访问成员方法规则

创建的对象是谁 就优先用谁 如果没有则向上找(去父类中找)

【注意】 无论成员方法还是成员变量,如果没有都是向上找父类,绝不会向下找子类

方法的重写:在继承关系中,方法的名与参数列表都一样的

重写(override):方法名一样,参数列表【也一样】。也叫覆盖,覆写。

重载(overload):方法名一样,参数列表【不一样】。

重写特点:创建的是子类对象,则优先用子类方法

【方法覆盖的注意事项】

1、保证父子类间的方法名相同,参数列表也相同

@Override 写在方法前面,用来检测是不是有效的正确的覆盖重写

2、子类方法的返回值必须小于等于父类方法的返回值范围

java.lang.Object类是所有类的公共最高父类(祖宗类),java.lang.String就是Object的子类

3、子类方法的权限必须【大于等于】父类方法的权限修饰符

public > protected > (default) > private

(default)不是关键字default 是什么都不写,即:(这里的空气就是default)int a;

静态方法的继承

静态方法也能被继承,但是静态方法不能被覆盖。如果父类中的静态方法在子类中被重新定义,那么定义在父类中的的静态方法将会被被隐藏。

可以使用 父类名.静态方法名 调用被隐藏的静态方法。

class Fu{
        public Fu(){

        }

        public static void sayHello(){
            System.out.println("Hello");
        }
    }

    class Zi extends Fu{
        public static void sayHello(){
            System.out.println("我不");
        }
    }

    public class StaticMethodInExtends {
        public static void main(String[] args) {
            Zi.sayHello();
            Fu.sayHello();
        }

运行结果:

我不
Hello

6、例题

public class HelloB extends HelloA{
    public HelloB(){//构造方法
       	System.out.println("\n子类中的构造方法");
    }
    
    {//构造代码块
        System.out.println("\n子类中的构造代码块");
    }
    
    //静态代码块
    static{
        System.out.println("\n子类中的静态代码块");
    }

    public static void main(String[] args) {
        new HelloB();
    }
}

public class HelloA {
    public HelloA(){
        System.out.println("\n父类中的构造方法");
    }
    
    {
        System.out.println("\n父类中的构造代码块");
    }
    
    static{
        System.out.println("\n父类中的静态代码块");
    }
}

运行结果是:

父类中的静态代码块

子类中的静态代码块

父类中的构造代码块

父类中的构造方法

子类中的构造代码块

子类中的构造方法

结论: 父类静态代码块内容先执行,后执行子类中的静态代码块,接着执行父类构造代码块和构造方法,然后执行子类构造代码块和构造方法。