建议下载 exe 版本,下载的内容较为干净,且为傻瓜式安装;会自动绑定对应的环境变量;不一定要装C盘
安装完成后,安装目录中会有多个文件夹,其中么个文件夹对应的关系为:
bin:该路径下存放各种工具命令,其中例如 javac 和 java
conf:该路径下存放了相关配置文件
include:该路径下存放了一些平台特定的头文件
jmods:该路径下存放了各种模块
legal:该路径下存放了各模块的授权文档
lib:该路径下存放了工具的一些补充 JAR包
编写第一段代码 helloworld ,cmd 运行 javac HelloWorld.java 进行编译,并使用 java Helloworld 试着跑通
- javac 指令为 JDK 提供的编译工具,对当前路径下的 java 文件编译成 class 文件
- Java 指令一般为运行当前路径下的 class 文件,无需添加文件后缀名
public class HelloWorld {
public static void main(String[] args){
System.out.println("hello word");
}
}
效果如下
很多软件都依赖环境变量中的 JAVA_HOME 参数,为了让 JAVA 适配后续开发。需要对当前环境变量进行调整
在安装完成后,JAVA默认会给我们自动配置环境变量。但是仅仅只有4个工具,这是明显不够的。
相当于仅为 Java 的阉割版,为了方便后续开发,我们最好是对自动配置的环境变量进行规范化调整。
操作步骤
- 打开环境变量 删除系统变量中 Path 的 C:\Program Files\Common Files\Oracle\Java\javapath 路径
- 在 系统变量 中,新建系统变量 JAVA_HOME 并指向 java 的安装目录
- 在 系统变量 的 Path 中,添加对应的位置。%JAVA_HOME%\bin
- 打开 cmd 控制台,测试是否完成 JAVA 的 环境变量 的调整操作。
Notepad++ 是程序员使用的 高级记事本,使用它主要目的是解决 JAVA 代码无法插入中文内容的问题
>>>下载地址
IntelliJ IDEA 是代码编辑器,用于开发 JAVA 语言开发的集成环境,使开发更高效、更愉快。
集成环境:把代码编写、编译、执行、调试 等多种功能综合到一起的开发工具
注意 IDEA 是收费的,目前也有免费的操作。可以参考这个视频:视频地址
另外提供对应解决的资源提供地址:网站地址,找到可选的地址后,选择 download 下载,并复制提供的 注册码即可
如若无法访问,下方提供需要的文件:
使用效果
目录介绍
IDEA 中创建 JAVA 项目会有 project(项目工程)、module(模块)、package(包)、class(类)组成,
它们的上下关系的顺序是从左往右。本质上都是文件夹;
快捷构建
通过指令 psvm,构建入口函数、通过指令 sout 完成打印模板的创建
多个运行操作的关系
界面上存在多个运行的标识,每个都有不同的用途
Debug 是供程序员使用的程序调试工具,它可以用于查看程序的执行流程,也可以追踪程序执行过程来调试程序
相比 run 代码,debug 并不是直接跑完代码,而是手动运行步骤操作
IDEA 内置了 Debug 工具,通过 设置断点 → 调试代码 → 下一步 来看代码一步一步执行过程
设置断点
在要调试的区域点击添加红点
鼠标右键,选择 调试 xxx.main()
点击 下一步图标 查看代码运行的过程
当添加多个断点时,通过左下角 快进下个断点 图标,快速跳到下个断点
一般创建 java 项目时候,会使用 软件包(Package) 去管理 Java类;当创建软件包时,本质是创建文件夹;包中的 .
就是其中的文件目录分割。
由于 java 是编译型语言,每次执行代码都要经过编译才可以使用。因此 main() 就作为了入口函数。会执行 main 下的代码
public class Main {
// 入口函数
public static void main(String[] args) {
// 打印字符串文本
System.out.println("Hello and welcome!");
// 执行循环操作
for (int i = 1; i <= 5; i++) {
System.out.println("i = " + i);
}
}
}
效果展示
JAVA 组成由 JAVA 的运行环境 和 JAVA 开发工具 组成
组成部分
JVM (Java Virtual Machine):Java虚拟机,真正运行 Java 程序的地方
核心类库:Java 自己写好的程序,给程序员自己的程序调用
JRE (Java Runtime Environment):Java 的运行环境
JDK (Java Developoment Kit):Java 开发工具包 (包括上面所有的)
注释
JAVA 通过如下方式注释内容,注释的内容不会被编译器进行编译
// 单行注释
/*
多行注释
多行注释
*/
/**
文档注释
文档注释
*/
如果在注释中标识 TODO: 会在 TODO 选项中显示,且这行注释会有专用颜色
public class ConstantDemo {
public static void main(String[] args) {
/*
*
* TODO: 待处理
* */
int a = 1, b = 2, c = 3;
System.out.println(a + b + c);
}
}
查看 TODO 位置
变量
变量就是内存中的存储空间,空间中存储着经常发生改变的数据;Java 定义变量通常为:数据类型 变量名 = 数值值;
// 声明整数变量,并将值 10000 存储在 salary 标识符中
int salary = 10000;
// 声明长整型变量,并将值 1000000000 存储在 revenue 标识符中
long revenue = 1000000000L;
// 声明双精度浮点型变量,并将值 3.14159 存储在 pi 标识符中
double pi = 3.14159;
// 声明单精度浮点型变量,并将值 2.71828 存储在 euler 标识符中
float euler = 2.71828f;
// 声明布尔型变量,并将值 true 存储在 isStudent 标识符中
boolean isStudent = true;
// 声明字符型变量,并将值 'A' 存储在 grade 标识符中
char grade = 'A';
char aChar = 97 ; // 也可以是 ASCII码 的值,对应 a
// 声明字符串型变量,并将值 "Hello, World!" 存储在 message 标识符中
String message = "Hello, World!";
特性
变量在同一作用域中不可以重复定义,当需要定义多个相同数据类型的变量时可以用 , 分隔
public class ConstantDemo {
public static void main(String[] args) {
int a = 1, b = 2, c = 3;
// int b = 3 // 报错 不可重复声明
System.out.println(a + b + c); // 6
}
}
java 通过字面量的 ” “ 和 ‘ ‘ 定义文本和单字符的 字符串。要注意单字符只能填一个字符 ,选择合适的操作有助于性能优化。
public class ConstantDemo {
public static void main(String[] args) {
// 单引号定义 只能填单个字符
System.out.println('男');
// 双引号定义 可以填多行文本
System.out.println("你好啊");
}
}
输出结果
字符串拼接
当 + 操作中,遇到了字符串,这时 + 就是字符串连接符,而不是算术运算
public class demo {
public static void main(String[] args) {
System.out.println("1" + 1); // 11
// 通过括号实现运算的优先级,实现先算数运算 再 拼接运算
System.out.println("1" + (1 + 1)); // 12
}
}
数据类型分为 基本数据类型 和 引用数据类型;下面是基本数据类型 和它的范围
当使用字面量直接声明的时候,默认所有整数都是 int 类型,所有浮点数都是 double 类型
使用 Scanner 让程序通过运行时候提供键盘传入,实现用户灵活赋值
键盘录入三步骤
在代码中最顶上位置使用 import 导入包,并实例化这个类,使用这个实例化对象的 nextInt 方法接收整数
import java.util.Scanner;
public class ConstantDemo {
public static void main(String[] args) {
// 创建一个 Scanner 对象 sc,用于接收用户输入
Scanner sc = new Scanner(System.in);
System.out.println("请输入值");
// 从用户输入中读取一个整数并存储在 num 变量中
int num = sc.nextInt();
// 输出用户输入的数值
System.out.println("你输入的数值为:" + num);
}
}
效果演示
Scanner 还可以接收其他类型的传参
// 从用户输入中读取一个整数并存储在 num 变量中
int num = sc.nextInt();
// 从用户输入中读取一个字符串(以空格为分隔符)并存储在 str 变量中
String str = sc.next();
// 从用户输入中读取一个字符串(以换行符为分隔符)并存储在 str 变量中
String str = sc.nextLine();
// 从用户输入中读取一个布尔值(true 或 false)并存储在 booleanValue 变量中
boolean booleanValue = sc.nextBoolean();
// 从用户输入中读取一个单精度浮点数(float)并存储在 floatValue 变量中
float floatValue = sc.nextFloat();
// 从用户输入中读取一个双精度浮点数(double)并存储在 doubleValue 变量中
double doubleValue = sc.nextDouble();
案例演示
步骤输入资料,打印信息
import java.util.Scanner;
public class ConstantDemo {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
System.out.println("请输入你的姓名");
String name = sc.next();
System.out.println("请输入你的年龄");
int age = sc.nextInt();
System.out.println("请输入的你身高");
double salary = sc.nextDouble();
System.out.println("当前是否结婚,输入 true or false");
boolean flag = sc.nextBoolean();
System.out.println("注册成功,以下是你的信息:\n"
+ name + '\n'
+ age + '\n'
+ salary + '\n'
+ (flag ? "已婚" : "未婚"));
}
}
效果展示
- 运算符:对字面量或者变量进行操作的符号
- 表达式:用运算符把字面量或者变量连接起来符合java语法的句子就可以称为表达式
- 例如:int a = a + b; (其中 + 是运算符,a + b 是表达式)
JAVA 除法中,整数相除只得整数
在Java中,整数相除得到的结果仍然是整数的原因是因为整数除法会向下取整,即舍弃小数部分,只保留整数部分。这种行为符合数学上的整数除法定义,但有时候可能会导致意外的结果
如果想要得到小数部分,需要在运算中引入浮点数。通过将其中一个操作数转换为浮点数,整数除法就会变为浮点数除法,从而得到包含小数部分的准确结果
这种设计的目的在于保持程序的可预测性和一致性。当进行整数除法时,如果结果也是整数,可以避免一些意外的舍入误差,同时也有助于程序员更清晰地理解代码的行为。如果需要小数部分,可以显式地将操作数转换为浮点数,以确保得到期望的结果
若想获得小数部分,需要有浮点数的数据参与运算即可实现小数位保留
public class ConstantDemo {
public static void main(String[] args) {
// 整数位 与 整数位 运算,结果为整数
System.out.println(5 / 2); // 2
// 浮点数 参与运算时,结果为浮点数
System.out.println(5 / 2.0); // 2.5
}
}
因此,在 JAVA 中,会有这样的情况
int result = 5 / 2; // 2
double result_2 = 5 / 2.0; // 2.5
System.out.println(result == result_2); // false
基本赋值运算
使用 =
去进行赋值运算,将右边返回的值赋值给左边;赋值运算分为 基本赋值运算 和 扩展赋值运算
int result = 2;
扩展赋值运算
扩展赋值运算符,内部自带强转效果
int a = 10;
a += 10; // 10 + 10 = 20
int b = 10;
b -= 10; // 10 - 10 = 0
int c = 10;
c *= 10; // 10 * 10 = 100
int d = 10;
d /= 10; // 10 / 10 = 1
int e = 10;
e %= 3; // 10 % 3 = 1
short f = 1;
f += 1; // 不报错,实际效果为 f = (short)(f + 1);
int num = 11.112;
int g = 233;
g += num; // 244 扩展赋值自带强转,转为原赋值的数据类型
关系运算符
判断左右两侧返回值的关系,最后返回一个 true 或者 false
效果如下
System.out.println(10 > 20); // false
System.out.println(10 >= 20); // false
System.out.println(10 < 20); // true
System.out.println(10 <= 20); // true
System.out.println(10 == 20); // false
System.out.println(10 != 20); // true
逻辑运算符
连接布尔类型的表达式,或者是值;可以用来整合多个条件,为一段整体的逻辑。
package cn.smmcat.operator;
public class OperatorDemo3 {
public static void main(String[] args) {
// 与
System.out.println(true & true); // true
System.out.println(false & true); // false
// 或
System.out.println(true | false); // true
System.out.println(false | false); // false
// 异或
System.out.println(false ^ true); // true
System.out.println(true ^ true); // false
}
}
逻辑运算中存在短路运算,操作为 ||
和 &&
,它们与 |
和 &
不同之处是,当逻辑判断得到结果时,结束后续的内容。
&&
:获得首个 false 后,结束后续业务||
:获得首个 true 后,结束后续业务
// 使用 | 进行逻辑判断时,不会中断后续业务操作
System.out.println(++a > b | --b < a);
System.out.println(a); // 2
System.out.println(b); // 2
// 使用 || 时,当返回需要的值后,直接中断后续操作
System.out.println(++a > b || --b < a);
System.out.println(a); // 2
System.out.println(b); // 3
常用的逻辑运算为:||
、&&
、!
操作
三元运算符
格式:判断条件 ? 值1 : 值2,如果判断条件的结果为 true,执行并返回 值1 的操作,结果为 false 则执行并返回 值2 的操作
int c = 33;
System.out.println(c >= 18 ? "成年" : "不成年"); // 成年
运算优先级
当一个表达式存在多个运算符,会执行优先顺序如下:
通过 ++
、--
实现自增自减
语法 | 描述 |
---|---|
++ | 让变量自身的值 + 1 (自增) |
-- | 让变量自身的值 – 1 (自增) |
注意书写特点,如果定义了数值 a,实现自增的逻辑是有不同的: a++
返回值后自增, ++a
自增后返回值
public class Demo2 {
public static void main(String[] args) {
int a = 1;
a++; // 2
a++; // 3
System.out.println(++a); // 4 注意位置顺序
}
}
在 JAVA 中,自增自减只能给变量赋值,不能给常量赋值。例如 字面量
public class Demo2 {
public static void main(String[] args) {
// 10++ → 10 = 11 [×]
System.out.println(10++); // 报错
}
}
JAVA 数据转换有两种方式,一种是 隐式转换,一种是 强制转换
隐式转换
把一个取值范围小的值或者变量,赋值给另一个取值大的值或者变量。(一般理解为:小空间的值可以放在大空间的值中)
public class Demo2 {
public static void main(String[] args) {
int a = 4; // 整数 占用 4 字节
double b = a; // 小数 占用 8 字节 (空间够,a 能放在 b 里)
System.out.println(b); // 4.0
}
}
因此,赋值的大小排序为如下内容,小的取值范围的值可以 直接赋值 给大的取值范围的值;
虽然 float 占用的字节为 4 字节,long 占用的字节为 8 字节。但是 float 的取值范围要比 long 的更大。
因为小数的 二进制 更加节省内容
运算过程中的隐式转换
JAVA 中,数据类型不统一时,是无法进行计算的
当 取值范围小的值 与 取值范围大的值 相运算的时候,取值范围小的值 会提成为 取值范围大的值,再进行运算
public class Demo2 {
public static void main(String[] args) {
int a = 1;
double b = 1.5;
// 取值范围扩大 只能使用 double 接收
double c = a + b; // 运算时,a 转为 double
System.out.println(c);
}
}
byte、short、char 三种数据类型在运算的时候,都会提升成 int,然后再进行运算
public class Demo2 {
public static void main(String[] args) {
byte a = 10;
byte b = 20;
// 运算时候会转 int 数据,因此只能使用 int 及以上取值范围的 数据类型 接收
int c = a + b;
System.out.println(c);
}
}
char 在运算时会转为字符对应的 ASCII 码,然后进行运算
public class Demo2 {
public static void main(String[] args) {
char a = 'a'; // ASCII => 97
byte b = 1;
// 运算时候会转 int 数据,因此只能使用 int 及以上取值范围的 数据类型 接收
int c = a + b;
System.out.println(c); // 98
}
}
强制类型转换
把一个取值范围大的数值或者变量,赋值给另一个取值范围小的变量;不允许直接赋值,需要加入强制转换
public class Demo2 {
public static void main(String[] args) {
double a = 10.23;
int b = (int) a; // 强制转换,可能会精度损失
System.out.println(b); // 10
int c = 10;
byte d = (byte) c; // 10 并没有损失,因为 byte 存在 10 的取值
}
}
强制转换存在的问题
强制转换时,是相当于砍掉多出的内容。由于二进制的原码原则(高位决定正负的取值范围),
高位的值是判断正数 和 负数的表示。因此 int 的 130 的值转为 byte 后为 -126;原因如下:
- 原码:用于存储十进制数值的二进制表示
- 反码:负数的反码是对原码进行取反,正数的反码就是它本身
- 补码:对反码+1,以便计算出正确结果
int a = 130;
byte b = a; // -126
1. 整数130,默认为 int ,int 占用 4 字节,也就是 4 组 8 位 的二进制原码的补码
int a =130; → (00000000 00000000 00000000 10000010)₂
2. int 强制转为 byte,相当于直接砍掉前面 3组 8位,结果为:
byte b = a; → (10000010)₂
3. 根据补码,反向推理原码:补码转原码需要将除高位的值按位取反,然后加1得到原码
补码 (10000010)₂ → 原码 (11111110)₂ → -126
方法(method)是 一段具有功能的代码块,不调用不执行;方法的出现可以进行分类管理,提高代码的复用性。
- 方法和方法之间应该是平级关系,不允许嵌套使用
- 方法需要调用才能执行
- 方法的定义顺序和执行顺序无关;执行顺序只看调用顺序
- 方法调用完成后,里面的内容就会直接销毁
package cn.smmcat.operator;
public class funtionDemo {
// 主方法会被虚拟机自动调用执行
public static void main(String[] args) {
// 方法都在 main 函数中执行
login();
int a = add(10,22);
int b = add(11,33);
System.out.println(a); // 32
System.out.println(b); // 44
}
// 定义无需传参函数方法 直接执行结束
public static void login() {
System.out.println("进入登录模块");
}
// 定义需要传参函数方法 调用时需要填对应的参数
// return 返回结果
public static int add(int a, int b) {
return a + b;
}
}
函数定义
如图所示,前面的 public 是权限修饰符,static 是静态修饰符;定义方法的格式如下
形参和实参
函数可以通过定义形参接收参数执行后续代码,在调用时候在对应形参中传入实参,实现调用时赋值;
public static void main(String[] args) {
// 调用 sayhi 函数并传 实参
sayHi("张三")
}
// 定义行参 name
public static void sayHi(String name) {
// 执行打印
System.out.println("大家好,我是" + name + "。");
}
函数返回值
函数操作后若有返回值,可使用 return 返回的一个结果,需要在函数声明时候修饰返回值类型,并返回对应的返回值;
没有返回值的函数使用 void 修饰返回值类型
public static int add(int a, int b) {
return a + b;
}
方法重载
在 Java 中,方法重载(Method Overloading)是指在同一个类中,可以有多个方法具有相同的名称,但是参数列表不同的情况。
方法重载使得我们可以使用相同的方法名来执行不同的操作,根据参数的类型、顺序或数量来区分不同的方法。
在方法重载中,方法的签名必须是不同的,方法的签名由方法的名称和参数列表组成。
参数列表必须不同,可以是参数的数量不同、类型不同或顺序不同。返回类型不同的方法不能构成方法重载。
调用方法的时候,Java虚拟机会通过参数的不同来区分同名方法;识别方法之间是否存在重载关系只看方法名和参数,与返回值无关
同一个类中,方法名相同,参数不同的方法就是方法重载,
参数不同:个数不同、类型不同、顺序不同
public class Calculator {
public int add(int a, int b) {
return a + b;
}
public double add(double a, double b) {
return a + b;
}
public int add(int a, int b, int c) {
return a + b + c;
}
}
在这个示例中,Calculator
类中有三个名为 add
的方法,它们的参数列表分别是 (int, int)
、(double, double)
和 (int, int, int)
,
这些方法构成了方法重载。根据传递给方法的参数类型或数量,编译器会决定调用哪个重载的方法
通过方法重载,我们可以提高代码的可读性和灵活性,使得我们可以使用相同的方法名来执行不同的操作,而无需为每个操作定义一个新的方法名,
其实 System.out.println()
就使用了方法重载的效果
// 通过方法重载 实现自由传值
System.out.println(1);
System.out.println(2.2);
System.out.println('s');
System.out.println("你好");
流程控制语句:通过一些语句,来控制程序的 执行流程,流程分支分为:顺序结构、分支结构、循环结构
顺序结构
Java 程序默认的执行流程,没有特定的语法结构。按照代码的先后顺序依次执行。
除了先后顺序的执行逻辑,还有其他顺序结构;
判断条件选择性执行指定语句体
条件判断
if (判断条件) {
语句体;
}
执行流程:
- 首先计算判断条件的结果
- 如果条件的结果为
true
就执行语句体 - 如果条件的结果为
false
就不执行语句体
public class judgeDemo {
public static void main(String[] args) {
checkAdult(19); // 你已成年
}
public static void checkAdult(int age) {
// 满足条件
if (age >= 18) {
// 执行语句体
System.out.println("你已经成年");
}
}
}
条件分支
通过判断的结果选择对应分支的语句体执行
if (判断条件) {
语句体1;
} else {
语句体2;
}
if (判断条件) {
语句体1;
} else if (判断条件) {
}
...
语句体2;
else {
语句体n;
}
执行流程:
- 首先计算判断条件的结果
- 如果条件的结果为
true
就执行语句体1 - 如果条件的结果为
false
且非else if
语句就执行语句体2 - 如果有
else if
会再次依次进行判断,直到没满足任何判断条件后,执行else
内的 语句体
public class judgeDemo {
public static void main(String[] args) {
checkAdult(13); // 你还未成年
}
public static void checkAdult(int age) {
if (age >= 18) {
System.out.println("你已经成年");
} else {
System.out.println("你还未成年");
}
}
}
public class judgeDemo {
public static void main(String[] args) {
checkAdult(4); // 你还是婴儿
}
public static void checkAdult(int age) {
// 结果 false 往另一个分支
if (age >= 18) {
System.out.println("你已经成年");
// 结果还为 false 执行另一个分支
} else if (age >= 6) {
System.out.println("你还未成年");
// 未找到任何匹配项 执行 else
} else {
System.out.println("你还是婴儿");
}
}
}
if
语句:适用于范围性判断switch
语句:适用于固定值的匹配
说明
在 Java 中,switch
是一种条件语句,用于根据表达式的值选择不同的执行路径。switch
语句允许程序根据一个表达式的值,在一系列可能的值中选择一个执行代码块。switch
语句通常用于替代多个 if-else
语句,使代码更加简洁和易读。
语法
程序会将 switch
中表达式返回的值对所有 case
的选项的值进行逐个匹配;匹配成功时执行对应的语句体;
并 break
结束整个 switch
语句。
如果 switch
中表达式返回的值在所有 case
选项的值都匹配失败,执行 default
分支;
switch
表达式接收的变量是有限制的,只支持 byte
、short
、char
、int
的类型的数据
(在 jdk5 版本可以是枚举类型,jdk7 版本开始可以是 String
类型)
并且 switch
进行匹配的 case
值必须是字面量,接收处理完成需要 break
跳出分支,避免出现穿透现象
switch (表达式) {
case 值1:
// 代码块1
break;
case 值2:
// 代码块2
break;
// 可以有更多的 case 分支
default:
// 默认情况下执行的代码块
break;
}
通过 switch
分支,实现指定分支的运行
public class switchDemo {
public static void main(String[] args) {
checkTest(); // 星期三
}
public static void checkTest() {
int week = 3;
switch (week) {
case 1:
System.out.println("星期一");
break;
case 2:
System.out.println("星期二");
break;
case 3:
System.out.println("星期三");
break;
case 4:
System.out.println("星期四");
break;
// ...
default:
System.out.println("输入有误");
break;
}
}
}
当 switch
的 case
分支执行时,若没有 break
进行跳出,会继续往下执行。这样叫做穿透现象;
通过穿透现象,也能实现一些代码优化
public class switchDemo {
public static void main(String[] args) {
checkTest();
}
public static void checkTest() {
int week = 4; // 工作日
switch (week) {
case 1:
case 2:
case 3:
case 4:
case 5:
System.out.println("工作日");
break;
case 6:
case 7:
System.out.println("休息日");
break;
default:
System.out.println("输入有误");
break;
}
}
}
jdk14 开始,case
允许编写多个数据,多个数据使用逗号表示,简化写法实现穿透现象
public class switchDemo {
public static void main(String[] args) {
checkTest();
}
public static void checkTest() {
int week = 4; // 工作日
switch (week) {
case 1, 2, 3, 4, 5:
System.out.println("工作日");
break;
case 6, 7:
System.out.println("休息日");
break;
default:
System.out.println("输入有误");
break;
}
}
}
并且可以再次简化书写方式,实现同样效果
public static void checkTest() {
int week = 4; // 工作日
switch (week) {
case 1, 2, 3, 4, 5 -> System.out.println("工作日");
case 6, 7 -> System.out.println("休息日");
default -> System.out.println("输入有误");
}
}
循环 + 分支嵌套方案
如果项目的代码需要持续进行选择提示,需要使用 while
+ switch
方案,并在 switch
上设置跳出循环的标记
lo:
while (true) {
Scanner sc = new Scanner(System.in);
// ...
int choice = sc.nextInt();
switch (choice) {
case 1:
System.out.println("修改学生");
break;
case 2:
System.out.println("查看学生");
break;
case 3:
System.out.println("感谢你的使用,再见");
break lo; // 指向跳出的位置 跳出大循环
default:
System.out.println("输入有误,重新输入");
break;
}
}
通过执行 System.exit(0);
代码,终止正在运行的JVM虚拟机;也能实现跳出 while
循环
while (true) {
Scanner sc = new Scanner(System.in);
// ...
int choice = sc.nextInt();
switch (choice) {
case 1:
System.out.println("修改学生");
break;
case 2:
System.out.println("查看学生");
break;
case 3:
System.out.println("感谢你的使用,再见");
System.exit(0); // 终止正在运行的JVM虚拟机
break;
default:
System.out.println("输入有误,重新输入");
break;
}
}
在 Java 中,for
是一种用于控制循环执行的关键字,通常用于遍历数组或执行一定次数的循环操作。for
循环提供了一种简洁的方式来重复执行一段代码,直到指定的条件不再满足为止。
for (initialization; condition; update) {
// 循环体
}
initialization
:在循环开始前执行的初始化语句,通常用于初始化循环变量。condition
:循环的条件表达式,每次迭代开始时都会被检查。只有当条件为真时,循环体才会被执行。如果条件为假,循环将结束。update
:在每次迭代结束后执行的更新语句,通常用于更新循环变量的值
下面是一个简单的示例,演示了 for
循环的用法:
for (int i = 0; i < 5; i++) {
System.out.println("Iteration " + i);
}
在这个示例中,for
循环从 i = 0
开始,每次迭代时检查 i < 5
是否为真,如果为真,则执行循环体内的代码并打印出相应的信息。
在每次迭代结束后,i
的值会递增,直到 i < 5
不再满足条件,循环结束。
注意事项
for
循环{}
中定义的变量,在每轮循环结束后,都会从内存中释放;for
循环()
中定义的变量,在整个循环结束后,将会从内存中释放;- 嵌套
for
循环时,先循环最内侧的循环体,再一步一步向外循环完毕
与 for
循环的区别:for
循环:用于控制循环的变量,在循环结束后就会从内存中消失(因为定义在循环体内)while
循环:用于控制循环的变量,在循环结束后不会从内存中消失(因为定义在循环体外)
在Java中,while
语句是一种常用的循环结构,它允许您重复执行一段代码块,直到指定的条件不再满足为止。while
循环分为 while
和 do while
循环,它们的特性和语法如下:
while
循环是先判断后执行do while
循环是先执行后判断
初始化语句;
while (条件判断语句) {
循环体语句;
条件控制语句;
}
初始化语句;
do {
循环体语句;
} while (条件判断语句) {
条件控制语句;
}
特性场景
与 for
循环使用基本一致,但是 for
循环一般用在明确知道循环次数的场景,而 while
循环可用在不明确循环次数的循环操作上使用;
通常可以 while(true)
搭配 break;
使用。用于跳出循环:
while (true) {
// 一些逻辑判断
if (condition) {
break; // 跳出循环
}
}
当满足某个条件时,break
语句会立即终止当前的 while
循环,然后程序会继续执行while
循环之后的代码
break
关键字只能在 循环 和 switch
语句中使用,用来 结束循环 或者 结束 switch
语句
案例演示
通过 while
循环和搭配键盘输入,实现密码错误重新输入效果
import java.util.Scanner;
public class whileDemo {
public static void main(String[] args) {
checkLogin();
}
public static void checkLogin(){
Scanner sc = new Scanner(System.in);
while (true){
System.out.println("请输入密码!");
int password = sc.nextInt();
if (password == 12345){
System.out.println("登录成功!");
break;
}else{
System.out.println("验证失败,请重新输入");
}
}
}
}
效果展示
说明
break:结束循环 或者 结束 switch
语句;可在 当前循环语句 中 或 switch
使用
continue:跳过当前循环语句中的该次循环,继续后续循环的执行,仅支持在 循环语句中 执行
与 return
类似的一个情况是:break
和 continue
都不允许在它们的当前区域的下面写代码语句,
因为无法执行,属于无效代码
案例演示
public static void test() {
for (int i = 0; i < 10; i++) {
// 当达成 if 条件时,跳过该循环
if (i == 3 || i == 5) {
continue;
}
System.out.println(i);
}
}
效果演示
指定结束循环
在多层循环或者 switch
语句需要结束外层循环时,需要给循环进行标号;方便 break
结束指定目标的循环语句
public static void test2() {
Scanner sc = new Scanner(System.in);
lo: // 标号
while (true) {
System.out.println("请输入你现在的日程,以序号填入:1.摸鱼 2.学习 3.结束选择");
int choice = sc.nextInt();
switch (choice) {
case 1:
System.out.println("你此期间选择了摸鱼,摸了个爽");
break;
case 2:
System.out.println("你此期间选择了学习,开始学习中..");
break;
case 3:
System.out.println("你结束了选择");
break lo;
default:
System.out.println("输入有误,重新输入");
break;
}
}
}
效果展示
Random
类是 Java 中用于生成伪随机数的一个类
使用方式
1. 实例化 Random 类
Random random = new Random(); // 创建一个新的 Random 对象
2. 生成随机数
int randomNumber = random.nextInt(100); // 生成一个0到99之间的随机整数
double randomDouble = random.nextDouble(); // 生成一个0.0到1.0之间的随机双精度浮点数
boolean randomBoolean = random.nextBoolean(); // 生成一个随机的布尔值
案例演示
通过随机数搭配流程控制,实现猜随机数游戏的效果
import java.util.Random;
import java.util.Scanner;
public static void RandomTest() {
Scanner sc = new Scanner(System.in);
Random r = new Random();
// 1-100以内
int num = r.nextInt(100) + 1;
System.out.println("随机数已经生成,请猜测:");
while (true) {
int i = sc.nextInt();
if (i == num) {
System.out.println("猜对了,正确数字是:" + num);
break;
} else {
System.out.println("不对噢,再猜猜~,提示:" +
(i > num ? "大了" : "小了"));
}
}
}
效果展示
数组指的是一种容器,可以用来存储同种数据类型的多个值
数组初始化,指的是 声明 + 赋值。若只是声明,在调用时候会报错;
// 声明方式1: 数据类型[] 数组名
int[] a;
// 声明方式2:数据类型 数组名[]
int a[];
静态初始化
声明且赋值,有复杂和简化写法,但是声明数组类型后,只能填对应数据,格式如下:
public static void main(String[] args) {
// 完整格式
int[] a = new int[]{1, 2, 3, 4};
// 简化写法
int[] b = {1, 2, 3, 4};
// 声明数组类型 只能填对应类型的数据
double[] c = {1.1, 2.6, 3.4}
}
动态初始化
在初始化时,只要指定数组的长度,系统就会分配默认值(初始值)
格式:数据类型[] 数组名 = new 数据类型[长度]
int arr = new int[5];
// 相当于定义了长度为 5 的 int数组,初始值为 0
当动态数组初始化时,它所对应数据类型的默认值的分类如下:
- 整数:
0
- 小数:
0.0
- 布尔值:
false
- 字符:
'\u0000'
- 字符串:
null
(也为引用数据类型的默认值)
动态和静态初始化区别
在初始化声明上的区别:
- 动态初始化:手动指定数组长度,由系统给出默认的初始化值
- 静态初始化:手动指定数组元素,系统会根据元素个数,计算出数组的长度
在不同场景中,对应初始化的使用选择:
- 静态初始化:如果要操作的数据,需求中已经明确给出,使用静态初始化
- 动态初始化:只明确元素个数,不明确具体数组(例如:键盘录入)
数组信息
当使用 System.out.println();
打印对应数组名的时候,会显示如下内容:
int[] b = {1, 2, 3, 4};
System.out.println(b); // [I@4eec7777
其中打印的内容对应的信息描述如下:
[I@4eec7777
@
:分隔符[
:当前空间是数组类型I
: 当前数组类型标识,I
是int
类型4eec7777
:数组的十六进制内存地址
数组内容直接显示的情况
当 System.out.println()
打印的数组是 char
类型的字符数组时,不是显示地址信息;而是一串数组内容拼接后的字符串
char[] charList = {'h','e','l','l','o'};
System.out.println(charList); // hello
原因:Java 源代码中可以看到当传入参数为 char[]
数组类型的数组时候,是直接执行遍历打印的;
因此,显示所有 a-z
、A-Z
、0-9
的内容就可以如下操作,创建一个验证码数组:
public static char[] getCode() {
char[] chs = new char[26 + 26 + 10];
int index = 0;
for (char i = 'a'; i <= 'z'; i++) {
chs[index] = i;
index++;
}
for (char i = 'A'; i <= 'Z'; i++) {
chs[index] = i;
index++;
}
for (char i = '0'; i <= '9'; i++) {
chs[index] = i;
index++;
}
// abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789
System.out.println(chs);
return chs;
}
通过此数组,搭配 random
类,实现自动生成随机验证码方法
// import java.util.Random;
public static String getCheckCode() {
char[] chs = getCode();
Random random = new Random();
String code = "";
for (int i = 0; i < 4; i++) {
code += chs[random.nextInt(chs.length)];
}
return code; // 随机4位数的验证码
}
指定元素
通过数组的索引可以访问对应数组下标中的元素
索引:索引是数组容器中空间编号,编号从 0
开始,逐个 +1
增长
访问数组元素的格式:数组名[索引]
int[] b = {11, 22, 33, 44};
// 获取数组中下标为 1 的元素
System.out.println(b[1]); // 22
遍历元素
通过 for
循环去遍历数组下标,数组遍历的快捷键: 数组名.fori
int[] a = new int[]{11, 22, 33, 44};
// 普通模式
for (int i = 0; i < a.length; i++) {
// 取得用于动态改变的下标值访问对应下标
System.out.println(a[i]);
}
// 增强模式
for (int j : a) {
System.out.println(j);
}
效果展示
案例演示
封装一个获取数组中最大值的方法
public class findMaxDemo {
public static void main(String[] args) {
int[] arr = {1, 4, 22, 44, 111, 42, 13};
int max = findMax(arr); // 111
System.out.println("数组最大值为:" + max);
}
public static int findMax(int[] arr) {
// 假设第一个值为 最大值
int max = arr[0];
// 遍历数组
for (int i = 1; i < arr.length; i++) {
// 逐个比较
if (max < arr[i]) {
// 获得最新的最大值
max = arr[i];
}
}
return max;
}
}
键盘录入5个学生成绩,打印其中最大值
import java.util.Scanner;
public class findMaxDemo {
public static void main(String[] args) {
test();
}
public static int findMax(int[] arr) {
int max = arr[0];
for (int i = 1; i < arr.length; i++) {
if (max < arr[i]) {
max = arr[i];
}
}
return max;
}
public static void test() {
// 实例化 键盘录入
Scanner sc = new Scanner(System.in);
System.out.println("----请输入班级学生人数----");
int count = sc.nextInt();
// 初始化指定 键盘录入的长度 数组
int[] arr = new int[count];
// 逐个遍历
for (int i = 0; i < arr.length; i++) {
System.out.println("请录入第" + (i + 1) + "个学生的成绩信息:");
// 逐个录入
arr[i] = sc.nextInt();
}
// 获取数组中的 最大值
int max = findMax(arr);
System.out.println("录入结束,最高成绩为:" + max);
}
}
效果展示
静态初始化
通过 数据类型[][]
数组名
= new
数据类型
[][] {{元素1,元素2},//...}
的方式创建二维数组;
动态数组的内容会被赋予对应数据类型的默认值
// 创建一个二维数组 完整格式
int[][] c = new int[][]{{1, 3, 3}, {2, 4, 4}};
// 简化格式
int[][] c = {{1, 3, 3}, {2, 4, 4}};
// 访问 0 下标的一维数组地址的 1 下标的具体值
System.out.println(c[0][1]); // 3
// 二维数组实际存储的是一维数组的地址值
System.out.println(c); // [[I@4eec7777 二维数组展示的地址
动态初始化
通过为对应维度的数组设置固定的长度,实现动态赋值:数据类型 数组名 = new 数据类型 [数量][数量];
// 动态初始化
int[][] arr = new int[3][3]; // 相当于 {{0,0,0},{0,0,0},{0,0,0}}
在Java中,数组是一种引用数据类型,它在内存中的存储方式可以分为两种情况:
- 栈内存(Stack Memory):
- 数组的引用(reference)存储在栈内存中。
- 当一个数组被定义时,只有数组的引用存储在栈内存中,而实际的数组元素则存储在堆内存中。
- 栈内存存储的是局部变量,包括方法中定义的变量和对象的引用。
- 数组引用存储在栈内存中,指向堆内存中实际存储数组元素的位置。
- 堆内存(Heap Memory):
- 数组的实际元素存储在堆内存中。
- 堆内存存储的是动态分配的对象,包括数组。
- 当使用
new
关键字创建一个数组时,实际的数组元素会被存储在堆内存中,而数组引用存储在栈内存中。
浅拷贝
复杂数据类型的赋值是内存堆地址的指向,而简单数据类型是直接赋值数据
因此,如果数组对应的变量赋值给另一个变量,相当于赋值堆中对应的地址,一旦某变量对应数据发生改变会同步改变
int[] a = new int[]{11, 22, 33, 44};
int[] b = a;
b[1] = 33; // 影响 b 数组的值后,同样影响 a 数组
类的本质是对事物进行的描述,而对象是这个描述所创建的实例
在面向对象编程中,类是一种模板或蓝图,用于定义对象的属性和行为。
当我们实例化一个类时,就创建了一个对象,这个对象继承了类的属性和行为,并可以独立地操作和与其他对象互动。
因此,对象可以看作是类的具体实现,是根据类定义而创建的具体实体。
对象是面向对象编程中的基本概念,它封装了数据和方法,实现了代码的重用性、可维护性和可扩展性。
Java 当中想要创建对象,必须先有类的存在:
- 类:一组相关属性和行为的集合,将看其看为是对象的设计图
- 对象:是根据设计图(类)创建出来的实体
- 类和对象的关系:
- 依赖关系:需要根据类创建对象
- 数量关系:根据一个类可以创建多个对象
- 类的组成:
- 属性:在代码中使用成员变量表示,成员变量跟之前定义变量的格式一样,只不过位置发生改变,类中的方法除外
- 行为:在代码中使用成员方法表示,成员方法跟之前定义方法的格式一样,只不过需要去掉
static
关键字
案例展示
创建类
public class ClassDemo {
String name = "张三";
int age = 18;
int total; // 成员变量若不赋值 则为默认值
public void study() {
System.out.println(this.name + "正在学习");
}
public void eat() {
System.out.println(this.name + "正在吃东西");
}
}
使用类:(需要新建一个类,并声明 mian
入口 才可以使用)
public class classTestDemo {
public static void main(String[] args) {
classDemo demo = new classDemo();
demo.eat(); // 张三正在吃东西
demo.total = 100; // 赋值 实例之间互不干扰
}
}
类的地址
直接打印通过类 实例化的 对象 的 对象标识符,显示的是该对象的地址信息,其中:
cn.smmcat.test.ClassDemo@3b07d329
上面地址信息分为:全类名(包名 + 地址)@堆内存地址
classDemo demo = new classDemo();
System.out.println(demo); // cn.smmcat.test.ClassDemo@3b07d329
成员变量和局部变量的区别
在类中定义的变量称之为成员变量,在类中的方法中定义的变量为局部变量
为了方便实例化的对象使用;当不给类中声明的属性赋值的时候,其根据自身数据类型会默认赋值一个默认值;
需要注意的是,局部变量(在方法中声明的变量)没有默认值,必须在使用之前显式初始化,否则会导致编译错误
public class Phone {
String brand; // null
boolean isUse; // false
int price; // 0
// ...
}
在 Java 中,不同类型的属性(即实例变量)会有默认的初始值。这些默认值是在没有显式初始化变量时自动赋予的。以下是一些常见数据类型的默认初始值:
- 对于数值类型(byte、short、int、long、float、double):
byte
,short
,int
,long
: 默认值为 0float
,double
: 默认值为 0.0
- 对于字符类型
char
:- 默认值为 ‘\u0000’(null 字符)
- 对于布尔类型
boolean
:- 默认值为
false
- 默认值为
- 对于引用类型(对象类型):
- 默认值为
null
- 默认值为
this
代表当前类对象的引用(地址),因此哪一个对象调用方法,而方法中的 this
就是指向哪个对象
this 可以指向成员变量
使用 this
关键字可以解决成员变量和局部变量的区分,即使可以省略也建议使用;
当存在同名的局部变量和成员变量时候,默认就近原则使用局部变量;使用 this
可以让该变量指向成员变量;
public class Phone {
String color;
public void test(String color) {
System.out.println(color);
System.out.println(this.color);
}
}
// 调用该类
Phone p1 = new Phone();
p1.color = "红色";
p1.test("黄色"); // [color] => 黄色 [this.color] => 红色
this 可以调用本类方法
this
可以调用本类成员或者本类方法,一般情况可以省略
public class Demo {
public void test1() {
this.sayHi() // "Hello world"
}
public void test2() {
sayHi() // "Hello world"
}
public void sayHi() {
System.out.println("Hello world");
}
}
构造函数又成为构造器,它用于初始化一个新对象,构造、创建对象时候所调用的方法
构造方法的概述
- 构造器
- 初始化一个新建的对象
- 构造、创建对象的时候,所调用的方法
- 格式
- 方法名与类名相同,大小写也要一致
- 没有返回值类型,连
void
都没有 - 没有具体的返回值(不能由
return
带回结果数据)
- 注意事项
- 如果类中没有编写构造方法,系统会提供一个默认的无参数的构造方法
- 如果类中已经手动编写了构造方法,系统就不再去提供默认的构造方法
- 建议:每次声明类的时候,都去手动创建一个无参数的构造方法
结构和案例
通过构造方法去接收参数,实现创建实例时赋值;在 new
的时候需要给对象赋值;
public class Student {
String name;
// 构造方法会在 new 创建对象的时候执行
public Student(String name) {
this.name = name;
}
}
public static void main(String[] args) {
// Student s1 = new Student(); //报错
Student s1 = new Student("张三");
System.out.println(s1.name); // 张三
}
通过重载关系的特性去多次创建构造函数,实现可选赋值;
package cn.smmcat.test;
public class Student {
String name;
int age;
public Student() {
this.name = "匿名";
this.age = 18;
}
public Student(String name) {
this.name = name;
this.age = 18;
}
public Student(String name, int age) {
this.name = name;
this.age = age;
}
}
public static void main(String[] args) {
Student s1 = new Student();
System.out.println(s1.name + "---" + s1.age); // 匿名---18
Student s2 = new Student("张三");
System.out.println(s2.name + "---" + s2.age); // 张三---18
Student s3 = new Student("王五",32);
System.out.println(s3.name + "---" + s3.age); // 王五---32
}
为了保证数据的安全性,通过类中的权限修饰符可以设置对应类方法或者属性是否允许向外暴露;其中应遵守 合理隐藏、合理暴露 原则
权限从小到大分为:private → (default) → proptected → public
public
最大权限,项目中任意地方都可以访问,修饰允许任何外界使用该属性和方法;
private
修饰该方法或者属性为私有,只允许在同一个类使用,该方法或属性将不否允许作用域外的区域调用
public class Student {
String name;
int age;
private String id = "1000";
public String getId() {
// 类中声明 private 的 format 方法只能内部调用,无法外界使用
return format();
}
private String format() {
// 类中声明 private 的 id 属性只能内部调用,无法外界使用
return "你的 id 为:" + this.id;
}
}
(default)
默认不用任何修饰符的情况下的属性或者方法,允许在同一个类中、同一个包中使用
/*
包可见性意味着该成员或方法只能在同一个包(package)内部被访问,
而在不同包中是不可见的。
这种访问级别是
介于 public 和 private 之间的一种访问级别
*/
public class Student {
String name;
int age;
String getAge() {
return this.age;
}
}
proptected
修饰属性或者方法允许在 同一个类中、同一个包中、不同包的子类中使用;
案例演示
实现私有化后,可预先验证判断用户数据是否正确后赋值,而不是直接赋值;
package cn.smmcat.test;
public class Student {
String name;
private int age;
// 不允许直接赋值,而是通过 setAge() 去额外验证输入是否有误
public void setAge(int age) {
if (age > 0 && age < 150) {
this.age = age;
} else {
System.out.println("年龄输入有误,请重新输入");
}
}
// 向外提供对应属性
public int getAge() {
return age;
}
}
说明
JavaBean 是一个代码书写规范,类中的成员变量都要私有,
并且要对外提供相应的 getXXX、setXXX 等方法。并要提供无参、带参的构造方法;
JavaBean 实体类只负责数据存取,而对数据的处理交给其他类来完成,以实现数据和业务处理相分离
public class Person implements Serializable {
// 属性全部私有
private String name;
private int age;
public Person() {
// 无参构造函数
}
// getter 和 setter 方法
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
}
快捷声明 构造方法、getset
在 IDEA 软件的代码书写区域右键选择对应操作去创建即可;
生成构造方法
生成私有属性对应的 get
和 set
方法
使用 ptg 插件快速创建
IDEA 安装 ptg 插件
使用 ptg 插件快速创建符合 JavaBean 规范类
更多内容可继续访问另外的页面
跳转到 java学习 第二部分