Java选择结构?从入门到精通全在这里!

94°C 16-11-2025 notbyai
最近更新于:2025-11-16 17:05:30

哈喽大家好!今天咱们来聊聊Java里的选择结构。说实话,这玩意儿虽然是编程基础中的基础,但我发现很多人用了好几年还是只会if-else硬堆,代码写得那叫一个”壮观”。今天我就把这些年踩过的坑、总结的经验都分享出来,保证让你对选择结构有个全新的认识。

选择结构是个啥?

咱们先从最本质的东西说起。程序不可能一条路走到黑,总得根据不同情况做不同的事儿吧?比如用户登录,密码对了进系统,错了就提示重新输入。这就是选择结构的作用——让程序具备”决策能力”。

用大白话说,选择结构就是”如果…那么…否则…”的逻辑。你每天都在用这种思维方式,只是可能没意识到而已。

单分支if:最简单的选择

这是最基础的形式,就一个判断条件。

int age = 17;

if (age < 18) {
    System.out.println("未成年人,禁止进入");
}

看着简单吧?但这里有个细节很多人会忽略——大括号要不要写?

// 可以不写大括号
if (age < 18)
    System.out.println("未成年人,禁止进入");

// 但强烈建议写上!
if (age < 18) {
    System.out.println("未成年人,禁止进入");
}

为什么建议写?因为不写大括号只有紧跟着的第一行代码受if控制。不然就可能遇到这样的Bug:

if (isVip)
    discount = 0.8;
    System.out.println("享受VIP折扣");  // 这行不管isVip是啥都会执行!

看出问题了吗?第二行代码其实不在if的控制范围内,无论是不是VIP都会打印。这种Bug特别隐蔽,养成写大括号的习惯能省不少麻烦。

双分支if-else:非此即彼

当你有两种互斥的情况时,就该if-else上场了。

int score = 75;

if (score >= 60) {
    System.out.println("恭喜你,考试通过了!");
} else {
    System.out.println("很遗憾,需要补考");
}

实战场景:判断奇偶数

public String checkEvenOdd(int number) {
    if (number % 2 == 0) {
        return "这是偶数";
    } else {
        return "这是奇数";
    }
}

这种情况其实还可以用三元运算符简化,后面会讲到。

小心逻辑陷阱

我见过不少新手写出这样的代码:

boolean hasTicket = true;

if (hasTicket == true) {  // 多此一举!
    System.out.println("可以进场");
}

hasTicket本身就是布尔值,直接写if (hasTicket)就行了。同理,if (hasTicket == false)应该写成if (!hasTicket)

多分支if-else if-else:层层筛选

当有多个条件需要判断时,就得用到这个结构了。

int score = 85;

if (score >= 90) {
    System.out.println("优秀,奖励现金500元");
} else if (score >= 80) {
    System.out.println("良好,奖励现金300元");
} else if (score >= 60) {
    System.out.println("及格,继续努力");
} else {
    System.out.println("不及格,需要补考");
}

这里有个重要的执行逻辑:从上往下判断,只要有一个条件满足就执行对应代码块,然后跳出整个结构。所以score=85时,只会打印”良好”那句,不会再去判断后面的条件。

条件的顺序很重要!

我之前就遇到过这样一个坑,写的代码逻辑完全反了:

// 错误示范!
if (score >= 60) {
    System.out.println("及格");
} else if (score >= 80) {  // 这个条件永远不会执行到
    System.out.println("良好");
} else if (score >= 90) {  // 这个也不会
    System.out.println("优秀");
}

为啥?因为score>=80的时候,早就满足score>=60了,直接在第一个if里就返回了。所以多分支判断一定要从严格到宽松排序,或者从大到小、从小到大,总之得有个明确的顺序。

实战案例:计算个税

import java.util.Scanner;

public class Main {
    public static double calculateTax(double income) {
        double tax = 0;
        double taxableIncome = income - 5000; // 应纳税所得额(扣除免征额5000)

        if (taxableIncome <= 0) {
            tax = 0; // 免征额以下,无税
        } else if (taxableIncome <= 3000) { 
            tax = taxableIncome * 0.03; // 3%税率
        } else if (taxableIncome <= 12000) { 
            tax = taxableIncome * 0.1 - 210; // 10%税率
        } else if (taxableIncome <= 25000) { 
            tax = taxableIncome * 0.2 - 1410; // 20%税率
        } else {
            tax = taxableIncome * 0.25 - 2660; // 25%税率
        }
        return tax;
    }

    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        System.out.print("请输入收入金额:");
        double income = sc.nextDouble();

        double tax = calculateTax(income);
        System.out.printf("应缴纳税费:%.2f 元%n", tax); // 保留2位小数

        sc.close();
    }
}

这个例子展示了如何用if-else if结构处理分段计算问题。实际工作中这种场景特别多,比如快递费计算、会员等级判定等等。

嵌套if:处理复杂逻辑

有时候判断条件不是线性的,需要在一个条件成立后再判断另一个条件。

int age = 25;
boolean hasLicense = true;

if (age >= 18) {
    if (hasLicense) {
        System.out.println("可以开车上路");
    } else {
        System.out.println("年龄够了,但需要先考驾照");
    }
} else {
    System.out.println("年龄不够,不能开车");
}

嵌套if很容易写得乱七八糟,特别是嵌套层次多了以后。我的建议是:

1. 控制嵌套层次,最好不超过3层

嵌套太深代码可读性会变得很差。如果逻辑确实复杂,考虑把部分判断提取成方法。

2. 能用逻辑运算符就用逻辑运算符

上面那个例子其实可以简化:

if (age >= 18 && hasLicense) {
    System.out.println("可以开车上路");
} else if (age >= 18 && !hasLicense) {
    System.out.println("年龄够了,但需要先考驾照");
} else {
    System.out.println("年龄不够,不能开车");
}

看着是不是清晰多了?

3. 善用卫语句(Guard Clauses)

什么是卫语句?就是把不符合条件的情况先排除掉。

// 传统写法
public void processOrder(Order order) {
    if (order != null) {
        if (order.isValid()) {
            if (order.getAmount() > 0) {
                // 处理订单...
            }
        }
    }
}

// 使用卫语句
public void processOrder(Order order) {
    if (order == null) return;
    if (!order.isValid()) return;
    if (order.getAmount() <= 0) return;

    // 处理订单...
}

第二种写法是不是顺眼多了?这种写法减少了嵌套层次,让主流程逻辑更清晰。这个技巧可以从《重构》那本书中学到,超级实用。

switch-case:多分支的优雅解决方案

当你要根据一个变量的不同值执行不同代码时,switch比一堆if-else if清晰多了。

基础用法

int dayOfWeek = 3;

switch (dayOfWeek) {
    case 1:
        System.out.println("星期一,打工人的煎熬开始了");
        break;
    case 2:
        System.out.println("星期二,继续加油");
        break;
    case 3:
        System.out.println("星期三,过半了");
        break;
    case 4:
        System.out.println("星期四,快到周末了");
        break;
    case 5:
        System.out.println("星期五,终于要解放了");
        break;
    case 6:
    case 7:
        System.out.println("周末,开心!");
        break;
    default:
        System.out.println("这是什么日子?");
}

注意几个要点:

1. break很重要!

如果不写break,代码会继续往下执行(这叫”fall through”)。有时候这是我们想要的效果(比如上面case 6和case 7共用一段代码),但更多时候忘记写break是Bug。

int month = 3;
switch (month) {
    case 3:
        System.out.println("三月");
        // 忘记写break了!
    case 4:
        System.out.println("四月");
        break;
}
// 输出:三月 四月(两行都打印了!)

一定要养成习惯,写完case就立马写break,不然因为这个就得Debug半天。

2. default是可选的,但建议写上

default相当于其他语言里的else,处理所有没匹配到的情况。就算你觉得已经覆盖了所有可能,也建议写个default,哪怕只是打个日志。这样将来代码出问题时,至少能知道走到了意外分支。

switch支持的数据类型

Java的switch支持这些类型:

  • byte、short、int、char(基本类型)
  • Byte、Short、Integer、Character(包装类)
  • String(Java 7开始支持)
  • enum(枚举类型)
// String类型的switch
String command = "start";

switch (command) {
    case "start":
        System.out.println("启动服务");
        break;
    case "stop":
        System.out.println("停止服务");
        break;
    case "restart":
        System.out.println("重启服务");
        break;
    default:
        System.out.println("未知命令");
}

注意:switch不支持long类型!这是个常见的坑。如果要判断long类型的值,老老实实用if-else。

实战:枚举类型配合switch

这是我认为switch最优雅的用法之一:

public enum OrderStatus {
    PENDING,      // 待支付
    PAID,         // 已支付
    SHIPPED,      // 已发货
    DELIVERED,    // 已送达
    CANCELLED     // 已取消
}

public void handleOrder(Order order) {
    switch (order.getStatus()) {
        case PENDING:
            System.out.println("订单待支付,发送支付提醒");
            break;
        case PAID:
            System.out.println("订单已支付,准备发货");
            break;
        case SHIPPED:
            System.out.println("订单已发货,更新物流信息");
            break;
        case DELIVERED:
            System.out.println("订单已送达,请用户确认收货");
            break;
        case CANCELLED:
            System.out.println("订单已取消,处理退款");
            break;
        default:
            throw new IllegalStateException("未知的订单状态");
    }
}

枚举+switch的组合既安全又清晰,而且如果你加了新的枚举值但忘记在switch里处理,编译器会给你警告。

Java 12+的新特性:switch表达式

如果你用的是Java 12及以上版本,那你有福了!新的switch表达式简直不要太香。

箭头语法

int dayOfWeek = 3;

String dayType = switch (dayOfWeek) {
    case 1, 2, 3, 4, 5 -> "工作日";
    case 6, 7 -> "休息日";
    default -> "无效日期";
};

System.out.println(dayType);

看到没?用箭头->替代了冒号和break,而且可以直接返回值!这个语法有几个好处:

  1. 不需要break:箭头语法自动break,不会fall through
  2. 可以直接赋值:switch本身可以作为表达式返回值
  3. 支持多个值:用逗号分隔,比写多个case简洁

yield关键字

如果switch分支里需要多行代码,就用yield返回值:

int score = 85;

String grade = switch (score / 10) {
    case 10, 9 -> "A";
    case 8 -> "B";
    case 7 -> "C";
    case 6 -> "D";
    default -> {
        System.out.println("成绩不及格");
        yield "F";  // 用yield返回值
    }
};

这个新语法真的让代码简洁了不少,而且更不容易出错。如果你的项目用的Java版本支持,强烈建议试试。

三元运算符:单行的if-else

对于简单的二选一逻辑,三元运算符是最简洁的写法。

int age = 20;
String result = (age >= 18) ? "成年人" : "未成年人";

等价于:

String result;
if (age >= 18) {
    result = "成年人";
} else {
    result = "未成年人";
}

什么时候用三元运算符?

我的原则是:逻辑简单时用,复杂了就别用

// 可以用
int max = (a > b) ? a : b;

// 最好别用(嵌套三元运算符)
String result = (score >= 90) ? "优秀" : 
                (score >= 80) ? "良好" : 
                (score >= 60) ? "及格" : "不及格";

嵌套的三元运算符可读性太差了,还不如老老实实写if-else if。记住,代码是给人看的,简洁不等于炫技。

一个实用技巧:避免空指针

// 传统写法
String name;
if (user != null) {
    name = user.getName();
} else {
    name = "匿名用户";
}

// 三元运算符
String name = (user != null) ? user.getName() : "匿名用户";

// 更优雅的写法(Java 8+)
String name = Optional.ofNullable(user)
                      .map(User::getName)
                      .orElse("匿名用户");

第三种用Optional的写法最安全,不过这是另一个话题了,有机会单独写一篇。

实战技巧:如何写出好的选择结构

技巧1:提前return,减少嵌套

// 不好的写法
public void processUser(User user) {
    if (user != null) {
        if (user.isActive()) {
            if (user.hasPermission()) {
                // 真正的业务逻辑
                doSomething();
            } else {
                System.out.println("权限不足");
            }
        } else {
            System.out.println("用户未激活");
        }
    } else {
        System.out.println("用户不存在");
    }
}

// 好的写法
public void processUser(User user) {
    if (user == null) {
        System.out.println("用户不存在");
        return;
    }

    if (!user.isActive()) {
        System.out.println("用户未激活");
        return;
    }

    if (!user.hasPermission()) {
        System.out.println("权限不足");
        return;
    }

    // 真正的业务逻辑
    doSomething();
}

第二种写法主流程一目了然,异常情况先处理掉,这就是所谓的”卫语句”模式。

技巧2:用策略模式替代大量if-else

当if-else分支特别多时,考虑用设计模式优化:

// 不好的写法
public double calculatePrice(String memberType, double amount) {
    if ("普通会员".equals(memberType)) {
        return amount * 0.95;
    } else if ("白银会员".equals(memberType)) {
        return amount * 0.90;
    } else if ("黄金会员".equals(memberType)) {
        return amount * 0.85;
    } else if ("铂金会员".equals(memberType)) {
        return amount * 0.80;
    } else {
        return amount;
    }
}

// 用策略模式
interface DiscountStrategy {
    double calculate(double amount);
}

Map<String, DiscountStrategy> strategies = new HashMap<>();
strategies.put("普通会员", amount -> amount * 0.95);
strategies.put("白银会员", amount -> amount * 0.90);
strategies.put("黄金会员", amount -> amount * 0.85);
strategies.put("铂金会员", amount -> amount * 0.80);

public double calculatePrice(String memberType, double amount) {
    return strategies.getOrDefault(memberType, amount -> amount)
                    .calculate(amount);
}

这样扩展起来特别方便,加个新会员类型就是往Map里加一项,不用改原来的代码。

技巧3:把复杂条件提取成方法

// 不好的写法
if (user.getAge() >= 18 && user.hasLicense() && 
    !user.hasDrunkDriving() && user.getCarInsurance() != null) {
    allowDriving();
}

// 好的写法
if (canDrive(user)) {
    allowDriving();
}

private boolean canDrive(User user) {
    return user.getAge() >= 18 
        && user.hasLicense()
        && !user.hasDrunkDriving()
        && user.getCarInsurance() != null;
}

给条件起个有意义的方法名,代码可读性立马提升。而且以后修改判断逻辑也方便,只改一个地方。

常见坑点总结

坑1:浮点数的相等判断

double price = 0.1 + 0.2;

if (price == 0.3) {  // 错误!这个条件可能不成立
    System.out.println("价格正确");
}

// 正确的做法
if (Math.abs(price - 0.3) < 0.00001) {
    System.out.println("价格正确");
}

浮点数有精度问题,直接用==判断会出问题。这是计算机底层表示浮点数的方式决定的。

坑2:字符串比较用equals

String input = new String("hello");

if (input == "hello") {  // 错误!比较的是引用
    System.out.println("相等");
}

// 正确的做法
if ("hello".equals(input)) {  // 把常量放前面,避免空指针
    System.out.println("相等");
}

字符串比较要用equals方法,用==比较的是引用地址。而且把常量放前面是个好习惯,即使input是null也不会报空指针异常。

坑3:if里不要有赋值操作

int x = 10;

if (x = 5) {  // 编译错误!Java不允许这样写
    System.out.println("x等于5");
}

幸好Java会报编译错误,不像C语言那样会导致逻辑Bug。但还是要注意,判断相等用==,赋值用=

坑4:注意运算符优先级

boolean result = true;

if (!result == false) {  // 这个条件永远不成立!
    System.out.println("结果为假");
}

// 正确的写法
if (result == false) {  // 或者直接写 !result
    System.out.println("结果为假");
}

!的优先级高于==,所以!result == false实际上是(!result) == false,也就是false == false,结果是true。遇到复杂表达式,多加括号总没错。

写在最后

选择结构虽然是基础,但要写好还真不容易。我的建议是:

  1. 保持代码简洁:能用简单的if就别用复杂的嵌套
  2. 注重可读性:给条件起好名字,复杂逻辑提取成方法
  3. 避免重复:相似的判断逻辑考虑复用
  4. 灵活运用:根据场景选择if、switch还是三元运算符

编程没有银弹,很多时候要靠经验判断。多写、多思考、多重构,慢慢就能写出优雅的代码了。


评论留言

欢迎您,!您可以在这里畅言您的的观点与见解!

0 条评论