Java中的继承
什么是继承?
继承是一种类与类之间的关系,使用已经存在的类的定义作为基础建立新类。新类可以增加新的数据或新的功能,也可以用父类的功能,但是不能选择性继承父类。
继承的关系:满足A is a B的关系就可以形成继承关系。
比如:Animal类作为父类,那么Dog类和Cat类都可以是Animal类的子类,因为它们都满足:一只狗是动物/一只猫是动物
继承的实现
在Java中,使用extends
关键字来表示继承关系,注意:在Java中只能使用extends
继承一个类!
例如:
Animal类
public class Animal{
private String name;
public Animal(){
}
public Animal(String name){
this.name = name;
}
public void setName(String name){
this.name = name;
}
public String getName(){
return this.name;
}
public void eat(){
System.out.println(this.getName() + "在吃东西~");
}
}
Dog类
public class Dog extends Animal{
// 子类会继承父类的相关属性和方法,同时也可以自己添加属性和方法,但是父类的构造方法不能被继承
private int month;
private String name;
public Dog(){
}
public Dog(int month, String name){
this.setMonth(month);
this.setName(name);
}
public void setMonth(int month){
this.month = month;
}
public int getMonth(){
return this.month;
}
public void setName(String name){
this.name = name;
}
public String getName(){
return this.name;
}
// 子类可以有它独特的方法
public void sleep(){
System.out.println(this.getName() + "在睡觉~");
}
}
Test类:
public class Test{
public static void main(String[] args){
Dog dog = new Dog(2, "牛牛");
// 调用父类的eat方法
dog.eat();
// 调用子类自己的sleep()方法
dog.sleep();
}
}
输出结果如下:
注意:
-
子类可以访问父类的非私有成员
-
父类不可以访问子类特有成员
访问修饰符
在java中,有四种访问修饰符,分别是:
-
public:允许在任意位置访问
-
private:只允许在本类中使用
-
protected:允许在本类/同包类/非同包子类中访问,不允许在非同包非子类中访问
-
默认访问权限:允许在本类/同包类中访问,不允许在非同包类中访问
下图是对访问修饰符的总结:
方法重载 vs 方法重写
方法重载:同一个类中方法名称相同,参数列表不同(包括参数顺序/参数个数/参数类型)的方法,与方法的返回值和修饰符无关,也与参数名称无关
在Dog类中重载sleep()方法
// 原有的sleep()方法
public void sleep(){
System.out.println(this.getName + "在睡觉~");
}
// 带一个参数的sleep()方法
public void sleep(int time){
System.out.println(this.getName + "睡了" + time + "s");
}
// 带两个参数的sleep()方法
public void sleep(int time, String mood){
System.out.println(this.getName + "睡了" + time + "s,真" + mood);
}
// 带两个参数的sleep()方法,将参数换位置
public void sleep(String mood, int time){
System.out.println(真" + mood + "," + this.getName + "睡了" + time + "s");
}
方法重写:在有继承关系的子类中,方法的返回值类型/方法名/参数类型/参数顺序/参数个数都要与父类相同,方法的访问修饰符的访问范围要大于等于父类的访问范围,与方法的参数名无关。
在Animal类中定义一个romr()方法,并在Dog类重写它。
Animal类:
public class Animal{
public void romr(){
System.out.println("动物在呼呼的叫~");
}
}
Dog类:
public class Dog extends Animal{
// 重写父类的romr()方法
public void romr(){
System.out.println("小狗在汪汪的叫~");
}
}
当子类重写父类的方法后,子类对象调用的是子类的方法。
super vs this
this关键字:本类对象的引用
super关键字:父类对象的引用
this():同类的无参构造方法,必须放在方法第一行
super():父类的无参构造方法,必须放在方法第一行
So,this()和super()不能出现在同一个构造方法中!
还需要注意:this和super都不能在静态方法中调用
构造方法
关于构造方法,需要注意以下几点:
-
父类的构造方法不允许被子类继承和重写,子类必须自己实现自己的构造方法
-
子类构造方法默认调用父类的无参构造方法,所以不要以为无参构造方法不重要
-
可以在子类构造方法中使用
super()
来调用父类允许被访问的其它构造方法,必须方法子类构造方法的第一行
Animal类:
public class Animal{
public Animal(){
System.out.println("我是父类的构造方法");
}
}
Dog类:
public class Dog extends Animal{
public Dog(){
// 使用super()调用父类的无参构造,必须方法第一行
super();
System.out.println("我是子类的构造方法");
}
}
Test类:
public class Test{
public static void main(String[] args){
Dog dog = new Dog();
}
}
输出结果如下:
继承后的初始化顺序
在Java中,创建一个子类对象时的初始化顺序是怎样的呢?通过下面这段代码的输出结果看一下
Animal类:
public class Animal{
static{
System.out.println("我是父类的静态代码块");
}
{
System.out.println("我是父类的构造代码块");
}
public Animal(){
System.out.println("我是父类的构造方法");
}
}
Dog类:
public class Dog extends Animal{
static{
System.out.println("我是子类的静态代码块");
}
{
System.out.println("我是子类的构造代码块");
}
public Dog(){
System.out.println("我是子类的构造方法");
}
}
Test类:
public class Test{
public static void main(String[] args){
Dog dog = new Dog();
}
}
输出结果如下:
总结:
-
子类实例化的过程中,首先把类加载到JVM中,查看其父类是否有静态成员(静态代码块/静态属性/静态方法),如果有,则初始化静态成员
-
查找子类是否有静态成员,如果有,则初始化静态成员
-
进行父类对象的构造,先查找父类的构造代码块,再执行父类的无参构造方法
-
进行子类对象的构造,执行子类的构造代码块,再执行子类的构造方法
Object类
一个类没有使用extends关键字明确标识继承关系,则默认继承Object类(包括数组)
任何java类都可以使用Object类中提供的可访问的方法
常用的方法:
- equals():比较两个对象引用的是否为同一块空间,返回布尔值
String类重写了equals()方法,只笔记内容是否相同:
String str1 = new String("hello");
String str2 = new String("hello");
System.out.println(str1.equals(str2)) ------->返回True
- toString()方法:返回对象的描述信息,格式:类型信息 + @ + 地址信息
final关键字
final关键字的几种用法:
-
final 类:表示该类不能有子类,可以写成
public final class
或final public class
-
final 方法:表示该方法不可以被重写,但是可以被子类对象使用
-
final 方法内局部变量:只要在具体使用之前赋值即可,一旦赋值不可更改
-
final 类成员属性:可以在三个地方赋值:定义时赋值/构造方法中赋值/构造代码块中赋值,一旦赋值不可更改
注意:final 不可以修饰构造方法
final可以配合static来修饰变量,比如配置信息,这样在类的加载时就可以初始化并且不能更改。