目录
1. 为什么需要继承
//定义一个狗类
class Dog{
public String name;
public int age;
public String color;
public void eat() {
System.out.println(this.name + "在吃东西");
}
public void run() {
System.out.println(this.name + "在跑");
}
}
//定义一个鸟类
class Bird{
public String name;
public int age;
public String color;
public void eat() {
System.out.println( this.name + "在吃东西");
}
public void fly() {
System.out.println( this.name + "在飞");
}
}
public class blog {
public static void main(String[] args) {
Dog dog = new Dog(); //创建对象
//初始化
dog.name = "小灰";
dog.age = 10;
dog.color = "灰色";
//调用方法
dog.eat();
dog.run();
Bird bird = new Bird();
bird.name = "小花";
bird.age = 10;
bird.color = "花色";
bird.eat();
bird.fly();
}
}
我们可以看到Dog类和Bird类中有以下相同的成员变量和成员方法
public String name;
public int age;
public String color;
public void eat() {
System.out.println(this.name + "在吃东西");
}
因此 我们为了节省代码,提高代码的复用性,我们引入了继承这个概念。
2. 继承的概念
3. 继承的语法
修饰符 class 子类名 extends class 父类名 {// ...}
关键字是 extends
因为 小狗和小鸟 都是属于动物,他们有自己的年龄、颜色等等属性。也会吃东西等等相同动作,并且作为宠物都会给他们给予名字 。
因此 我们创建一个新的类(Animals) 作为父类,并将 这些相同的属性和动作作为成员变量和成员方法 放入其中。
以下代码为使用继承之后。我们可以明显的看到 代码少了很多。
并且在现实生活中我们只需要关注子类特有的属性和方法即可
class Animals{
public String name;
public int age;
public String color;
public void eat() {
System.out.println(this.name + "在吃东西");
}
}
class Dog extends Animals{
public void run() {
System.out.println(this.name + "在跑");
}
}
class Bird extends Animals{
public void fly() {
System.out.println( this.name + "在飞");
}
}
public class blog {
public static void main(String[] args) {
Dog dog = new Dog();
dog.name = "小灰";
dog.age = 10;
dog.color = "灰色";
dog.eat();
dog.run();
Bird bird = new Bird();
bird.name = "小花";
bird.age = 10;
bird.color = "花色";
bird.eat();
bird.fly();
}
}
4. 父类成员访问
4.1 子类中访问父类的成员变量
1. 子类和父类不存在同名成员变量
class Father {
public int a = 10;
public int b = 20;
}
class Son extends Father {
public int c = 30;
public void Method(){
System.out.println(a);
System.out.println(b);
System.out.println(c);
}
}
public class Test {
public static void main(String[] args) {
Son son = new Son();
son.Method();
}
}
程序正常运行,并且子类可以访问父类的成员变量,并打印值。
2. 子类和父类成员变量同名
使用关键字 super
我们先来看 如果 子类和父类成员变量同名 会输出什么?
class Father {
public int a = 10;
public int b = 20;
}
class Son extends Father {
public int a = 100; // 如果子类有 先访问子类的。 子类没有的 才会去看父类是否有
public int c = 30;
public void Method(){
System.out.println(a);
System.out.println(b);
System.out.println(c);
}
}
public class Test {
public static void main(String[] args) {
Son son = new Son();
son.Method();
}
}
由此我们可知,会输出子类的 同名成员变量。
那么如果我们非要访问 父类变量呢?
就是前面所提的 super
class Father {
public int a = 10;
public int b = 20;
}
class Son extends Father {
public int a = 100;
public int c = 30;
public void Method(){
System.out.println(super.a);// 输出父类的 a
System.out.println(this.a);// 谁调用 就输出谁的a
System.out.println(b);
System.out.println(c);
}
}
public class Test {
public static void main(String[] args) {
Son son = new Son();
son.Method();
}
}
在子类方法中 或者 通过子类对象访问成员时 :1.如果访问的成员变量子类中有,优先访问自己的成员变量。2.如果访问的成员变量子类中无,则访问父类继承下来的,如果父类也没有定义,则编译报错。3.如果访问的成员变量与父类中成员变量同名,则优先访问自己的。成员变量访问遵循就近原则,自己有优先自己的,如果没有则向父类中找 。
4.2 子类中访问父类的成员方法
1. 成员方法名字不同
class Father{
public void Func() {
System.out.println("Func");
}
}
class Son extends Father{
public void Func2() {
System.out.println("Func2");
}
public void Func3() {
Func();
Func2();
}
}
public class Test {
public static void main(String[] args) {
Son son = new Son();
son.Func3();
}
}
2. 成员方法名字相同
我们在Son类中添加一个 和 Father类一模一样的Func成员方法,看结果如何?
class Father{
public void Func() {
System.out.println("Func");
}
}
class Son extends Father{
public void Func() {
System.out.println("Son::Func");
}
public void Func2() {
System.out.println("Func2");
}
public void Func3() {
Func();
Func2();
}
}
public class Test {
public static void main(String[] args) {
Son son = new Son();
son.Func3();
}
}
结果是 子类有同名方法,先调用自己的方法。
如果想要在子类调用 父类中的同名成员方法,还是需要用到关键字super
//在此就不过多 赘写代码了
//基于上面的代码
//这里我将最核心的代码修改部分,单提了出来
public void Func3() {
this.Func();
super.Func();
this.Func2();
}
5. super关键字
作用:在子类方法中访问父类的成员。
如何使用,前面代码已经展示了其作用。
注意事项:1. 只能在非静态方法中使用2. 在子类方法中,访问父类的成员变量和方法。
6. 子类构造方法
父子,即先有父,后有子。所以在我们的代码中,当我们想创建子类对象时,要先调用和实现父类的构造方法,然后再实现子类构造方法
在之前的代码中,虽然父类和子类都没有显式的定义构造方法。但其实 子类中 有隐藏的代码
public class Father {
public Father(){
System.out.println("Father()");
}
}
public class Son extends Father{
public Son(){
// super(); // 注意子类构造方法中默认会调用基类的无参构造方法:super(),
// 用户没有写时,编译器会自动添加,而且super()必须是子类构造方法中第一条语句,
// 并且只能出现一次
System.out.println("Son()");
}
}
下面代码是父类已经有定义好的构造方法
class Animals{
public String name;
public int age;
public String color;
//父类的构造方法
public Animals(String name, int age, String color) {
this.name = name;
this.age = age;
this.color = color;
}
public void eat() {
System.out.println(this.name + "在吃东西");
}
}
class Dog extends Animals {
//子类的构造方法
public Dog() {
super("小罗",10,"白色");
}
public void run() {
System.out.println(this.name + "在跑");
}
}
class Bird extends Animals {
//子类的构造方法
public Bird(String name, int age, String color) {
super(name, age, color);
}
public void fly() {
System.out.println( this.name + "在飞");
}
}
public class Blog {
public static void main(String[] args) {
Dog dog = new Dog();// 根据构造方法
//第一种初始化方式
dog.name = "小灰";
dog.age = 10;
dog.color = "灰色";
dog.eat();
dog.run();
Bird bird = new Bird("小辣椒",10,"红色");// 根据构造方法 //第二种初始化方法
bird.eat();
bird.fly();
}
}
注意:
1. 若父类显式定义无参或者默认的构造方法,在子类构造方法第一行默认有隐含的super()调用,即调用基类构造方法2. 如果父类构造方法是带有参数的,此时需要用户为子类显式定义构造方法,并在子类构造方法中选择合适的父类构造方法调用,否则编译失败。3. 在子类构造方法中,super(...)调用父类构造时,必须是子类构造函数中第一条语句。4. super(...)只能在子类构造方法中出现一次,并且不能和this同时出现
7. super和this
相同点
不同点
8. 继承关系的执行顺序
在之前的学习中我们知道了
1.静态代码块先执行,并且只执行一次,在类加载阶段执行
2.当有对象创建时,才会执行实例代码块,实例代码块执行完成后,最后构造方法执行
class Persons{
String name;
int age;
public Persons(String name, int age) {
this.name = name;
this.age = age;
System.out.println("Person构造方法被调用");
}
{
System.out.println("Person实例代码块被调用");
}
static {
System.out.println("Person静态代码块被调用");
}
}
class Students extends Persons{
public Students(String name, int age) {
super(name, age);
System.out.println("Students构造方法被调用");
}
{
System.out.println("Students实例代码块被调用");
}
static {
System.out.println("Students静态代码块被调用");
}
}
public class job {
public static void main(String[] args) {
Students students = new Students("小罗",18);
Students students2 = new Students("小米",18);
}
}
无奖竞猜:大家猜一下输出结果是什么?
9. 继承方式
单继承
多层继承
不同类继承同一个类
最重要的是,不可以同一个类继承不同的类
10. final关键字
final关键可以用来修饰变量、成员方法以及类。
1.修饰变量或字段,表示常量(即不能修改)
2.修饰类:表示此类不能被继承
3.修饰方法:表示该方法不能被重写