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);
}
}
- 构造器
- 和类名相同
- 没有返回值
- 作用
- new 本质在调用构造方法
- 初始化对象的值
- 注意点
- 一旦定义了有参构造,如果想使用无参构造,必须显式地定义一个无参构造
自动生成构造器: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());
}
}
- 提高程序的安全性,保护数据
- 隐藏代码的实现细节
- 统一接口
- 增加系统可维护性
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
- super 调用父类的构造方法,必须在构造方法的第一个
- super 必须只能出现在子类的方法或者构造方法中
- super 和 this 不能同时调用构造方法
- vs this:
- 代表对象不同:
this:本身调用这个对象
super:代表父类对象的引用 - 前提:
this:没有继承也可以使用
super:只能在继承条件下才能使用 - 构造方法:
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 的
总结:
前提:需要有继承关系,子类重写父类的方法
- 方法名必须相同
- 参数列表必须相同
- 修饰符:范围可以扩大:
public > protected > default > private
- 抛出的异常:范围可以被缩小,但不能扩大:
ClassNotFoundException --> Exception(大)
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
: 单继承 (接口可以多继承) -
注:
- 不能 new 这个抽象类(即 不能实例化),只能靠子类去实现它:约束
- 抽象类中可以写普通的方法
- 抽象方法必须在抽象类中
4.8 在继承中使用 final 关键字
final 关键字的三个用途:
- 用于创建已命名常量的等价物
- 在继承中使用 final 关键字阻止重写
- 在继承中使用 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(); // 强制类型转换
}
}
注意事项:
- 多态是方法的多态,属性没有多态
- 父类和子类,有联系 类型转换异常
ClassCastException
- 存在条件:继承关系,方法需要重写,父类的引用指向子类对象
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;
}
}
- 父类的引用指向子类的对象
- 把子类转为父类,向上转型
- 把父类转为子类,向下转型;强制转换
- 方便方法的调用,减少重复的代码
-
抽象:封装、继承、多态
-
抽象类:接口
7. 包和接口
7.1 引入 - 包
创建包:package mypackage;
多层次包:package a.b.c;
(存储在 a\b\c
目录中)
包和类成员的访问:
(只适用于类的成员)
private | 无访问修饰符 | protected | public | |
---|---|---|---|---|
在同一个类中可见 | 是 | 是 | 是 | 是 |
对相同包中的子类可见 | 否 | 是 | 是 | 是 |
对相同包中的非子类可见 | 否 | 是 | 是 | 是 |
对不同包中的子类可见 | 否 | 否 | 是 | 是 |
对不同包中的非子类可见 | 否 | 否 | 否 | 是 |
- 所有声明为 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
- 如果类包含了一个接口,但没有实现该接口的全部方法,则必须将该类声明为 abstract
- 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 默认接口方法
主要动机:
- 提供一种扩展接口的方法,而不破坏现有代码
- 希望在接口中根据接口的使用方式选择使用的方法
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