Post

java_01

java_01

继承

方法重写

如果一个类从它的父类继承了一个方法,如果这个方法没有被标记为 final 或 static,就可以对这个方法进行重写。 在要重写的方法上面,可以选择使用 @Override 注解,让编译器帮助检查是否进行了正确的重写。如果重写有误,编译器会提示错误。

关于方法重写,有以下规则:

  • 重写方法的参数列表应该与原方法完全相同;
  • 返回值类型应该和原方法的返回值类型一样或者是它在父类定义时的子类型;
  • 重写方法访问级别限制不能比原方法高。例如:如果父类方法声明为公有的,那么子类中的重写方法不能是私有的或是保护的。具体限制级别参考访问修饰符;
  • 只有被子类继承时,方法才能被重写;
  • 方法定义为 final,将不能被重写(final 关键字将在本节后面讲到);
  • 一个方法被定义为 static,将使其不能被重写,但是可以重新声明;
  • 一个方法不能被继承,那么也不能被重写;
  • 和父类在一个包中的子类能够重写任何没有被声明为 private 和 final 的父类方法;
  • 和父类不在同一个包中的子类只能重写 non-final 方法或被声明为 public 或 protected 的方法;
  • 一个重写方法能够抛出任何运行时异常,不管被重写方法是否抛出异常。然而重写方法不应该抛出比被重写方法声明的更新更广泛的已检查异常。重写方法能够抛出比被重写方法更窄或更少的异常;
  • 构造方法不能重写。

方法重写和方法重载的区别

方法重写(Overriding)是说子类重新定义了父类的方法。方法重写必须有相同的方法名,参数列表和返回类型。覆盖者访问修饰符的限定大于等于父类方法。 而方法重载(Overloading)发生在同一个类里面两个或者是多个方法的方法名相同但是参数不同的情况。

4 种访问修饰符:

  1. private:私有的,只允许在本类中访问;
  2. protected:受保护的,允许在同一个类、同一个包以及不同包的子类中访问;
  3. 默认的:允许在同一个类,同一个包中访问;
  4. public:公共的,可以再任何地方访问。
访问控制修饰符同一个类同一个包不同包的子类不同包的非子类
private(私有的)
default(默认的)
protected(受保护的)
public(公共的)

super

super 是用在子类中的,目的是访问直接父类的变量或方法。注意:

  • super 关键字只能调用父类的 public 以及 protected 成员;
  • super 关键字可以用在子类构造方法中调用父类构造方法;
  • super 关键字不能用于静态 (static) 方法中。

调用父类构造方法

1
2
3
4
5
6
7
8
public Pet(String name) {
    System.out.println("宠物实例被创建了,宠物名字为" + name);
}

public Dog(String name) {
  	super(name);
  	System.out.println("小狗实例被创建了");
}

调用父类成员变量

1
2
3
4
5
6
7
8
9
class Pet {
  	protected String birthday;
}

class Dog extends Pet {
  	public Dog() {
  	    System.out.println("宠物生日:" + super.birthday);
    }
}

调用父类方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
class Pet {
  	public void eat() {
      	System.out.println("宠物吃东西");
    }
}

class Cat extends Pet{
  	public void eat() {
      	// 在 eat 方法中调用父类 eat 方法
      	super.eat();
      	System.out.println("小猫饭量很小");
    }
}

class Test {
  	public static void main(String[] args) {
      	Cat cat = new Cat();
      	cat.eat();
    }
}

super 与 this 的对比

this 关键字指向当前类对象的引用,它的使用场景为:

  • 访问当前类的成员属性和成员方法;
  • 访问当前类的构造方法;
  • 不能在静态方法中使用。

super 关键字指向父类对象的引用,它的使用场景为:

  • 访问父类的成员属性和成员方法;
  • 访问父类的构造方法;
  • 不能在静态方法中使用。

多态

实现条件

  1. 满足继承关系
  2. 要有重写
  3. 父类引用指向子类对象
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
class Pet {
  	// 定义方法 eat
  	public void eat() {
      	System.out.println("宠物吃东西");
    }
}
class Dog extends Pet { // 继承父类
  	// 重写父类方法 eat
  	public void eat() {
      	System.out.println("狗狗吃狗粮");
    }
}
// 子类Cat继承Pet
class Cat extends Pet { // 继承父类
  	// 重写父类方法 eat
   	public void eat() {
      	System.out.println("猫猫吃猫粮");
    }
}

public void main(String[] args) {
  	// 分别实例化三个对象,并且保持其类型为父类Pet
  	Pet pet = new Pet();
  	Pet dog = new Dog();
  	Pet cat = new Cat();
  	// 调用对象下方法
  	pet.eat();
  	dog.eat();
  	cat.eat();
}

在代码中,Pet dog = new Dog();、Pet cat = new Cat();这两个语句,把Dog和Cat对象转换为Pet对象,这种把一个子类对象转型为父类对象的做法称为向上转型。父类引用指向了子类的实例。也就实现了多态。

instanceof

运行时使用 instanceof 运算符指出某个对象是否满足一个特定类型的实例特征

1
<对象引用> instanceof 特定类型

抽象类

  1. 抽象类不能被实例化
    1
    2
    3
    4
    
    // 声明抽象类
    abstract class Pet {
     abstract void eat();
    }
    

    抽象方法使用 abstract 关键字声明,它没有方法体,而直接使用 ; 结尾。

  2. 抽象类不能被实例化,抽象类中的方法必须被非抽象子类实现,它必须被继承才能被使用。

  3. 抽象类中不一定包含抽象方法,但抽象方法一定在抽象类中。

interface

定义接口、实现接口

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
//定义了一个常量 NAME 和两个抽象方法 walk()、run()
public interface Person {
  	final String NAME = "我是Person接口中的常量";
	void walk();
  	void run();
}
// 实现了 Person 接口的 Student 类
public class Student implements Person {
    @Override
    public void walk() {
      	// 打印接口中的常量
        System.out.println(Person.NAME);
        System.out.println("学生可以走路");
    }

    @Override
    public void run() {
        System.out.println("学生可以跑步");
    }
}

接口继承

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// MyInterface1.java
public interface MyInterface1 {
    void abstractMethod1();
}

// MyInterface2.java
public interface MyInterface2 {
    void abstractMethod2();
}

// MyInterface3.java
public interface MyInterface3 extends MyInterface1, MyInterface2 {
    void abstractMethod3();
}

// 当一个实现类存在 extends 关键字,那么 implements 关键字应该放在其后
public class MyClass extends SuperClass implements MyInterface {
   ...
}

接口和抽象类的区别

  1. 接口的方法默认是 public ,所有方法在接口中不能有实现(Java 8开始接口方法可以有默认实现),而抽象类可以有非抽象的方法;
  2. 接口中除了 static 、final 变量,不能有其他变量,而抽象类可以;
  3. 一个类可以实现多个接口,但只能实现一个抽象类。接口自己本身可以通过 extends 关键字扩展多个接口;
  4. 接口方法默认修饰符是 public ,抽象方法可以有 public 、protected 和 default 这些修饰符(抽象方法就是为了被重写所以不能使用 private 关键字修饰!);
  5. 从设计层面来说,抽象是对类的抽象,是一种模板设计,而接口是对行为的抽象,是一种行为的规范。

内部类

1
2
3
4
5
6
7
8
// 外部类 Car
public class Car {
    // 内部类 Engine
    class Engine {
        private String innerName = "发动机内部类";
    }
}

分类

成员内部类、静态内部类、方法内部类和匿名内部类

  1. 成员内部类
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
// 实例化
// 外部类 Car
public class Car {

    String name;

    public Engine getEngine() {
        return new Engine();
    }
    // 汽车的跑动方法
    public void run(String name) {
        System.out.println(name + "跑起来了!");
    }

    // 内部类 Engine
    private class Engine {
        private String name = "引擎";
        // 发动机的起动方法
        private void run() {
            System.out.println("Engine中的成员属性name=" + name);
            System.out.println(Car.this.name + "的发动机启动了!");
            Car.this.run(Car.this.name);
        }
    }

    public static void main(String[] args) {
        // 实例化外部类
        Car car = new Car();
        // 为实例属性赋值
        car.name = "大奔奔";
        // 获取内部类实例
        Engine engine = car.getEngine();
        // 调用内部类的方法
        engine.run();
    }
}
  1. 静态内部类
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public class Car1 {

    String brand = "宝马";

    static String name = "外部类的静态属性name";

    // 静态内部类
    static class Engine {
        public void run() {
            System.out.println(name);
        }
    }

    public static void main(String[] args) {
        Engine engine = new Engine();
        engine.run();
    }
}
  1. 方法内部类
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
public class Car2 {
	
	// 外部类的run()方法
    public void run() {
        class Engine {
            public void run() {
                System.out.println("方法内部类的run()方法");
                System.out.println("发动机启动了");
            }
        }
        // 在Car2.run()方法的内部实例化其方法内部类Engine
        Engine engine = new Engine();
        // 调用Engine的run()方法
        engine.run();
    }

    public static void main(String[] args) {
        Car2 car2 = new Car2();
        car2.run();
    }
}
  • 方法内定义的局部内部类只能在方法内部使用;
  • 方法内不能定义静态成员;
  • 不能使用访问修饰符。
  1. 匿名内部类 使用匿名内部类,通常令其实现一个抽象类或接口。 匿名内部类的方式将子类的定义和对象的实例化放到了一起
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    
    // 定义一个交通工具抽象父类,里面只有一个run()方法
    public abstract class Transport {
     public void run() {
         System.out.println("交通工具run()方法");
     }
    
     public static void main(String[] args) {
         // 此处为匿名内部类,将对象的定义和实例化放到了一起
         Transport car = new Transport() {
             // 实现抽象父类的run()方法
             @Override
             public void run() {
                 System.out.println("汽车跑");
             }
         };
         // 调用其方法
         car.run();
    
         Transport airPlain = new Transport() {
             // 实现抽象父类的run()方法
             @Override
             public void run() {
                 System.out.println("飞机飞");
             }
         };
         airPlain.run();
    
     }
    }
    

包 package

This post is licensed under CC BY 4.0 by the author.