Java基础之输入输出、方法、数组完全攻略
作者:神明不懂浪漫
写在前面:
本文整合了Java入门阶段四个核心知识点——输入输出、方法的使用、数组的定义与使用、以及IDEA调试技巧,适合Java初学者系统学习,也适合作为期末复习资料。建议收藏后配合动手实践,效果更佳!
一、Java输入输出
1.1 本节目标
- 熟练使用Java当中的输入方法
- 熟练使用Java当中的输出方法
- 熟悉Java的多组输入
1.2 输出到控制台
基本语法
System.out.println(msg); // 输出一个字符串, 带换行 System.out.print(msg); // 输出一个字符串, 不带换行 System.out.printf(format, msg); // 格式化输出
注意:
println输出的内容自带\n,\nprintf的格式化输出方式和C语言的printf是基本一致的
代码示例
System.out.println("hello world");
int x = 10;
System.out.printf("x = %d\n", x);
格式化字符串参考
常用的格式化占位符:
| 转换符 | 类型 | 举例 |
|---|---|---|
| %d | 十进制整数 | printf("%d", 100) |
| %f | 浮点数 | printf("%f", 3.14) |
| %s | 字符串 | printf("%s", "hello") |
| %c | 单个字符 | printf("%c", 'A') |
| %b | 布尔值 | printf("%b", true) |
1.3 从键盘输入
使用 Scanner 读取字符串/整数/浮点数,使用时需要导入该类(该过程为导包):
import java.util.Scanner; // 需要导入 util 包
Scanner sc = new Scanner(System.in);
System.out.println("请输入你的姓名:");
String name = sc.nextLine();
System.out.println("请输入你的年龄:");
int age = sc.nextInt();
System.out.println("请输入你的工资:");
float salary = sc.nextFloat();
System.out.println("你的信息如下:");
System.out.println("姓名: " + name + "\n" + "年龄:" + age + "\n" + "工资:" + salary);
sc.close(); // 注意, 要记得调用关闭方法
执行结果:
请输入你的姓名: 张三 请输入你的年龄: 18 请输入你的工资: 1000.0 你的信息如下: 姓名: 张三 年龄:18 工资:1000.0
思考:如果先输入年龄、再输入姓名、再输入工资,程序会发生什么问题?
编译器会把换行当做姓名读进去,导致姓名无法正确匹配。这时再写一个Nextline即可正确换行。
1.4 如何处理多组输入?
使用 Scanner 循环读取 N 个数字,并求取其平均值:
Scanner sc = new Scanner(System.in);
int sum = 0;
int num = 0;
while (sc.hasNextInt()) {
int tmp = sc.nextInt();
sum += tmp;
num++;
}
System.out.println("sum = " + sum);
System.out.println("avg = " + sum / num);
sc.close();
注意:当循环输入多个数据时,使用
Ctrl + Z(Windows CMD)或Ctrl + D(Linux/Mac/IDEA控制台)来结束输入。
1.5 实战:猜数字游戏
游戏规则:系统自动生成一个随机整数(1-100),然后由用户输入一个猜测的数字。如果输入的数字比随机数小,提示"低了",如果输入的数字比随机数大,提示"高了",如果相等则提示"猜对了"。
import java.util.Random;
import java.util.Scanner;
class Test {
public static void main(String[] args) {
Random random = new Random();
Scanner sc = new Scanner(System.in);
int toGuess = random.nextInt(100);
while (true) {
System.out.println("请输入要输入的数字: (1-100)");
int num = sc.nextInt();
if (num < toGuess) {
System.out.println("低了");
} else if (num > toGuess) {
System.out.println("高了");
} else {
System.out.println("猜对了");
break;
}
}
sc.close();
}
}
二、方法的使用
2.1 本节目标
- 掌握方法的定义以及使用
- 掌握方法传参
- 掌握方法重载
- 掌握递归
2.2 方法的通俗理解
想象一下你在家里制作咖啡。你有一个"制作咖啡"的过程,这个过程其实就可以看作一个方法:
- 准备材料:咖啡豆、水、糖、牛奶
- 磨咖啡豆:将咖啡豆磨成粉
- 煮水:将水煮沸
- 冲泡:将热水倒入咖啡粉中,浸泡几分钟
- 添加配料:根据个人喜好添加糖或牛奶
- 享用:最后倒入杯中,享用美味的咖啡
接受食材(参数),执行一系列步骤(代码),最后得到一杯咖啡(返回值)
2.3 什么是方法(method)
方法就是一个代码片段,类似于C语言中的"函数"。方法存在的意义:
- 能够模块化的组织代码(当代码规模比较复杂的时候)
- 做到代码被重复使用,一份代码可以在多个位置使用
- 让代码更好理解更简单
- 直接调用现有方法开发,不必重复造轮子
2.4 方法定义
// 方法定义
修饰符 返回值类型 方法名称([参数类型 形参 ...]){
方法体代码;
[return 返回值];
}
示例一:实现一个函数,检测一个年份是否为闰年
public class Method {
// 方法定义
public static boolean isLeapYear(int year) {
if ((0 == year % 4 && 0 != year % 100) || 0 == year % 400) {
return true;
} else {
return false;
}
}
}
示例二:实现一个两个整数相加的方法
public class Method {
// 方法的定义
public static int add(int x, int y) {
return x + y;
}
}
【注意事项】
| 序号 | 注意点 | 说明 |
|---|---|---|
| 1 | 修饰符 | 现阶段直接使用 public static 固定搭配 |
| 2 | 返回值类型 | 如果有返回值必须与返回实体类型一致,没有必须写 void |
| 3 | 方法名字 | 采用小驼峰命名 |
| 4 | 参数列表 | 无参数则()为空,有参数需指定类型,多个参数用逗号隔开 |
| 5 | 方法体 | 方法内部要执行的语句 |
| 6 | 在Java中,方法必须写在类当中 | |
| 7 | 在Java中,方法不能嵌套定义 | |
| 8 | 在Java中,没有方法声明一说 |
2.5 方法的调用
问题:方法写好之后,就可以直接运行吗?
方法的执行需要被调用,调用方式:
public class Method {
// 方法的定义
public static int add(int x, int y) {
return x + y;
}
public static void main(String[] args) {
// 调用方法
int ret = add(1, 2);
System.out.println("ret = " + ret);
int ret2 = add(3, 4);
System.out.println("ret2 = " + ret2);
}
}
总结:
- 定义方法的时候,不会执行方法的代码。只有调用的时候才会执行
- 调用方法的时候,需要使用
方法名 + 参数列表的形式进行调用 - 如果方法有返回值,需要接收返回值结果
2.6 实参和形参的关系
方法的形参相当于数学函数中的自变量。在Java中,实参的值永远都是拷贝到形参中,形参和实参本质是两个实体。
注意:实参传值给形参,必须做到类型匹配、顺序匹配、个数匹配
经典案例:交换实参的值
public class TestMethod {
public static void main(String[] args) {
int a = 10;
int b = 20;
System.out.println("交换前: a = " + a + " b = " + b);
swap(a, b);
System.out.println("交换后: a = " + a + " b = " + b);
}
public static void swap(int x, int y) {
int tmp = x;
x = y;
y = tmp;
}
}
// 运行结果
交换前: a = 10 b = 20
交换后: a = 10 b = 20
原因分析:实参
a和b是main方法中的变量,空间在main方法的栈中;形参x和y是swap方法中的变量,两者空间独立。在swap调用时,只是将实参的值拷贝了一份传给形参,因此对形参的操作不会影响实参。
2.7 方法重载
为什么需要方法重载?
当既想计算两个整数相加,又想计算两个小数相加时,会出现参数类型不匹配的问题。方法重载可以完美解决!
public class TestMethod {
public static void main(String[] args) {
add(1, 2); // 调用add(int, int)
add(1.5, 2.5); // 调用add(double, double)
add(1.5, 2.5, 3.5); // 调用add(double, double, double)
}
public static int add(int x, int y) {
return x + y;
}
public static double add(double x, double y) {
return x + y;
}
public static double add(double x, double y, double z) {
return x + y + z;
}
}
重载的规则:
- 方法名必须相同
- 参数列表必须不同(个数不同 / 类型不同 / 类型次序不同)
- 与返回值类型是否相同无关(仅返回值不同不能构成重载)
编译器在编译代码时,会对实参类型进行推演,根据推演结果来确定调用哪个方法。
方法签名
方法签名即经过编译器编译修改过之后方法最终的名字。具体方式:方法全路径名 + 参数列表,构成方法完整的名字。
2.8 递归
生活中的故事
从前有座山,山上有座庙,庙里有个老和尚给小和尚讲故事,讲的就是:
“从前有座山,山上有座庙,庙里有个老和尚给小和尚讲故事…”
(自身中又包含了自身)
递归的概念
一个方法在执行过程中调用自身,就称为"递归"。递归相当于数学上的"数学归纳法",有一个起始条件,然后有一个递推公式。
例如,我们求 N!:
- 起始条件:N = 1 的时候,N! 为 1(递归的结束条件)
- 递归公式:N! => N * (N-1)!
代码示例:递归求 N 的阶乘
public static void main(String[] args) {
int n = 5;
int ret = factor(n);
System.out.println("ret = " + ret);
}
public static int factor(int n) {
if (n == 1) {
return 1;
}
return n * factor(n - 1); // factor 调用函数自身
}
// 执行结果
ret = 120
更多递归练习
1. 按顺序打印一个数字的每一位(例如 1234 打印出 1 2 3 4)
public static void print(int num) {
if (num > 9) {
print(num / 10);
}
System.out.println(num % 10);
}
2. 递归求 1 + 2 + 3 + … + 10
public static int sum(int num) {
if (num == 1) {
return 1;
}
return num + sum(num - 1);
}
3. 求斐波那契数列的第 N 项
public static int fib(int n) {
if (n == 1 || n == 2) {
return 1;
}
return fib(n - 1) + fib(n - 2);
}
注意:当求
fib(40)时会发现程序执行速度极慢,原因是进行了大量的重复运算。在实际开发中,递归虽然代码简洁,但要注意性能问题。
三、数组的定义与使用
3.1 本节目标
- 理解数组基本概念
- 掌握数组的基本用法
- 数组与方法互操作
- 熟练掌握数组相关的常见问题和代码
3.2 为什么使用数组?
假设要存5个学生的考试成绩并输出:
public class TestStudent {
public static void main(String[] args) {
int score1 = 70;
int score2 = 80;
int score3 = 85;
int score4 = 60;
int score5 = 90;
// ... 逐个输出
}
}
如果有100个学生呢?创建100个变量?数组就是用来解决这个问题的!
3.3 什么是数组?
数组:可以看成是相同类型元素的一个集合,在内存中是一段连续的空间。
关键特点:
- 数组的空间是连在一起的
- 每个空间有自己的编号,起始位置的编号为0,即数组的下标
3.4 数组的创建及初始化
数组的创建
T[] 数组名 = new T[N];
T:表示数组中存放元素的类型T[]:表示数组的类型N:表示数组的长度
int[] array1 = new int[10]; // 容纳10个int类型元素的数组 double[] array2 = new double[5]; // 容纳5个double类型元素的数组 String[] array3 = new String[3]; // 容纳3个字符串元素的数组
数组的初始化
1. 动态初始化:在创建数组时,直接指定数组中元素的个数
int[] array = new int[10];
2. 静态初始化:在创建数组时直接指定具体的数据内容
int[] array1 = new int[]{0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
double[] array2 = new double[]{1.0, 2.0, 3.0, 4.0, 5.0};
String[] array3 = new String[]{"hello", "Java", "!!!"};
静态初始化可以简写:
int[] array1 = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
double[] array2 = {1.0, 2.0, 3.0, 4.0, 5.0};
String[] array3 = {"hello", "Java", "!!!"};
【注意事项】
- 静态初始化虽然没有指定数组长度,编译器在编译时会根据
{}中元素个数确定 - 静态初始化时,
{}中数据类型必须与[]前数据类型一致 - 省略格式不可以拆分,只能在定义的同时初始化
3.5 数组的基本使用
数组中元素访问
int[] array = new int[]{10, 20, 30, 40, 50};
System.out.println(array[0]); // 10
System.out.println(array[1]); // 20
// 通过[]修改元素
array[0] = 100;
System.out.println(array[0]); // 100
注意:下标从0开始,范围是
[0, N),不能越界,否则会抛出ArrayIndexOutOfBoundsException
遍历数组
方式一:普通for循环
int[] array = new int[]{10, 20, 30, 40, 50};
for (int i = 0; i < array.length; i++) {
System.out.println(array[i]);
}
在数组中可以通过
数组对象.length来获取数组的长度
方式二:for-each 遍历
int[] array = {1, 2, 3};
for (int x : array) {
System.out.println(x);
}
for-each是for循环的另一种使用方式,能更方便地完成对数组的遍历,可以避免循环条件和更新语句写错。
3.6 数组是引用类型
JVM内存分布
JVM对所使用的内存按功能进行了划分:
| 区域 | 作用 |
|---|---|
| 程序计数器 (PC Register) | 保存下一条执行指令的地址 |
| 虚拟机栈 (JVM Stack) | 与方法调用相关的信息,每个方法执行时会创建栈帧 |
| 本地方法栈 (Native Method Stack) | 保存Native方法的局部变量 |
| 堆 (Heap) | JVM管理的最大内存区域,new 创建的对象都在堆上 |
| 方法区 (Method Area) | 存储类信息、常量、静态变量等 |
基本类型 vs 引用类型
- 基本数据类型:变量空间中直接存放的是其对应的值
- 引用数据类型:变量空间中存储的是对象所在空间的地址
public static void func() {
int a = 10; // a空间中存的就是10
int b = 20; // b空间中存的就是20
int[] arr = new int[]{1, 2, 3}; // arr存的是数组在堆中的地址
}
认识 null
null 在Java中表示"空引用",也就是一个不指向对象的引用。
int[] arr = null; System.out.println(arr[0]); // 抛出 NullPointerException
null的作用类似于C语言中的NULL(空指针),表示一个无效的内存位置,因此不能对其进行任何读写操作。
3.7 数组的应用场景
数组作为函数的参数
public static void main(String[] args) {
int[] arr = {1, 2, 3};
func(arr);
System.out.println("arr[0] = " + arr[0]); // arr[0] = 10
}
public static void func(int[] a) {
a[0] = 10;
System.out.println("a[0] = " + a[0]); // a[0] = 10
}
数组是引用类型,传参时传递的是地址,因此在方法内部修改数组内容,外部的数组也会改变。
数组作为函数的返回值
获取斐波那契数列的前N项:
public static int[] fib(int n) {
if (n <= 0) {
return null;
}
int[] array = new int[n];
array[0] = array[1] = 1;
for (int i = 2; i < n; ++i) {
array[i] = array[i - 1] + array[i - 2];
}
return array;
}
3.8 操作数组工具类 Arrays
数组转字符串
import java.util.Arrays;
int[] arr = {1, 2, 3, 4, 5, 6};
String newArr = Arrays.toString(arr);
System.out.println(newArr); // [1, 2, 3, 4, 5, 6]
数组拷贝
int[] arr = {1, 2, 3, 4, 5, 6};
int[] newArr = Arrays.copyOf(arr, arr.length); // 完整拷贝
int[] newArr2 = Arrays.copyOfRange(arr, 2, 4); // 拷贝[2,4)范围
二分查找
针对有序数组,可以使用更高效的二分查找:
public static int binarySearch(int[] arr, int toFind) {
int left = 0;
int right = arr.length - 1;
while (left <= right) {
int mid = (left + right) / 2;
if (toFind < arr[mid]) {
right = mid - 1; // 去左侧区间找
} else if (toFind > arr[mid]) {
left = mid + 1; // 去右侧区间找
} else {
return mid; // 找到了
}
}
return -1; // 没找到
}
冒泡排序
public static void bubbleSort(int[] arr) {
for (int i = 0; i < arr.length; i++) {
for (int j = 1; j < arr.length - i; j++) {
if (arr[j - 1] > arr[j]) {
int tmp = arr[j - 1];
arr[j - 1] = arr[j];
arr[j] = tmp;
}
}
}
}
Java 内置了更高效的排序:
Arrays.sort(arr);
数组逆序
public static void reverse(int[] arr) {
int left = 0;
int right = arr.length - 1;
while (left < right) {
int tmp = arr[left];
arr[left] = arr[right];
arr[right] = tmp;
left++;
right--;
}
}
3.9 二维数组
二维数组本质上也就是一维数组,只不过每个元素又是一个一维数组。
int[][] arr = {
{1, 2, 3, 4},
{5, 6, 7, 8},
{9, 10, 11, 12}
};
for (int row = 0; row < arr.length; row++) {
for (int col = 0; col < arr[row].length; col++) {
System.out.printf("%d\t", arr[row][col]);
}
System.out.println("");
}
arr.length代表二维数组的行数arr[row].length代表二维数组的列数
不规则的二维数组:
int[][] array = new int[2][]; array[0] = new int[3]; // 第1行有3列 array[1] = new int[5]; // 第2行有5列
四、IDEA调试案例讲解
4.1 什么是调试?
当我们发现程序中存在的问题时,去查找和改正错误的过程称为调试(debugging)。
调试的一般途径是采用各种方法逐步缩小 bug 所在的范围,最终定位 bug 的准确位置。
4.2 常见的调试方式
| 方式 | 适用场景 | 具体做法 |
|---|---|---|
| 阅读代码跟踪 | 代码量短小 | 逐行阅读代码 + 插入打印语句观察 |
| 使用工具 | 复杂程序 | 使用集成开发工具中集成的调试器 |
4.3 使用IDEA调试Java程序
打断点
点击鼠标左键,出现红色按钮即打断点成功。
启动调试
鼠标右键,点击 Debug… 即可启动调试模式。
4.4 IDEA调试核心功能速查表
| 功能 | 快捷键 | 说明 |
|---|---|---|
| 逐过程 (Step Over) | F8 | 逐行执行,不进入方法内部 |
| 逐语句 (Step Into) | F7 | 进入方法内部执行 |
| 强制步入 (Force Step Into) | Alt + Shift + F7 | 强制进入任何方法(包括库方法) |
| 跳出 (Step Out) | Shift + F8 | 跳出当前方法,返回调用位置 |
| 运行到光标处 | Alt + F9 | 程序跳转到光标所在行 |
| 重新调试 | Ctrl + F5 | 重新开始调试 |
| 暂停调试 | Ctrl + F2 | 停止当前调试 |
| 显示所有断点 | Ctrl + Shift + F8 | 集中管理所有断点 |
普通 Step Into vs Force Step Into:普通F7通常会跳过库方法、无源代码的方法等,而Force Step Into会进入任何方法。
4.5 调试界面重要功能
Debugger标签
展示调用堆栈以及变量等表达式的值。例如 add:13, main:9 属于调用堆栈信息,先调用 main 方法,后调用 add 方法。
在 Evaluate expression 输入框中可以输入表达式进行求值或添加到监视列表。
Console标签
展示控制台的输入输出信息,遇到输出语句时可以查看输出结果。
条件断点
当一个代码需要满足某种条件才进行调试时:
- 打断点
- 鼠标右键,添加条件
- 开始调试,程序会停在满足条件的位置
五、总结
本文从Java基础入门的四个核心模块出发,系统梳理了:
| 模块 | 核心内容 |
|---|---|
| 输入输出 | println/print/printf输出、Scanner输入、多组输入处理、猜数字实战 |
| 方法 | 方法定义与调用、传参机制、方法重载、递归思想与实践 |
| 数组 | 数组创建与初始化、遍历方式、引用类型原理、Arrays工具类、二分查找、排序算法、二维数组 |
| IDEA调试 | 断点设置、Step Over/Into/Out、条件断点、变量监视 |
学习建议:理论结合实践,多敲代码多Debug,才能真正掌握这些基础知识点。遇到问题时善用IDEA的调试功能,事半功倍!
那么本章就到此结束了。本章为大家展示了输入输出,方法,数组和IDEA中的一些调试方法
到此这篇关于Java基础之输入输出、方法、数组的文章就介绍到这了,更多相关Java输入输出、方法、数组内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!
