优化java代码的一些小技巧(二)————常量尽量使用枚举

2、常量尽量使用枚举

    需要用户列出一组预定义或常量值的场景有很多,例如在web应用程序中可能遇到的HTTP响应代码。最常见的实现技术之一是新建类,该类里面有很多静态的final类型的值,每个值都应该有一句注释,描述该值的含义是什么。例如:

public class HttpResponseCodes {  
    public static final int OK = 200;  
    public static final int NOT_FOUND = 404;  
    public static final int FORBIDDEN = 403;  
}  
if (getHttpResponse().getStatusCode() == HttpResponseCodes.OK) {  
    // Do something if the response code is OK   
}  

    能够有这种思路就已经非常好了,但这还是有一些缺点:     

  • 没有对传入的整数值进行严格的校验
  • 由于是基本数据类型,因此不能调用状态代码上的方法

    用静态final常量只是简单的创建了一个特定的常量来表示特殊的整数值,但并没有对方法或变量进行限制,因此使用的值可能会超出定义的范围。例如: 

public class HttpResponseHandler {  
    public static void printMessage(int statusCode) {  
        System.out.println("Recieved status of " + statusCode);   
    }  
}  
HttpResponseHandler.printMessage(15000);  

    尽管15000并不是有效的HTTP响应代码,但是,由于服务器端也没有限制客户端必须提供有效的整数。所以,我们没有办法为状态代码定义方法。

    例如,如果想要检查给定的状态代码是否是一个成功的代码,那就必须定义一个单独的函数: 

public class HttpResponseCodes {  
    public static final int OK = 200;  
    public static final int NOT_FOUND = 404;  
    public static final int FORBIDDEN = 403;  
    public static boolean isSuccess(int statusCode) {  
        return statusCode >= 200 && statusCode < 300;   
    }  
}  
if (HttpResponseCodes.isSuccess(getHttpResponse().getStatusCode())) {  
    // Do something if the response code is a success code   
}  

    再看看以下的解决方法。

    我们将常量类型从基本数据类型改为自定义类型,并只允许自定义类的特定对象。这正是Java枚举(enum)的用途。使用enum,我们可以一次性解决参数校验方法调用的问题: 

public enum HttpResponseCodes {  
    OK(200),  
    FORBIDDEN(403),  
    NOT_FOUND(404);  
    private final int code;   
    HttpResponseCodes(int code) {  
        this.code = code;  
    }  
    public int getCode() {  
        return code;  
    }  
    public boolean isSuccess() {  
        return code >= 200 && code < 300;  
    }  
}  
if (getHttpResponse().getStatusCode().isSuccess()) {  
    // Do something if the response code is a success code   
}  

    现在还可以要求在调用方法的时候提供必须有效的状态代码: 

public class HttpResponseHandler {  
    public static void printMessage(HttpResponseCode statusCode) {  
        System.out.println("Recieved status of " + statusCode.getCode());   
    }  
}  
HttpResponseHandler.printMessage(HttpResponseCode.OK); 

    注意,举这个例子是想说明如果是常量,则应该尽量使用枚举,但并不是说什么情况下都应该使用枚举

    在某些情况下,可能希望使用一个常量来表示某个特殊值,但是也允许提供其它的值。例如,大家可能都知道圆周率,我们可以用一个常量来捕获这个值(并重用它): 

public class NumericConstants {  
    public static final double PI = 3.14;  
    public static final double UNIT_CIRCLE_AREA = PI * PI;  
}  
public class Rug {  
    private final double area;  
    public class Run(double area) {  
        this.area = area;  
    }  
    public double getCost() {  
        return area * 2;  
    }  
}  
// Create a carpet that is 4 feet in diameter (radius of 2 feet)  
Rug fourFootRug = new Rug(2 * NumericConstants.UNIT_CIRCLE_AREA); 

    使用枚举的规则可以归纳为: 

    当所有可能的离散值都已经提前知道了,那么就可以使用枚举

    再拿上文中所提到的HTTP响应代码为例,我们可能知道HTTP状态代码的所有值(可以在RFC 7231中找的到,它定义了HTTP 1.1协议)。因此使用了枚举。

    在计算圆周率的情况下,我们不知道关于圆周率的所有可能值(任何可能的double都是有效的),但同时又希望为圆形的rugs创建一个常量,使计算更容易(更容易阅读);因此定义了一系列常量。 

    如果不能提前知道所有可能的值,但是又希望包含每个值的字段或方法,那么最简单的方法就是可以新建一个类来表示数据。

    尽管没有说过什么场景应该绝对不用枚举,但要想知道在什么地方、什么时间不使用枚举的关键是提前意识到所有的值,并且禁止使用其他任何值。

转载于:https://my.oschina.net/fl128818/blog/1550489

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
以下是一个简单的扫雷小游戏的 JAVA 代码,实现了基本的游戏功能: ``` import java.util.Random; import java.util.Scanner; public class Minesweeper { private static final int ROWS = 10; private static final int COLS = 10; private static final int BOMBS = 10; private static final char COVERED = '-'; private static final char UNCOVERED = ' '; private static final char BOMB = '*'; private char[][] board; private boolean[][] bombPositions; public Minesweeper() { board = new char[ROWS][COLS]; bombPositions = new boolean[ROWS][COLS]; initBoard(); initBombs(); } private void initBoard() { for (int i = 0; i < ROWS; i++) { for (int j = 0; j < COLS; j++) { board[i][j] = COVERED; } } } private void initBombs() { Random random = new Random(); int count = 0; while (count < BOMBS) { int row = random.nextInt(ROWS); int col = random.nextInt(COLS); if (!bombPositions[row][col]) { bombPositions[row][col] = true; count++; } } } private int countAdjacentBombs(int row, int col) { int count = 0; for (int i = row - 1; i <= row + 1; i++) { for (int j = col - 1; j <= col + 1; j++) { if (i >= 0 && i < ROWS && j >= 0 && j < COLS && bombPositions[i][j]) { count++; } } } return count; } public boolean playMove(int row, int col) { if (bombPositions[row][col]) { board[row][col] = BOMB; return false; } int adjacentBombs = countAdjacentBombs(row, col); board[row][col] = (char) ('0' + adjacentBombs); if (adjacentBombs == 0) { for (int i = row - 1; i <= row + 1; i++) { for (int j = col - 1; j <= col + 1; j++) { if (i >= 0 && i < ROWS && j >= 0 && j < COLS && board[i][j] == COVERED) { playMove(i, j); } } } } return true; } public void playGame() { Scanner scanner = new Scanner(System.in); boolean gameOver = false; while (!gameOver) { printBoard(); System.out.print("Enter row and column (e.g. 1 2): "); int row = scanner.nextInt() - 1; int col = scanner.nextInt() - 1; if (row < 0 || row >= ROWS || col < 0 || col >= COLS) { System.out.println("Invalid row or column!"); continue; } gameOver = !playMove(row, col); if (checkWin()) { System.out.println("You win!"); gameOver = true; } } printBoard(); } private boolean checkWin() { for (int i = 0; i < ROWS; i++) { for (int j = 0; j < COLS; j++) { if (board[i][j] == COVERED && !bombPositions[i][j]) { return false; } } } return true; } private void printBoard() { System.out.print(" "); for (int j = 0; j < COLS; j++) { System.out.print((j + 1) + " "); } System.out.println(); for (int i = 0; i < ROWS; i++) { System.out.print((i + 1) + " "); for (int j = 0; j < COLS; j++) { System.out.print(board[i][j] == UNCOVERED && bombPositions[i][j] ? BOMB : board[i][j]); System.out.print(" "); } System.out.println(); } } public static void main(String[] args) { Minesweeper minesweeper = new Minesweeper(); minesweeper.playGame(); } } ``` 代码中定义了游戏的行数,列数和雷数,以及用来表示板块状态的字符常量。在 `Minesweeper` 类中,通过维数组 `board` 和 `bombPositions` 来表示游戏面板和雷的位置。通过 `initBoard` 和 `initBombs` 方法初始化游戏面板和雷的位置。 在 `countAdjacentBombs` 方法中,根据当前位置的周围八个位置计算雷的个数。 在 `playMove` 方法中,根据当前位置是否有雷计算该位置的状态,并根据周围是否有雷递归更新周围的位置状态。 在 `playGame` 方法中,通过 `Scanner` 类读取玩家的输入,并根据输入更新游戏状态,直到游戏结束。 在 `checkWin` 方法中,检查游戏是否胜利。 在 `printBoard` 方法中,输出游戏面板。 在 `main` 方法中,创建 `Minesweeper` 对象并调用 `playGame` 方法开始游戏。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值