分类 JavaSE基础入门 下的文章


一、一维数组的核心定义与初始化

1.1 数组的本质

数组是Java中用于存储一批同类型数据的线性容器,核心特性如下:

  • 容器内所有元素的数据类型必须完全一致;
  • 数组在内存中占用连续的存储空间;
  • 元素通过索引(下标)访问,索引从0开始计数;
  • 数组长度一旦初始化,不可动态修改。

使用数组存储批量数据,相比独立变量具备显著优势:

  1. 代码结构简洁,避免定义大量冗余变量;
  2. 支持批量遍历与统一处理,大幅提升数据处理效率;
  3. 内存连续分配,数据访问性能稳定可控。

1.2 数组的两种初始化方式

Java数组提供静态初始化与动态初始化两种方式,分别适配不同的业务场景。

1.2.1 静态初始化

静态初始化适用于数组元素在定义时已完全明确的场景,定义时直接指定具体元素值,数组长度由元素个数自动确定。

标准语法:

// 简化格式(开发中主流写法)
数据类型[] 数组名 = {元素1, 元素2, 元素3, ...};

// 完整格式
数据类型[] 数组名 = new 数据类型[]{元素1, 元素2, 元素3, ...};

代码示例:

// 简化格式:存储3个int类型整数
int[] arr1 = {12, 24, 36};

// 完整格式:存储学生姓名列表
String[] names = new String[]{"张三", "李四", "王五", "赵六"};

注意事项:

  • 标准写法为数据类型[] 数组名,虽支持数据类型 数组名[]的写法,但易造成语义混淆;
  • 静态初始化时禁止在[]中指定数组长度,例如int[] arr = new int[3]{12,24,36};为非法语法,会编译报错。

1.2.2 动态初始化

动态初始化适用于数组元素在定义时未明确,需先确定容器长度,后续再赋值的场景,定义时仅指定数据类型与数组长度,元素采用Java默认值初始化。

标准语法:

数据类型[] 数组名 = new 数据类型[数组长度];

代码示例:

// 动态初始化长度为3的int数组,元素默认值为0
int[] arr = new int[3];

// 动态初始化长度为8的double数组,用于存储学生成绩,元素默认值为0.0
double[] scores = new double[8];

动态初始化的元素默认值规则,由数组的数据类型决定,具体如下表:

数据类型分类具体数据类型默认初始值
基本数据类型byte、short、char、int、long0
基本数据类型float、double0.0
基本数据类型booleanfalse
引用数据类型类、接口、数组、Stringnull

1.3 数组的元素访问与核心属性

数组元素通过数组名[索引] 的格式访问,支持元素的读取与修改;数组提供length属性,用于获取数组的元素个数(长度)。

核心语法:

// 1. 读取元素
元素类型 变量名 = 数组名[索引];

// 2. 修改元素
数组名[索引] = 新值;

// 3. 获取数组长度
int 长度变量 = 数组名.length;

代码示例:

public class ArrayAccessDemo {
    public static void main(String[] args) {
        int[] arr = {12, 24, 36};
        
        // 读取索引0的元素
        System.out.println(arr[0]); // 输出:12
        
        // 修改索引1的元素值
        arr[1] = 100;
        System.out.println(arr[1]); // 输出:100
        
        // 获取数组长度
        System.out.println(arr.length); // 输出:3
    }
}

注意事项:数组的合法索引范围为0 ~ 数组长度-1,若访问超出该范围的索引,会抛出ArrayIndexOutOfBoundsException(数组索引越界异常),是数组操作中最常见的运行时异常。


二、数组的核心遍历操作与经典场景实现

2.1 数组的遍历

数组遍历指依次访问数组中的每一个元素,是数组所有批量操作的基础,广泛应用于数据求和、最值查找、元素筛选、批量赋值等场景。

Java中最基础的遍历方式为for循环遍历,语法如下:

int[] arr = {20, 30, 40, 50};
// 遍历数组:i为索引,从0开始,到数组长度-1结束
for (int i = 0; i < arr.length; i++) {
    System.out.println(arr[i]);
}

2.2 经典场景1:数组元素求最值

业务需求:获取数组中元素的最大值、最小值,是业务开发中数据统计的基础能力。

实现思路:

  1. 定义变量存储当前最值,初始值赋值为数组第一个元素;
  2. 遍历数组,将每个元素与当前最值比较,若符合条件则更新最值变量;
  3. 遍历结束后,最值变量即为数组的最终结果。

代码实现:

public class ArrayMaxMinDemo {
    public static void main(String[] args) {
        int[] faceScores = {15, 9000, 10000, 20000, 9500, -5};
        
        // 初始化最值为数组第一个元素
        int max = faceScores[0];
        int min = faceScores[0];
        
        // 遍历数组更新最值
        for (int i = 1; i < faceScores.length; i++) {
            if (faceScores[i] > max) {
                max = faceScores[i];
            }
            if (faceScores[i] < min) {
                min = faceScores[i];
            }
        }
        
        // 输出结果
        System.out.println("数组最大值:" + max); // 输出:20000
        System.out.println("数组最小值:" + min); // 输出:-5
    }
}

2.3 经典场景2:学生成绩统计

业务需求:录入班级8名学生的Java课程成绩,计算并输出班级成绩的平均分、最高分、最低分。

代码实现:

import java.util.Scanner;

public class ScoreStatisticsDemo {
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        // 动态初始化数组,存储8名学生的成绩
        double[] scores = new double[8];
        
        // 循环录入学生成绩
        for (int i = 0; i < scores.length; i++) {
            System.out.println("请输入第" + (i+1) + "名学生的Java成绩:");
            scores[i] = scanner.nextDouble();
        }
        scanner.close();
        
        // 初始化统计变量
        double sum = 0.0;
        double max = scores[0];
        double min = scores[0];
        
        // 遍历数组完成统计
        for (double score : scores) {
            sum += score;
            if (score > max) max = score;
            if (score < min) min = score;
        }
        
        // 计算平均分
        double average = sum / scores.length;
        
        // 输出统计结果
        System.out.println("班级成绩最高分:" + max);
        System.out.println("班级成绩最低分:" + min);
        System.out.println("班级成绩总分:" + sum);
        System.out.println("班级成绩平均分:" + average);
    }
}

2.4 数组元素交换

数组元素交换是排序算法、数组乱序(洗牌)的基础操作,核心实现思路是通过临时变量中转两个元素的值,完成交换。

代码实现:

public class ArraySwapDemo {
    public static void main(String[] args) {
        int[] arr = {10, 20, 30, 40, 50};
        // 交换索引0和索引4的元素
        int temp = arr[0]; // 临时变量存储第一个元素的值
        arr[0] = arr[4];   // 将最后一个元素赋值给第一个位置
        arr[4] = temp;     // 将临时变量的值赋值给最后一个位置
        
        // 遍历输出交换后的数组:50 20 30 40 10
        for (int num : arr) {
            System.out.print(num + " ");
        }
    }
}

三、数组的工程化实践案例

3.1 案例1:随机点名系统

业务需求:开发班级随机点名程序,从学生名单中随机抽取一名学生,实现课堂随机点名场景。

代码实现:

import java.util.Random;

public class RandomCallNameDemo {
    public static void main(String[] args) {
        // 数组存储班级学生姓名
        String[] names = {
            "张誉", "刘疏桐", "田启峰", "伊成元", "赵志刚",
            "解天野", "高续升", "王世举", "周字伦", "刘帅",
            "陈小伟", "陈伟北", "高明", "毕振宇", "王乾麟"
        };
        
        // 生成随机索引:范围 0 ~ names.length-1
        Random random = new Random();
        int randomIndex = random.nextInt(names.length);
        // 获取随机抽取的学生姓名
        String selectedName = names[randomIndex];
        
        // 输出点名结果
        System.out.println(selectedName + "同学,出来回答问题!");
    }
}

3.2 案例2:斗地主游戏-卡牌初始化与洗牌

业务需求:开发简易版斗地主游戏的核心基础能力,完成54张卡牌的初始化(做牌)与洗牌功能。

实现思路:

  1. 定义两个数组,分别存储卡牌的花色与点数;
  2. 动态初始化长度为54的String数组,存储所有卡牌;
  3. 嵌套循环组合花色与点数,完成52张普通卡牌初始化,单独添加大小王;
  4. 洗牌功能:遍历数组,随机生成索引,交换当前元素与随机索引的元素,完成数组乱序。

代码实现:

import java.util.Random;

public class LandlordCardDemo {
    public static void main(String[] args) {
        // 1. 定义卡牌的花色与点数
        String[] colors = {"♠", "♥", "♣", "♦"};
        String[] numbers = {"3", "4", "5", "6", "7", "8", "9", "10", "J", "Q", "K", "A", "2"};
        
        // 2. 动态初始化数组,存储54张卡牌
        String[] cards = new String[54];
        int index = 0; // 卡牌数组的索引
        
        // 3. 组合花色与点数,初始化普通卡牌
        for (String number : numbers) {
            for (String color : colors) {
                cards[index] = color + number;
                index++;
            }
        }
        
        // 4. 添加大小王
        cards[index++] = "小王";
        cards[index] = "大王";
        
        // 输出初始化完成的卡牌
        System.out.println("初始化完成的卡牌:");
        for (String card : cards) {
            System.out.print(card + " ");
        }
        System.out.println("\n");
        
        // 5. 洗牌:数组乱序
        Random random = new Random();
        for (int i = 0; i < cards.length; i++) {
            int randomIndex = random.nextInt(cards.length);
            // 元素交换
            String temp = cards[i];
            cards[i] = cards[randomIndex];
            cards[randomIndex] = temp;
        }
        
        // 输出洗牌后的卡牌
        System.out.println("洗牌后的卡牌:");
        for (String card : cards) {
            System.out.print(card + " ");
        }
    }
}

四、二维数组的核心定义与实践

4.1 二维数组的本质

二维数组是数组的数组,即数组中的每一个元素,都是一个独立的一维数组。二维数组适用于存储具有行、列二维结构的业务数据,如班级座位信息、表格数据、矩阵运算、2D游戏地图等场景。

4.2 二维数组的初始化方式

4.2.1 静态初始化

静态初始化适用于二维数组的行、列数据在定义时已明确的场景,直接指定每一行的一维数组元素。

标准语法:

// 简化格式
数据类型[][] 数组名 = {{元素1,元素2...}, {元素1,元素2...}, ...};

// 完整格式
数据类型[][] 数组名 = new 数据类型[][]{{元素1,元素2...}, {元素1,元素2...}, ...};

代码示例:存储班级座位信息

// 二维数组存储班级座位,每一行对应一排座位
String[][] seats = {
    {"张无忌", "赵敏", "周芷若"},
    {"张三丰", "宋远桥", "殷梨亭"},
    {"灭绝", "陈昆", "玄冥二老", "金毛狮王"},
    {"杨逍", "纪晓芙"}
};

4.2.2 动态初始化

动态初始化分为两种模式,适配不同的业务场景:

  1. 固定列数初始化:每一行的列数完全一致,适用于规则的表格、矩阵场景

    // 3行5列的二维数组
    数据类型[][] 数组名 = new 数据类型[行数][列数];
  2. 不规则列数初始化:先指定行数,后续为每一行单独定义列数,适用于行列数不统一的不规则场景

    // 先指定4行,列数后续定义
    数据类型[][] 数组名 = new 数据类型[行数][];
    // 为每一行初始化一维数组
    数组名[0] = new 数据类型[3];
    数组名[1] = new 数据类型[4];

代码示例:

// 固定列数:3行5列的int二维数组,初始默认值为0
int[][] arr1 = new int[3][5];

// 不规则列数:4行的二维数组,每行列数不同
String[][] seats = new String[4][];
seats[0] = new String[3]; // 第一排3个座位
seats[1] = new String[3]; // 第二排3个座位
seats[2] = new String[4]; // 第三排4个座位
seats[3] = new String[2]; // 第四排2个座位

4.3 二维数组的元素访问

二维数组的元素通过行索引列索引进行访问,语法如下:

// 访问某一行的一维数组:数组名[行索引]
// 访问具体元素:数组名[行索引][列索引]
// 修改元素值:数组名[行索引][列索引] = 新值;

代码示例:

public class TwoDArrayAccessDemo {
    public static void main(String[] args) {
        int[][] arr = new int[][]{{12, 24, 36}, {666, 888}, {10, 20, 30}, {999}};
        
        // 访问第3行(索引2)的一维数组
        int[] row = arr[2];
        // 遍历输出该行元素:10 20 30
        for (int num : row) {
            System.out.print(num + " ");
        }
        System.out.println();
        
        // 访问第3行第2列(索引2、1)的元素
        System.out.println(arr[2][1]); // 输出:20
        
        // 修改第4行第1列(索引3、0)的元素
        arr[3][0] = 111;
        System.out.println(arr[3][0]); // 输出:111
    }
}

4.4 二维数组的遍历

二维数组的遍历需要使用嵌套循环,外层循环遍历每一行,内层循环遍历当前行的每一列元素。

标准语法:

int[][] arr = {{12, 24, 36}, {666, 888}, {10, 20, 30}, {999}};
// 外层循环:遍历每一行
for (int i = 0; i < arr.length; i++) {
    // 内层循环:遍历当前行的每一列
    for (int j = 0; j < arr[i].length; j++) {
        System.out.print(arr[i][j] + "\t");
    }
    // 每行遍历完成后换行
    System.out.println();
}

4.5 实践案例:班级座位信息管理

业务需求:存储并输出班级的座位信息,要求输出时直观展示学生所在的排数与座位号。

代码实现:

public class SeatManagerDemo {
    public static void main(String[] args) {
        // 二维数组存储班级座位信息
        String[][] seats = {
            {"张无忌", "赵敏", "周芷若"},
            {"张三丰", "宋远桥", "殷梨亭"},
            {"灭绝", "陈昆", "玄冥二老", "金毛狮王"},
            {"杨逍", "纪晓芙"}
        };
        
        // 遍历输出座位信息
        System.out.println("===== 班级座位信息 =====");
        for (int i = 0; i < seats.length; i++) {
            System.out.print("第" + (i+1) + "排:");
            for (int j = 0; j < seats[i].length; j++) {
                System.out.print(seats[i][j] + "  ");
            }
            System.out.println();
        }
    }
}

执行结果:

===== 班级座位信息 =====
第1排:张无忌  赵敏  周芷若  
第2排:张三丰  宋远桥  殷梨亭  
第3排:灭绝  陈昆  玄冥二老  金毛狮王  
第4排:杨逍  纪晓芙  

4.6 拓展场景:2D游戏地图初始化

二维数组广泛应用于2D游戏的地图数据存储,例如石头迷阵、推箱子、贪吃蛇等游戏,均可通过二维数组存储地图格子数据,实现地图的初始化与渲染。

示例代码:石头迷阵地图初始化

public class StoneMazeDemo {
    public static void main(String[] args) {
        // 4行4列的二维数组,存储16格石头迷阵的数字
        int[][] maze = new int[4][4];
        int num = 1;
        // 初始化地图数字 1-15,最后一格为0(代表空位)
        for (int i = 0; i < maze.length; i++) {
            for (int j = 0; j < maze[i].length; j++) {
                maze[i][j] = num <= 15 ? num++ : 0;
            }
        }
        
        // 输出初始化的迷阵地图
        System.out.println("===== 石头迷阵初始地图 =====");
        for (int[] row : maze) {
            for (int data : row) {
                // 空位显示为空格,数字格式化输出
                System.out.print(data == 0 ? "  \t" : data + "\t");
            }
            System.out.println();
        }
    }
}


一、类与对象的核心定义

1.1 对象的本质

在Java中,对象是一种结构化的数据载体,本质是用于描述一个具体事物的专属数据结构,它可以完整存储一个事物的相关属性数据,并承载该事物对应的行为能力。

在实际开发中,无论是现实世界中的人物、商品,还是业务系统中的用户、订单、电影,都可以被抽象为对象,通过对象来统一管理其相关数据与行为。

1.2 类与对象的关系

类是对象的模板(也称为设计图),它定义了一类事物所具备的通用属性与行为;而对象是类的实例化产物,通过类可以创建出多个具备相同特征的独立对象。

简单来说,类是对一类事物的抽象描述,对象是该类事物的具体实例。例如:“明星”是一个类,而赵丽颖、杨幂就是该类的具体对象;“学生”是一个类,而播妞、播仔就是该类的具体对象。

1.3 类的基础语法

一个标准的类主要由两部分组成:

  • 成员变量(属性):用于描述事物的特征数据;
  • 成员方法(行为):用于描述事物的行为能力,即对数据的处理逻辑。

类的基础定义语法如下:

/**
 * 明星类(模板)
 * 定义了明星这类事物的通用属性
 */
public class Star {
    // 成员变量(属性)
    String name;    // 姓名
    int age;        // 年龄
    double height;  // 身高
    double weight;  // 体重
}

1.4 对象的创建与使用

在Java中,通过new关键字可以实例化类,得到一个具体的对象。每执行一次new操作,都会在内存中创建一个全新的独立对象。

对象创建完成后,可以通过对象名.属性名的方式为属性赋值,也可以通过对象名.方法名()的方式调用对象的行为方法。

public class StarTest {
    public static void main(String[] args) {
        // 实例化Star类,创建第一个明星对象
        Star s1 = new Star();
        // 为对象属性赋值
        s1.name = "赵丽颖";
        s1.age = 36;
        s1.height = 165.0;
        s1.weight = 44.6;

        // 实例化Star类,创建第二个明星对象
        Star s2 = new Star();
        s2.name = "杨幂";
        s2.age = 37;
        s2.height = 166.5;
        s2.weight = 45.0;
    }
}

1.5 带行为方法的类定义

类中不仅可以定义属性,还可以定义处理属性数据的行为方法。以学生类为例,我们可以在类中定义计算总成绩、平均成绩的方法,实现“谁的数据谁处理”的面向对象核心思想。

/**
 * 学生类
 * 包含学生属性与成绩处理的行为方法
 */
public class Student {
    // 成员变量(属性)
    String name;        // 学生姓名
    double chinese;     // 语文成绩
    double math;        // 数学成绩

    // 成员方法(行为):打印总成绩
    public void printTotalScore() {
        System.out.println(name + "同学的各科总分是:" + (chinese + math));
    }

    // 成员方法(行为):打印平均成绩
    public void printAverageScore() {
        System.out.println(name + "同学的各科平均分是:" + (chinese + math) / 2.0);
    }
}

方法的调用示例:

public class StudentTest {
    public static void main(String[] args) {
        // 创建学生对象并赋值
        Student s1 = new Student();
        s1.name = "播妞";
        s1.chinese = 100;
        s1.math = 100;
        // 调用对象的行为方法
        s1.printTotalScore();
        s1.printAverageScore();

        Student s2 = new Student();
        s2.name = "播仔";
        s2.chinese = 59;
        s2.math = 100;
        s2.printTotalScore();
        s2.printAverageScore();
    }
}

执行结果:

播妞同学的各科总分是:200.0
播妞同学的各科平均分是:100.0
播仔同学的各科总分是:159.0
播仔同学的各科平均分是:79.5

1.6 对象的内存模型

Java程序运行时,对象的内存分配主要涉及三块内存区域:

  1. 栈内存:存储对象的引用变量(即对象的内存地址),方法执行时的局部变量也存放在栈中;
  2. 堆内存:存储new关键字创建的对象本身,包括对象的所有成员变量;
  3. 方法区:存储类的字节码文件(.class),包括类的成员变量定义、方法定义等信息。

当执行Student s1 = new Student();时,会在堆内存中创建Student对象的实例,在栈内存中创建引用变量s1s1中存储的是堆内存中对象的内存地址,通过该地址实现对对象属性和方法的访问。


二、构造器

2.1 构造器的定义与作用

构造器(也称为构造方法)是类中一种特殊的方法,它在使用new关键字创建对象时会被自动调用,核心作用是完成对象的初始化操作,最常见的场景是在创建对象的同时为成员变量赋值。

2.2 构造器的语法规范

构造器的定义需要遵循以下语法规则:

  • 构造器的名称必须与类名完全一致;
  • 构造器没有返回值类型,连void都不能写;
  • 构造器可以定义参数,也可以无参数。

基础语法如下:

public class Student {
    String name;
    int age;

    // 无参构造器
    public Student() {
        // 初始化逻辑
    }

    // 有参构造器
    public Student(String name, int age) {
        this.name = name;
        this.age = age;
    }
}

2.3 构造器的使用注意事项

  1. 默认无参构造器:任何一个类在定义时,即使不手动编写构造器,Java编译器都会自动为该类生成一个默认的无参构造器;
  2. 构造器的覆盖规则:如果手动为类定义了有参构造器,编译器将不再生成默认的无参构造器。此时如果需要使用无参构造器,必须手动显式定义;
  3. 构造器的重载:一个类中可以定义多个参数列表不同的构造器,即构造器的重载,以满足不同的初始化需求。

构造器的使用示例:

public class StudentTest {
    public static void main(String[] args) {
        // 调用无参构造器创建对象
        Student s1 = new Student();
        s1.name = "播妞";
        s1.age = 18;

        // 调用有参构造器创建对象,创建时直接完成赋值
        Student s2 = new Student("播仔", 19);
    }
}

三、this关键字

3.1 this的本质

this是Java中的一个关键字,本质是当前对象的引用。在成员方法中,this指向调用该方法的当前对象;在构造器中,this指向正在创建的对象。简单来说,哪个对象调用方法,this就指向哪个对象

3.2 this的核心应用场景

this最主要的作用是解决成员变量与方法内局部变量的命名冲突问题。当方法内的局部变量与类的成员变量名称相同时,Java会默认采用就近原则访问局部变量,此时需要通过this.变量名的方式明确指定访问类的成员变量。

示例代码:

public class Student {
    String name;
    double score;

    // 带参数的方法,参数名与成员变量名冲突
    public void checkPass(double score) {
        // this.score 访问成员变量,score 访问方法入参的局部变量
        if (this.score >= score) {
            System.out.println("成绩达标");
        } else {
            System.out.println("成绩未达标");
        }
    }

    // 有参构造器中使用this
    public Student(String name, double score) {
        this.name = name;
        this.score = score;
    }
}

四、封装特性

4.1 封装的核心定义

封装是面向对象编程的三大核心特性之一(封装、继承、多态)。其核心思想是:在设计类时,将事物的属性数据与处理数据的行为方法封装到同一个类中,对外部隐藏内部的实现细节,仅暴露必要的访问与操作入口。

4.2 封装的设计原则

封装的核心设计原则是合理隐藏、合理暴露

  • 合理隐藏:对类内部的属性数据进行隐藏,避免外部直接随意修改,保证数据的安全性;
  • 合理暴露:对外提供统一的、受控的访问与操作入口,通过方法实现对数据的校验与处理。

在Java代码层面,通过访问修饰符实现隐藏与暴露的控制:

  • private(私有):被修饰的成员只能在当前类内部访问,实现成员的隐藏;
  • public(公开):被修饰的成员可以在任意类中访问,实现成员的暴露。

4.3 封装的标准实现

规范的封装实现,通常会将类的成员变量用private修饰进行隐藏,然后为每个成员变量提供public修饰的getter(获取值)和setter(设置值)方法,实现对属性的受控访问。

示例代码:

public class Student {
    // 私有成员变量,外部无法直接访问
    private String name;
    private double chinese;
    private double math;

    // 公开的getter/setter方法
    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public double getChinese() {
        return chinese;
    }

    public void setChinese(double chinese) {
        // 可以在setter方法中添加数据校验逻辑
        if (chinese >= 0 && chinese <= 100) {
            this.chinese = chinese;
        } else {
            System.out.println("语文成绩输入不合法,必须在0-100之间");
        }
    }

    public double getMath() {
        return math;
    }

    public void setMath(double math) {
        if (math >= 0 && math <= 100) {
            this.math = math;
        } else {
            System.out.println("数学成绩输入不合法,必须在0-100之间");
        }
    }

    // 成绩处理方法
    public void printTotalScore() {
        System.out.println(name + "同学的各科总分是:" + (chinese + math));
    }
}

通过封装,我们可以在setter方法中添加数据校验逻辑,避免非法数据的写入,保证了对象数据的安全性与完整性,这也是封装的核心价值之一。


五、JavaBean(实体类)规范

5.1 实体类的定义

JavaBean也称为实体类,是Java开发中一种符合特定规范的特殊类,其核心作用是对业务数据进行封装,仅负责数据的存取,是实现数据与业务逻辑分离的基础。

5.2 实体类的规范要求

一个标准的JavaBean实体类,必须满足以下两个核心要求:

  1. 类中的所有成员变量必须用private修饰私有化,并为每个成员变量提供对应的public修饰的gettersetter方法;
  2. 类中必须提供一个无参构造器,有参构造器为可选。

5.3 实体类的设计思想

实体类的设计遵循数据与业务处理相分离的原则:实体类的对象仅负责数据的存储与读取,而对数据的业务处理逻辑,交由专门的业务处理类来实现。这种设计方式可以让代码职责更加清晰,提升代码的可维护性与可扩展性。

标准实体类示例:

/**
 * 用户实体类,仅负责用户数据的存取
 */
public class User {
    // 私有成员变量
    private Long id;
    private String username;
    private Integer age;

    // 必须的无参构造器
    public User() {
    }

    // 可选的有参构造器
    public User(Long id, String username, Integer age) {
        this.id = id;
        this.username = username;
        this.age = age;
    }

    // 所有成员变量的getter/setter方法
    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    public Integer getAge() {
        return age;
    }

    public void setAge(Integer age) {
        this.age = age;
    }
}

业务处理类示例:

/**
 * 用户业务处理类,负责用户相关的业务逻辑
 */
public class UserOperator {
    private User user;

    public UserOperator(User user) {
        this.user = user;
    }

    // 业务方法:判断用户是否成年
    public boolean isAdult() {
        return user.getAge() >= 18;
    }
}

六、static关键字

static关键字意为静态,可以用于修饰类的成员变量、成员方法,被修饰的成员称为静态成员,属于类本身,而非类的实例对象。

6.1 静态变量与实例变量

成员变量根据是否被static修饰,分为静态变量(类变量)和实例变量两类,二者的核心区别如下表:

特性静态变量(类变量)实例变量(对象变量)
修饰符static修饰static修饰
所属主体属于类本身属于每个实例对象
内存存储内存中仅存在一份,与类一起加载每个对象都有独立的一份,随对象创建
访问方式推荐:类名.变量名;也可通过对象访问仅能通过对象名.变量名访问
共享特性被该类的所有实例对象共享每个对象独有,互不影响

6.1.1 静态变量的使用示例

public class User {
    // 静态变量:统计创建的用户对象数量,属于类,所有对象共享
    public static int number;
    // 实例变量:每个用户的用户名,属于每个对象
    private String username;

    public User() {
        // 每创建一个对象,静态变量number自增1
        User.number++;
    }

    // getter/setter省略
}
public class UserTest {
    public static void main(String[] args) {
        // 类名直接访问静态变量,初始值为0
        System.out.println(User.number);

        // 创建3个用户对象
        new User();
        new User();
        new User();

        // 静态变量值变为3,被所有对象共享
        System.out.println(User.number);
    }
}

执行结果:

0
3

6.1.2 静态变量的应用场景

当某个数据只需要一份,且需要被该类的所有实例对象共享访问、修改时,该数据就适合定义为静态变量。例如:系统配置项、计数器、全局常量等。

6.2 静态方法与实例方法

成员方法根据是否被static修饰,分为静态方法(类方法)和实例方法两类,二者的核心区别如下:

特性静态方法(类方法)实例方法
修饰符static修饰static修饰
所属主体属于类本身属于每个实例对象
访问方式推荐:类名.方法名();也可通过对象调用仅能通过对象名.方法名()调用
访问限制只能直接访问静态成员,不能直接访问实例成员和this关键字可以直接访问静态成员和实例成员,可使用this

6.2.1 静态方法的典型应用:工具类设计

静态方法最核心的应用场景是设计工具类。工具类是一种仅提供通用功能方法的类,其中的方法都是静态方法,每个方法完成一个通用的功能,供开发者直接调用,无需创建对象。

使用静态方法设计工具类的优势:

  1. 调用方便,直接通过类名即可调用方法,无需实例化对象;
  2. 节省内存,避免了创建大量仅用于调用方法的对象,减少了堆内存的开销。

工具类的设计规范:

  1. 工具类中的所有方法都定义为静态方法;
  2. 将工具类的构造器私有化,避免外部创建该类的实例对象。

标准工具类示例:

/**
 * 字符串处理工具类
 */
public class StringUtils {
    // 私有化构造器,禁止外部实例化
    private StringUtils() {
    }

    /**
     * 判断字符串是否为空
     * @param str 待判断的字符串
     * @return 为空返回true,否则返回false
     */
    public static boolean isEmpty(String str) {
        return str == null || str.trim().length() == 0;
    }

    /**
     * 判断字符串是否为邮箱格式
     * @param email 待判断的邮箱字符串
     * @return 符合邮箱格式返回true,否则返回false
     */
    public static boolean isEmail(String email) {
        if (isEmpty(email)) {
            return false;
        }
        String emailRegex = "^[a-zA-Z0-9_-]+@[a-zA-Z0-9_-]+(\\.[a-zA-Z0-9_-]+)+$";
        return email.matches(emailRegex);
    }
}

工具类的使用示例:

public class UtilsTest {
    public static void main(String[] args) {
        // 直接通过类名调用静态方法
        System.out.println(StringUtils.isEmpty(""));
        System.out.println(StringUtils.isEmail("test@163.com"));
    }
}

6.3 static关键字的使用注意事项

  1. 静态方法中可以直接访问类中的静态成员,不可以直接访问实例成员
  2. 实例方法中既可以直接访问静态成员,也可以直接访问实例成员;
  3. 实例方法中可以使用this关键字,静态方法中不可以使用this关键字
  4. 在本类中访问静态成员时,可以省略类名不写;访问其他类的静态成员时,必须携带类名访问。

七、综合实践:电影信息展示系统

7.1 需求说明

  1. 展示系统中的全部电影基础信息(电影名称、票价);
  2. 支持用户根据电影编号(ID)查询电影的详细信息。

7.2 实现思路

  1. 设计电影实体类Movie,封装电影的相关属性,符合JavaBean规范;
  2. 设计电影业务处理类MovieSystem,实现电影列表展示、根据ID查询电影详情的业务逻辑;
  3. 编写测试主类,完成功能的测试验证。

7.3 代码实现

7.3.1 电影实体类

/**
 * 电影实体类,封装电影数据
 */
public class Movie {
    // 电影属性
    private int id;             // 电影编号
    private String name;        // 电影名称
    private double price;       // 电影票价
    private double score;       // 电影评分
    private String director;    // 导演
    private String actor;       // 主演
    private String description; // 电影简介

    // 无参构造器
    public Movie() {
    }

    // 有参构造器
    public Movie(int id, String name, double price, double score, String director, String actor, String description) {
        this.id = id;
        this.name = name;
        this.price = price;
        this.score = score;
        this.director = director;
        this.actor = actor;
        this.description = description;
    }

    // getter/setter方法
    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public double getPrice() {
        return price;
    }

    public void setPrice(double price) {
        this.price = price;
    }

    public double getScore() {
        return score;
    }

    public void setScore(double score) {
        this.score = score;
    }

    public String getDirector() {
        return director;
    }

    public void setDirector(String director) {
        this.director = director;
    }

    public String getActor() {
        return actor;
    }

    public void setActor(String actor) {
        this.actor = actor;
    }

    public String getDescription() {
        return description;
    }

    public void setDescription(String description) {
        this.description = description;
    }
}

7.3.2 电影系统业务处理类

/**
 * 电影信息系统业务处理类
 */
public class MovieSystem {
    // 存储电影数据的数组
    private Movie[] movies;

    // 初始化电影数据
    public MovieSystem() {
        movies = new Movie[4];
        movies[0] = new Movie(1, "长津湖之水门桥", 45.0, 9.6, "徐克", "吴京、易烊千玺", "抗美援朝战争题材电影");
        movies[1] = new Movie(2, "一点就到家", 38.0, 8.7, "许宏宇", "刘昊然、彭昱畅", "农村电商创业题材电影");
        movies[2] = new Movie(3, "月球陨落", 52.0, 7.9, "罗兰·艾默里奇", "哈莉·贝瑞", "科幻灾难题材电影");
        movies[3] = new Movie(4, "出拳吧,妈妈", 35.0, 7.9, "唐晓白", "谭卓、田雨", "女性励志题材电影");
    }

    /**
     * 展示全部电影的基础信息
     */
    public void showAllMovies() {
        System.out.println("===== 系统全部电影信息 =====");
        for (Movie movie : movies) {
            System.out.println("电影编号:" + movie.getId() +
                    " | 电影名称:" + movie.getName() +
                    " | 电影票价:" + movie.getPrice() + "元");
        }
        System.out.println("============================");
    }

    /**
     * 根据电影编号查询电影详情
     * @param id 电影编号
     * @return 查到的电影对象,未查到返回null
     */
    public Movie queryMovieById(int id) {
        for (Movie movie : movies) {
            if (movie.getId() == id) {
                return movie;
            }
        }
        return null;
    }
}

7.3.3 测试主类

public class MovieTest {
    public static void main(String[] args) {
        // 初始化电影系统
        MovieSystem movieSystem = new MovieSystem();

        // 1. 展示全部电影基础信息
        movieSystem.showAllMovies();

        // 2. 根据ID查询电影详情
        int queryId = 2;
        Movie movie = movieSystem.queryMovieById(queryId);
        if (movie != null) {
            System.out.println("\n===== 电影详情信息 =====");
            System.out.println("电影编号:" + movie.getId());
            System.out.println("电影名称:" + movie.getName());
            System.out.println("电影评分:" + movie.getScore() + "分");
            System.out.println("电影票价:" + movie.getPrice() + "元");
            System.out.println("导演:" + movie.getDirector());
            System.out.println("主演:" + movie.getActor());
            System.out.println("电影简介:" + movie.getDescription());
            System.out.println("=========================");
        } else {
            System.out.println("未查询到编号为" + queryId + "的电影");
        }
    }
}

7.4 执行结果

===== 系统全部电影信息 =====
电影编号:1 | 电影名称:长津湖之水门桥 | 电影票价:45.0元
电影编号:2 | 电影名称:一点就到家 | 电影票价:38.0元
电影编号:3 | 电影名称:月球陨落 | 电影票价:52.0元
电影编号:4 | 电影名称:出拳吧,妈妈 | 电影票价:35.0元
============================

===== 电影详情信息 =====
电影编号:2
电影名称:一点就到家
电影评分:8.7分
电影票价:38.0元
导演:许宏宇
主演:刘昊然、彭昱畅
电影简介:农村电商创业题材电影
=========================

一、对象与类的核心认知

1.1 对象的本质

对象是一种特殊的数据结构,用于承载一个事物的完整数据,从而在程序中对该事物进行具象化表达。

相较于变量、数组等基础数据存储结构,对象具备更强的结构化能力:可将一个事物的多维度属性与行为进行整合,实现数据与处理逻辑的一体化管理。例如对于影视演员这一事物,可通过对象承载其姓名、年龄、身高、体重等多维度属性;对于学生这一事物,可通过对象承载姓名、各科成绩等数据,同时内置成绩计算的相关行为。

1.2 类的核心定位

类也被称为对象的设计图或模板,是创建对象的基础。在Java中,必须先定义类,才能通过类创建对应的对象;每一次通过new关键字实例化类,都会得到一个全新的、独立的对象。

类的核心组成分为两部分:

  • 成员变量(属性):用来说明对象可存储的数据,描述事物的静态特征;
  • 成员方法(行为):描述对象具备的功能,即对数据可执行的处理操作。

二、类的基础语法与对象实例化

2.1 类的基础语法结构

/**
 * 学生类:作为创建学生对象的模板
 */
public class Student {
    // 成员变量:学生的属性
    String name;        // 姓名
    double chinese;     // 语文成绩
    double math;        // 数学成绩

    // 成员方法:计算并打印总成绩
    public void printTotalScore() {
        System.out.println(name + "同学的各科总分是:" + (chinese + math));
    }

    // 成员方法:计算并打印平均成绩
    public void printAverageScore() {
        System.out.println(name + "同学的各科平均分是:" + (chinese + math) / 2.0);
    }
}

2.2 对象的实例化与使用

通过new关键字完成类的实例化,得到对象后,可通过对象名.属性名访问成员变量,通过对象名.方法名()调用成员方法。

/**
 * 测试类:完成学生对象的创建与功能验证
 */
public class StudentTest {
    public static void main(String[] args) {
        // 实例化第一个学生对象并赋值
        Student s1 = new Student();
        s1.name = "播妞";
        s1.chinese = 100;
        s1.math = 100;
        // 调用对象的方法
        s1.printTotalScore();
        s1.printAverageScore();

        // 实例化第二个学生对象并赋值
        Student s2 = new Student();
        s2.name = "播仔";
        s2.chinese = 59;
        s2.math = 100;
        // 调用对象的方法
        s2.printTotalScore();
        s2.printAverageScore();
    }
}

程序执行结果:

播妞同学的各科总分是:200.0
播妞同学的各科平均分是:100.0
播仔同学的各科总分是:159.0
播仔同学的各科平均分是:79.5

2.3 对象的内存模型

Java程序运行时,对象的相关数据会分布在三个核心内存区域:

  1. 栈内存:存储对象的引用(地址),方法的局部变量,方法执行完毕后会自动释放;
  2. 堆内存:存储实例化对象的实际数据(成员变量),每一个new出来的对象都会在堆内存中分配独立的空间;
  3. 方法区:存储类的字节码信息,包括类的成员变量、成员方法定义,类加载时仅会加载一次。

三、构造器的定义与应用

3.1 构造器的核心作用

构造器是类的特殊成员方法,在对象通过new关键字创建时,会被自动调用,核心应用场景为完成对象成员变量的初始化赋值

3.2 构造器的语法规范

构造器的名称必须与类名完全一致,且无返回值类型声明(无需写void),基础语法如下:

public class Student {
    String name;
    int age;

    // 无参构造器
    public Student() {
    }

    // 有参构造器
    public Student(String name, int age) {
        this.name = name;
        this.age = age;
    }
}

3.3 构造器的使用规则

  1. 类在定义时,会默认自带一个无参构造器,无需手动声明;
  2. 若为类显式定义了有参构造器,默认的无参构造器会自动失效,若需使用无参构造器,必须手动显式定义;
  3. 一个类可定义多个重载的构造器,根据参数列表的不同完成不同的初始化逻辑。

3.4 构造器的实战使用

public class ConstructorTest {
    public static void main(String[] args) {
        // 调用无参构造器创建对象,后续通过set方法赋值
        Student s1 = new Student();
        s1.name = "张三";
        s1.age = 20;

        // 调用有参构造器创建对象,同时完成初始化赋值
        Student s2 = new Student("李四", 22);
    }
}

四、this关键字的核心作用

4.1 this的本质

this是一个引用类型变量,存在于类的成员方法与构造器中,指向当前调用该方法的对象,即“哪个对象调用方法,this就指向哪个对象”。

4.2 核心应用场景

this的核心作用是解决成员变量与方法内局部变量的命名冲突问题。当方法内的局部变量与类的成员变量名称相同时,通过this.变量名的方式,显式指定访问的是类的成员变量。

public class Student {
    // 成员变量
    String name;
    double score;

    // 带参方法,局部变量与成员变量重名
    public void checkPass(double score) {
        // this.score 代表当前对象的成员变量score
        // score 代表方法传入的局部变量score
        if(this.score >= score) {
            System.out.println("成绩达标");
        } else {
            System.out.println("成绩未达标");
        }
    }
}

除此之外,this也可用于在构造器中调用本类的其他重载构造器,简化初始化代码。

五、封装特性的设计与实现

5.1 封装的核心定义

封装是面向对象编程的三大核心特征(封装、继承、多态)之一,其核心设计思想是:将事物的数据与处理数据的方法设计在同一个类的对象中,遵循合理隐藏、合理暴露的设计规范。

5.2 封装的代码实现

通过Java的访问权限修饰符,控制类成员的可见性,实现封装的设计规范:

  • 隐藏成员:使用private(私有)修饰符修饰成员变量,仅当前类内部可访问,外部类无法直接操作;
  • 暴露成员:使用public(公开)修饰符修饰成员方法,对外提供统一的访问与操作入口。

5.3 封装的设计优势

  1. 提升数据安全性,避免外部类直接修改成员变量,可在暴露的方法中增加数据校验逻辑;
  2. 规范数据的访问方式,降低代码耦合度;
  3. 隐藏代码的实现细节,仅对外暴露必要的功能入口。

封装后的标准类示例:

public class Student {
    // 私有成员变量:对外隐藏
    private String name;
    private double chinese;
    private double math;

    // 公开的get/set方法:对外暴露,提供访问入口
    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public double getChinese() {
        return chinese;
    }

    public void setChinese(double chinese) {
        // 增加数据校验,确保成绩合法
        if(chinese >= 0 && chinese <= 100) {
            this.chinese = chinese;
        } else {
            System.out.println("语文成绩输入不合法,需在0-100之间");
        }
    }

    public double getMath() {
        return math;
    }

    public void setMath(double math) {
        if(math >= 0 && math <= 100) {
            this.math = math;
        } else {
            System.out.println("数学成绩输入不合法,需在0-100之间");
        }
    }

    // 公开的业务方法:对外暴露功能
    public void printTotalScore() {
        System.out.println(name + "同学的各科总分是:" + (chinese + math));
    }
}

六、JavaBean(实体类)规范与应用

6.1 JavaBean的核心规范

JavaBean是Java开发中约定俗成的实体类规范,是一种仅负责数据存取的特殊类,核心作用是实现数据与业务处理逻辑的分离,是MVC、分层开发架构的基础。

标准JavaBean需满足以下3个核心要求:

  1. 类中的所有成员变量必须使用private修饰实现私有化;
  2. 为每一个私有化的成员变量,提供public修饰的getter(取值)与setter(赋值)方法;
  3. 必须提供一个public修饰的无参构造器,有参构造器为可选定义。

6.2 标准JavaBean示例

/**
 * 教师实体类:标准JavaBean规范实现
 */
public class Teacher {
    // 私有成员变量
    private String name;
    private String workId;

    // 无参构造器(必须)
    public Teacher() {
    }

    // 有参构造器(可选)
    public Teacher(String name, String workId) {
        this.name = name;
        this.workId = workId;
    }

    // getter与setter方法
    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getWorkId() {
        return workId;
    }

    public void setWorkId(String workId) {
        this.workId = workId;
    }

    // 成员方法
    public void teach() {
        System.out.println("工号为" + workId + "的" + name + "老师正在讲课");
    }
}

6.3 JavaBean的使用方式

public class TeacherTest {
    public static void main(String[] args) {
        // 方式1:无参构造器创建对象 + setter方法赋值
        Teacher t1 = new Teacher();
        t1.setName("杨老师");
        t1.setWorkId("t001");
        t1.teach();

        // 方式2:有参构造器创建对象,直接完成赋值
        Teacher t2 = new Teacher("李老师", "t002");
        t2.teach();
    }
}

6.4 应用场景

JavaBean实体类的对象仅负责数据的承载与存取,数据的业务处理逻辑由专门的业务类实现,从而实现数据与业务的解耦,提升代码的可维护性与可扩展性。

七、static关键字与静态成员

static意为静态,可用于修饰类的成员变量与成员方法,被修饰的成员属于类本身,而非类的实例对象,与类一起加载,无需创建对象即可访问。

7.1 静态成员变量(类变量)

7.1.1 核心定义与特性

成员变量根据是否被static修饰,分为两类:

类型定义核心特性访问方式
静态变量(类变量)static修饰的成员变量与类一起加载,内存中仅存在一份,被该类的所有实例对象共享推荐:类名.静态变量名
不推荐:对象.静态变量名
实例变量(对象变量)未被static修饰的成员变量属于每个独立的对象,每个对象都有一份独立副本,互不影响仅支持:对象.实例变量名

7.1.2 应用场景与代码示例

当某个数据仅需一份,且需要被所有对象共享访问、修改时,可定义为静态变量。典型场景:统计类的实例化对象总数。

public class User {
    // 静态变量:统计对象创建数量,被所有对象共享
    public static int userCount = 0;

    // 无参构造器:每创建一个对象,计数+1
    public User() {
        User.userCount++;
    }
}
public class StaticFieldTest {
    public static void main(String[] args) {
        // 类名直接访问静态变量
        System.out.println(User.userCount); // 输出:0

        // 创建3个对象
        new User();
        new User();
        new User();

        // 静态变量已被所有对象共享修改
        System.out.println(User.userCount); // 输出:3
    }
}

7.2 静态成员方法(类方法)

7.2.1 核心定义与特性

成员方法根据是否被static修饰,分为两类:

类型定义核心特性访问方式
静态方法(类方法)static修饰的成员方法属于类本身,无需创建对象即可调用推荐:类名.静态方法名
不推荐:对象.静态方法名
实例方法(对象方法)未被static修饰的成员方法属于对象,必须创建实例对象后才能调用仅支持:对象.实例方法名

7.2.2 核心应用场景:工具类设计

静态方法的核心应用场景是开发工具类。工具类是一种通用功能类,其中的方法均为静态方法,每个方法完成一个通用功能,可直接被开发者调用,无需创建工具类的对象。

工具类设计的核心优势:

  1. 提高代码复用性,通用功能可全局调用;
  2. 调用便捷,无需实例化对象,直接通过类名调用;
  3. 节省内存,避免创建大量仅用于调用方法的对象。

7.2.3 工具类设计规范与示例

工具类无需创建对象,建议将构造器私有化,避免被外部类实例化。

/**
 * 字符串工具类:静态方法实现通用功能
 */
public class StringUtil {
    // 私有化构造器:禁止外部实例化
    private StringUtil() {
    }

    // 静态方法:判断字符串是否为空
    public static boolean isEmpty(String str) {
        return str == null || str.trim().length() == 0;
    }

    // 静态方法:判断邮箱格式是否合法
    public static boolean isEmail(String email) {
        if(isEmpty(email)) {
            return false;
        }
        return email.matches("^[a-zA-Z0-9_-]+@[a-zA-Z0-9_-]+(\\.[a-zA-Z0-9_-]+)+$");
    }
}

工具类的使用:

public class UtilTest {
    public static void main(String[] args) {
        // 直接通过类名调用静态方法,无需创建对象
        System.out.println(StringUtil.isEmpty("")); // 输出:true
        System.out.println(StringUtil.isEmail("test@163.com")); // 输出:true
    }
}

7.3 静态成员的访问注意事项

  1. 静态方法中可以直接访问静态成员,无法直接访问实例成员
  2. 实例方法中既可以直接访问静态成员,也可以直接访问实例成员;
  3. 实例方法中可以使用this关键字,静态方法中不允许使用this关键字

八、实战案例:简易电影信息展示系统

8.1 需求说明

  1. 展示系统中的全部电影信息(每部电影展示名称、价格);
  2. 支持根据电影编号(id)查询对应电影的详细信息。

8.2 代码实现

步骤1:定义电影实体类(JavaBean)

public class Movie {
    // 私有成员变量
    private int id;         // 电影编号
    private String name;    // 电影名称
    private double price;   // 电影票价
    private String director;// 导演
    private String cast;    // 主演
    private double score;   // 电影评分

    // 无参构造器
    public Movie() {
    }

    // 有参构造器
    public Movie(int id, String name, double price, String director, String cast, double score) {
        this.id = id;
        this.name = name;
        this.price = price;
        this.director = director;
        this.cast = cast;
        this.score = score;
    }

    // getter与setter方法
    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public double getPrice() {
        return price;
    }

    public void setPrice(double price) {
        this.price = price;
    }

    public String getDirector() {
        return director;
    }

    public void setDirector(String director) {
        this.director = director;
    }

    public String getCast() {
        return cast;
    }

    public void setCast(String cast) {
        this.cast = cast;
    }

    public double getScore() {
        return score;
    }

    public void setScore(double score) {
        this.score = score;
    }
}

步骤2:定义电影业务处理类

public class MovieOperator {
    private Movie[] movies;

    // 构造器:传入电影数据数组
    public MovieOperator(Movie[] movies) {
        this.movies = movies;
    }

    // 功能1:展示全部电影的名称与价格
    public void showAllMovies() {
        System.out.println("===== 系统全部电影信息 =====");
        for (Movie movie : movies) {
            System.out.println("电影名称:" + movie.getName() + ",票价:" + movie.getPrice() + "元");
        }
    }

    // 功能2:根据电影编号查询电影详情
    public void queryMovieById(int id) {
        for (Movie movie : movies) {
            if(movie.getId() == id) {
                System.out.println("===== 电影详情信息 =====");
                System.out.println("电影编号:" + movie.getId());
                System.out.println("电影名称:" + movie.getName());
                System.out.println("电影评分:" + movie.getScore());
                System.out.println("电影票价:" + movie.getPrice() + "元");
                System.out.println("导演:" + movie.getDirector());
                System.out.println("主演:" + movie.getCast());
                return;
            }
        }
        System.out.println("未查询到编号为" + id + "的电影信息");
    }
}

步骤3:测试主类,实现功能调用

public class MovieSystem {
    public static void main(String[] args) {
        // 1. 初始化电影数据
        Movie[] movies = new Movie[3];
        movies[0] = new Movie(1, "长津湖之水门桥", 45.0, "徐克", "吴京、易烊千玺", 9.6);
        movies[1] = new Movie(2, "一点就到家", 38.0, "许宏宇", "刘昊然、彭昱畅", 8.7);
        movies[2] = new Movie(3, "月球陨落", 52.0, "罗兰·艾默里奇", "哈莉·贝瑞", 7.9);

        // 2. 创建业务处理对象
        MovieOperator operator = new MovieOperator(movies);

        // 3. 调用功能1:展示全部电影信息
        operator.showAllMovies();

        // 4. 调用功能2:根据编号查询电影详情
        operator.queryMovieById(2);
        operator.queryMovieById(5);
    }
}

8.3 程序执行结果

===== 系统全部电影信息 =====
电影名称:长津湖之水门桥,票价:45.0元
电影名称:一点就到家,票价:38.0元
电影名称:月球陨落,票价:52.0元
===== 电影详情信息 =====
电影编号:2
电影名称:一点就到家
电影评分:8.7
电影票价:38.0元
导演:许宏宇
主演:刘昊然、彭昱畅
未查询到编号为5的电影信息

Java入门学习笔记:程序流程控制

程序流程控制决定了代码的执行顺序,是简单输出语句,到能实现复杂业务逻辑的关键一步。

一、程序的三大基本执行结构

Java中所有代码的执行逻辑,都可以归为三种基础结构,所有复杂的程序都是这三种结构的组合与嵌套。

1. 顺序结构

最基础的执行结构,代码会自上而下依次执行,没有任何判断和跳转,是绝大多数基础代码都默认遵循这个结构。

public class Test {
    public static void main(String[] args) {
        // 会按照从上到下的顺序,依次输出A、B、C
        System.out.println("A");
        System.out.println("B");
        System.out.println("C");
    }
}

2. 分支结构

根据预设的条件,选择性地执行某一段代码,实现逻辑的判断与分流。Java中主要提供了ifswitch两种分支实现方式。

3. 循环结构

控制某一段代码重复执行多次,大幅减少冗余代码,灵活控制程序的执行节奏。Java中提供了forwhiledo-while三种循环实现方式。


二、分支结构详解

分支结构的核心是「条件判断」,根据判断结果的真假,决定执行哪一段代码,是实现业务逻辑判断的核心语法。

1. if分支结构

if分支是最常用的分支语法,支持区间判断、复杂条件判断,功能覆盖范围最广,共有三种书写形式。

(1)单if分支

仅针对单个条件做判断,条件为true时执行对应代码,为false则跳过。

语法格式

if (条件表达式) {
    语句体; // 条件为true时执行的代码
}

执行流程

  1. 先计算条件表达式的结果,结果为布尔类型(true/false
  2. 结果为true,执行大括号内的语句体;结果为false,直接跳过整个分支

使用注意

  • if(条件)后的括号不能跟分号;,否则大括号内的代码将不受if条件控制
  • 如果大括号内只有一行代码,大括号可以省略,但不推荐,会降低代码可读性

示例代码

// 判断BMI是否在正常范围
public class BMITest {
    public static void main(String[] args) {
        double weight = 65; // 体重 单位:kg
        double height = 1.75; // 身高 单位:m
        double bmi = weight / (height * height);
        
        // 单if判断:BMI正常时输出提示
        if (bmi >= 18.5 && bmi <= 24.9) {
            System.out.println("你的BMI指数正常,继续保持!");
        }
    }
}

(2)if-else双分支

针对一个条件,提供「满足/不满足」两种执行路径,非此即彼。

语法格式

if (条件表达式) {
    语句体1; // 条件为true时执行
} else {
    语句体2; // 条件为false时执行
}

执行流程

  1. 计算条件表达式的布尔结果
  2. 结果为true,执行语句体1;结果为false,执行语句体2

示例代码

// 判断一个数是奇数还是偶数
public class OddEvenTest {
    public static void main(String[] args) {
        int num = 15;
        if (num % 2 == 0) {
            System.out.println(num + "是偶数");
        } else {
            System.out.println(num + "是奇数");
        }
    }
}

(3)if-else if-else多分支

针对多个连续的条件做判断,从上到下依次匹配,匹配到第一个符合的条件后,执行对应代码并结束整个分支,适合多区间、多条件的判断场景。

语法格式

if (条件表达式1) {
    语句体1;
} else if (条件表达式2) {
    语句体2;
} else if (条件表达式3) {
    语句体3;
} 
// 可以写任意多个else if
else {
    语句体n; // 所有条件都不满足时,执行这里的代码
}

执行流程

  1. 先判断条件1,结果为true则执行语句体1,分支结束;为false则继续判断条件2
  2. 条件2为true则执行语句体2,分支结束;为false则继续判断后续条件
  3. 若所有条件都为false,则执行else中的语句体n

示例代码(完整BMI指数判断)

public class BMIFullTest {
    public static void main(String[] args) {
        double weight = 80;
        double height = 1.75;
        double bmi = weight / (height * height);
        System.out.println("你的BMI指数为:" + bmi);

        // 多分支判断BMI所处区间
        if (bmi < 18.5) {
            System.out.println("体重过低");
        } else if (bmi <= 24.9) {
            System.out.println("体重正常");
        } else if (bmi <= 29.9) {
            System.out.println("超重");
        } else {
            System.out.println("肥胖");
        }
    }
}

2. switch分支结构

switch分支专门针对固定值匹配的场景,通过匹配表达式的值与case后的值是否相等,决定执行哪条分支,代码结构更清晰,性能更优。

(1)基础语法与执行流程

语法格式

switch(表达式) {
    case 值1:
        执行代码1;
        break; // 跳出分支,避免穿透
    case 值2:
        执行代码2;
        break;
    // 可以写任意多个case
    default:
        执行代码n; // 所有case都不匹配时,执行这里的代码
}

执行流程

  1. 先计算表达式的值,再拿着这个值与case后的值依次匹配
  2. 与哪个case的值匹配成功,就执行该case块内的代码,遇到break就跳出整个switch分支
  3. 所有case都匹配失败,则执行default块内的代码

(2)使用注意事项

  1. 表达式的类型有严格限制:仅支持byteshortintchar,JDK5开始支持枚举,JDK7开始支持String不支持doublefloatlong
  2. case后的值不允许重复,且必须是字面量,不能是变量
  3. 正常使用时不要忘记写break,否则会出现case穿透现象

(3)case穿透性的应用

当多个case分支需要执行的代码完全相同时,可以利用穿透性简化代码,不用重复书写相同逻辑。

示例代码

// 根据星期数,输出当日安排
public class WeekPlanTest {
    public static void main(String[] args) {
        int week = 3;
        switch (week) {
            case 1:
                System.out.println("埋头苦干,解决bug");
                break;
            // 周二、周三、周四执行相同逻辑,利用穿透简化
            case 2:
            case 3:
            case 4:
                System.out.println("请求大牛程序员帮忙");
                break;
            case 5:
                System.out.println("整理代码");
                break;
            // 周六、周日执行相同逻辑
            case 6:
            case 7:
                System.out.println("打游戏放松");
                break;
            default:
                System.out.println("输入的星期数有误");
        }
    }
}

3. if与switch的适用场景对比

语法核心优势最佳适用场景
if功能更强大,支持区间判断、复杂逻辑判断条件为区间范围、需要做复杂逻辑运算的场景
switch代码结构清晰,固定值匹配性能更优条件为单个固定值、多个值匹配的场景

三、循环结构详解

循环结构的核心是「重复执行」,可以让一段代码按照规则循环执行,避免手动重复书写代码,是批量处理数据、重复执行逻辑的核心语法。

1. for循环

for循环是最常用的循环语法,适合明确知道循环次数的场景,语法结构紧凑,循环变量的生命周期仅在循环内有效,更安全。

(1)基础语法与执行流程

语法格式

for (初始化语句; 循环条件; 迭代语句) {
    循环体语句; // 重复执行的代码
}

执行流程

// 示例代码
for (int i = 0; i < 3; i++) {
    System.out.println("Hello World");
}
  1. 循环开始时,先执行一次初始化语句int i = 0,整个循环只会执行一次
  2. 判断循环条件i < 3,结果为true则执行循环体,结果为false则直接结束循环
  3. 循环体执行完毕后,执行迭代语句i++,然后再次回到步骤2判断循环条件
  4. 重复步骤2-3,直到循环条件为false,循环结束

(2)案例

求1-5之间的数字和

public class SumTest {
    public static void main(String[] args) {
        int sum = 0; // 定义求和变量
        // 循环生成1-5的数字
        for (int i = 1; i <= 5; i++) {
            sum += i; // 累加每个数字到sum中
        }
        System.out.println("1-5的和为:" + sum); // 输出结果15
    }
}

查找所有水仙花数
水仙花数定义:是一个三位数,且个位、十位、百位的数字立方和等于原数。

public class NarcissisticNumberTest {
    public static void main(String[] args) {
        int count = 0; // 统计水仙花数的个数
        // 遍历所有三位数
        for (int i = 100; i <= 999; i++) {
            // 提取个位、十位、百位
            int ge = i % 10;
            int shi = i / 10 % 10;
            int bai = i / 100 % 10;
            // 判断是否符合水仙花数规则
            if (ge*ge*ge + shi*shi*shi + bai*bai*bai == i) {
                System.out.println(i);
                count++;
            }
        }
        System.out.println("水仙花数总共有:" + count + "个");
    }
}

2. while循环

while循环的功能与for循环完全一致,核心区别是适合不知道循环次数的场景,循环变量在循环结束后仍可使用。

(1)基础语法与执行流程

语法格式

初始化语句;
while (循环条件) {
    循环体语句;
    迭代语句;
}

执行流程

  1. 先执行一次初始化语句
  2. 判断循环条件,结果为true则执行循环体和迭代语句,结果为false则结束循环
  3. 每次迭代语句执行完毕后,再次回到步骤2判断循环条件,重复执行

(2)经典实战案例

纸张折叠多少次能达到珠峰高度
珠峰高度8848.86米=8848860毫米,纸张厚度0.1毫米,每次折叠厚度翻倍,求折叠次数。

public class PaperFoldTest {
    public static void main(String[] args) {
        double peakHeight = 8848860; // 珠峰高度 单位:毫米
        double paperThickness = 0.1; // 纸张初始厚度 单位:毫米
        int count = 0; // 统计折叠次数
        
        // 不知道循环次数,用while循环
        while (paperThickness < peakHeight) {
            paperThickness *= 2; // 每次折叠厚度翻倍
            count++;
        }
        System.out.println("需要折叠的次数:" + count);
    }
}

3. do-while循环

do-while循环的核心特点是先执行,后判断,哪怕循环条件一开始就不成立,循环体也会至少执行一次。

(1)基础语法与执行流程

语法格式

初始化语句;
do {
    循环体语句;
    迭代语句;
} while (循环条件);

执行流程

  1. 先执行一次初始化语句,然后直接执行循环体和迭代语句
  2. 执行完毕后,再判断循环条件,结果为true则再次执行循环体,结果为false则结束循环

(2)适用场景

适合需要先执行一次操作,再判断是否继续循环的场景,比如12306抢票时,先执行一次刷票,再判断是否需要继续刷新。

4. 三种循环的核心区别总结

  1. 执行顺序区别

    • forwhile:先判断循环条件,再执行循环体,条件不成立则一次都不执行
    • do-while:先执行循环体,再判断循环条件,至少执行一次
  2. 使用场景区别

    • 明确知道循环次数:优先使用for循环
    • 不知道循环次数:优先使用while循环
  3. 循环变量区别

    • for循环的初始化变量定义在循环内,仅在循环中可使用
    • whiledo-while的初始化变量定义在循环外,循环结束后仍可继续使用

5. 死循环

死循环是指循环条件一直为true,会一直执行下去的循环,除非手动干预,否则不会停止。在服务器程序、持续监听的场景中非常常用。

三种常见写法

// 1. for循环死循环
for ( ; ; ) {
    System.out.println("死循环执行");
}

// 2. while循环死循环(最经典、最常用)
while (true) {
    System.out.println("死循环执行");
}

// 3. do-while循环死循环
do {
    System.out.println("死循环执行");
} while (true);

6. 循环嵌套

循环嵌套指的是在一个循环内部,又包含了另一个循环,外层循环每执行一次,内层循环会完整执行完一轮。

示例代码

public class LoopNestTest {
    public static void main(String[] args) {
        // 外层循环控制行数
        for (int i = 1; i <= 4; i++) {
            // 内层循环控制每行的星号个数
            for (int j = 1; j <= 5; j++) {
                System.out.print("*");
            }
            // 每行打印完后换行
            System.out.println();
        }
    }
}

四、循环控制关键字:break & continue

breakcontinue是专门用来控制循环执行流程的关键字,可以灵活地中断循环、跳过某次循环。

1. break关键字

核心作用:跳出并结束当前所在的整个循环,也可以用来结束switch分支的执行。
使用限制:只能在循环和switch分支中使用。

示例代码

// 循环到5时,结束整个循环
public class BreakTest {
    public static void main(String[] args) {
        for (int i = 1; i <= 10; i++) {
            if (i == 5) {
                break; // 遇到5,直接结束整个循环
            }
            System.out.println(i);
        }
        // 最终只会输出1、2、3、4
    }
}

2. continue关键字

核心作用:跳出当前循环的本次执行,直接进入循环的下一次迭代。
使用限制:只能在循环中使用。

示例代码

// 跳过偶数,只输出奇数
public class ContinueTest {
    public static void main(String[] args) {
        for (int i = 1; i <= 10; i++) {
            if (i % 2 == 0) {
                continue; // 遇到偶数,跳过本次循环,进入下一次
            }
            System.out.println(i);
        }
        // 最终输出1、3、5、7、9
    }
}

五、补充工具:Random随机数生成

在很多实战案例中(比如猜数字游戏、验证码生成),我们需要生成随机数,Java提供了java.util.Random类专门用来处理随机数生成。

1. 使用步骤

  1. 导包:在类的最上方导入Random类

    import java.util.Random;
  2. 创建Random对象

    Random r = new Random();
  3. 调用方法生成随机数

    // 生成0到9之间的随机数(包含0,不包含10)
    int num = r.nextInt(10);

2. 区间随机数生成技巧

nextInt(n)只能生成0 ~ n-1之间的随机数,要生成指定区间[min, max]的随机数,使用减加法
公式:r.nextInt(max - min + 1) + min

示例

// 生成1-100之间的随机数
int num1 = r.nextInt(100) + 1;

// 生成65-91之间的随机数
int num2 = r.nextInt(27) + 65;

六、案例

求三个整数中的最大值

import java.util.Scanner;

public class MaxNumberTest {
    public static void main(String[] args) {
        // 创建键盘录入对象
        Scanner sc = new Scanner(System.in);
        // 录入三个整数
        System.out.println("请输入第一个整数:");
        int a = sc.nextInt();
        System.out.println("请输入第二个整数:");
        int b = sc.nextInt();
        System.out.println("请输入第三个整数:");
        int c = sc.nextInt();
        
        // 用if分支求最大值
        int max;
        if (a >= b && a >= c) {
            max = a;
        } else if (b >= a && b >= c) {
            max = b;
        } else {
            max = c;
        }
        // 输出结果
        System.out.println("三个数中的最大值是:" + max);
        sc.close();
    }
}

根据工龄计算涨薪额度

import java.util.Scanner;

public class SalaryTest {
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        // 录入工龄和基本工资
        System.out.println("请输入你的工作工龄:");
        int workYears = sc.nextInt();
        System.out.println("请输入你的基本工资:");
        int baseSalary = sc.nextInt();
        
        // 计算应涨工资
        int addSalary;
        if (workYears >= 10 && workYears < 15) {
            addSalary = 20000;
        } else if (workYears >= 5 && workYears < 10) {
            addSalary = 10000;
        } else if (workYears >= 3 && workYears < 5) {
            addSalary = 5000;
        } else if (workYears >= 1 && workYears < 3) {
            addSalary = 3000;
        } else {
            addSalary = 0;
        }
        
        // 输出结果
        System.out.println("您目前工作了" + workYears + "年,基本工资为 "
                + baseSalary + "元, 应涨工资 " + addSalary + "元,涨后工资 " + (baseSalary + addSalary) + "元");
        sc.close();
    }
}

求1-100中既是3又是5的倍数的数字及和

public class MultipleSumTest {
    public static void main(String[] args) {
        int sum = 0;
        // 遍历1-100的数字
        for (int i = 1; i <= 100; i++) {
            // 判断既是3的倍数,又是5的倍数
            if (i % 3 == 0 && i % 5 == 0) {
                System.out.println(i);
                sum += i;
            }
        }
        System.out.println("以上满足条件的数字之和:" + sum);
    }
}

筛选指定区间内符合条件的三位数

需求:录入一个大于100的三位数,打印100到该数字之间,满足「个位数不为7、十位数不为5、百位数不为3」的数字、个数及和。

import java.util.Scanner;

public class NumberFilterTest {
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        System.out.println("请输入大于100的三位整数:");
        int maxNum = sc.nextInt();
        
        int count = 0;
        int sum = 0;
        // 遍历100到输入的数字
        for (int i = 100; i <= maxNum; i++) {
            // 提取个位、十位、百位
            int ge = i % 10;
            int shi = i / 10 % 10;
            int bai = i / 100 % 10;
            // 判断条件
            if (ge != 7 && shi != 5 && bai != 3) {
                System.out.println(i);
                sum += i;
                count++;
            }
        }
        // 输出统计结果
        System.out.println("以上满足条件的数字之和:" + sum);
        System.out.println("以上满足条件的数字个数:" + count);
        sc.close();
    }
}

筛选符合条件的四位数

需求:打印所有四位数中,满足「个位 + 千位 == 百位 + 十位」的数字,并统计总个数。

public class FourDigitFilterTest {
    public static void main(String[] args) {
        int count = 0;
        // 遍历所有四位数
        for (int i = 1000; i <= 9999; i++) {
            // 提取个位、十位、百位、千位
            int ge = i % 10;
            int shi = i / 10 % 10;
            int bai = i / 100 % 10;
            int qian = i / 1000 % 10;
            // 判断条件
            if ((ge + qian) == (bai + shi)) {
                System.out.print(i + " ");
                count++;
                // 每行输出5个数字,换行
                if (count % 5 == 0) {
                    System.out.println();
                }
            }
        }
        // 输出总个数
        System.out.println("\n以上满足条件的四位数总共有 " + count + " 个");
    }
}

Java零基础入门:基础语法核心知识点全解析(运算符+方法+类型转换+输入输出)

本文整理了JavaSE入门阶段最核心的基础语法知识点,涵盖数据类型转换、各类运算符、Scanner键盘输入、方法详解四大核心模块,所有代码均基于JDK8+环境测试运行。

一、数据类型转换

Java中不同数据类型之间的赋值和运算,需要遵循固定的类型转换规则,主要分为自动类型转换强制类型转换,以及表达式中的自动类型提升三大类。

1.1 自动类型转换

核心规则:类型范围小的变量,可以直接赋值给类型范围大的变量,无需手动处理,由JVM自动完成转换。

数据类型范围从小到大排序:
byte → short → int → long → float → double
其中char类型可直接提升为int类型参与运算。

public class Type1 {
    public static void main(String[] args) {
        // 自动类型转换:byte → int
        byte a = 12;
        print(a);
        // 自动类型转换:byte → double
        print2(a);
    }

    // 定义方法接收 int 类型
    public static void print(int num) {
        System.out.println("转换为int:" + num);
    }

    // 定义方法接收 double 类型
    public static void print2(double num) {
        System.out.println("转换为double:" + num);
    }
}

运行结果:

转换为int:12
转换为double:12.0

1.2 强制类型转换

核心规则:类型范围大的变量,无法直接赋值给范围小的变量,会直接编译报错,必须手动完成强制类型转换。

语法格式:

目标数据类型 变量名 = (目标数据类型) 待转换的变量/值;

⚠️ 注意事项:

  1. 强制类型转换可能出现数据溢出,导致结果不符合预期;
  2. 浮点型强转为整型时,会直接截断小数部分,仅保留整数部分返回。
public class Type2 {
    public static void main(String[] args) {
        // 强制类型转换:int → byte
        int i = 20;
        byte j = (byte) i;
        print3(j);

        // 强制类型转换导致数据溢出
        int m = 1500;
        byte n = (byte) m;
        System.out.println(m); // 1500
        System.out.println(n); // -36 数据溢出

        // 浮点型强转整型,直接舍弃小数部分
        double k = 3.14;
        int l = (int) k;
        System.out.println(l); // 3
    }
    public static void print3(byte j) {
        System.out.println(j);
    }
}

1.3 表达式的自动类型提升

核心规则:在表达式运算中,小范围类型的变量会自动提升为表达式中范围最大的类型,再参与运算。

重点:

  1. 表达式最终结果的类型,由表达式中的最高类型决定;
  2. byteshortchar类型在运算时,会直接提升为int类型再参与运算。
public class Type3 {
    public static void main(String[] args) {
        // byte运算时自动提升为int,结果为int类型
        int result = calc2((byte) 110, (byte) 120);
        System.out.println(result);
        // 手动强制转换为byte类型
        int result2 = calc3((byte) 110, (byte) 120);
        System.out.println(result2);
    }

    // byte + byte 自动提升为int运算,返回int
    public static int calc2(byte a, byte b) {
        return a + b;
    }

    // 手动强制转换,将结果转回byte
    public static byte calc3(byte a, byte b) {
        byte c = (byte) (a + b);
        return c;
    }

    // 表达式最终结果由最高类型double决定
    public static double calc(int a, int b, double c, char r) {
        return a + b + c + r;
    }
}

二、Java核心运算符

2.1 算术运算符与+号的特殊用法

基础算术运算符包括:+(加)、-(减)、*(乘)、/(除)、%(取余/取模)。

⚠️ 核心避坑点:两个整数相除,结果只会保留整数部分,直接舍弃小数。若想要得到小数结果,必须先将其中一个操作数转为浮点型。

+号的两种核心用法:

  1. 常规算术加法运算;
  2. 字符串连接符:当+号遇到字符串时,会作为连接符使用,运算结果仍为字符串。
    执行规则:能算则算,不能算就连接,从左到右依次执行。
public class Operator1 {
    public static void main(String[] args) {
        // 基础算术运算符演示
        print(10, 3);
        System.out.println("---------------------------------");
        // +号连接符用法演示
        print2();
    }

    public static void print(int a, int b) {
        System.out.println(a + b); // 13
        System.out.println(a - b); // 7
        System.out.println(a * b); // 30
        System.out.println(a / b); // 3 整数相除舍弃小数
        System.out.println((double) a / b); // 3.3333333333333335 强转浮点型
        System.out.println(1.0 * a / b); // 3.3333333333333335 转为浮点型运算
        System.out.println(a % b); // 1 取余
    }

    // 研究+符号做连接符还是运算符
    public static void print2() {
        int a = 5;
        System.out.println("abc" + a); // abc5 字符串连接
        System.out.println(a + 5); // 10 算术运算
        System.out.println("itheima" + a + 'a'); // itheima5a 从左到右连接
        System.out.println(a + 'a' + "itheima"); // 102itheima 先算算术,再连接
    }
}

2.2 自增自减运算符

  • ++(自增):对变量自身的值+1
  • --(自减):对变量自身的值-1

核心规则:

  1. 只能操作变量,不能操作字面量;
  2. 单独使用时,放在变量前后无任何区别;
  3. 非单独使用时(在表达式/赋值语句中):

    • 放在变量前:先自增/自减,再参与运算(先加后用)
    • 放在变量后:先参与运算,再自增/自减(先用后加)
public class Operator2 {
    public static void main(String[] args) {
        // 自增自减单独使用
        print(10);
        System.out.println("=========================");
        // 自增自减前后置区别
        print2(10);
    }

    public static void print(int a) {
        a++;
        ++a;
        System.out.println(a); // 12
        a--;
        --a;
        System.out.println(a); // 10
    }

    public static void print2(int a) {
        int b = a++; // 先用后加
        System.out.println(a); // 11
        System.out.println(b); // 10
        int c = ++a; // 先加后用
        System.out.println(a); // 12
        System.out.println(c); // 12
    }
}

2.3 赋值运算符

分为基本赋值运算符扩展赋值运算符两类:

  1. 基本赋值运算符:=,执行逻辑为从右往左赋值;
  2. 扩展赋值运算符:+=-=*=/=%=

核心特点:扩展赋值运算符自带强制类型转换,底层会自动处理类型转换,不会出现编译报错。
底层等价逻辑:a += b 等价于 a = (a的数据类型)(a + b);

public class Operator3 {
    public static void main(String[] args) {
        receive(5);
        print();
    }

    // 收红包案例,演示+=的用法
    public static void receive(int b) {
        int a = 100;
        a += b; // 等价于 a = (int)(a + b);
        System.out.println("收红包后,我的钱包金额:" + a); // 105
        // 扩展赋值运算符自带强制类型转换
        byte a1 = 10;
        byte a2 = 20;
        a1 += a2; // 等价于 a1 = (byte) (a1 + a2);
        System.out.println(a1); // 30
    }

    // 其他扩展赋值运算符演示
    public static void print() {
        int a = 10;
        a -= 5;
        System.out.println(a); // 5
        int b = 10;
        b *= 5;
        System.out.println(b); // 50
        int c = 10;
        c /= 5;
        System.out.println(c); // 2
        int d = 10;
        d %= 5;
        System.out.println(d); // 0
    }
}

2.4 关系运算符

用于判断两个数据的大小/相等关系,运算结果固定为布尔类型true/false

常用关系运算符:>>=<<===(等于)、!=(不等于)。

⚠️ 致命避坑点:判断两个值是否相等必须使用==,而不是使用赋值运算符=

public class Operator4 {
    public static void main(String[] args) {
        print(10, 2);
        print2(10, 10);
    }

    public static void print(int a, int b) {
        System.out.println(a > b); // true
        System.out.println(a < b); // false
        System.out.println(a >= b); // true
        System.out.println(a <= b); // false
        System.out.println(a == b); // false
        System.out.println(a != b); // true
    }

    public static void print2(int a, int b) {
        System.out.println(a > b); // false
        System.out.println(a < b);  // false
        System.out.println(a >= b);  // true
        System.out.println(a <= b);  // true
        System.out.println(a == b);  // true
        System.out.println(a != b);  // false
    }
}

2.5 三元运算符

语法格式:

条件表达式 ? 值1 : 值2;

执行流程:先计算条件表达式的结果,结果为true返回值1,结果为false返回值2。
适用场景:简单的二选一逻辑,也可嵌套使用实现多条件判断。


public class Operator5 {
    public static void main(String[] args) {
        // 求两个数的最大值
        print(10, 40);
        // 判断成绩是否通过
        print(59);
        // 求三个数的最大值
        System.out.println("较大值:" + getMax(10, 20, 30));
        System.out.println("较大值:" + getMax2(10, 20, 30));
    }

    // 求两个整数的较大值
    public static void print(int a, int b) {
        int max = a > b ? a : b;
        System.out.println("较大值:" + max);
    }

    // 判断成绩是否通过
    public static void print(int score) {
        String result = score >= 60 ? "通过" : "挂科";
        System.out.println(result); // 挂科
    }

    // 分步求三个整数的较大值
    public static int getMax(int a, int b, int c) {
        int max = a > b ? a : b;
        max = max > c ? max : c;
        return max;
    }

    // 三元运算符嵌套求三个整数的较大值
    public static int getMax2(int a, int b, int c) {
        int max = a > b ? (a > c ? a : c) : (b > c ? b : c);
        return max;
    }
}

2.6 逻辑运算符

用于将多个条件组合起来运算,最终结果为布尔类型,开发中最常用的是&&||!

分为两大类:

  1. 普通逻辑运算符:&(逻辑与)、|(逻辑或)、!(逻辑非)、^(逻辑异或)
  2. 短路逻辑运算符:&&(短路与)、||(短路或)

区别:

  • &|:无论左边条件结果如何,右边代码一定会执行;
  • &&:左边为false时,右边直接不执行(短路),执行效率更高;
  • ||:左边为true时,右边直接不执行(短路),执行效率更高。
public class Operator6 {
    public static void main(String[] args) {
        // 逻辑与&:所有条件都为true,结果才为true
        System.out.println(isMatch(180, 70, '女')); // true
        System.out.println(isMatch(180, 70, '男'));  // false
        // 逻辑或|:只要有一个条件为true,结果就为true
        System.out.println(isMatch2(180, 10000)); // true
        System.out.println(isMatch2(160, 1000000)); // true
        System.out.println(isMatch2(160, 100)); // false
        // 逻辑非!:取反
        isMatch3(true); // false
        System.out.println("-----------------------------");
        // 逻辑异或^:条件结果相同为false,不同为true
        isMatch4();
        System.out.println("-------------------------------");
        // 短路与&&、短路或|| 和 普通运算符的区别
        print();
    }

    // 逻辑与&演示:择偶条件判断
    public static boolean isMatch(int height, int weight, char sex) {
        boolean result = height > 170 & weight >= 60 & weight <= 80 & sex == '女';
        return result;
    }

    // 逻辑或|演示:择偶条件判断
    public static boolean isMatch2(int height, int income) {
        boolean result = height >= 180 | income > 300000;
        return result;
    }

    // 逻辑非!演示
    public static void isMatch3(boolean flag) {
        System.out.println(!flag);
    }

    // 逻辑异或^演示
    public static void isMatch4() {
        System.out.println(false ^ false); // false
        System.out.println(true ^ true); // false
        System.out.println(true ^ false); // true
        System.out.println(false ^ true); // true
    }

    // 短路与非短路的区别演示
    public static void print() {
        int a = 111;
        int b = 2;
        System.out.println(a > 1000 & ++b > 1);  // false 左边为false,右边仍执行
        System.out.println(b); // 3
        int i = 10;
        int j = 20;
        System.out.println(i > 6 | ++j > 1); // true 左边为true,右边仍执行
        System.out.println(j); // 21
    }
}

三、Scanner键盘输入

Java中通过java.util.Scanner类可以接收用户键盘输入的数据,实现程序与用户的交互。

使用固定三步骤:

  1. 导包:import java.util.Scanner;java.lang包下的类无需导包,如StringSystem
  2. 创建Scanner对象:Scanner sc = new Scanner(System.in);
  3. 接收用户输入:根据数据类型调用对应方法,如next()接收字符串、nextInt()接收整数、nextDouble()接收小数。
package com.itheima.scanner;
// 1. 导包:告诉程序去JDK的哪个包中找Scanner类
import java.util.Scanner;
public class ScannerDemo1 {
    public static void main(String[] args) {
        printUserInfo();
    }

    // 接收用户输入的用户名和年龄,并打印输出
    public static void printUserInfo() {
        // 2. 创建Scanner扫描器对象
        Scanner sc = new Scanner(System.in);
        // 3. 接收用户输入的字符串
        System.out.println("请输入用户名:");
        String username = sc.next();
        System.out.println("您叫:" + username);
        // 接收用户输入的整数
        System.out.println("请输入年龄:");
        int age = sc.nextInt();
        System.out.println("您多少岁:" + age);
    }
}

四、Java方法详解

方法是Java程序中功能的最小单元,是用于执行特定任务的代码块,可接收数据进行处理,并返回处理结果。将重复逻辑封装为方法,可大幅提高代码的复用性和可维护性。

4.1 方法的定义格式

完整语法格式:

修饰符 返回值类型 方法名(形参列表) {
    方法体代码(执行功能的核心代码)
    return 返回值;
}

定义方法的两个核心判断:

  1. 方法是否需要接收数据?需要则定义对应的形参列表;
  2. 方法是否需要返回数据?需要则定义对应返回值类型,不需要则写void

4.2 方法的四种常见形式

根据有无参数、有无返回值,方法可分为四种常见形式,覆盖绝大多数开发场景:

public class MethodDemo1 {
    public static void main(String[] args) {
        // 有参有返回值:求两个整数的和
        int sum = getSum(10, 20);
        System.out.println("和是:" + sum);
        int sum2 = getSum(100, 200);
        System.out.println("和是:" + sum2);
        System.out.println("----------------------------");
        // 无参无返回值:打印3行HelloWorld
        printHelloWorld();
        System.out.println("----------------------------");
        // 有参有返回值:获取指定位数的验证码
        System.out.println(getCode(4));
        System.out.println(getCode(5));
    }

    // 有参有返回值:求任意两个整数的和并返回
    public static int getSum(int a, int b) {
        return a + b;
    }

    // 无参无返回值:打印3行HelloWorld
    public static void printHelloWorld() {
        System.out.println("HelloWorld");
        System.out.println("HelloWorld");
        System.out.println("HelloWorld");
    }

    // 有参有返回值:获取指定位数的数字验证码
    public static String getCode(int len) {
        String code = "";
        for (int i = 0; i < len; i++) {
            int num = (int) (Math.random() * 10);
            code += num;
        }
        return code;
    }
}

4.3 方法重载

定义:一个类中,多个方法名称相同,但形参列表不同,就构成了方法重载。

形参列表不同的判定标准:形参的类型不同、个数不同、顺序不同,满足其一即可。
⚠️ 注意:方法重载与返回值类型、修饰符无关,仅看方法名和形参列表。

package com.itheima.method;
public class MethodDemo2 {
    public static void main(String[] args) {
        // 重载方法的调用,会根据传入的参数自动匹配对应的方法
        print(10);
        print("黑马程序员");
        print(10.5, 2);
    }

    // 定义一个方法,打印一个整数
    public static void print(int a) {
        System.out.println(a);
    }

    // 重载方法:参数类型不同
    public static void print(String str) {
        System.out.println(str);
    }

    // 重载方法:参数个数不同
    public static void print(double d, int a) {
        System.out.println(d);
    }

    // 重载方法:参数顺序不同
    public static void print(int a, double d) {
        System.out.println(d);
    }
}

4.4 return的特殊用法

在返回值类型为void的方法中,可使用单独的return;,作用是提前结束当前方法的执行,常用在卫语句中,提前处理异常/非法情况。

package com.itheima.method;
public class MethodDemo3 {
    public static void main(String[] args) {
        div(10, 0);
        div(10, 2);
    }

    // 除法功能,处理除数为0的异常情况
    public static void div(int a, int b) {
        if (b == 0) {
            System.out.println("除数不能为0");
            return; // 提前结束方法,后续代码不再执行
        }
        System.out.println(a / b);
    }
}

五、案例

程序接收用户输入的身高、体重、性别、年龄,自动计算并输出用户的BMI指数和BMR基础代谢率。

计算公式

  1. BMI指数BMI = 体重(kg) / (身高(m) * 身高(m))
  2. BMR基础代谢率(Harris-Benedict公式)

    • 男性:BMR = 88.362 + (13.397 × 体重) + (4.799 × 身高) - (5.677 × 年龄)
    • 女性:BMR = 447.593 + (9.247 × 体重) + (3.098 × 身高) - (4.330 × 年龄)

代码实现

import java.util.Scanner;
public class AllTest {
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        // 1. 接收用户输入的个人信息
        System.out.println("请您输入您的身高(单位:cm):");
        double height = sc.nextDouble();
        System.out.println("请您输入您的体重(单位:kg):");
        double weight = sc.nextDouble();
        System.out.println("请您输入您的性别(男/女):");
        String sex = sc.next();
        System.out.println("请您输入您的年龄:");
        int age = sc.nextInt();

        // 2. 调用方法计算BMI和BMR
        double bmi = calcBMI(height / 100, weight);
        System.out.println("您的BMI值是:" + String.format("%.2f", bmi));
        double bmr = calcBMR(height, weight, sex, age);
        System.out.println("您的BMR基础代谢率是:" + String.format("%.2f", bmr) + " 卡路里/天");
    }

    // 封装方法:计算BMI指数
    public static double calcBMI(double height, double weight) {
        return weight / (height * height);
    }

    // 封装方法:计算BMR基础代谢率
    public static double calcBMR(double height, double weight, String sex, int age) {
        double bmr = 0;
        if ("男".equals(sex)) {
            bmr = 88.362 + (13.397 * weight + 4.799 * height - 5.677 * age) ;
        } else {
            bmr = 447.593 + (9.247 * weight + 3.098 * height - 4.330 * age);
        }
        return bmr;
    }
}

以上就是Java入门阶段基础语法的核心知识点,这些内容是后续学习面向对象、流程控制、集合框架等内容的核心基础。建议大家把每个代码示例都手动敲一遍,加深对知识点的理解。