Java - 面向对象

1. 初识面向对象

OOP:以类的方式组织代码,以对象的组织(封装)数据

  • 三大特性:
    • 封装
    • 继承
    • 多态

 

2. 类与对象的创建

Person.java

package oop.Demo;

public class Person {
    // 显式地定义构造器
    String name;
    // 实例化初始值
    // 构造器作用:
    // 1. 使用 new 关键字,本质是在调用构造器
    // 2. 用来初始化值
    public Person() {
        this.name = "Red";
    }

    // 有参构造:一旦定义了有参构造,无参就必须显式定义
    public Person(String name) {
        this.name = name;
    }

    // 自动生成构造器:alt + insert
}

Application.java

package oop.Demo;

public class Application {
    public static void main(String[] args) {
        // new 实例化一个对象
        // 一个类即使什么都没写,也会存在一个方法
        Person person = new Person("Hey");
        System.out.println(person.name);
    }
}
  • 构造器
    1. 和类名相同
    2. 没有返回值
  • 作用
    1. new 本质在调用构造方法
    2. 初始化对象的值
  • 注意点
    1. 一旦定义了有参构造,如果想使用无参构造,必须显式地定义一个无参构造

自动生成构造器:alt + insert

 

3. 封装

Student.java

package oop.Demo;

// private: 私有
public class Student {
    // 属性私有
    private String name;
    private int id;
    private char sex;

    // 提供一些可以操作这个属性的方法
    // public 的 get set 方法

    // get 获得这个数据
    public String getName() {
        return this.name;
    }

    // set 给这个数据设置值
    public void setName(String name) {
        this.name = name;
    }
}

Application.java

package oop.Demo;

public class Application {
    public static void main(String[] args) {
        Student s1 = new Student();
        s1.setName("Red");
        System.out.println(s1.getName());
    }
}
  1. 提高程序的安全性,保护数据
  2. 隐藏代码的实现细节
  3. 统一接口
  4. 增加系统可维护性

 

4. 继承

  • 继承时类和类之间的一种关系。除此之外,类和类之间的关系还有依赖、组合、聚合等
  • 继承关系的两个类,一个为子类(派生类),一个为父类(基类)。子类继承父类,使用 extends 来表示
  • 子类和父类之间,从意义上讲应该具有 “is a” 的关系
class Box {
    double width;
    double height;
    double depth;

    Box(Box ob) {
        width = ob.width;
        height = ob.height;
        depth = ob.depth;
    }

    Box(double w, double h, double d) {
        width = w;
        height = h;
        depth = d;
    }

    double volume() {
        return width * height * depth;
    }
}

class BoxWeight extends Box {
    double weight;
    BoxWeight(double w, double h, double d, double m) {
        width = w;
        height = h;
        depth = d;
        weight = m;
    }
}

public class DemoBoxWeight {
    public static void main(String[] args) {
        BoxWeight mybox1 = new BoxWeight(10, 20, 15, 34.3);
        BoxWeight mybox2 = new BoxWeight(2, 3, 4, 0.076);
        double vol;

        vol = mybox1.volume();
        System.out.println("mybox1 体积为: " + vol);
        System.out.println("mybox1 质量为: " + mybox1.weight);
        System.out.println();

        vol = mybox2.volume();
        System.out.println("mybox2 体积为: " + vol);
        System.out.println("mybox2 质量为: " + mybox2.weight);
    }
}
Output:
mybox1 体积为: 3000.0
mybox1 质量为: 34.3

mybox2 体积为: 24.0
mybox2 质量为: 0.076

查看层次结构快捷键:Ctrl + H

  • 注:被声明为私有的类成员对于所属的类来说仍然是私有的。类之外的任何代码都不能访问,包括子类。

  • 继承的主要优点:

    • 一旦创建了一个定义一系列对象共同特征的超类,就可以使用这个超类创建任意数量的更具体的子类。每个子类都可以进行精确的调整以适应它自己的类别。

 

4.1 超类对象可以引用子类对象

当将指向子类对象的引用赋给超类的引用变量时,只能访问子类对象在超类中定义的那些部分:

class RefDemo {
    public static void main(String[] args) {
        // 子类对象
        // 指向 BoxWeight 对象的引用
        BoxWeight weightbox = new BoxWeight(3, 5, 7, 8.37);
        // 超类对象
        // 指向 Box 对象的引用
        Box plainbox = new Box();
        double vol;
        
        vol = weightbox.volume();
        System.out.println("weightbox 的体积为: " + vol);
        System.out.println("weightbox 的质量为: " + weightbox.weight);
        System.out.println();
        
        vol = plainbox.volume();
        System.out.println("plainbox 的体积为: " + vol);
        // 报错: Box 对象中不包含 weight 变量
        // System.out.println("plainbox 的质量为: " + plainbox.weight);
    }
}

 

4.2 super 关键词

super 两种用法:

  • 调用超类的构造函数
  • 访问超类中被子类的某个成员隐藏的成员

 

4.2.1 使用 super 调用超类的构造函数

super(arg-list)
arg-list 是超类中构造函数需要的全部参数

class Box {
    private double width;
    private double height;
    private double depth;

    Box(Box ob) {
        width = ob.width;
        height = ob.height;
        depth = ob.depth;
    }

    Box(double w, double h, double d) {
        width = w;
        height = h;
        depth = d;
    }

    double volume() {
        return width * height * depth;
    }
}

class BoxWeight extends Box {
    double weight;
    BoxWeight(BoxWeight ob) {
        super(ob);
        weight = ob.weight;
    }

    BoxWeight(double w, double h, double d, double m) {
        super(w, h, d);
        weight = m;
    }
}

public class DemoSuper {
    public static void main(String[] args) {
        BoxWeight mybox = new BoxWeight(10, 20, 15, 34.3);
        double vol;

        vol = mybox.volume();
        System.out.println("体积为: " + vol);
        System.out.println("质量为: " + mybox.weight);
        // 所有不同传参的构造器均同理
    }
}
Output:
体积为: 3000.0
质量为: 34.3

注:

  • super() 传递的是 BoxWeight 类型的对象,而不是 Box 类型的对象,这仍然会调用 Box(Box ob) 构造函数。
  • super() 总算是引用调用类的直接超类;即使在多层次继承中也如此。
  • super() 必须是子类构造函数中执行的第一条语句。

 

4.2.2 使用 super 访问超类中被子类的某个成员隐藏的成员

super.mamber
member 既可以是方法,也可以是实例变量

class A {
    int i;
}

class B extends A {
    int i;   // 隐藏了 A 中的 i

    B(int a, int b) {
        super.i = a;   // A 中的 i
        i = b;   // B 中的 i
    }

    void show() {
        System.out.println("super.i = " + super.i);
        System.out.println("i = " + i);
    }
}

public class UseSuper {
    public static void main(String[] args) {
        B subOb = new B(1, 2);
        subOb.show();
    }
}
Output:
super.i = 1
i = 2

this 关键词比较

  • super
    1. super 调用父类的构造方法,必须在构造方法的第一个
    2. super 必须只能出现在子类的方法或者构造方法中
    3. super 和 this 不能同时调用构造方法
  • vs this:
    1. 代表对象不同:
      this:本身调用这个对象
      super:代表父类对象的引用
    2. 前提:
      this:没有继承也可以使用
      super:只能在继承条件下才能使用
    3. 构造方法:
      this(); 本类的构造
      super(); 父类的构造

 

4.3 创建多级继承层次

class Box {
    private double width;
    private double height;
    private double depth;

    Box(Box ob) {
        width = ob.width;
        height = ob.height;
        depth = ob.depth;
    }

    Box(double w, double h, double d) {
        width = w;
        height = h;
        depth = d;
    }

    double volume() {
        return width * height * depth;
    }
}

class BoxWeight extends Box {
    double weight;
    BoxWeight(BoxWeight ob) {
        super(ob);
        weight = ob.weight;
    }

    BoxWeight(double w, double h, double d, double m) {
        super(w, h, d);
        weight = m;
    }
}

// 二级继承,增加 cost
class Shipment extends BoxWeight {
    double cost;

    Shipment(Shipment ob) {
        super(ob);
        cost = ob.cost;
    }

    Shipment(double w, double h, double d, double m, double c) {
        super(w, h, d, m);
        cost = c;
    }
}

class DemoShipment {
    public static void main(String[] args) {
        Shipment shipment = new Shipment(10, 20, 15, 10, 3.41);
        double vol;

        // 父类的父类的方法
        vol = shipment.volume();
        System.out.println("shipment 体积为: " + vol);
        // 父类的方法
        System.out.println("shipment 质量为: " + shipment.weight);
        // 本类的方法
        System.out.println("shipment 价格为: " + shipment.cost);
        System.out.println();
    }
}
Output:
shipment 体积为: 3000.0
shipment 质量为: 10.0
shipment 价格为: 3.41

super() 总是引用最近超类的构造函数:

  • Shipment 类中的 super() 调用 BoxWeight 类的构造函数
  • BoxWeight 类中的 super() 调用 Box 类的构造函数

在类层次中,如果超类的构造函数需要形参,那么所有子类必须“向上”传递这些形参,不管子类本身是否需要这些形参:

BoxWeight(double w, double h, double d, double m) {
    super(w, h, d);
    weight = m;
}

Shipment(double w, double h, double d, double m, double c) {
    super(w, h, d, m);
    cost = c;
}

以上,整个类层次(包括 Box、BoxWeight 和 Shipment)都位于一个文件中。实际上,这三个类可以放到不同的文件中单独进行编译。

 

4.4 构造函数的执行时机

在类层次中,按照继承的顺序从超类到子类执行构造函数

如果没有使用 super(),将执行每个超类的默认构造函数或无参构造函数

class A {
    A() {
        System.out.println("A 构造函数");
    }
}

class B extends A {
    B() {
        System.out.println("B 构造函数");
    }
}

class C extends B {
    C() {
        System.out.println("C 构造函数");
    }
}

public class CallingCons {
    public static void main(String[] args) {
        C c = new C();
    }
}
Output:
A 构造函数
B 构造函数
C 构造函数

 

4.5 方法重写

class A {
    int i, j;
    A(int a, int b) {
        i = a;
        j = b;
    }

    void show() {
        System.out.println("i = " + i + ", j = " + j);
    }
}

class B extends A {
    int k;

    B(int a, int b, int c) {
        super(a, b);
        k = c;
    }

    // 重写 A 类中的 show()
    void show() {
        System.out.println("k = " + k);
    }
}

public class Override {
    public static void main(String[] args) {
        B subOb = new B(1, 2, 3);

        subOb.show();
    }
}
Output:
k = 3
  • 当在子类中调用被重写的方法时,总是调用由子类定义的方法版本,由超类定义的方法版本会被隐藏。

如果希望访问超类中被重写的方法,可以使用 super

class B extends A {
    int k;
    
    B(int a, int b, int c) {
        super(a, b);
        k = c;
    }
    
    void show() {
        super.show();
        System.out.println("k = " + k);
    }
}
Output:
i = 1, j = 2
k = 3

只有当两个方法的名称和类型签名都相同时才会发生重写。否则,这两个方法就只是简单的重载关系:

class A {
    int i, j;
    A(int a, int b) {
        i = a;
        j = b;
    }

    void show() {
        System.out.println("i = " + i + ", j = " + j);
    }
}

class B extends A {
    int k;

    B(int a, int b, int c) {
        super(a, b);
        k = c;
    }

    void show(String msg) {
        System.out.println(msg + k);
    }
}

public class Override {
    public static void main(String[] args) {
        B subOb = new B(1, 2, 3);

        subOb.show("k = ");
        subOb.show();
    }
}
Output:
k = 3
i = 1, j = 2

  • 方法的调用只和左边定义的数据类型有关:
// A.java
package oop.Demo;

public class A {
    public static void test() {
        System.out.println("A -> test()");
    }
}
// B.java
package oop.Demo;

public class B extends A {
    public static void test() {
        System.out.println("B -> test()");
    }
}
// Application
package oop;

import oop.Demo.A;
import oop.Demo.B;

public class Application {
    public static void main(String[] args) {
        // 方法的调用只和左边定义的数据类型有关
        // 父类
        A a = new B();
        a.test();

        B b = new B();
        b.test();
    }
}
Output:
A -> test()
B -> test()
  • 非静态方法重写:
// A.java
package oop.Demo;

public class A {
    public void test() {
        System.out.println("A -> test()");
    }
}
// B.java
package oop.Demo;

public class B extends A {
    public void test() {
        System.out.println("B -> test()");
    }
}
package oop;

import oop.Demo.A;
import oop.Demo.B;

public class Application {
    public static void main(String[] args) {
        // 方法的调用只和左边定义的数据类型有关
        A a = new B();
        a.test();

        B b = new B();
        b.test();
    }
}
Output:
B -> test()
B -> test()

有 static 时,a 调用了 A 类的方法,因为 a 是用 A 类定义的
没有 static 时,a 调用的是对象的方法,而 a 是用 B 类 new 的


总结:

前提:需要有继承关系,子类重写父类的方法

  1. 方法名必须相同
  2. 参数列表必须相同
  3. 修饰符:范围可以扩大: public > protected > default > private
  4. 抛出的异常:范围可以被缩小,但不能扩大: ClassNotFoundException --> Exception(大)
  5. static 方法:属于类,不属于实例
    final 常量;
    private 方法:私有
    以上均不能重写

重写,子类和父类的方法必须要一致;方法体不同

为什么需要重写?

  • 父类的功能,子类不一定需要,或者不一定满足

    快捷键:Alt + insert

 

4.6 动态方法调度

方法重写形成了动态方法调度(dynamic method dispatch)的基础

 

4.6.1 应用方法重写

重写方法为 Java 支持运行时多态奠定了基础

class Figure {
    double dim1;
    double dim2;

    Figure(double a, double b) {
        dim1 = a;
        dim2 = b;
    }

    double area() {
        System.out.println("未定义");
        return 0;
    }
}

// 矩形
class Rectangle extends Figure {
    Rectangle(double a, double b) {
        super(a, b);
    }

    @Override
    double area() {
        System.out.println("矩形的面积");
        return dim1 * dim2;
    }
}

// 三角形
class Triangle extends Figure {
    Triangle(double a, double b) {
        super(a, b);
    }

    @Override
    double area() {
        System.out.println("三角形的面积");
        return dim1 * dim2 / 2;
    }
}

public class FindAreas {
    public static void main(String[] args) {
        Figure f = new Figure(10, 10);
        Rectangle r = new Rectangle(9, 5);
        Triangle t = new Triangle(10, 8);
        Figure figref;

        figref = r;
        System.out.println("面积: " + figref.area());

        figref = t;
        System.out.println("面积: " + figref.area());

        figref = f;
        System.out.println("面积: " + figref.area());
    }
}
Output:
矩形的面积
面积: 45.0
三角形的面积
面积: 40.0
未定义
面积: 0.0

 

4.7 使用抽象类

只定义被所有子类共享的一般形式,而让每个子类填充细节

abstract 类型修饰符,要求特定的方法必须被子类重写(除非子类也是抽象的)

一般形式:
abstract type name(parameter-list);

  • 不能实例化抽象类

Figure 类中将 area() 方法声明为抽象方法;派生自 Figure 的所有类都必须重写 area() 方法

abstract class Figure {
    double dim1;
    double dim2;

    Figure(double a, double b) {
        dim1 = a;
        dim2 = b;
    }

    // area() 方法被声明为抽象方法
    // 只有方法的名字,没有方法的实现
    abstract double area();
}

// 矩形
class Rectangle extends Figure {
    Rectangle(double a, double b) {
        super(a, b);
    }

    @Override
    double area() {
        System.out.println("矩形的面积");
        return dim1 * dim2;
    }
}

// 三角形
class Triangle extends Figure {
    Triangle(double a, double b) {
        super(a, b);
    }

    @Override
    double area() {
        System.out.println("三角形的面积");
        return dim1 * dim2 / 2;
    }
}

public class AbstractAreas {
    public static void main(String[] args) {
        // 抽象类不能被实例化
        // Figure f = new Figure(10, 10);
        Rectangle r = new Rectangle(9, 5);
        Triangle t = new Triangle(10, 8);
        // 没有实例被创建,ok
        Figure figref;

        figref = r;
        System.out.println("面积: " + figref.area());

        figref = t;
        System.out.println("面积: " + figref.area());
    }
}

  • abstract 抽象类:类
    extends: 单继承 (接口可以多继承)

  • 注:

    1. 不能 new 这个抽象类(即 不能实例化),只能靠子类去实现它:约束
    2. 抽象类中可以写普通的方法
    3. 抽象方法必须在抽象类中

 

4.8 在继承中使用 final 关键字

final 关键字的三个用途:

  1. 用于创建已命名常量的等价物
  2. 在继承中使用 final 关键字阻止重写
  3. 在继承中使用 final 关键字阻止继承

 

4.8.1 使用 final 关键字阻止重写

class A {
    final void meth() {
        System.out.println("final 方法");
    }
}

class B extends A {
    // 报错
    void meth() {
        System.out.println("非法!");
    }
}

 

4.8.2 使用 final 关键字阻止继承

final class A {
    // ...
}

// 非法!
class B extends A {   // 报错
    // ...
}

 

4.9 Object 类

(暂作简单介绍,待补充)

Object 是所有其他类的超类

Object 类型的引用变量可以引用任何其他类的对象

方法用途
Object clone()创建一个与要复制的对象完全相同的新对象
boolean equals(Object object)判定一个对象是否和另一个对象相等
void finalize()在回收不再使用的对象之前调用(JDK9不推荐使用)
final Class<?> getClass()在运行时获取对象所属的类
int hashCode()返回与调用对象相关联的哈希值
final void notify()恢复执行在调用对象上等待的某个线程
final void notifyAll()恢复执行在调用对象上等待的所有线程
String toString()返回一个描述对象的字符串
final void wait()
void wait(long milliseconds)
void wait(long milliseconds, int nanoseconds)
等待另一个线程的执行
  • equals 判断对象是否相等(只判断内容,== 还判断存储地址是否相等)

 

5. 多态

  • 即同一方法可以根据发送对象的不同而采用多种不同的行为方式
  • 一个对象的实际类型是确定的,但可以指向对象的引用的类型有很多(父类、有关系的类)
// Person.java
package oop.Demo;

public class Person {
    public void run() {
        System.out.println("run");
    }
}
// Student.java
package oop.Demo;

public class Student extends Person{
    @Override
    public void run() {
        System.out.println("son");
    }

    public void eat() {
        System.out.println("eat");
    }
}
// Application.java
package oop;

import oop.Demo.Person;
import oop.Demo.Student;

public class Application {
    public static void main(String[] args) {
        // 一个对象的实际类型是确定的
        // new Student();
        // new Person();

        // 可以指向的引用类型就不确定了: 父类的引用指向子类

        // Student 能调用的方法都是自己的或者继承父类的
        Student s1 = new Student();
        // Person 父类型,可以指向子类,但不能调用子类独有的方法
        Person s2 = new Student();
        Object s3 = new Student();

        // 对象能执行哪些方法,主要看对象左边的类型,和右边关系不大
        s2.run();   // 子类重写了父类的方法,执行子类的方法   // son
        s1.run();   // son
        ((Student) s2).eat();   // 强制类型转换
    }
}

注意事项:

  1. 多态是方法的多态,属性没有多态
  2. 父类和子类,有联系 类型转换异常 ClassCastException
  3. 存在条件:继承关系,方法需要重写,父类的引用指向子类对象
    Father f1 = new Son();

 

6. instanceof 和类型转换

  • instanceof (类型转换) 引用类型,判断一个对象是什么类型
package oop;

import oop.Demo.Person;
import oop.Demo.Student;
import oop.Demo.Teacher;

public class Application {
    public static void main(String[] args) {
        // System.out.println(X instanceof Y);    // 编译是否能通过,看 X 和 Y 是否是父子关系
        // 父子关系:true;兄弟关系:false;毫无关系:编译报错
        // Object > String
        // Object > Person > Teacher
        // Object > Person > Student

        Object object = new Student();
        System.out.println(object instanceof Student);    // true
        System.out.println(object instanceof Person);    // true
        System.out.println(object instanceof Object);    // true
        System.out.println(object instanceof Teacher);    // false
        System.out.println(object instanceof String);    // false

        System.out.println("===========================");
        Person person = new Student();
        System.out.println(person instanceof Student);    // true
        System.out.println(person instanceof Person);    // true
        System.out.println(person instanceof Object);    // true
        System.out.println(person instanceof Teacher);    // false
        // System.out.println(person instanceof String);    // 编译报错

        System.out.println("===========================");
        Student student = new Student();
        System.out.println(student instanceof Student);    // true
        System.out.println(student instanceof Person);    // true
        System.out.println(student instanceof Object);    // true
        // System.out.println(student instanceof Teacher);    // 编译报错
        // System.out.println(student instanceof Stirng);    // 编译报错
    }
}
package oop;

import oop.Demo.Person;
import oop.Demo.Student;
import oop.Demo.Teacher;

public class Application {
    public static void main(String[] args) {
        // 类型之间的转换:父子
        Person obj = new Student();
        // 将 student 对象转换为 Student 类型,就可以使用 Student 类型的方法了
        // 高 -> 低
        Student student = (Student) obj;
        student.go();
        // 或:
        // ((Student) obj).go();

        // 子类转换为父类,可能丢失自己本来的一些方法
        Student student = new Student();
        student.go();
        Person person = student;
    }
}
  1. 父类的引用指向子类的对象
  2. 把子类转为父类,向上转型
  3. 把父类转为子类,向下转型;强制转换
  4. 方便方法的调用,减少重复的代码
  • 抽象:封装、继承、多态

  • 抽象类:接口

 

7. 包和接口

7.1 引入 - 包

创建包:package mypackage;

多层次包:package a.b.c; (存储在 a\b\c 目录中)

 

包和类成员的访问:

(只适用于类的成员)

private无访问修饰符protectedpublic
在同一个类中可见
对相同包中的子类可见
对相同包中的非子类可见
对不同包中的子类可见
对不同包中的非子类可见
  • 所有声明为 public 的成员可以在其他类和其他包中进行访问
  • 所有声明为 private 的成员在类的外部不可见
  • 无访问修饰符的成员对于子类和相同包的其他类可见
  • 声明为 protected 的成员在当前包的外部可见,但只允许对类的直接子类可见
     

7.2 接口

从类的实现中完全抽象出类的接口。即 可以使用 interface 指定类必须执行哪些工作,而不指定如何执行这些工作。

普通类:只有具体实现

抽象类:具体实现和规范(抽象方法)都有

接口:只有规范,无法自己写方法,是专业的约束;约束和实现分离:面向接口编程

  • 接口就是规范,定义的是一组规则,体现了现实世界中“如果你是…则必须能…”的思想
  • 接口的本质是契约,制定好后必须遵守
  • 使用接口能够充分利用多态机制的“一个接口,多种方法”的特征
  • 接口的真正功能:就类层次而言,不相关的类可以实现相同的接口
  • 接口是面向对象的精髓,是对对象的抽象
// Callback.java
package com.feiye.Demo01;

interface Callback {
    void callback(int param);
}

// 实现接口
class Client implements Callback {
    // 实现接口方法时,必须将其声明为 public
    // 实现了接口的类,就需要重写接口中的方法
    @Override
    public void callback(int param) {
        System.out.println("callback: " + param);
    }

    // 定义类中自己的其他成员
    void nonIfaceMeth() {
        System.out.println("Client 类中的其他成员");
    }
}

class AnotherClient implements Callback {
    @Override
    public void callback(int param) {
        System.out.println("callback02: " + param);
        System.out.println("parm 的平方: " + (param * param));
    }
}
// TestIface.java
package com.feiye.Demo01;

class TestIface {
    public static void main(String[] args) {
        // 将变量声明为使用接口(而不是使用类)的对象引用
        // 被声明为接口类型 Callback,被赋值为 Client 类的一个实例
        Callback c = new Client();
        c.callback(42);
        // 不能直接使用 c 访问 nonIfaceMeth()
        ((Client) c).nonIfaceMeth();
    }
}
Output:
callback: 42
Client 类中的其他成员
// TestIface2.java
package com.feiye.Demo01;

class TestIface2 {
    public static void main(String[] args) {
        Callback c = new Client();
        AnotherClient ob = new AnotherClient();

        c.callback(42);

        System.out.println("========================");

        c = ob;
        c.callback(42);
    }
}
Output:
callback: 42
========================
callback02: 42
parm 的平方: 1764
  1. 如果类包含了一个接口,但没有实现该接口的全部方法,则必须将该类声明为 abstract
  2. public 接口(public interface interfaceName {})需要放在一个单独的文件中,而普通的接口(interface interfaceName {})不需要

 

7.2.1 嵌套接口

class A {
    public interface NestedIF {
        boolean isNotNegative(int x);
    }
}

class B implements A.NestedIF {
    @Override
    public boolean isNotNegative(int x) {
        return x < 0 ? false: true;
    }
}

public class NestedIFDemo {
    public static void main(String[] args) {
        A.NestedIF nif = new B();
        if (nif.isNotNegative(10))
            System.out.println("10不小于0");
        if (nif.isNotNegative(-12))
            System.out.println("不会显示");
    }
}
Output:
10不小于0

 

7.2.2 应用接口

三个栈接口的应用:

IntStack.java

package com.feiye.Demo03;

interface IntStack {
    // 入栈
    void push(int item);
    // 出栈
    int pop();
}

IFTest.java

package com.feiye.Demo03;

class FixedStack implements IntStack {
    private int[] stck;
    private int tos;

    // 栈的初始化
    FixedStack(int size) {
        stck = new int[size];
        tos = -1;
    }

    @Override
    public void push(int item) {
        if (tos==stck.length-1) {
            System.out.println("栈满");
        } else {
            stck[++tos] = item;
        }
    }

    @Override
    public int pop() {
        if (tos < 0) {
            System.out.println("栈空");
            return 0;
        } else {
            return stck[tos--];
        }
    }
}

public class IFTest {
    public static void main(String[] args) {
        FixedStack mystack1 = new FixedStack(5);
        FixedStack mystack2 = new FixedStack(8);

        for (int i = 0; i < 5; i++) {
            mystack1.push(i);
        }
        for (int i = 0; i < 8; i++) {
            mystack2.push(i);
        }

        System.out.println("mystack1:");
        for (int i = 0; i < 5; i++) {
            System.out.print(mystack1.pop() + " ");
        }
        System.out.println();

        System.out.println("mystack2:");
        for (int i = 0; i < 8; i++) {
            System.out.print(mystack2.pop() + " ");
        }
    }
}
Output:
mystack1:
4 3 2 1 0 
mystack2:
7 6 5 4 3 2 1 0 

IFTest2.java

package com.feiye.Demo03;

class DynStack implements IntStack {
    private int[] stck;
    private int tos;

    // 初始化栈
    DynStack(int size) {
        stck = new int[size];
        tos = -1;
    }

    @Override
    public void push(int item) {
        if (tos==stck.length-1) {
            int[] temp = new int[stck.length * 2];
            for (int i = 0; i < stck.length; i++) {
                temp[i] = stck[i];
            }
            stck = temp;
            stck[++tos] = item;
        } else {
            stck[++tos] = item;
        }
    }

    @Override
    public int pop() {
        if (tos < 0) {
            System.out.println("栈空");
            return 0;
        } else {
            return stck[tos--];
        }
    }
}

public class IFTest2 {
    public static void main(String[] args) {
        DynStack mystack1 = new DynStack(5);
        DynStack mystack2 = new DynStack(8);

        for (int i = 0; i < 12; i++) {
            mystack1.push(i);
        }
        for (int i = 0; i < 20; i++) {
            mystack2.push(i);
        }
        System.out.println("mystack1:");
        for (int i = 0; i < 12; i++) {
            System.out.print(mystack1.pop() + " ");
        }
        System.out.println();

        System.out.println("mystack2:");
        for (int i = 0; i < 20; i++) {
            System.out.print(mystack2.pop() + " ");
        }
    }
}
Output:
mystack1:
11 10 9 8 7 6 5 4 3 2 1 0 
mystack2:
19 18 17 16 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0 

IFTest3.java

package com.feiye.Demo03;

class IFTest3 {
    public static void main(String[] args) {
        // mystack 指向 IntStack 接口的引用
        // 实现通过接口引用变量访问接口的多个实现
        IntStack mystack;
        DynStack ds = new DynStack(5);
        FixedStack fs = new FixedStack(8);

        mystack = ds;
        for (int i = 0; i < 12; i++) {
            mystack.push(i);
        }

        mystack = fs;
        for (int i = 0; i < 8; i++) {
            mystack.push(i);
        }

        mystack = ds;
        System.out.println("ds:");
        for (int i = 0; i < 12; i++) {
            System.out.print(mystack.pop() + " ");
        }
        System.out.println();

        mystack = fs;
        System.out.println("fs:");
        for (int i = 0; i < 8; i++) {
            System.out.print(mystack.pop() + " ");
        }
    }
}
Output:
ds:
11 10 9 8 7 6 5 4 3 2 1 0 
fs:
7 6 5 4 3 2 1 0 

 

7.2.3 接口中的变量

自动化的“决策生成器”

SharedConstants.java

package com.feiye.Demo04;

interface SharedConstants {
    int NO = 0;
    int YES = 1;
    int MAYBE = 2;
    int LATER = 3;
    int SOON = 4;
    int NEVER = 5;
}

AskMe.java

package com.feiye.Demo04;

import java.util.Random;

class Question implements SharedConstants {
    Random rand = new Random();
    int ask() {
        // rand.nextDouble() 返回0.0~1.0内的随机数
        int prob = (int) (100 * rand.nextDouble());
        if (prob<30) {
            return NO;
        } else if (prob<60) {
            return YES;
        } else if (prob<75) {
            return LATER;
        } else if (prob<98) {
            return SOON;
        } else {
            return NEVER;
        }
    }
}

class AskMe implements SharedConstants {
    static void answer(int result) {
        switch(result) {
            case NO:
                System.out.println("No");
                break;
            case YES:
                System.out.println("Yes");
                break;
            case MAYBE:
                System.out.println("Maybe");
                break;
            case LATER:
                System.out.println("Later");
                break;
            case SOON:
                System.out.println("Soon");
                break;
            case NEVER:
                System.out.println("Never");
                break;
        }
    }

    public static void main(String[] args) {
        Question q = new Question();

        answer(q.ask());
        answer(q.ask());
        answer(q.ask());
        answer(q.ask());
    }
}
Output:(每次输出都不一样)
Soon
Soon
Later
Soon

以上使用接口定义共享常量的方法具有争议性

 

7.2.4 接口可以扩展

接口可以继承,实现接口的所有类必须实现接口定义的所有方法,包括从其他接口继承而来的所有方法

interface A {
    void meth1();
    void meth2();
}

interface B extends A {
    void meth3();
}

class MyClass implements B {
    @Override
    public void meth1() {
        System.out.println("meth1()");
    }

    @Override
    public void meth2() {
        System.out.println("meth2()");
    }

    @Override
    public void meth3() {
        System.out.println("meth3()");
    }
}

public class IFExtend {
    public static void main(String[] args) {
        MyClass ob = new MyClass();

        ob.meth1();
        ob.meth2();
        ob.meth3();
    }
}
Output:
meth1()
meth2()
meth3()

 

7.3 默认接口方法

主要动机:

  1. 提供一种扩展接口的方法,而不破坏现有代码
  2. 希望在接口中根据接口的使用方式选择使用的方法

MyIF.java

package com.feiye.Demo06;

public interface MyIF {
    // 普通的接口方法
    int getNumber();
    // 默认接口方法
    // 包含一个默认实现
    // 实现该接口的类不需要重写该方法
    default String getString() {
        return "Default String";
    }
}

DefaultMethodDemo.java

package com.feiye.Demo06;

class MyIFImp implements MyIF {
    // 不需要重写默认方法
    @Override
    public int getNumber() {
        return 100;
    }
}

class DefaultMethodDemo {
    public static void main(String[] args) {
        MyIFImp obj = new MyIFImp();

        System.out.println(obj.getNumber());

        System.out.println(obj.getString());
    }
}
Output:
100
Default String
  • 24
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值