Java
开始
项目里面放模块,模块里放包。
main方法快捷键 psvm+enter:
1 | public static void main(String[] args) { |
控制台原样输出 sout+enter:
1 | System.out.println(); |
ctrl+y:删除一行ctrl+d:复制一行
注释:单行//,多行/*+enter 文档注释
无论主方法main在哪个类里,只要文件中存在public类,文件名就由那个类决定。
Java概述
诞生:Java是1995年6月由Sun公司引进到我们这个世界的革命性的编程语言。1990年Sun公司成立了由James Gosling领导的开发小组,开始致力于开发一种可移植的、跨平台的语言Java。
Java,一门很好的面向对象语言,其平台无关性让Java成为编写网络应用程序的佼佼者,而且Java也提供了许多以网络应用为核心的技术,使得Java特别适合于网络应用软件的设计与开发。
Java特点:简单、面向对象、平台无关、多线程、动态。
- 简单:C++需要手动管理内存,而Java采用自动垃圾回收机制。
- 面向对象:通过封装数据和行为、利用继承建立类的层次结构、借助多态实现灵活性以及运用抽象简化复杂性,来组织代码和解决问题。
- 平台无关:平台由操作系统OS和处理器CPU所构成。平台无关就是指软件的运行不因操作系统、处理器的变化而无法运行或出现运行错误。
- 动态:Java程序的基本组成单元是类(一切代码都必须定义在类中),有些类是自己编写的,有一些是从类库中引入的,而类又是运行时动态装载的,这就使得Java可以在分布环境中动态地维护程序及类库。
每个平台都会形成自己独特的机器指令(由0、1组成的序列代码),相同的CPU和不同的操作系统所形成的平台的机器指令可能是不同的。
- 为什么平台无关?
Java可以在平台之上再提供一个Java运行环境,该运行环境由Java虚拟机、类库以及一些核心文件组成。Java虚拟机核心可以直接识别字节码指令(由0、1组成的序列代码)。Java针对不同平台提供的Java虚拟机的字节码指令都是相同的。所以要注意字节码≠机器指令。
Java三大特性:封装、继承、多态。
拓展:javac(Java Compiler)是JDK提供的编译器,它的作用是后缀名为.java的源代码文件编译成后缀名为.class的节码文件。
数据类型
标识符
在Java中,标识符是用来命名各种程序元素的有效字符序列。简单来说,标识符就是这些元素的名字。
命名注意:Java是区分大小写的语言,标识符的第一个字符不能是数字。
小驼峰命名法、大驼峰命名法、全大写命名法(如MAX_VALUE、DATABASE_URL)。
基本数据类型
Java语言中有8种基本数据类型:boolean、byte、short、int、long、float、double、char。
8种基本数据类型习惯上可分为以下四大类型:
- 逻辑类型:boolean
- 整数类型:byte、int、short、long
- 字符类型:char
- 浮点类型:float、double
整数类型
int型常量共有4种表示方法
- 十进制: 123,6000
- 八进制: 077( 数字零做前缀 )
- 十六进制: 0x3ABC( 0x或 0X做前缀 )
- 二进制 :0b111( 用0b或0B做前缀 )
对于int型变量,内存分配给4个字节(byte),占32位。(取值范围是
-2**31 ~ 2**31-1)
对于byte型内存分配给1个字节,占8位 .(
−𝟐**𝟕 ~ 𝟐**𝟕 − 𝟏, 取值范围是从 -128 到 127)
对于short型变量,内存分配给2个字节,占16位.
对于long型变量,内存分配给8个字节,占64位。
字符类型
对于char型变量,内存分配给2个字节,占16位
转义符号的使用:\n(换行),\b(退格),\t(水平制表)
获取Unicode码点值,使用int型显示转换。
如果要得到一个0~65536之间的数所代表的Unicode表中相应位置上的字符,必须使用char型显示转换。
1 | int position = (int) 'a';//直接转换并打印'a'的Unicode码点值97 |
浮点类型
float类型(单精度型)
float变量在存储时保留8位有效数字。
对于float型变量,内存分配给4个字节,占32位。
float常量后面必须要有后缀f或F。
1 | float x=22.76f, tom=1234.987f, weight=1e-12F; |
double类型(双精度型)
double变量在存储double型数据时保留16位有效数字。
double常量后缀有“d”或“D” ,但允许省略后缀。
- 一个具有小数部分的数据的默认类型是double而不是float,为什么?
因为float常量后面必须要有后缀“f”或“F”。
类型转换
Java中数据的基本类型(不包括逻辑类型)按精度从“低”到“高”排列:
byte -> short ->char -> int -> long -> float -> double
低->高:系统自动完成转换。
高->低:必须自己使用类型转换运算。
- 当把一个int型常量赋值给一个byte和short型变量时(判断:高->低),不可以超出这些变量的取值范围,否则必须进行类型转换运算。
1 | byte b = (byte) 128; //(√) |
输入输出
1 | Scanner reader=new Scanner(System.in); |
reader对象用空白做分隔标记,读取当前程序的键盘缓冲区中的“单词”。
1 | import java.util.Scanner; |
System.out.println()与 System.out.print()的区别是前者输出数据后换行,后者不换行。
格式控制符:
%d:输出int类型数据值
%c:输出char型数据。
%f:输出浮点型数据,小数部分最多保留6位
%s:输出字符串数据。
数组
数组是相同类型的数据按顺序组成的一种复合数据类型。
数组属于引用型变量,数组变量中存放着数组首元素的内存地址。
数组声明:
1 | //一维 |
为数组分配元素:
1 | float boy[] = new float[4]; |
在声明数组的同时也可以给数组的元素一个初始值,如:float boy[] = { 21.3f, 23.89f, 2.0f, 23f};
“
int a[20];”是正确的数组声明。(x)
Java里面的声明于创建/实例化是两个独立的步骤。
数组长度查询
对于一维数组,float boy[]=new float[4],boy.length = 4.
对于二维数组,int [][] a = new int[3][8],a.length = 3(含有的一维数组的个数 ).a[0].length = 8
因为数组是引用类型,所以不同的变量可以指向同一个数组对象。改变其中一个变量所指向的数组内容会影响所有指向该数组的对象。
运算符和语句
运算符
java中数据类型的精度从“低”到“高”排列的顺序是:
byte -> short ~ char -> int -> long -> float -> double
- 表达式按最高精度进行运算。
- 如果表达式中最高精度低于int型整数,则按int精度进行运算。
赋值运算符“=”
等号逻辑运算符“==”
instanceof 运算符:是二目运算符,左面的操作元是一个对象,右面是一个类。
当左面的对象是右面的类或子类创建的对象时,该运算符运算的结果是true ,否则是false。
语句
break语句,那么整个循环语句就结束。
continue语句,那么本次循环就结束。
for循环语句:
1 | for(int n=0; n<b.length; n++) { //传统方式 |
类
类声明的变量被称作对象。类是用来创建对象的模板。
写类的目的:为了描述一类事物共有的属性和功能。
如果类名使用拉丁字母,那么名字的首字母使用大写字母。
类体的内容有两部分:一部分是变量的声明,用来刻画属性;另一部分是方法的定义,用来刻画行为功能。
声明成员变量时如果没有指定初始值,Java编译器会为其指定默认值。(boolean默认false,整数类型默认0,char变量默认空字符……)
区分成员变量与局部变量:
- 如果局部变量的名字与成员变量的名字相同,则成员变量被隐藏。(如果想在该方法中使用被隐藏的成员变量,必须使用关键字this。)
- 成员变量有默认值,但局部变量没有默认值,也没有访问修饰符。
局部变量可以用
final修饰符,注意它不是访问修饰符,易搞混。
➢类是体现了Java封装性的一种数据类型,封装了数据和对数据的操作。
注意:Java 中,类体中只能定义成员变量、成员方法、构造方法等,不能直接写执行语句(如赋值语句、打印语句等)—— 执行语句必须放在「方法、构造方法或代码块」中。
关于主类main:
| 元素 | 访问权限 | 说明 |
|---|---|---|
| main方法 | 必须public | 为了让JVM(它位于任何包之外)能够顺利地访问并执行这个方法,该main方法必须被声明为public。 |
| main方法所在的类 | 默认或者public | 只需具备包访问权限即可被JVM加载和执行。 |
|
UML图
UML(Unified Modeling Language,统一建模语言)
类的UML图,使用一个长方形描述一个类的主要构成,将长方形垂直地分为三层。
- 如果A类中的成员变量是B类声明的对象,那么A类的对象关联于B类的对象(或 A类的对象组合了B类的对象)。

- 如果A类中某个方法的参数是用B类声明的对象或某个方法返回的数据类型是B类对象,A依赖于B。

- 继承关系的类UML图,实线的起始端是子类的UML图,终点端是父类的UML图,但终点端使用一个空心的三角形表示实线的结束。

创建对象
类声明的变量被称作对象。创建对象(也称给出了这个类的一个实例)包括对象的声明和为对象分配变量两个步骤。
空对象不能使用,因为他还没有得到任何的实体,必须进行对象分配变量的操作,即对象分配实体。
用new运算符给类的成员变量分配内存空间。如果成员变量在声明时没有指定初值,则使用默认值。各成员变量是属于该对象的实体。
然后计算出一个引用值(包含成员变量内存位置及相关信息)。例如new XiyoujiRenwu() 是一个引用值。
问:用new运算符和构造方法创建对象的正确步骤到底是什么?
1.成员变量分配内存空间,并指定默认值。
2.初始化成员变量,即用户声明成员变量时给定的默认值。
3.执行构造方法。
4.计算出一个引用值。
对象的组合:一个类可以把某个对象作为自己的一个成员变量。注意组合的时候不要用new!
1 | class A{ |
如果一个对象a组合了对象b,那么对象a就可以委托对象b调用b的方法,即对象a以组合的方式复用对象b的方法。
构造方法
构造方法的作用:在创建对象时使用,主要是用来初始化各个成员变量。
构造方法的名字必须与它所在的类的名字完全相同,而且没有类型。
◆允许一个类中编写多个构造方法,但必须保证他们的参数不同,即参数的个数不同,或者是参数的类型不同。
◆如果类中没有编写构造方法,系统会默认该类只有一个构造方法,该默认的构造方法是无参数的,且方法体中没有语句。
◆如果类里定义了一个或多个构造方法,那么Java不提供默认的构造方法 。
参数传值
当对象调用方法时,参数被分配内存空间:当调用一个方法并传递参数给它时,这些参数会在方法的作用域内被分配内存空间。
◆对于基本数据类型,Java采用的是值传递,方法接收的是实际值的一个副本,方法内对参数的任何修改都不会影响原始变量。
1 | public static void main(String[] args){ |
◆对于对象引用,传递的是引用的一个副本。方法不能直接修改传递进来的引用本身(比如让其指向另一个对象),但它可以修改该引用所指向对象的状态。
运行结果:After method call, b = Hello World!
传值机制的意义:确保了方法只能访问它自己的副本,从而保护了外部代码的数据完整性,同时也允许方法有效地操作复杂的数据结构。
关键字static
在声明成员变量时,用关键字static给予修饰的称作类变量(也称静态变量),否则称作实例变量。
二者区别?
➢ 不同对象的实例变量互不相同(不同的内存空间)
➢ 所有对象共享类变量(相同的一处内存),通过类名直接访问类变量 (类名.类变量名)
类似地,类中的方法也可分为实例方法和类方法(或静态方法)。
但是,和实例方法不同的是,类方法不可以操作实例变量,为什么?
➢因为在类创建对象之前,实例成员变量还没有分配内存。
其实也可以访问,但是必须显式传入该对象。
1 | pulic class Student{ |
类方法不属于任何对象,它属于“类”本身。用对象调用会让人误以为这个方法和“这个对象的状态”有关,但实际上它和对象毫无关系。
1 | public class MathUtils{ |
this不可以出现在static方法中,因为this表示当前对象。
方法重载
Java中存在两种多态:重载(Overload)和重写(Override)。
方法重载:允许在同一个类中定义多个方法名相同但参数列表不同(参数个数不同 、参数类型相同但是类型不同)的方法。编译器根据调用时传递的参数类型和数量来决定具体调用哪个方法。
关键字this
this是Java的一个关键字,表示某个对象。this可以出现在实例方法和构造方法中,但不可以出现在static方法中,因为this表示当前对象。
主要用途:
- 引用当前对象实例。
- 区分成员变量和局部变量:当局部变量与成员变量同名时,this可以用来明确被隐藏的成员变量。
- 调用另一个构造方法:使用this语法可以在一个构造方法中调用该类的另一个构造方法。

包
包名的目的是有效地区分名字相同的类。不同Java源文件中两个类名字相同时,它们可以通过隶属不同的包来相互区分。
包声明:`package com.example.app;
如果不写 package 声明,java 文件放在哪个物理文件夹,它就在哪个文件夹里。但 Java 不认为它‘属于某个命名包’,没有“包名”,所以不能用import,也不能被其他包访问!唯一能访问 Test 的,是和它在同一个默认包下的其他类。
import
import java.until.Date; 表示引入java.util包中的Date类 。
import java.util.*;表示引入java.util包中所有的类。
访问权限

局部变量没有访问修饰符。
如果一个类的所有构造方法的访问权限都是private的,意味着这个类不能有子类,理由是: 一个类的private方法不能在其他类中被使用,但子类的构造方法中一定会调用父类的某个构造方法。
继承
子类与父类
Java不支持多重继承(子类只能有一个父类)。Java的类按继承关系形成树形结构。这个树形结构中,根节点是Object类(Object是java.lang包中的类),即Object是所有类的祖先类。
如果一个类(除了Object类)的声明中没有使用extends关键字,这个类被系统默认为是Object的子类,即类声明“classA”与“class A extends Object”是等同的。
继承与多态:这里的多态性就是指父类的某个方法被其子类重写时,可以各自产生自己的功能行为。
子类的继承性
如果子类和父类在同一个包中,那么,子类自然地继承其父类中不是private的成员变量作为自己的成员变量和方法。继承的成员变量或方法的访问权限保持不变。
如果子类和父类不在同一个包中,那么,子类继承父类的protected、public成员变量和方法。
子类与对象
用子类的构造方法创建一个子类的对象时,不仅子类中声明的成员变量被分配了内存空间,而且父类的成员变量也都分配了内存空间,但只将其中一部分作为分配给子类对象的变量(子类继承的那部分成员变量)。
父类中的private变量不作为子类对象的变量,但分配了内存空间。那么这部分内存空间是不是就变成垃圾了呢?
➢不是。因为子类有一部分方法是从父类继承来的,这部分方法是可以用来操作这部分未继承变量的。(实例变量是静态绑定的,这点理解去看上转型对象部分,我悟了!)
子类创建对象时,子类的构造方法总是先调用父类的某个构造方法,完成父类部分的创建;然后再调用子类自己的构造方法,完成子类部分的创建。
父类的所有的成员变量也都分配了内存空间,但子类只能操作继承的那部分成员变量 。
成员变量的隐藏和方法重写
如果子类中声明的成员变量和父类中的成员变量同名时,子类就隐藏了继承的父类成员变量。
同样,子类通过重写可以隐藏已继承的实例方法。(不允许降低方法的访问权限,但可以提高访问权限)
(访问限制修饰符按访问权限从高到低的排列顺序是:public、protected、友好的、private。如果父类是protected或者public级别,子类重写时只能是public级别。
方法重写(Override)的规则:
◆方法名相同。
◆参数列表完全相同(类型、数量、顺序)。
◆返回类型相同或是父类方法返回类型的子类型(协变返回类型)。
◆访问权限相同或者更宽松。
◆不能抛出比父类方法更多或更广泛的检查异常。
关键字super
作用:
- 在子类中想使用被子类隐藏的成员变量或方法就可以使用关键字super。
- 子类不继承父类的构造方法,因此,子类如果想使用父类的构造方法,必须使用关键字super来调用,而且super必须是子类构造方法中的第一条语句。
关键字final
- final关键字可以修饰类、成员变量和方法中的局部变量。
- final类不能被继承,即不能有子类。
- 如果用final修饰父类中的一个方法,那么这个方法不允许子类重写。
- 如果成员变量或局部变量被修饰为final的,就是常量。
✓ 对于基本数据类型的变量,这意味着其值不可变;
✓ 对于对象引用,这意味着引用本身不可变(即不能指向另一个对象),但对象的内容仍然可以修改。
构造函数不能声明为final,因为构造函数本身就不能加访问修饰符。
对象的上转型对象
假设Animal类是Tiger的父类
1 | Animal a = new Tiger(); |
对象的上转型对象的实体是子类负责创建的,但上转型对象会失去原对象的一些属性和功能。
上转型对象不能操作子类新增的成员变量;不能调用子类新增的方法。
1 | class Animal{} |
方法调用是动态绑定的(基于实际对象类型)。
实例变量是静态绑定的(看调用方法的引用类型,而不是实际对象的类型)。
一个很好的例子:
1 | class Animal{ |
正确输出:20:20:10
挑错题(ABCD注释标注的哪行代码有错误?)
1 | abstract class Animal { |
正确答案:D
分析:animal是Animal类型的引用,访问m时遵循「静态绑定」:看Animal类型;
抽象类与抽象方法
用关键字abstract修饰的类称为抽象类。方法即抽象方法。父类Animal的m是int类型,而3.14是double类型;会导致精度丢失。
抽象类
抽象类是一种不能被实例化的类,主要用于定义一个基础框架或通用行为,供其他具体(非抽象)子类继承并实现具体的细节。
抽象方法
抽象方法是指没有具体实现的方法,只有方法签名(即方法名、参数列表和返回类型)。抽象方法必须在抽象类中声明,并且必须由子类提供具体的实现。
为什么要使用抽象类和抽象方法?
- 强制规范:通过定义抽象方法,确保所有子类都实现特定的行为。
- 代码复用:可以在抽象类中提供一些通用的功能实现,避免重复代码。
- 灵活性:子类可以根据自己的需求实现抽象方法,同时可以使用父类提供的其他功能。
面向抽象编程
◆面向抽象编程,是指编程时依赖“抽象”,而不是依赖“具体实现类”。
◆在设计一个程序时,可以先声明一个abstract类,通过在类中声明若干个abstract方法,表明这些方法在整个系统设计中的重要性,方法体的内容细节由它的非abstract子类去完成。
◆利用多态实现编程。使用多态进行程序设计的核心技术是使用方法重写和上转型对象,即将abstract类声明对象作为其子类的上转型对象,那么这个上转型对象就可以调用子类重写的方法。
abstract与interface区别
| 特性 | 接口(Interface) | 抽象类(Abstract Class) |
|---|---|---|
| 修饰符 | 只能用public(默认也是 public) |
可使用public/protected/default(包访问) |
| 成员变量 | 只能是公共静态常量public static final,(可省略修饰符,编译器自动补充) |
可包含任意成员变量(private/protected/public、实例变量 / 静态变量) |
| 成员方法 | Java 8 前:仅抽象方法; Java 8 后:抽象方法、默认方法( default)、静态方法(static);Java 9 后:新增私有方法( private) |
可包含抽象方法(abstract)、非抽象方法(普通实例方法、静态方法),也支持私有方法 |
| 构造方法 | 无构造方法(因为接口不能实例化,也无需初始化成员) | 有构造方法(用于子类继承时初始化父类成员),但不能直接调用 |
| 代码块 | 不支持静态代码块 / 实例代码块 | 支持静态代码块和实例代码块 |
开闭原则
“开-闭原则”(Open-Closed Principle,OCP):软件实体(类、模块、函数等)应该对扩展开放,对修改关闭。
为什么需要开-闭原则?
(1)提高可维护性;
(2)增强灵活性。
内部类
Java支持在一个类中声明另一个类,这样的类称作内部类,而包含内部类的类成为内部类的外嵌类。
外嵌类和内部类在编译时,生成两个独立.class文件。
成员内部类
➢可以访问外部类的所有成员(包括私有成员)。
➢需要通过外部类的实例来创建成员内部类的实例。
1 | public class OuterClass { |
局部内部类
➢其作用域仅限于该方法或构造方法内部。
➢不能使用访问修饰符(如public, private等),但可以使用final关键字。
匿名内部类
➢无类名,直接在创建对象时定义。通常用于实现接口或扩展抽象类,特别是在只需要一次性使用的场景下。
➢不能定义构造方法(因为没有类名)。
➢是内部类的特殊形式,仅能用一次。
➢可以继承父类和实现接口。(如接口临时实现、父类方法临时重写)
1 | public class OuterClass { |
new Message() { ... }创建了一个匿名类,直接实现了Message接口,并重写了其中的greet()方法。
Java允许直接用接口名和一个类体创建一个匿名对象,此类体被认为是实现了Computable接口的类去掉类声明后的类体,称作匿名类。
static内部类
➢使用static关键字定义的内部类。
➢不需要外部类的实例即可创建对象(不依赖外嵌类实例)。
➢仅能访问外嵌类静态成员。
成员内部类、局部内部类、匿名类不能定义静态成员(仅静态内部类可以)
1 | public class OuterClass { |
匿名类
匿名类(Anonymous Class)是Java中一种没有显式类名的内部类,核心作用是在创建对象的同时直接定义类的实现,省去了单独定义类的代码冗余。它专为只需要使用一次的临时类场景设计,是Java简化代码的重要语法糖之一。
特点
◆匿名类一定是内部类。
◆可访问外嵌类的成员,不能定义静态成员。
◆必须继承一个父类或实现一个接口,且只能创建一个对象。它的构造逻辑完全依赖 “父类 / 接口”,自身不能显式写构造方法(无类名)。
匿名类要创建对象,必须有构造方法(否则无法初始化),这个构造方法由编译器隐式生成,我们看不到但实际存在。
匿名类继承父类后,创建匿名类对象时,必须调用父类的构造方法(无参或带参)来初始化父类的成员,这和普通子类创建对象时 “先调用父类构造” 的规则一致。
Java允许直接用接口名和一个类体创建一个匿名对象,此类体被认为是实现了Computable接口的类去掉类声明后的类体,称作匿名类。
分类
(1)与子类相关的匿名类
本质:父类的匿名子类,创建对象时直接重写父类方法。
语法:父类名 对象名 = new 父类名 (){ 重写的方法体};
特点:继承、临时重写、依赖外嵌类成员。
(2)与接口相关的匿名类
本质:实现接口的匿名类,创建对象时直接实现接口抽象方法。
语法:接口名 对象名 = new 接口名 (){ 实现的方法体};
替代方案:Lambda表达式(仅适用于「函数接口」,即只有一个抽象方法的接口)
Lambda语法:(参数列表)->{ 方法体 }(简化匿名类代码)
用Lambda表达式代替匿名类:如果一个接口被标记为函数接口,那么你可以使用Lambda表达式来实现这个接口。
1 | //假设有一个函数式接口 |
异常类
◆异常类一般分为两大类:
Error
Exception
◆异常对象:异常发生时 JVM 自动创建,携带错误信息(消息、堆栈跟踪)。
异常对象常用方法
- e.getMessage ():获取异常具体消息(给用户看);
- e.printStackTrace ():打印异常堆栈(给开发者调试,显示异常发生路径);
- e.toString ():获取异常类型 + 消息(用于日志记录)。
try-catch-finally语句
◆try 块:包含可能会抛出异常的代码。如果在try块中的任何语句抛出了一个异常,程序会立即跳转到匹配的catch块。
在多 try-catch 结构中,如果一个 try块中捕获了异常,这不会直接影响其他独立的 try 块中的代码执行。
◆catch 块:每个catch块都指定了要捕获的异常类型。当try块内抛出的异常与某个catch块指定的异常类型匹配时,该catch块将被执行。可以有多个catch块来处理不同类型的异常。
◆finally 块:无论 try 块是否抛异常、catch 块是否执行,都会执行(特殊情况除外)。
基本语法:
1 | try { |
但需要注意以下两种特殊情况:
(1)如果在trycatch语句中执行了catch语句中执行了程序退出代码,即执行return语句,那么finally子语句仍然会被执行先执行finally,再return)。
(2)**trySystem.exit(0);,则不执行finally子语句(当然包括其后的所有语句)**。
异常抛出throw与throws
| 关键字 | 作用 | 位置 | 核心用法 |
|---|---|---|---|
| throw | 手动抛出异常对象 | 方法体内部 | 主动抛出异常(如检测到非法参数时) 例: throw new IOException ("故意抛异常"); |
| throws | 声明方法可能抛出的异常 | 方法签名后(方法名 + 参数列表后) | 告诉调用者 “该方法可能抛异常,需处理” 例: public void test () throws IOException, MyException {} |
自定义异常类
标准异常类无法描述特殊业务错误(如 “收入和支出不能同号”),需自定义异常。
实现步骤:
- 自定义类继承「Exception」(受检异常)或其子类;
- 提供构造方法,调用父类构造方法传递异常消息;
- 在方法中用「throw」抛出自定义异常,用「throws」声明异常。
实例代码:
1 | // 1.自定义异常类 |
断言
断言(Assertions)常用于开发和调试阶段以捕获程序中的逻辑错误。断言语句可以用来验证程序的某些状态是否如预期那样正确(真),如果条件不成立,则抛出一个AssertionError异常,终止程序。
语法:
- assert 布尔表达式。例如
assert score >= 0; - assert 布尔表达式:异常消息。例如
assert score >= 0 : "Error!负数不能是成绩";
注意:
- 默认情况下,JVM禁用断言(断言语句不执行)。
- 启用断言:运行程序时加参数「-ea」(enableassertions);例如
java -ea 类名.java。 - 断言仅用于调试,生产环境禁用(避免影响性能)。
示例代码:
1 | public class TestAssertion { |
解释:x > 0是布尔表达式,是断言的检查条件。若为true,断言通过,程序继续正常执行;若为false,断言失败,抛出 java.lang.AssertionError 异常。"x must be greater than 0"是自定义错误消息,可以是任意表达式(字符串、基本类型、对象等),当断言失败时,该消息会被传入AssertionError的构造方法,作为异常的提示信息,方便开发者定位问题。
常用实用类
String类
核心特点
- 位于
java.lang包,默认导入,被声明为final类,不可继承,用来处理字符序列。 - 字符串有两种创建方式,常量池创建和堆内存创建,前者可共享,后者每次生成新对象。
- 字符串不可变。一旦创建,字符序列无法修改,拼接、替换等操作会生成新 String 对象。
字符串两种创建方式的区别
| 创建方式 | 存储位置 | 引用对比(==) |
适用场景 |
|---|---|---|---|
| 常量赋值 | 字符串常量池 | 相同内容引用相同(返回true) |
固定不变的字符串 |
new关键字 |
堆内存 | 相同内容引用不同(返回false) |
动态生成的字符串 |
◆常量池工作方式:
1 | String str1 == "st"; |
step1:JVM先处理str1,由于字符串常量池里面没有找到“st”,所以JVM会在常量池中创建一个新的String对象“st”,并将其引用赋给str1。
step2:接下来处理str2,JVM检查字符串常量池,发现已经存在 “st”这个字符串,于是直接将之前创建的 “st” 的引用赋给 str2。
String常量本质也是对象,所以也有自己的引用和实体。

◆堆内存工作方式:
1 | String s = new String("xixi"); |
凡是new运算符构造出的对象都不在常量池,new运算符如它名字一样,每次都要在堆内存中开辟新天地。
也可以用一个已创建的String对象创建另一个String对象。
1 | //创建一个String常量对象 |
还有两个较常用的构造方法:
1 | char a[] = {'j','a','v','a'}; |
注意:System.out.println(s)输出的是对象的实体。int address = System.identityHashCode(s)返回String对象s的引用。
字符串的并置
字符串并置指通过+运算符或concat()方法,将两个或多个字符串首尾相接生成新字符串的操作。
◆如果参与并置运算的String对象,只要有一个是变量,那么Java就会在动态区存放所得到的新String对象的实体和引用(new String())
1 | String a = "你"; |
◆纯常量拼接:直接将拼接结果存入字符串常量池,若池中有相同内容则直接复用引用,无需创建新对象。
1 | String str1 = "你" + "好"; // 编译器优化为 String str1 = "你好" |
String类常用方法
◆equals(String s):判断当前 String 对象的字符序列与参数s的字符序列是否完全相同(区分大小写)。
◆compareTo(String s):按Unicode字典顺序比较两个字符串的字符序列,返回整数结果(正→当前字符串大,负→当前字符串小,0→相等)。
◆compareToIgnoreCase(String s):比较两个字符串的字典顺序,但忽略大小写。
◆startsWith(String s)/endsWith(String s):分别判断当前字符串是否以参数s为前缀 / 后缀。
◆indexOf(String str):从字符串起始位置(索引 0)开始,检索子串str首次出现的位置,未找到返回-1。
◆indexOf(String str, int startPoint):从指定索引startPoint开始,检索子串str首次出现的位置,未找到返回-1。
◆contains(String s):判断当前字符串的字符序列是否包含子串s,返回布尔值。
◆substring(int startpoint):从指定索引startpoint开始,截取到字符串末尾的子串,返回新 String 对象。
◆substring(int start, int end):从索引start(包含)截取到索引end(不包含)的子串,返回新 String 对象(左闭右开区间)。
◆trim():去除字符串首尾的空格(不处理中间空格),返回新 String 对象。
1 | "Hello".equals("hello"); //false |
字符串向基本数据转化
➢Java.lang包中的Integer类调用其类方法parseInt(Strings)。
1 | int x; |
类似地,可以使用String类的类方法,valueOf()。
正则表达式
核心概念
正则表达式(Regular Expression,简称 Regex)是一套用于描述字符串模式的语法规则。通过 “普通字符 + 元字符” 的组合实现对字符串的匹配、查找、替换、分解四大核心操作。其核心价值是简化复杂字符串处理逻辑(如验证手机号、提取数字、清理特殊字符),避免重复编写大量判断代码。
例如:正则表达式"\\dcat"中,\\d是元字符(代表 0-9 任意数字),cat是普通字符,整体可匹配"0cat"“1cat”…“9cat”
元字符
三种分类:
1.基础元字符(匹配单个字符)
| 元字符 | 含义 | 示例 |
|---|---|---|
\\d |
匹配 0-9 任意一个数字(digit) | \\d可匹配”5”“8”,不匹配”a” |
\\D |
匹配非数字字符(反义) | \\D可匹配”a”“#”,不匹配”3” |
\\w |
匹配字母、数字、下划线(word) | \\w可匹配”A”“5”“_”,不匹配”@” |
\\W |
匹配非字母、数字、下划线(反义) | \\W可匹配”@”“&”,不匹配”b” |
.(点号) |
匹配任意单个字符(除换行符) | "h.l"可匹配”hal”“hbl”,不匹配”hl” |
| 2.方括号元字符(自定义字符集合) | ||
通过[]包裹字符,定义 “匹配集合内任意一个字符”。 |
| 表达式 | 含义 | 示例 |
|---|---|---|
[abc] |
匹配 a、b、c 中的任意一个 | "[abc]at"可匹配"aat"“bat” |
[^abc] |
匹配除 a、b、c 外的任意字符(^ 表否定) | "[^abc]at"可匹配"dat"“eat” |
[a-zA-Z] |
匹配任意大小写英文字母 | "[a-zA-Z]123"可匹配"A123"“b123” |
[0-9] |
等价于\\d,匹配 0-9 数字 |
"[0-9]cat"等价于"\\dcat" |
3.量词元字符(匹配字符出现次数)
控制前一个字符或子表达式的匹配次数,如整数、浮点数匹配:
| 量词 | 含义 | 示例 |
|---|---|---|
* |
匹配前一个字符 0 次或多次 | "a*b"可匹配"b"“ab”“aaab” |
+ |
匹配前一个字符 1 次或多次 | "a+b"可匹配"ab"“aaab”,不匹配"b" |
? |
匹配前一个字符 0 次或 1 次 | "a?b"可匹配"b"“ab”,不匹配"aab" |
{n} |
匹配前一个字符恰好 n 次 | "a{2}b"可匹配"aab",不匹配"ab" |
{n,} |
匹配前一个字符至少 n 次 | "a{2,}b"可匹配"aab"“aaab” |
{n,m} |
匹配前一个字符 n 到 m 次 | "a{2,3}b"可匹配"aab"“aaab” |
| 应用场景: | ||
①匹配整数(十进制)的正表达式regex: String regex = "-?[1-9]\\d*"; |
-?:
?表示匹配前面的-(负号)0 次或 1 次。
②匹配浮点数的正表达式regex:String regex = "-?[0-9][0-9]*[.][0-9]+";
- 第一个
[0-9]:表示匹配0、1、2…9 中的任意一个数字,首位可以是 0;- 第二个
[0-9]*:*是量词,代表 “匹配前面的字符 0 次或多次”,这里就是匹配 0 个或多个 0-9 的数字,是对第一个数字的后续补充。
③匹配email的正表达式regex: String regex = "\\w+@\\w+\\.[a-z]+(\\.[a-z]+)?";
字符串的替换
String replaceAll(String regex, String replacement)是 Java String类的核心方法之一,用于按照正则表达式匹配的规则,替换字符串中所有符合匹配条件的子串,返回替换后的新字符串。
例如:
1
String str ="12hello567bird".replaceAll("[a-zA-Z]+","你好"); //“12你好567你好”
字符串的分解
split(String regex)是 Java String类的核心方法,用于按照指定的正则表达式作为分隔符,将原字符串拆分为子字符串数组。
案例1:
1 | String str = "2006年1月6日是我的生日"; |
解释:
\\D的含义:正则表达式中,\\D是元字符,代表匹配任意一个非数字字符(等价于[^0-9]);+是量词,代表匹配前面的字符 / 元字符 1 次或多次。因此\\D+的整体含义是:匹配 1 个及以上连续的非数字字符。split()的分割逻辑:split(regex)会将字符串中所有匹配regex的子串作为 “分隔符”,然后把分隔符之间的内容拆分为数组元素;如果字符串开头 / 结尾没有匹配regex的内容,也会正常拆分,且忽略末尾的空字符串。
split()方法的核心规则之一:如果字符串的前缀直接匹配分隔符正则 ,那么分隔符左侧没有任何内容,会生成一个空字符串""作为数组的第一个元素;而如果前缀是目标内容,则直接从目标内容开始拆分,不会产生空串。
案例2:
1 | String str1 = "公元2006年1月6日是我的生日"; |
| 案例 | 原字符串 | 字符串前缀内容 | 前缀是否匹配\\D+(非数字字符 1 次及以上) |
|---|---|---|---|
| 案例 1 | "2006年1月6日是我的生日" |
2006(数字) |
❌ 不匹配 |
| 案例 2 | "公元2006年1月6日是我的生日" |
公元(非数字) |
✅ 匹配 |
StringTokenizer类
StringTokenizer 类是 Java java.util包中专门用于字符串分解的工具类,核心作用是按指定分隔标记拆分字符串,获取其中的 “单词”(语言符号)。它不依赖正则表达式。
构造函数
StringTokenizer 类提供两个核心构造方法,用于初始化字符串分析器:
| 构造方法 | 语法格式 | 含义解释 | 示例 |
|---|---|---|---|
| 无参分隔符 | StringTokenizer(String s) |
为字符串s创建分析器,使用默认分隔标记(空白字符,换行符,回车符,Tab符) |
new StringTokenizer("we are students") → 按空格拆分 |
| 自定义分隔符 | StringTokenizer(String s, String delim) |
为字符串s创建分析器,delim中的任意字符(或组合) 作为分隔标记 |
new StringTokenizer("you#*are*##welcome", "#*") → 按#或*的任意组合拆分 |
注意:delim参数是 “字符集合”,而非 “字符串”:例如delim="#*"表示分隔标记是#、*,或二者的任意连续组合(如#*、***##等)。
核心方法
| 方法名 | 语法格式 | 功能描述 | 返回值 |
|---|---|---|---|
hasMoreTokens() |
boolean hasMoreTokens() |
判断字符串中是否还有未提取的单词 | true(有下一个单词)/false(无) |
nextToken() |
String nextToken() |
提取并返回下一个单词,同时移动指针 | 下一个单词的字符串 |
countTokens() |
int countTokens() |
统计当前剩余未提取的单词总数 | 剩余单词的个数(int 类型) |
1 | StringTokenizer tokenizer = new StringTokenizer(We are family!); //创建对象 |
Scanner类
Scanner 类是 Java java.util包中用于解析输入数据的核心工具类,既能读取键盘输入(交互式输入),也能解析字符串、文件等数据源中的数据,支持按自定义分隔符提取不同类型的信息(如整数、浮点数、字符串),是日常开发中处理输入解析的高频工具。
构造方法
| 构造方法 | 语法格式 | 含义解释 | 示例 |
|---|---|---|---|
| 解析字符串 | Scanner(String source) |
绑定字符串数据源,解析该字符串中的数据 | new Scanner("I Love Java 123") |
| 解析键盘输入(交互式) | Scanner(InputStream source) |
绑定字节输入流数据源,最常用System.in(代表键盘输入),实现交互式输入 |
new Scanner(System.in) |
| 代码实例: |
1 | String str = "xixi"; |
核心方法
1.分隔符相关
| 方法名 | 语法格式 | 功能描述 |
|---|---|---|
useDelimiter() |
Scanner useDelimiter(String regex) |
用正则表达式regex作为分隔标记,替代默认的空白字符分隔符 |
delimiter() |
Pattern delimiter() |
返回当前使用的分隔符(以Pattern对象形式,需调用pattern()获取正则字符串) |
1 | Scanner scanner = new Scanner("市话76.8元,长途167.38元"); |
2.数据读取方法
| 方法类别 | 方法名 | 功能描述 | 示例 |
|---|---|---|---|
| 字符串读取 | next() |
读取下一个单词(从当前位置到下一个分隔符之间的内容,不包含分隔符) | 解析"Hello World"时,next()返回"Hello" |
nextLine() |
读取一整行内容(从当前位置到换行符\n,包含换行符前的所有字符) |
读取键盘输入的一行文本 | |
| 基本类型读取 | nextInt() |
读取下一个单词,并转换为int类型 |
解析"123"时,返回123(int类型) |
nextDouble() |
读取下一个单词,并转换为double类型 |
解析"76.8"时,返回76.8(double类型) |
|
nextBoolean() |
读取"true"或"false",转换为boolean类型 |
解析"true"时,返回true |
|
| 3.判断数据是否存在的方法 | |||
在读取数据前,先用以下方法判断目标类型的数据是否存在,可避免NoSuchElementException(无数据可读取)或InputMismatchException(类型不匹配): |
| 方法名 | 语法格式 | 功能描述 |
|---|---|---|
hasNext() |
boolean hasNext() |
判断当前数据源中是否还有未读取的单词(无论类型,只要有内容) |
hasNextInt() |
boolean hasNextInt() |
判断下一个单词是否能转换为int类型 |
hasNextDouble() |
boolean hasNextDouble() |
判断下一个单词是否能转换为double类型 |
hasNextLine() |
boolean hasNextLine() |
判断当前数据源中是否还有未读取的整行内容 |
代码实例:
1 | import java.util.Scanner; |
日期与时间
Java 中日期与时间的处理主要依赖 Java 8+ 引入的java.time包(如LocalDate、LocalTime、LocalDateTime),该包解决了传统Date/Calendar类的线程不安全、API 设计混乱等问题,是当前开发的标准工具。
核心类
| 类名 | 核心功能 | 包含字段 | 典型用法 |
|---|---|---|---|
LocalDate |
处理仅日期(无时间、无时区) | 年、月、日 | 生日、订单日期、节假日判断 |
LocalTime |
处理仅时间(无日期、无时区) | 小时、分钟、秒、纳秒 | 闹钟时间、会议开始时间(不含日期) |
LocalDateTime |
处理日期 + 时间(无时区) | 年、月、日、小时、分钟、秒、纳秒 | 订单创建时间、日志时间戳 |
当前日期和时间通过
now方法获取。
代码实例:
1 | import java.time.LocalDate; |
运行结果:
1 | 当前日期:2025-11-30 |
创建指定的时间日期通过
of()方法指定。
代码示例:
1 | LocalData Data = LocalData.of(2021, 10, 31); |
核心操作
1.日期的修改
由于LocalDate等类是不可变的,修改操作(如增减年 / 月 / 日)不会改变原对象,而是返回一个新对象,需要用变量接收结果。
| 操作类型 | 方法示例(以LocalDate为例) |
说明 |
|---|---|---|
| 增减年份 | date.plusYears(18) / date.minusYears(1) |
增加 18 年 / 减少 1 年 |
| 增减月份 | date.plusMonths(23) / date.minusMonths(1) |
增加 23 个月 / 减少 1 个月 |
| 增减天数 | date.plusDays(8976) / date.minusDays(10) |
增加 8976 天 / 减少 10 天 |
| 调整到指定字段 | date.withYear(2007) / date.withMonth(5) |
将年份调整为 2007 / 将月份调整为 5 月 |
| 2.日期的比较 | ||
通过isAfter()/isBefore()/isEqual()方法判断两个日期 / 时间的先后关系,返回boolean值。 |
1 | LocalDate dateA = LocalDate.of(2025, 3, 18); |
3.日期的格式化String.format()的格式化占位符:
| 占位符 | 含义 | 示例(日期 2025-03-18,时间 23:30:01) |
|---|---|---|
%tY |
4 位年份 | 2025 |
%tm |
2 位月份(01-12) | 03 |
%td |
2 位日期(01-31) | 18 |
%tH |
24 小时制小时(00-23) | 23 |
%tM |
2 位分钟(00-59) | 30 |
%tS |
2 位秒(00-60) | 01 |
1 | //获取当前日期 |
4.时间差计算LocalDate/LocalTime/LocalDateTime均提供until()方法,用于计算两个时间之间的差值,需指定结束时间和时间单位(如天、月、年),返回long类型的差值。
语法:long diff = 起始时间.until(结束时间, 时间单位);
时间单位又是什么呢?(ChronoUnit枚举)
ChronoUnit.YEARS:年ChronoUnit.MONTHS:月ChronoUnit.DAYS:天ChronoUnit.HOURS:小时ChronoUnit.MINUTES:分钟ChronoUnit.SECONDS:秒import java.time.temporal.ChronoUnit;
例如:计算2021年10月31日到此刻(2025年11月30日)的时间差。
1 | import java.time.LocalDate; |
运行结果:
1 | 2021-10-31到此刻(2025年11月30日)相差: |
Math类
Math 类位于java.lang包(默认导入),所有方法均为静态方法,无需创建对象即可调用,底层多为native实现(调用 C/C++ 代码,效率极高),是日常开发中最常用的数学工具类。
不可实例化:构造方法被private修饰,无法创建 Math 对象,直接通过Math.方法名()调用;
常用常量:
Math.PI:圆周率(约3.14)Math.E:自然常数(约2.7),用于对数、指数运算。
方法:
(1)绝对值与极值运算
| 方法 | 语法 | 功能 | 示例 |
|---|---|---|---|
abs() |
static double abs(double a) |
返回参数的绝对值(支持int/long/float/double) |
Math.abs(-5.2) → 5.2 |
max() |
static double max(double a, double b) |
返回两个数的最大值 | Math.max(3.8, 5.1) → 5.1 |
min() |
static double min(double a, double b) |
返回两个数的最小值 | Math.min(2, 7) → 2 |
(2)幂运算与开方
| 方法 | 语法 | 功能 | 示例 |
|---|---|---|---|
pow() |
static double pow(double a, double b) |
计算a的b次幂(a^b) |
Math.pow(2, 3) → 8.0(2³) |
sqrt() |
static double sqrt(double a) |
计算a的平方根(a≥0,否则返回NaN) |
Math.sqrt(16) → 4.0 |
cbrt() |
static double cbrt(double a) |
计算a的立方根(支持负数) |
Math.cbrt(-8) → -2.0 |
(3)取整运算
| 方法 | 规则 | 示例(输入→输出) |
|---|---|---|
ceil(double a) |
向上取整(取≥a 的最小整数) | 2.1→3.0、-1.5→-1.0 |
floor(double a) |
向下取整(取≤a 的最大整数) | 2.9→2.0、-1.2→-2.0 |
round(double a) |
四舍五入(double返回long,float返回int) |
2.5→3、-3.5→-3 |
| (4)随机数 | ||
static double random()返回[0.0, 1.0)之间的伪随机浮点数(包含 0.0,不包含 1.0)。 |
1 | //可以扩展生成指定范围[min, max)的整数 |
BigInteger类
long类型最大只能表示9223372036854775807(约 9e18),当需要处理更大整数(如密码学、金融计算)时,需使用java.math.BigInteger类,它支持任意精度整数运算,仅受内存限制。
不可变性:对象创建后值无法修改,所有运算(加 / 减 / 乘 / 除)均返回新的BigInteger对象。
方法:
| 方法 | 语法 | 功能 | 示例 |
|---|---|---|---|
add() |
BigInteger add(BigInteger val) |
加法运算 | a.add(b) → a+b |
subtract() |
BigInteger subtract(BigInteger val) |
减法运算 | a.subtract(b) → a-b |
multiply() |
BigInteger multiply(BigInteger val) |
乘法运算 | a.multiply(b) → a×b |
divide() |
BigInteger divide(BigInteger val) |
除法运算(整除,无小数) | BigInteger.valueOf(10).divide(BigInteger.valueOf(3)) → 3 |
remainder() |
BigInteger remainder(BigInteger val) |
取余运算 | BigInteger.valueOf(10).remainder(BigInteger.valueOf(3)) → 1 |
compareTo() |
int compareTo(BigInteger val) |
比较大小(1→当前大,-1→当前小,0→相等) | a.compareTo(b) > 0 → a > b |
pow() |
BigInteger pow(int exponent) |
幂运算(当前数的exponent次幂) |
BigInteger.valueOf(2).pow(10) → 1024 |
Random类
java.util.Random类是 Java 生成伪随机数的核心类,比Math.random()更灵活(支持多种数据类型、指定种子),适用于游戏开发、测试数据生成、随机事件模拟等场景。
伪随机数:生成的随机数序列是 “确定性” 的 —— 只要种子(seed)相同,生成的随机数序列完全一致;
import java.util.Random;
构造方法
| 构造方法 | 语法 | 功能 | 适用场景 |
|---|---|---|---|
| 默认种子 | Random() |
用当前系统时间毫秒数作为种子 | 需每次运行生成不同随机序列(如游戏随机事件) |
| 指定种子 | Random(long seed) |
使用seed作为种子创建一个Random对象(如果用相同的种子值多次创建实例,那么将生成完全相同的随机数序列) |
需重现相同随机结果(如测试固定随机数据) |
生成方法
| 方法 | 语法 | 功能 | 示例 |
|---|---|---|---|
nextInt() |
int nextInt() |
返回任意int值(-2³¹ ~ 2³¹-1) |
random.nextInt() → -123456(随机正负整数) |
nextInt(int bound) |
int nextInt(int bound) |
返回[0, bound)的整数(bound>0) |
random.nextInt(10) → 0~9的随机整数 |
nextDouble() |
double nextDouble() |
返回[0.0, 1.0)的浮点数 |
random.nextDouble() → 0.678 |
nextBoolean() |
boolean nextBoolean() |
返回随机true或false(概率各 50%) |
random.nextBoolean() → true |
nextBytes(byte[] bytes) |
void nextBytes(byte[] bytes) |
生成随机字节填充数组 | byte[] arr = new byte[5]; random.nextBytes(arr); |
代码示例:
1 | Random random1 = new Random(); |
第一次运行结果:
1 | 30 |
第二次运行结果:
1 | 49 |
拓展:生成[min, max)范围的整数(如 1~100)。
1 | int min = 1, max = 100; |
输入流与输出流
【一个讲得很好的视频】33_【IO】IO流的引入_哔哩哔哩_bilibili
input output -> I/O流
File类
File 类是 Java java.io包中用于操作文件和目录属性的核心工具类,其核心作用是 “描述文件 / 目录的信息”,而非直接读写文件内容。它能获取文件路径、大小、权限等属性,也能实现文件 / 目录的创建、删除、遍历等操作,是所有 I/O 流操作的 “前置基础”。
import java.io.*;
构造方法
| 构造方法 | 语法格式 | 含义解释 | 示例 |
|---|---|---|---|
| 直接指定路径 | File(String pathname) |
用字符串路径创建 File 对象,路径可绝对可相对 | new File("D:\\letter.txt")(绝对路径)、new File("src\\test.java")(相对路径) |
| 父路径 + 子名称 | File(String parent, String child) |
父路径为字符串,子名称为文件 / 目录名,自动拼接路径 | new File("D:\\myDir", "note.txt") → 路径为D:\myDir\note.txt |
| 父 File 对象 + 子名称 | File(File parent, String child) |
父目录为已存在的 File 对象,子名称为文件 / 目录名,灵活适配目录复用场景 | File parent = new File("D:\\myDir"); new File(parent, "note.txt") |
常用方法
| 方法名 | 语法 | 功能描述 | 示例(文件存在时) |
|---|---|---|---|
getName() |
String getName() |
获取文件 / 目录的名称(不含路径) | new File("D:\\note.txt").getName() → "note.txt" |
getAbsolutePath() |
String getAbsolutePath() |
获取绝对路径(从系统根目录到目标的完整路径) | 相对路径创建的对象调用后返回完整路径 |
getParent() |
String getParent() |
获取父目录路径(若没有则返回 null) | new File("D:\\myDir\\note.txt").getParent() → "D:\\myDir" |
length() |
long length() |
获取文件长度(单位:字节),目录返回 0 | 文本文件"abc"返回 3L |
exists() |
boolean exists() |
判断文件 / 目录是否存在 | 存在返回true,否则false |
isFile() |
boolean isFile() |
判断是否为普通文件(非目录) | 文件返回true,目录返回false |
isDirectory() |
boolean isDirectory() |
判断是否为目录 | 目录返回true,文件返回false |
canRead() |
boolean canRead() |
判断是否可读 | 允许读取返回true |
canWrite() |
boolean canWrite() |
判断是否可写 | 允许修改返回true |
isHidden() |
boolean isHidden() |
判断是否为隐藏文件 / 目录 | 隐藏返回true |
lastModified() |
long lastModified() |
获取最后修改时间(毫秒时间戳) | 需转换为日期格式(如new Date(file.lastModified())) |
代码示例:
1 | File f = new File("src\\Chapter9\\Example1\\tom\\cat","Example9_1.java"); //Windows系统用\(需转义为\\) |
运行结果:
1 | Example9_1.java是可读的吗: |
目录相关操作
◆boolean mkdir() → 创建单级目录;
◆boolean mkdirs() → 创建多级目录(父目录不存在时自动创建)。
1 | File dir1 = new File("D:\\myDir"); |
◆boolean createNewFile() → 创建普通文件(目录不存在时抛出异常)。
1 | File file = new File("D:\\note.txt"); |
◆boolean delete() → 删除文件或空目录(非空目录无法直接删除)。
1 | File javaDir = new File("D:\\javaTest"); |
运行可执行文件
通过Runtime类配合File对象,可打开本地可执行文件。
代码示例:
1 | Runtime runtime; |
核心讲解:exec(String command) 是 Runtime 类的核心方法,功能是将参数 command 作为 “操作系统命令” 执行。此处command是记pythonIDE的绝对路径 C:\Users\惠普\Downloads\Edge\pycharm-community-2025.1.3.1.exe,因此该方法会告诉操作系统:“启动路径为C:\Users\惠普\Downloads\Edge\pycharm-community-2025.1.3.1.exe的程序”。
文件字节输入、输出流
1 | // 1.创建流对象(关联目标文件) |
文件字符输入、输出流
与字节流(FileInputStream/FileOutputStream)的区别:
| 对比维度 | 字符流(FileReader/FileWriter) |
字节流(FileInputStream/FileOutputStream) |
|---|---|---|
| 操作单位 | 字符(按编码映射字节) | 字节(1 字节 = 8 位) |
| 适用文件 | 文本文件(.txt、.java等) |
所有文件(文本、图片、音频等二进制文件) |
| 编码处理 | 自动处理编码转换,不易乱码 | 需手动处理编码,读写文本易乱码 |
| 核心方法 | 支持字符串、字符数组读写 | 仅支持字节、字节数组读写 |
1 | // 1.创建字符输入流(读源文件)和输出流(写目标文件) |
文件锁
文件锁是 Java 中用于控制多进程 / 多线程对同一文件访问权限的机制,核心作用是避免多个程序同时修改文件导致的数据错乱。文件锁的本质是给文件加 “访问权限控制”,让多个程序按规则有序访问文件,避免数据错乱。
两种锁类型
- 独占锁(Exclusive Lock,写锁)
同一时间,仅允许一个程序持有独占锁,其他程序既不能获取独占锁,也不能获取共享锁 - 共享锁(Shared Lock,读锁)
同一时间,允许多个程序持有共享锁,但所有持有共享锁的程序仅能读取文件,不能修改;若已有程序持有共享锁,其他程序不能获取独占锁;
文件锁使用步骤
- 创建支持读写的文件流
1 | // 创建RandomAccessFile流,模式为"rw"(读写权限),否则无法获取锁 |
- 从流中获取
FileChannel对象
1 | // 从RandomAccessFile流中获取FileChannel对象 |
- 获取文件锁
1 | // 方式1:阻塞获取独占锁(默认对整个文件加锁) |
- 执行文件操作并释放锁
1 | try { |
操作完成后必须手动释放锁(FileLock.release()),否则其他程序会一直阻塞,导致资源泄漏;
JDBC
概述
JDBC(Java Database Connectivity)是 Java 提供的一套访问数据库的标准 API—— 简单说,它就是 Java 程序和数据库之间的 “桥梁”。
JDBC 采用 “Java 程序 → JDBC API → 数据库驱动 → 数据库” 的分层结构。
JDBC的API提供了以下接口和类:DriverManager:这个类管理一系列数据库驱动程序。匹配连接使用通信子协议从 JAVA 应用程序中请求合适的数据库驱动程序。Connection:此接口具有接触数据库的所有方法。该连接对象表示通信上下文,即,所有与数据库的通信仅通过这个炸接对象进体。
增删改查
代码示例:用 Java 连接 MySQL,查一张表的所有数据并打印。
1 | import java.sql.*; //导入JDBC核心包 |
代码示例:给 bookList 表加 2 本新书;把其中一本叫 “大学英语”的书,出版日期改成 2019-12-26;然后查所有书的信息,打印出来确认操作是否成功。
1 | import java.sql.*; //导入JDBC核心包 |
上一个案例是手动写 DriverManager.getConnection(...) 建立连接,这个案例用了 自定义工具类 GetDBConnection:把 “加载驱动、建立连接” 的重复代码封装起来,后续用的时候直接调用 connectDB(数据库名, 用户名, 密码) 就行,简化开发。
◆执行增删改 SQL:用 executeUpdate() 方法,返回 “影响的行数”(比如新增 2 条就返回 2,修改 1 条就返回 1)。
◆执行查询 SQL:用 executeQuery() 查全表数据,结果存入 rs。
◆遍历结果集:用rs.next() 移动指针,逐行获取数据并打印,确认新增和修改是否生效;
预处理
Statement:每次执行 SQL 都要 “拼接 SQL 字符串 → 数据库编译 SQL生成内部命令 → 执行”。
PreparedStatement:对参数sql指定的SQL语句进行预编译处理,生成数据库底层的内部命令,并封装在该对象中,只要调用就能直接执行。




