Java基础 | Java继承 | 05
继承的概念和实现
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中的注解
* 我们自定义的注解
元注解:解释注解的注解