java继承

1.方法继承

animal类

package com.imooc.animal;

public class Animal {
    protected String name;  //名字
    private int month;  //月份
    private String species;  //品种

    //如果这里没有定义构造方法,编译器会自动生成
    public Animal(){}

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getMonth() {
        return month;
    }

    public void setMonth(int month) {
        this.month = month;
    }

    public String getSpecies() {
        return species;
    }

    public void setSpecies(String species) {
        this.species = species;
    }

    public void eat(){
        //子类只能继承父类的非私有成员,protected可以允许系内继承。
        System.out.println(this.getName() + " eating");
    }
}

cat类

package com.imooc.animal;

public class Dog extends Animal {
    private String sex;  //性别

    public Dog() {}

    public String getSex() {
        return sex;
    }

    public void setSex(String sex) {
        this.sex = sex;
    }

    //睡觉
    public void sleep(){
        //以下几种打印方式都是可行的
        System.out.println(this.getName() + " sleep");
//        System.out.println(getName() + " sleep");

        //下面方式必须是属性可继承。public/protected
//        System.out.println(super.name + " sleep");
//        System.out.println(this.name + " sleep");
//        System.out.println(name + " sleep");

        //使用this.name和name都可以的原因,编译器会寻找这个
        //现在本实例空间内寻找,然后去父类空间寻找
    }
}

Dog类

package com.imooc.animal;

public class Cat extends Animal {
    private double weight; //体重

    public Cat() {}

    public double getWeight() {
        return weight;
    }

    public void setWeight(double weight) {
        this.weight = weight;
    }

    //跑动方法
    public void run() {
        System.out.println(this.getName()+" Running");
    }
}

Test测试方法

package com.imooc.test;

import com.imooc.animal.Cat;
import com.imooc.animal.Dog;

public class Test {
    public static void main(String[] args) {
        Cat one = new Cat();
        one.setName("花花");
        one.setSpecies("中华田园猫");
        one.eat();
        one.run();
        System.out.println("---------------------------------");
        Dog two = new Dog();
        two.setName("妞妞");
        two.setMonth(1);
        two.eat();
        two.sleep();
    }
}

 

 

2.方法的重写

animal类

package com.imooc.animal;

public class Animal {
    protected String name;  //名字
    private int month;  //月份
    private String species;  //品种
    public int temp = 15;

    //如果这里没有定义构造方法,编译器会自动生成
    public Animal(){}

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getMonth() {
        return month;
    }

    public void setMonth(int month) {
        this.month = month;
    }

    public String getSpecies() {
        return species;
    }

    public void setSpecies(String species) {
        this.species = species;
    }

    public void eat(){
        //子类只能继承父类的非私有成员,protected可以允许系内继承。
        System.out.println(this.getName() + " eating");
    }
    public void eat(String name){
        System.out.println(name + " eating");
    }
}

Dog类

package com.imooc.animal;

public class Dog extends Animal {
    private String sex;  //性别
    // 属性值也是可以重写的,不过必须是public的访问类型
    public int temp = 300;

    public Dog() {}

    public String getSex() {
        return sex;
    }

    public void setSex(String sex) {
        this.sex = sex;
    }

    //睡觉
    public void sleep(){
        System.out.println(this.getName() + " sleep");
    }
    /*
    * 方法重载
    * 1、同一个类中
    * 2、方法名相同,参数列表不同(参数顺序、个数、类型)
    * 3、方法返回值、访问修饰符任意
    * 4、与方法的参数名无关
    * */
    private String sleep(String name) {
        return name;
    }
    /*
    * 方法重写(覆写)
    * 1、有继承关系的子类中
    * 2、方法名相同,参数列表相同(参数顺序、个数、类型),方法返回值相同
    * 3、访问修饰符,访问范围需要大于等于父类的访问范围
    * 4、与参数名无关
    * */
    public void eat(){
        System.out.println(this.name + " 最近没有食欲,Dog重写");
    }
    //方法重写,与参数名无关,与参数顺序、个数、类型有关
    public void eat(String dogName) {
        System.out.println(dogName + "Dog被重写");
    }
}

 

 

3.访问修饰符

Java中的访问修饰符主要有以下几类:
  • 公有的:public 允许在任意位置访问
  • 私有的:private 只允许在本类中进行访问
  • 受保护的:protected 允许在当前类、同包子类/非子类、跨包子类调用、跨包非子类不允许
  • 默认:允许在当前类、同包子类调用;跨包子类不允许调用

访问修饰符的作用域范围:

 

 

4.super关键字——调用父类的方法和属性

当子类继承父类的时候,如果子类将一个方法进行重写了,
那么在调用的时候,是调用子类的方法还是调用的父类的方法了?

animal类

package com.imooc.animal;

public class Animal {
    protected String name;  //名字

    public Animal(){}

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    //父类定义了eat方法
    public void eat(){
        System.out.println(this.getName() + " eating");
    }
    public void eat(String name){
        System.out.println(name + " eating");
    }
}

Dog类

package com.imooc.animal;

public class Dog extends Animal {

    public Dog() {}

    public void eat(){
        System.out.println(this.name + " 最近没有食欲,Dog重写");
    }

    public void eat(String dogName) {
        System.out.println(dogName + "Dog被重写");
    }

    public void sleep(){
        eat();
    }
}

测试代码:

package com.imooc.test;

import com.imooc.animal.Dog;

public class Test {
    public static void main(String[] args) {
        Dog two = new Dog();
        two.setName("妞妞");
        two.eat();  //妞妞 最近没有食欲,Dog重写
    }
}

我们发现,默认会调用子类的方法,这也符合调用顺序。

那如果我就想调用父类的方法,该如何了?可以使用super关键字。
super.eat();
不仅是方法,父类中允许子类派生的属性,也可以通过super来继承。

父类的构造方法不允许被继承、不允许被重写。

 

5.继承的初始化顺序

(1)静态优先
(2)父类优先
(3)非静态块优于构造函数

写出下面代码的执行结果:

public class ExecutionSequence {
    public static void main(String[] args) {
        new GeneralClass();
    }
}

class ParentClass{
    //静态代码块
    static {
        System.out.println("①我是父类静态块");
    }
    //非静态块
    {
        System.out.println("②我是父类非静态块");
    }
    //父类构造方法
    public ParentClass(){
        System.out.println("③我是父类构造函数");
    }
}

class GeneralClass extends ParentClass {
    //子类静态代码块
    static {
        System.out.println("④我是子类静态块");
    }
    {
        System.out.println("⑤我是子类非静态块");
    }
    public GeneralClass(){
        System.out.println("⑥我是子类构造函数");
    }
}
/*
①我是父类静态块
④我是子类静态块
②我是父类非静态块
③我是父类构造函数
⑤我是子类非静态块
⑥我是子类构造函数
*/

  为什么父类的构造函数在子类的非静态代码块之前?
“父类优先的原则”优于“非静态块优于构造函数的原则”。

 

6.super继承父类的构造方法

public class Animal {
    String name;  //名字
}

public class Cat extends Animal {
    private double weight; //体重
}

public class Test {
    public static void main(String[] args) {
        Cat one = new Cat();
    }
}

如果都不定义给构造方法,那么编译器会默认创建无参构造

public class Animal {
    String name;  //名字

    public Animal(){
        System.out.println("我是父类的无参构造,方法Animal");
    }

    public Animal(String name){
        this.name = name;
        System.out.println("我是父类的带参构造,方法名Animal,参数name");
    }
}

public class Cat extends Animal {
    private double weight; //体重

    public Cat(){
        System.out.println("我是子类的无参构造,方法Cat");
    }

    public Cat(String name,double weight) {
        this.name = name;
        this.weight = weight;
        System.out.println("我是子类的带参构造,方法名Cat,参数name、weight");
    }
}

情况一:

public class Test {
    public static void main(String[] args) {
        Cat one = new Cat();
    }
}

结果:
我是父类的无参构造,方法Animal
我是子类的无参构造,方法Cat

 

情况二:

public class Test {
    public static void main(String[] args) {
        Cat one = new Cat("ke",58);
    }
}

结果:
我是父类的无参构造,方法Animal
我是子类的带参构造,方法名Cat,参数name、weight

 

虽然构造方法不允许被继承、被重写,但是会影响子类的构造。
子类构造默认调用父类的无参构造方法。


如果你重新定义了有参构造方法,那么构造的时候就必须带参,所以一般都需要写上无参构造。

如果子类希望调用父类的有参构造方法,可以使用super显式继承。
同时super()必须放在子类构造方法有效代码的第一行。

public class Cat extends Animal {
    private double weight; //体重

    public Cat(){
        System.out.println("我是子类的无参构造,方法Cat");
    }

    public Cat(String name,double weight) {
        super(name);
        this.name = name;
        this.weight = weight;
        System.out.println("我是子类的带参构造,方法名Cat,参数name、weight");
    }
}

public class Test {
    public static void main(String[] args) {
        Cat one = new Cat("mao",68);
    }
}

结果:

我是父类的带参构造,方法名Animal,参数name
我是子类的带参构造,方法名Cat,参数name、weight

 

下面是一个常见问题:

public class Animal {
    String name;  //名字

    public Animal(String name){
        this.name = name;
        System.out.println("我是父类的带参构造,方法名Animal,参数name");
    }
}

public class Cat extends Animal {
    private double weight; //体重

    public Cat(){
        System.out.println("我是子类的无参构造,方法Cat");  //报错
    }

    public Cat(String name,double weight) {
        this.name = name;
        this.weight = weight;
        System.out.println("我是子类的带参构造,方法名Cat,参数name、weight");
    }
}

上面会直接报错。
子类会默认调用父类的无参构造,但是父类只有有参构造,所以就会报错。
需要super()显式继承或者定义父类的无参构造。

 

super代表父类对象,this代表当前对象。
当然构造方法也是可以调用同类中的构造方法。

public class Cat extends Animal {
    private double weight; //体重

    public Cat(){
        System.out.println("我是子类的无参构造,方法Cat");
    }

    public Cat(String name,double weight) {
        this();
        this.name = name;
        this.weight = weight;
        System.out.println("我是子类的带参构造,方法名Cat,参数name、weight");
    }
}

结果:
我是子类的无参构造,方法Cat
我是子类的带参构造,方法名Cat,参数name、weight

既然super和this都可以调用构造方法,那么二者可以共存吗?

当你在调用构造方法的时候,super和this只能使用一个。

 

7.Object类

Object类是所有类的父类。
一个类没有使用extends关键字明确标识继承关系,则默认继承Object类(包括数组)
Java中的每个类都可以使用Object中定义的方法。
Object类官网:https://docs.oracle.com/javase/8/docs/api/
Object类中定义的方法:

 

 

上面都是Object类内置的方法,所以Java中所有的对象都可以调用这些方法。以equals为例:
下面是其源码,主要是比较两个对象是否是同一个对象。

public boolean equals(Object obj) {
    return (this == obj);
}

示例:

public class Test {
    public static void main(String[] args) {
        Animal one = new Animal("mao");
        Animal two = new Animal("mao");
        //equals:比较两个引用是否指向同一个对象
        boolean flag = one.equals(two);
        System.out.println("one和two的equals比较: "+flag);
        System.out.println("one和two的==比较:"+(one==two));
    }
}
//one和two的equals比较: false
//one和two的==比较:false

那如果,我们需要比较两个对象的值该怎么做了?我们可以重写equals方法

public boolean equals(Object obj){
    if (obj == null){
        return false;
    }
    Animal temp = (Animal) obj;  //对象类型强制转换
    if (this.getName().equals(temp.getName())){
        return true;
    }else {
        return false;
    }
}

上面这种写法,可以在类型转换的时候会报错,改进方式是直接传入Animal对象:

public boolean equals(Animal obj){
    if (obj == null){
        return false;
    }
    if (this.getName().equals(obj.getName())){
        return true;
    }else {
        return false;
    }
}

需要说明的是toString这个方法:

public class Test {
    public static void main(String[] args) {
        Animal one = new Animal("mao");
        System.out.println(one);
        System.out.println(one.toString());
    }
}
//com.imooc.animal.Animal@1b6d3586
//com.imooc.animal.Animal@1b6d3586

可以看出,输出对象名时,默认会直接调用类中的toString。
继承Object中的toString方法时,输出对象的字符串表示形式:类型信息+@+内存地址。
但是对字符串类型例外:

String str1 = new String("hello");
System.out.println(str1);  //hello

很多时候我们会重写对象的toString方法,这样可以改变对象的默认打印。

 

8.final关键字

如果你希望一个类不被别人继承,那么可以定义final关键字:

public final class Animal {
    String name;  //名字

    public Animal(){}
}

final class:该类没有子类
final 方法:该方法不允许被子类重写,但是可以被子类继承使用。
final 方法内局部变量:只要在具体被使用之前进行赋值即可,一旦赋值不允许被修改
final 类中成员属性:赋值过程:1.定义直接初始化 2.构造方法 3.构造代码块

可配合static使用,来定义只需要设置一次的内容

 

posted @ 2020-03-15 21:56  明王不动心  阅读(231)  评论(0编辑  收藏  举报