构造器和类的继承

构造器

package testClass;

public class Person {
    String name;
    int age;

    //构造器用来生成实例,可以设置一些默认值.
    //如果不写出来,编译器也会自动生成一个无参构造器
    //构造器和类同名,不能有返回值,前面加一个public
    //使用new关键字其实是在调用构造器,并在堆中开辟空间
    public Person(){
        this.name = "Tom";
    }
    //有参构造,调用时可以传入参数
    //如果写了有参构造,就必须手动写出无参构造
    public Person(String name){
        this.name = name;
    }
    //可以有多个构造器
    //类里的方法可以互相调用
    public Person(String name, int age) {
        this(name);//调用其他构造器。当构造器的代码很多时,常用此方法
        this.age = age;
    }
    public void talk(){
        System.out.println("我是"+this.name);
    }
}

如果一个类只提供了一些方便的函数,不希望用户创建此类的对象,可以提供一个private修饰的无参构造,这样就不能使用构造函数了。

private Math(){}//有了private修饰的无参构造就不能创建对了,因为Math类只提供了一些方便的函数,不需要创建

继承

继承的实现

  • 继承的概念

    • 继承是面向对象三大特征之一,可以使得子类具有父类的属性和方法,还可以在子类中重新定义,以及追加属性和方法
  • 实现继承的格式

    • 格式:class 子类 extends 父类 { }
      • 举例:class Dog extends Animal { }
  • 继承带来的好处

    • 继承可以让类与类之间产生关系,子父类关系,产生子父类后,子类则可以使用父类中非私有的成员。
package testClass;

//一个子类可以拥有父类的属性和方法,称为继承
//java不允许多继承,但允许多层继承,不过为了结构的清晰,建议最多继承三层。
public class Student  extends Person{

    //Person类public的属性,Student对象都可以使用
    //Private属性不可以继承
    public void study(){
    //创建子类的对象时会先调用父类的无参构造器,然后再调用子类的构造器
    //可以在子类的构造方法中加上super(),明确要先调用父类的构造器。
        super();//如果想要使用父类的有参构造,就加上参数。也可以用super关键字调用父类的其他属性和方法
        //如果父类有无参构造,super()可以省略。但是如果父类没有无参构造,必须明确使用super()调用父类有参构造。
        super.talk();
        System.out.println(this.name+"在学习。");
    }

}

下面是测试类:

public class Application {
    public static void main(String[] args) {

        //类是抽象的,需要实例化创建对象
        Student jack = new Student();
        jack.name = "Jack";
        jack.age = 15;
        jack.study();
    }
}

继承的好处和缺点

  • 继承好处
    • 提高了代码的复用性(多个类相同的成员可以放到同一个类中)
    • 提高了代码的维护性(如果方法的代码需要修改,修改一处即可)
  • 继承弊端
    • 继承让类与类之间产生了关系,类的耦合性增强了,当父类发生变化时子类实现也不得不跟着变化,削弱了子类的独立性
  • 继承的应用场景:
    • 使用继承,需要考虑类与类之间是否存在is..a的关系,不能盲目使用继承
      • is..a的关系:谁是谁的一种,例如:老师和学生是人的一种,那人就是父类,学生和老师就是子类

继承中构造方法的访问特点

注意:子类中所有的构造方法默认都会访问父类中无参的构造方法

子类会继承父类中的数据,可能还会使用父类的数据。所以,子类初始化之前,一定要先完成父类数据的初始化,原因在于,每一个子类构造方法的第一条语句默认都是:super()

问题:如果父类中没有无参构造方法,只有带参构造方法,该怎么办呢?

1. 通过使用super关键字去显示的调用父类的带参构造方法
2. 在父类中自己提供一个无参构造方法

推荐方案:

​ 自己给出无参构造方法

继承中成员的访问特点

如果子类定义了和父类完全相同的属性名称时,称为属性的覆盖。当父类子类和方法中都有同名的变量时,优先调用方法中的变量,如果想要使用父类的变量就使用super关键字,想使用子类的变量就使用this关键字。同名方法的调用方式类似。

不过属性的覆盖在实际开发中没有意义。在任何开发中,属性都将使用private封装,一旦封装后属性的覆盖是没有意义的。因为父类定义的私有属性子类根本就看不见,更不会相互影响了。

public class Fu{
String info = "Hello";
}

public class Zi extends Fu{
int info = 30;
public void print(){
int info = 20;
System.out.println(info);//使用方法中的info属性
System.out.println(super.info);//使用父类的info属性
System.out.println(this.info);//使用子类的info属性
}
//测试类
public class Demo{
public static void main(String[] args){
	Zi z = new Zi();
	z.print();
	}
}

this&super

  • this&super关键字:
    • this:代表本类对象的引用
    • super:代表父类存储空间的标识(可以理解为父类对象引用)
  • this和super的使用分别
    • 成员变量:
      • this.成员变量 - 访问本类成员变量
      • super.成员变量 - 访问父类成员变量
    • 成员方法:
      • this.成员方法 - 访问本类成员方法
      • super.成员方法 - 访问父类成员方法
  • 构造方法:
    • this(…) - 访问本类构造方法
    • super(…) - 访问父类构造方法

方法的重写

当子类定义了和父类的方法名称、返回值类型、参数类型和个数完全相同的方法时,就称为方法的重写。如果子类覆写了父类的方法,而且实例化了子类对象,那么调用的一定是子类的方法。
方法重写注意事项:

  1. 在覆写的过程中,必须注意到权限问题:被子类覆写的方法不能拥有比父类更严格的访问控制权限。即如果父类的方法是public,那么子类覆写的方法也必须是public;如果父类的方法是default,子类覆写方法是可以是default或public。
  2. 私有方法不能被重写(父类私有成员子类是不能继承的)
class Fu{
    public void fun(){
     	System.out.println("父类的fun()方法"); 
    }
}
class Zi{
    void fun(){//错误!父类方法权限是public,子类不能是default。
        System.out.println("子类的fun()方法"); 
    }
}
上一篇:UVA12298 Super Poker II 题解


下一篇:python面向对象super()和mro机制