• 周四. 4月 25th, 2024

5G编程聚合网

5G时代下一个聚合的编程学习网

热门标签

Java基础 | Java继承 | 05

admin

11月 28, 2021

继承的概念和实现

Cat和Dog两个类中有许多共有的属性和方法,同理,如果是其他类型的动物,也会有这些共有的属性和方法,可以将这些共性的东西抽取出来,方便复用。

Animal类就是父类,然后父类也就只有共性的方法和属性。

只要是继承了这个父类的类,都可以使用父类中的方法和属性。

继承:

  • 一种类与类之间的关系 A is a B
  • 使用已存在的类的定义作为基础建立新类
  • 新类的定义可以增加新的数据或新的功能,也可以用父类的功能,但不能选择性地继承父类

注意:继承就是要全盘接受父类… 也就是可以选择不用,但是不能不要..

继承需要满足“A is a B”的关系,比如猫和轿车都有name和age的属性,但是轿车不能继承自Animal,因为不满足“A is a B”的关系。

继承关键字: extends

注意: 只能继承一个父类

Animal类

package com.imooc.animal;

public class Animal {
	private 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() {
		System.out.println(this.getName() + "吃东西");
	}

}

Cat类

package com.imooc.animal;

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

	public double getWeight() {
		return weight;
	}

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

	public void run() {
		System.out.println(this.getName() + "是一只" + this.getSpecies()+",它在快乐的奔跑~");
	}

}

  • 子类可以访问父类非私有成员

在子类中可以直接通过this进行访问

  • 父类不可以访问子类特有成员

可以利用工具快速生成子类

Dog类

package com.imooc.animal;

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

	public String getSex() {
		return sex;
	}

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

	public void sleep() {
		System.out.println(this.getName() + "现在" + this.getMonth() + "个月大,它正在睡觉~");
	}
}

测试代码

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();
	}

}

方法的重写

方法重载:

  • 同一类中
  • 方法名相同,参数列表不同(参数顺序、个数、类型)
  • 方法返回值、访问修饰符任意 只是对同名函数做出了限制!
  • 与方法的参数名无关 和参数的类型、个数有关
public void sleep(){}
private String sleep(String name){} 
public void sleep(String name,int month){}
public void sleep(int month,String name){}
     public void sleep(int name,String month){}

方法重写:

  • 有继承关系的子类中

  • 方法名相同,参数列表相同(参数顺序、个数、类型),方法返回值相同 重写方法要和父类的方法完全一致

  • 访问修饰符,访问范围需要大于等于父类的访问范围

  • 与方法的参数名无关

  • 当方法返回值是void或基本数据类型时,必须相同;当返回值是引用类型时,可以是父类或其子类

当子类重写父类方法后,子类对象调用的是重写后的方法。

如果在子类中重新定义了父类中有的属性,那么就会以子类属性为准。

访问修饰符的分类及作用

  • private:只允许在本类中进行访问

  • public:允许在任意位置访问

  • protected:允许在当前类、同包子类/非子类、跨包子类调用;跨包非子类不允许

  • 默认:允许在当前类、同包子类/非子类调用;跨包子类/非子类不允许调用

super关键字

如何判断同名的方法是父类的方法还是自己的方法呢?

如果重写了父类方法,那么该方法肯定就是在子类中重写的,如果要调用父类的成员,就要使用super()关键字。

super: 父类对象的引用

package com.imooc.animal;

public class Dog extends Animal {
...
	public void sleep() {
		this.eat(); // 自己重写的eat()
		super.eat(); // 父类的eat()
		System.out.println(this.getName() + "现在" + this.getMonth() + "个月大,它正在睡觉~");
	}
	
	// 重写吃东西
	public void eat() {
		System.out.println(this.getName() + "最近没有食欲...");
	}
}

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

package com.imooc.animal;

public class Animal {
...

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

package com.imooc.animal;

public class Cat extends Animal {
...
	public Cat() {
		System.out.println("Cat类的无参构造");
	}
...

}
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();
...
	}
}

new Cat(),会首先去执行Animal的无参构造,给Animal进行初始化。

构造子类的时候会能不能只当父类的构造方法呢?

子类构造方法默认使用父类的无参构造 super() 而且必须在第一行。

可以使用super(参数)来指定父类的构造方法

package com.imooc.animal;

public class Animal {
...
    public Animal() {
		System.out.println("这是父类的无参构造");
	}

	public Animal(String name, int month) {
		System.out.println("这是父类的有参构造");
	}
...
}
package com.imooc.animal;

public class Cat extends Animal {
...
	public Cat() {
    	super();
		System.out.println("Cat类的无参构造");
	}

	public Cat(String name, int month) {
		super(name,month);
		System.out.println("Cat类的有参构造方法");
	}
...
	}
}

this & super

继承的初始化顺序

new一个子类的时候,会如上图,一直去寻找父类,然后再一层层进行实例的操作。

访问修饰符不影响成员加载顺序,跟书写的位置有关。(不如静态成员和静态代码块,谁写在前面就先加载谁)

Object类

Object类:

  • Object类是所有类的父类
  • 一个类没有使用extends关键字明确标识继承关系,则默认继承Object类(包括数组)
  • Java中的每个类都可以使用Object中定义的方法

关于equals方法

  • 默认的Object类的equals就是和 == 一致 就是比较两个引用是否指向一个对象
  • 子类可以通过重写equals方法的形式,改变比较内容

String中的equals有进行重写,可以判断两个字符串的值是否相等

package com.imooc.test;

import com.imooc.animal.Animal;

public class TestThree {

	public static void main(String[] args) {
		Animal one = new Animal("花花", 2);
		Animal two = new Animal("花花", 2);

		/*
		 * equals测试: 1、继承Object中的equals方法时,比较的是两个引用是否指向同一个对象
		 * 2、子类可以通过重写equals方法的形式,改变比较的内容
		 */
		boolean flag = one.equals(two);
		System.out.println("one 和 two的引用比较:" + flag); // false
		System.out.println("one 和 two的引用比较:" + (one == two)); // false
		System.out.println("======================================");
		String str1 = new String("hello");
		String str2 = new String("hello");
		flag = str1.equals(str2);
		System.out.println("str1 和 str2的引用比较:" + flag); // true
		System.out.println("str1 和 str2的引用比较:" + (str1 == str2)); // false
		System.out.println("======================================");
	}

}

可以在Animal中进行equals的重写,将相等的规则自定义。

package com.imooc.animal;

public class Animal {
...

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

}

关于toString方法

  • 输出对象名时,默认会直接调用类中的toString
  • 继承Object中的toString方法时,输出对象的字符串表现形式:类型信息+@+地址信息
  • 子类可以通过重写toString,改变输出的内容以及表现形式
package com.imooc.test;

import com.imooc.animal.Animal;

public class TestThree {

	public static void main(String[] args) {
...
		/*toString测试:
		 * 1、输出对象名时,默认会直接调用类中的toString
		 * 2、继承Object中的toString方法时,输出对象的字符串表示形式:类型信息+@+地址信息
		 * 2、子类可以通过重写equals方法的形式,改变输出的内容以及表现形式
		 */
		System.out.println(one.toString()); // com.imooc.animal.Animal@6d06d69c
		System.out.println(one); // com.imooc.animal.Animal@6d06d69c
	}
}

可以重写Animal类的toString方法,那么输出就是该字符串。

package com.imooc.animal;

public class Animal {
...

	public String toString() {
		return "昵称:" + this.getName() + ";年龄:" + this.getMonth();
	}

}

再次执行测试代码

package com.imooc.test;

import com.imooc.animal.Animal;

public class TestThree {

	public static void main(String[] args) {
...
		/*toString测试:
		 * 1、输出对象名时,默认会直接调用类中的toString
		 * 2、继承Object中的toString方法时,输出对象的字符串表示形式:类型信息+@+地址信息
		 * 2、子类可以通过重写equals方法的形式,改变输出的内容以及表现形式
		 */
		System.out.println(one.toString()); // 昵称:花花;年龄:2
		System.out.println(one); // 昵称:花花;年龄:2
	}
}

final关键字

继承大大提高了代码的复用性和灵活性,但是有些时候,我们并不需要这个类被继承,这个方法被重写,这个变量的值被修改,那么此时就要用到关键字final。

关键字 final:

  • final class:该类没有子类 public final class or final public class
  • final 方法:该方法不允许被子类重写,但是可以正常被子类继承使用
  • final 方法内部局部变量:只要在具体被使用之前赋值即可,一旦赋值不运行被修改

这里的final修改局部变量,类似Cpp中的const。

  • final 成员属性:赋值过程 1、定义直接初始化 2、构造方法 3、构造代码块

final修饰的成员属性,如果一开始只定义并没有赋值,那么只能通过以上三种方式进行赋值操作。

  • final 修饰引用: 那么就这个变量只能是这个引用,不能进行更改

注解

注解:

  • JDK1.5版本引入的一个特性
  • 可以声明在包、类、属性、方法、局部变量、方法参数等的前面,用来对这些元素进行说明、注释

可以把注解理解为一种特殊的标记,在一段代码前加上注解,就是通知编译器和IDE,这段代码是有特殊含义的。

上图代码中的@override,就告诉了编译器和IDE这是一个重写方法。然后编译器就会检查父类是否有该支持重写的方法。

@override就是一种源码注解,就是给编译器做代码检测用的,一旦程序进入编译过程,那么该注解就会被抛弃。

Spring框架中的@NotNull就是编译注解。

运行时注解还可以通过反射获取到它,这种注解的存在就会影响程序运行逻辑和结果。比如Spring中的@Autowired这个注解。

小结

什么是继承?

Cat和Dog两个类中有许多共有的属性和方法,同理,如果是其他类型的动物,也会有这些共有的属性和方法,可以将这些共性的东西抽取出来,方便复用。

Animal类就是父类,然后父类也就只有共性的方法和属性。
只要是继承了这个父类的类,都可以使用父类中的方法和属性。

继承:
*   一种类与类之间的关系 A is a B
*   使用已存在的类的定义作为基础建立新类
*   新类的定义可以增加新的数据或新的功能,也可以用父类的功能,**但不能选择性地继承父类**

**注意:继承就是要全盘接受父类... 也就是可以选择不用,但是不能不要..**


特点:
* 利用代码复用
* 缩短开发周期

继承需要满足“A is a B”的关系,比如猫和轿车都有name和age的属性,但是轿车不能继承自Animal,因为不满足“A is a B”的关系。

如何实现继承?

关键字: extends
**注意: 只能继承一个父类**



*   子类可以访问父类非私有成员 
在子类中可以直接通过this进行访问
*   父类不可以访问子类特有成员

可以利用工具快速生成子类

方法重写 & 方法重载

方法重载:
*   同一类中
*   方法名相同,参数列表不同(参数顺序、个数、类型)
*   **方法返回值、访问修饰符任意** 只是对同名函数做出了限制!
*   与方法的参数名无关 和参数的类型、个数有关


方法重写:
*   有继承关系的子类中
*   方法名相同,参数列表相同(参数顺序、个数、类型),方法返回值相同 **重写方法要和父类的方法完全一致**
*   访问修饰符,访问范围需要大于等于父类的访问范围
*   与方法的参数名无关
*   当方法返回值是void或基本数据类型时,必须相同;当返回值是引用类型时,可以是父类或其子类


当子类重写父类方法后,子类对象调用的是重写后的方法。
如果在子类中重新定义了父类中有的属性,那么就会以子类属性为准。

访问修饰符

         本类  同包  子类  其他
private   *    
默认       *    *     
protected *    *     *
public    *     *    *     *

同包:可以理解为“一个房间”,有无“血缘关系”都可
子类:如果是不同包,那么有“血缘关系”就可以访问

继承的初始化顺序

父类静态成员 -> 子类静态成员 -> 父类对象构造 -> 子类对象构造

new一个子类的时候,会如上图,一直去寻找父类,然后再一层层进行实例的操作。

访问修饰符不影响成员加载顺序,跟书写的位置有关。(不如静态成员和静态代码块,谁写在前面就先加载谁)

super关键字

* super代表父类的引用
super.print() // 访问父类方法
super.name // 访问父类属性
super() // 访问父类构造方法

* 子类的构造的过程必须调用其父类的构造方法
* 如果子类的构造中没有显式标注,则系统默认调用父类无参构造方法
* 如果子类构造方法中既没有显示标注,且父类中没有无参构造方法,则编译出错
* 使用super调用父类指定构造方法,必须在子类的方法的第一行


注意:super&this在构造方法中只能选择用一个!因为它们都规定在第一行,那么就一山不能容二虎。

Object类

Object类:

*   Object类是所有类的父类
*   一个类没有使用extends关键字明确标识继承关系,则默认继承Object类(包括数组)
*   Java中的每个类都可以使用Object中定义的方法

equals & tostring

关于equals方法
*   默认的Object类的equals就是和 == 一致 就是比较两个引用是否指向一个对象
*   子类可以通过重写equals方法的形式,改变比较内容
String中的equals有进行重写,可以判断两个字符串的值是否相等

关于toString方法
*   输出对象名时,默认会直接调用类中的toString
*   继承Object中的toString方法时,输出对象的字符串表现形式:类型信息+@+地址信息
*   子类可以通过重写toString,改变输出的内容以及表现形式

final关键字

继承大大提高了代码的复用性和灵活性,但是有些时候,我们并不需要这个类被继承,这个方法被重写,这个变量的值被修改,那么此时就要用到关键字final。



关键字 final:

*   final class:该类没有子类 public final class or final public class
*   final 方法:该方法不允许被子类重写,但是可以正常被子类继承使用
*   final 方法内部局部变量:只要在具体被使用之前赋值即可,一旦赋值不运行被修改 

这里的final修改局部变量,类似Cpp中的const。

*   final 成员属性:赋值过程 1、定义直接初始化 2、构造方法 3、构造代码块

final修饰的成员属性,如果一开始只定义并没有赋值,那么只能通过以上三种方式进行赋值操作。

*   final 修饰引用: 那么就这个变量只能是这个引用,不能进行更改

注解

注解:
*   JDK1.5版本引入的一个特性
*   可以声明在包、类、属性、方法、局部变量、方法参数等的前面,用来对这些元素进行说明、注释
可以把注解理解为一种特殊的标记,在一段代码前加上注解,就是通知编译器和IDE,这段代码是有特殊含义的。
上图代码中的@override,就告诉了编译器和IDE这是一个重写方法。然后编译器就会检查父类是否有该支持重写的方法。

注解分类

按照运行机制分:
* 源码注解
* 编译时注解
* 运行时注解

按照来源分:
* 来自JDK的注解 比如@override
* 来自第三方的注解 比如spring中的注解
* 我们自定义的注解

元注解:解释注解的注解

发表回复

您的电子邮箱地址不会被公开。 必填项已用*标注