1、抽象
抽象函数——能表达出概念而无法实现具体代码的函数
抽象类——能表达出概念而无法实现具体代码的类
抽象函数和抽象类的注意点:
- 带有abstract修饰符的函数
- 有抽象函数的类一定是抽象类
- 抽象类不能制造对象,但是可以定义变量,任何继承了抽象类的非抽象类的对象可以赋给这个变量
!继承自抽象类的子类必须覆盖父类的抽象函数!
举个栗子
//Shape.java
package shapes;
import java.awt.Graphics;
public abstract class Shape {
public abstract void draw(Graphics g);
}
//Line.java
package shapes;
import java.awt.Graphics;
public class Line extends Shape {
private int x1;
private int y1;
private int x2;
private int y2;
public Line(int x1, int y1, int x2, int y2)
{
this.x1 = x1; this.y1 = y1;
this.x2 = x2; this.y2 = y2;
}
@Override
public void draw(Graphics g) {//要覆盖Shape抽象类的draw函数
g.drawLine(x1, y1, x2, y2);
}
}
2、数据与表现分离(一个细胞自动机的例子)
思想都在代码里体现
代码运行之后是酱紫的~
//Cell.java
package cell;
import java.awt.Graphics;
public class Cell {
private boolean alive = false;
public void die() {
alive = false;
}
public void reborn() {
alive = true;
}
public boolean isAlive() {
return alive;
}
public void draw(Graphics g, int x, int y, int size) {//每个细胞是一个小矩形,黑框框代表是活细胞
g.drawRect(x, y, size, size);
if (alive) {
g.fillRect(x, y, size, size);
}
}
}
//Field.java
package field;
import java.util.ArrayList;
import cell.Cell;
public class Field {
private int width;
private int height;
private Cell[][] field;
public Field(int width, int height) {
this.width = width;
this.height = height;
field = new Cell[height][width];
}
public int getWidth() {
return width;
}
public int getHeight() {
return height;
}
public Cell place(int row, int col, Cell o) {// 放置细胞
Cell ret = field[row][col];
field[row][col] = o;
return ret;
}
public Cell get(int row, int col) {// 取得细胞
return field[row][col];
}
public Cell[] getNeighbour(int row, int col) {// 获得一个细胞的八个邻居,返回一个Cell数组
ArrayList<Cell> list = new ArrayList<Cell>();
for (int i = -1; i < 2; i++) {
for (int j = -1; j < 2; j++) {
int r = row + i;
int c = col + j;
if (r > -1 && r < height && c > -1 && c < width && !(r == row && c == col)) {
list.add(field[r][c]);
}
}
}
return list.toArray(new Cell[list.size()]);
}
public void clear() {
for (int i = 0; i < height; i++) {
for (int j = 0; j < width; j++) {
field[i][j] = null;
}
}
}
}
//View.java
package field;
import java.awt.Dimension;
import java.awt.Graphics;
import javax.swing.JPanel;
import cell.Cell;
public class View extends JPanel {
private static final long serialVersionUID = -5258995676212660595L;
private static final int GRID_SIZE = 16;// 初始化设定表格大小为16
private Field theField;
public View(Field field) {// View的构造函数
theField = field;
}
@Override
public void paint(Graphics g) {// View只需要把field画出来就行,不需要管数据,这就是数据和表现分离的体现
super.paint(g);
for (int row = 0; row < theField.getHeight(); row++) {
for (int col = 0; col < theField.getWidth(); col++) {
Cell cell = theField.get(row, col);
if (cell != null) {
cell.draw(g, col * GRID_SIZE, row * GRID_SIZE, GRID_SIZE);
}
}
}
}
@Override
public Dimension getPreferredSize() {
return new Dimension(theField.getWidth() * GRID_SIZE + 1, theField.getHeight() * GRID_SIZE + 1);
}
public static void main(String[] args) {
}
}
package cellmachine;
import javax.swing.JFrame;
import cell.Cell;
import field.Field;
import field.View;
//CellMachine.java
public class CellMachine {
public static void main(String[] args) {
Field field = new Field(30, 30);// 设定长宽各30个格子
for (int row = 0; row < field.getHeight(); row++) {
for (int col = 0; col < field.getWidth(); col++) {
field.place(row, col, new Cell());// 初始化表格,默认细胞都是死亡状态
}
}
for (int row = 0; row < field.getHeight(); row++) {
for (int col = 0; col < field.getWidth(); col++) {
Cell cell = field.get(row, col);
if (Math.random() < 0.2) {// 初始化:细胞有1/5的概率能重生
cell.reborn();
}
}
}
View view = new View(field);
JFrame frame = new JFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setResizable(false);
frame.setTitle("Cells");
frame.add(view);
frame.pack();
frame.setVisible(true);//要能显示出来
for (int i = 0; i < 1000; i++) {
for (int row = 0; row < field.getHeight(); row++) {
for (int col = 0; col < field.getWidth(); col++) {
Cell cell = field.get(row, col);
Cell[] neighbour = field.getNeighbour(row, col);//得到邻居数组
int numOfLive = 0;
for (Cell c : neighbour) {//统计邻居活细胞数
if (c.isAlive()) {
numOfLive++;
}
}
System.out.print("[" + row + "][" + col + "]:");
System.out.print(cell.isAlive() ? "live" : "dead");
System.out.print(":" + numOfLive + "-->");
if (cell.isAlive()) {
if (numOfLive < 2 || numOfLive > 3) {//如果邻居活细胞数不为3那么就要die
cell.die();
System.out.print("die");
}
} else if (numOfLive == 3) {
cell.reborn();
System.out.print("reborn");
}
System.out.println();
}
}
System.out.println("UPDATE");
frame.repaint();
try {
Thread.sleep(200);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
3、接口
接口是纯抽象类,意思是它其中所有的函数都是抽象函数,所有的成员变量都是public static final
接口规定了长什么样,但是不管里面有什么
接口要用关键字interface
之前的细胞自动机,如果我们要改造成狐狸与兔子的自动机,因为动物有年龄,有动作,所以Cell的地位很尴尬,如图所示
因此我们可以把Cell定义为一个接口
可以让Fox和Rabbit去实现这个Cell接口,这样以后Rox和Rabbit就可以放到Cell里边去了
实现接口要用关键字implements
具体代码就不贴了,比较复杂
在实现接口的过程中,类用extends,接口用implements
- 类可以实现很多接口
- 接口可以继承接口,但不能继承类
- 接口不能实现接口
这里就引出Java的这种面向接口的编程方式
- 设计程序时先定义接口,再实现类
- 任何需要在函数间传入传出的一定是接口而不是具体的类
- 是Java成功的关键之一,因为这种方式很适合多人协作写项目,你想让同事的程序满足你的要求,只需要写一个接口就好
- 但这个也是被Java批评的要点,因为代码膨胀起来量很快也很大