class
中;private
或者public
;main()
方法是必需的,每一个Java程序都有;main()
方法中的任何代码都会被执行;xxx
类中的main()
方法中的方法(函数
),相当于嵌套调用;可以使用
println()
方法将一行文本打印到屏幕上,如:public static void main(String[] args) { System.out.println("Hello World"); }
//
开头,java将默认忽略//
和行尾之间的任何文本;/*
开头,*/
结尾,同样两者之间的文本会被Java忽略;string
:文本,字符串;int
:有符号数;float
:浮点数;char
:存储单个字符,通过"……"
的形式接收,当然'……'
也行;boolean
:存储布尔值,有true
和false
两种;type variable (= value)
;final
关键字声明在变量定义前面,如:final int num=1;
System.out.println()
的方式用于显示变量;,
隔开,如int i1=1,i2=2;
$
和_
开头;
byte
,short
之类的;
String
,Arrays
和Classes
;
数据类型 | 大小 | 描述 |
---|---|---|
byte | 1B | 有符号二进制数 |
short | 2B | 范围小有符号数 |
int | 4B | 范围适中的有符号数 |
long | 8B | 范围较大的有符号数 |
float | 4B | 范围适中的浮点数 |
double | 8B | 范围较大的浮点数 |
boolean | 1b,即一位 | 存储true 和false |
char | 2B | 存储单个字符 |
long---L
,float---f
,double---d
;true
或者false
;e
表示10的幂,如12e9=12*10^9
;注意,字符串的string要写成大写的String,而且它不是原始数据类型,因为使用它的时候需要引用到一个对象;
null
填充;相对固定
);byte-->short-->char-->int-->long-->float-->double
;(……)
int myInt = (int)myDouble;
%
取模;++
自加1,--
自减1;+=
自加,-=
自减,/=
自除,|=
位或运算,^=
异或运算,&=
位并运算,>>=无符号部分右移得到移码
,无符号部分左移得到移码
;==
等于,!=
不等于;&&
逻辑与、||
逻辑或、!
逻辑非;&
位交,|
位并,~
位取反,^
位异或,<<
左移码,>>
右移码,>>>
右移码并补零;String
来存储文本,用双引号包围的字符来传值;.length()
返回;toUpperCase()
将字符串转变成大写,toLowerCase()
将字符串转变成小写;indexOf()
返回第一次出现的位置(学了数据结构的都知道从零开始
);+
可以连接前后两个字符串、又或者使用<前者>.concat(<后者>)
的方法;\
将特殊字符转换为字符串;\n
:换行;\r
:回车;\t
:制表;\b
:空格;\f
:换页;注意:如果是一个数字和一个字符串执行
+
操作,则数字部分会被强制转化为字符串进行运算。
用
Math
类的方法
max(x,y)
:用于查找x
和y
的最大值;min(x,y)
:用于查找x
和y
的最小值;sqrt(x)
:用于返回x的平方根;abs(x)
:用于返回x的绝对值;random()
:返回一个介于[0.0,1.0)的随机数,至于如果你要0到10^k之间的随机数,可以用上述随机数乘以10^k+1
来获得;所有的数学方法都是
static
静态的;
variable=(condition)? expressionTrue:expressionFalse;
其中前者的表达式是条件为真使用的,后边是条件为假使用的。switch……case
型;switch(expression){
case x:
代码块1;
(break;)
case y:
代码块2;
(break;)
····
(default:
代码块n;)
}
switch
先计算一次;case
将计算的结果和每个case
后的情况进行条件比较。如果符合的话执行后边相关的代码块;break
、default
是可有可无的;expression
必须是整数、字符、字符串、枚举或者是它们的表达式;break
用于跳出switch
语句,没有它会导致继续执行下一个case
代码块。所以通常在每一个case
语句的末尾添加break
;default
少用,建议在处理不期望的输入时使用它。 for(代码块第一次执行前执行一次;判断代码块是否执行的条件;代码块执行后执行){
代码块
}
for-each
循环,它专门用于循环在数组Array
中的元素。X型数组A=(A1,A2,...);
for (X型值i : A){
代码块
}
典型的有:String[] cars = {"Volvo", "BMW", "Ford", "Mazda"};
for (String i : cars) {
System.out.println(i);
}
break
跳出循环,continue
跳出本轮循环。他们俩常配置着循环语句使用。数组类型[] 数组名;
{···}
里。.length
可以返回数组有多少个元素;for
循环常用于遍历数组,不过for-each
可读性更强,更推荐使用;有大有小
),所以方法必须在类里边申明。()
的方式来传递参数,申明时也要标准形参的数据类型,不同参数之间用,
隔开。return
传递。如果是不需要返回值,用void
申明;operator
运算符,直接下边重写就可以了。class
关键字开始,类的权限则出现在一开头class
的左边。类名 对象名 = new 类名(可能的传参);
其中等号的后半部分是为了申明该对象需要分配的物理存储空间;变量
),另一个类则含有main()
方法;在命令行中,需要用:
javac 对象名.java//编译文件 java 对象名//执行文件
.
取值符来访问,而且可以在新生成的对象中修改它,但只对对象自己有效。这样看来,类中赋值了的属性,字段或者说变量更像是default默认值一般的存在。final
关键字锁死。对象名.方法名();
static
静态和public
公共属性和方法的Java程序;static
:可以在不创建类的对象的情况下访问该方法,例如Math
中的数学计算方法都是static
的就是这个原因;public
:只能用对象来访问方法;public
的类方法,我们必须先创建对象再调用。.
用于访问对象的属性和方法。void
;public class MyClass {
int x;
public MyClass(int y) {
x = y;
}
public static void main(String[] args) {
MyClass myObj = new MyClass(5);
System.out.println(myObj.x);
}
}
// 输出 5
class
常用的修饰符有:
public
:任何类都可以访问;default
:只能由同一包中的类访问(一般出现在不指定修改器
);final
:该类不能被其他类继承(和用在属性上一样,表示不改了
);abstract
:该类被抽象了出来,自身不能创建对象,只能通过继承的方式创建;attribute
或者means
和constructor
来说:
public
:所有类都可以访问;private
:只能该类内部访问,即通过类的方法,其他类无法直接访问属性(便于实现封装、隐藏内部实现细节,提高代码的安全性和可维护性
);default
:该类只能由同一包中的类进行访问,在不指定修改器时使用;protected
:代码可以在相同包,和子类中访问;final
;static
:属性和方法属于类而不是对象;abstract
:只能在抽象类中使用,且只能在方法上使用,即主体部分只能由生成的对象提供。transient
*:序列化包含属性和方法的对象时,将跳过属性和方法
- 在Java中,序列化(Serialization)是指将对象的状态转换为字节流的过程,从而可以将对象保存到文件、数据库或通过网络传输给其他Java虚拟机(JVM)。反序列化(Deserialization)是将字节流转换回对象的过程。序列化的主要作用是持久化对象状态和对象之间的远程通信。
- 关键点:
- 实现Serializable接口:要使一个Java对象可以序列化,该类必须实现java.io.Serializable接口。
- 序列化过程:使用ObjectOutputStream类的writeObject()方法将对象转换为字节流。
- 反序列化过程:使用ObjectInputStream类的readObject()方法将字节流转换回对象。
- transient关键字:用transient关键字修饰的字段不会被序列化。
volatile
:属性值不是本地缓存的线程,总是从主存中读取
- 用于修饰变量,确保在多个线程间对该变量的读写操作的可见性。它的主要作用是保证变量的可见性和防止指令重排序。
主要用途:
- 变量的可见性:
- 当一个变量被声明为volatile时,Java内存模型保证所有线程都能看到该变量的最新值。当一个线程修改了volatile变量的值,新值会立即被刷新到主内存中,其他线程读取时可以立即获得最新值。
- 防止指令重排序:
- volatile变量在读取和写入时都会插入内存屏障(memory barrier),这可以防止编译器和处理器对这些操作进行重排序,从而保证了代码执行的顺序一致性。
private
;get
和set
方法来访问和更新private
私有变量的值;get
方法返回变量值,set
方法设置值,两者的语法都是以get
或set
开头,后跟变量名,第一个字母大写;如果只使用get方法
),也可以设置为只写(如果只使用set方法
);package
)API
- Java API 是Java开发环境中包含的一个预编写类库,该库分为包和类,可以免费使用;
- 完整列表可在Oracles网站上找到;
- 该库包含用于管理输入、数据库编程等的组件;
import .
,而且要使用导入的类需要创建该类的对象;import .*
;package
关键字;package <自定义包名>
class <自建类>{……}
-d 关键字指定保存类文件的目标位置,空格后可以直接跟地址也可以直接跟
.
表示在同一目录下(linux
)。
java <自定义包名>.<自建类>
;extends
关键字。总结:访问权限
private
:仅在同一个类可访问;default
(无修饰符):仅在同一个包中可访问;protected
:在同一个包,以及不同包的子类中可以访问;public
:在任何地方都可以访问;
【注意】:protected
成员在子类中可以重写,但在子类外部(即使是子类的实例)不能直接访问这些成员。
protected
进行安全性的保障,但要注意子类的实例无法使用带该关键字的属性、方法。final
关键字;一层一层实体化
);public
或者default
两种,内部类新增加了private
和protected
两种。如果你想对类设置一些权限,可以采用内部类的方式,甚至直接private
禁止访问。static
的,也就是静态的,属于类但不属于对象,这意味着可以在不创建外部类的对象的情况下访问它。
但是这与
static
静态属性、方法一样,static
的内部类无法访问外部类的属性、方法;
abstract class
抽象类或interfaces
接口来实现;abstract
关键字是非访问修饰符,用于类和方法:
Java 中实现abstraction
抽象的另一种方法是使用接口,实际上是高度抽象;
接口,即interface
关键字定义的部分,是一个完全抽象“类”,它将相关方法和空实体分组,在接口中所有方法都没有主体代码块;
要访问接口方式,需要创建一个由implements
关键字引导“继承的类”。一般情况下,接口的方法在implement类中定义;
Java中的类只能继承一个父类,但可以实现多个接口,这实现了多重继承的效果。
为了实现安全性-隐藏某些细节,只给对象(接口)显示重要细节,例如api
;
一些性质的简要说明:
即使不加abstract关键字
);implement
类提供;abstract
抽象的和public
公共的;public
, static
和 final
;public static final
的;在Java中,接口(interface)是一种引用类型,用于定义一组抽象方法和常量。接口不能包含具体实现,具体的实现由实现接口的类提供。接口在Java中扮演着重要角色,特别是在设计良好的API和实现多重继承时。
abstract
关键字),在Java 8之后,可以包含默认方法和静态方法。public static final
的。public
:接口中的方法默认是public
的,不能是private
或protected
。public interface Animal {
void eat();
void sleep();
}
public class Dog implements Animal {
@Override
public void eat() {
System.out.println("Dog is eating");
}
@Override
public void sleep() {
System.out.println("Dog is sleeping");
}
}
public class Cat implements Animal {
@Override
public void eat() {
System.out.println("Cat is eating");
}
@Override
public void sleep() {
System.out.println("Cat is sleeping");
}
}
使用接口实现多态性:
public class AnimalFeeder {
public void feed(Animal animal) {
animal.eat();
}
public static void main(String[] args) {
AnimalFeeder feeder = new AnimalFeeder();
Animal dog = new Dog();
Animal cat = new Cat();
feeder.feed(dog); // 输出: Dog is eating
feeder.feed(cat); // 输出: Cat is eating
}
}
default
关键字。static
关键字。public interface Animal {
void eat();
void sleep();
default void breathe() {
System.out.println("Animal is breathing");
}
static void description() {
System.out.println("This is an animal");
}
private void privateMethod() {
// 仅供默认方法或静态方法调用
}
}
implements
说明之后用,
隔开多个接口名。enum
枚举是一个特殊的"类",它表示一组常量;
常量指不可更改的变量,如final
定义下的变量;
要创建enum,请使用enum
关键字,并用逗号,
分隔常量;
【注意】:定义的常量名应该为大写字母;
与数组一样,可以使用.
来访问枚举中的常量;
可以在类中创建enum
枚举;
枚举通常用于switch
语句中作为case
的比较值检查相应的值;
枚举类型有一个values()
方法,该方法返回所有枚举常量的数组。如果要循环遍历枚举的常量,可以用.values()
先将枚举转化成数组,再使用for-each
方法对其遍历输出/访问即可;
枚举常量的赋值方式是常量名{常量的值,……}
,属性的赋值方法是常量名(属性值)
;
enum
枚举可以像class
一样具有属性和方法。唯一的区别是枚举常量是public static final
;枚举常量可以带有属性,这些属性可以在构造函数中进行初始化。
public enum Day {
MONDAY("Start of the work week"),
TUESDAY("Second day"),
WEDNESDAY("Midweek"),
THURSDAY("Almost there"),
FRIDAY("End of the work week"),
SATURDAY("Weekend"),
SUNDAY("Rest day");
private final String description;
// 构造函数
Day(String description) {
this.description = description;
}
// 获取描述的方法
public String getDescription() {
return description;
}
}
在这个示例中,每个枚举常量都带有一个描述属性,并通过构造函数进行初始化。
枚举还可以包含方法,可以根据需要定义实例方法和静态方法。
public enum Operation {
ADDITION("+") {
public double apply(double x, double y) {
return x + y;
}
},
SUBTRACTION("-") {
public double apply(double x, double y) {
return x - y;
}
},
MULTIPLICATION("*") {
public double apply(double x, double y) {
return x * y;
}
},
DIVISION("/") {
public double apply(double x, double y) {
return x / y;
}
};
private final String symbol;
Operation(String symbol) {
this.symbol = symbol;
}
@Override
public String toString() {
return symbol;
}
// 抽象方法,每个枚举实例必须实现此方法
public abstract double apply(double x, double y);
}
在这个示例中,每个枚举常量都表示一个数学运算,并实现了apply
方法。
public class EnumTest {
public static void main(String[] args) {
// 使用 Day 枚举
for (Day day : Day.values()) {
System.out.println(day + ": " + day.getDescription());
}
// 使用 Operation 枚举
double x = 10.0;
double y = 5.0;
for (Operation op : Operation.values()) {
System.out.printf("%f %s %f = %f%n", x, op, y, op.apply(x, y));
}
}
}
输出:
MONDAY: Start of the work week
TUESDAY: Second day
WEDNESDAY: Midweek
THURSDAY: Almost there
FRIDAY: End of the work week
SATURDAY: Weekend
SUNDAY: Rest day
10.000000 + 5.000000 = 15.000000
10.000000 - 5.000000 = 5.000000
10.000000 * 5.000000 = 50.000000
10.000000 / 5.000000 = 2.000000
private
修饰符。通过这些特性,Java的枚举类型不仅仅是简单的常量集合,还能实现复杂的行为和属性,使其在代码设计中更加灵活和强大。
scanner
类用于获取用户输入,这个类属于java.util
包中;scanner
类,需要创建该类的对象,从而使用scanner
类文档中任何可用的方法;next<数据类型,记得开头字母要大写>
:
nextBoolean()
:从用户处读取boolean布尔值;nextByte()
:从用户处读取byte字节值;nextDouble()
:从用户处读取double双精度值;与C++不同,Java没有内置的Date类,但是我们可以导入java.time
包来使用Date
类和time API
;
java.time
包含的时间和日期类有:
LocalDate
:表示日期,格式是年–月–日;LocalTime
:表示时间,格式是小时–分钟–秒钟–纳秒;LocalDateTime
:表示日期和时间;DateTimeFormatter
:格式化显示日期时间;now()
方法可以调出当前的时间数据,不同的类都有其自己的now()
方法;
如果你想让时间按照自己喜欢的格式显示,可以用DateTimeFormatter
类格式化,并用.offpattern
描述自己需要的格式,并接纳;
上述描述,小写的dd
代表日期,大写的若干个M
代表月份,yyyy
象征年份。
java.util
包中,包含ArrayList
类,它是一个可以调整大小的数组(array
),或者说C++的向量
、顺序表
;ArrayLis
和Array
最大的区别就是可以改变大小,可以随意增加或者删除元素;生成:
import java.util.ArrayList; // 导入 ArrayList 类
ArrayList<ElemType> <Name> = new ArrayList<ElemType>(); // 创建一个 ArrayList 对象
增加:.add(<值>)
的方法,且增加在末尾;
查询:.get位置索引)
的方法;
修改:。set(位置索引,值)
的方法;
删除:.remove(位置索引)
;
计算大小:.size()
的方法;
遍历:for
循环 + .size()
方法找到数量大小 / for-each
循环;
排序:在java.util
包中,还有一个非常有用的类Collection
。该类中的.sort()
方法可用于按字母或者数字顺序列表的排序,默认是升序。当然除此之外,你还可以传递一个实现了comparator
接口的实例给该方法,以自定义的顺序进行排序,如:
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
public class Main {
public static void main(String[] args) {
ArrayList<Integer> list = new ArrayList<>();
list.add(3);
list.add(1);
list.add(2);
// 自定义排序:降序
Collections.sort(list, new Comparator<Integer>() {
@Override
public int compare(Integer o1, Integer o2) {
return o2 - o1; // 降序排序
}
});
System.out.println(list); // 输出: [3, 2, 1]
}
ArrayList
几乎相同,LinkedList
的差异只是它是链表;LinkedList
也实现了List
接口,所以增删改查是完全一样的。ArrayList
通过数组储存元素,当输入超过容量,它会新建一个更大的数组并迁移其中,删去旧有的;LinkedList
则是以容器为单位构建的链表。不同数据类型对应不同的容器。Linkedlist
有以下方法,如:
addFirst()
:将一个项目添加到列表的开头;addLast()
:将项目添加到列表末尾;removeFirst()
:从列表的开头删除一个项目;removeLast()
:从列表末尾删除一个项目;getFirst()
:获取列表开头的项目;getLast()
:获取列表末尾的项目;在数组列表中,查找元素都需要使用int
型的索引;而在HashMap
中,使用了键值对(Python中的数据类型是字典
),将元素存在key/value
中,故可能可以使用Char
型或者String
型的索引进行查找;
同样要从java.util
的库中调用HashMap
类,并创建对象实体化后使用,具体格式如下:
import java.util.HashMap
HashMap<key,value> name = new HashMap<key,value>();
关于HashMap
有几种常用的方法:
.put()
:添加键值对,参数用,
隔开;.get()
:使用key值查找value;.remove()
:使用key值删除对应的键值对;.size()
:返回这个表的大小;for-each
循环遍历整个表;.keyset()
框定范围;同理,可以使用.values()
框定返回所有值;值得注意的是,使用HashMap<……,……>
中创建对象时,数据类型同样填入包含类的而不是基本类型,也就是:
同样在java.util
的包中,使用时创建对象;
区别在于它是集合,类似于Python,每个元素都是唯一的;
创建:
import java.util.HashSet;
Hashset<ElemType> name = new HashSet<Elemtpye>();
一些常用的方法:
.add()
:添加元素;.contain()
:查找是否存在该元素;.remove()
:删去对应的元素;.size()
:返回这个集合的大小;.clear()
:一次性删除所有的元素;for-each
循环遍历整个表;同样,创建对象时声明的数据类型使用的是对象名
或者说包装类
而不是基本数据类型
;
ArrayList
和HashSet
就是两种常用的类容器;.iterator()
可以获取任何集合的迭代器,并创建对象://依照一个集合创建一个迭代器对象
Iterator<ElemType> Name
= SetName.iterator();
.next()
访问这个迭代器的头指针,多次使用可以从头开始遍历整个迭代器(很明显的顺序表思想
);//.next()方法和.hasNest()方法配合,可以想顺序表一样遍历全部的集合,如head指针和p指针
//例如:
while(Name.hasNext()) {
System.out.println(Name.next());
}
使用for
循环或者for-each
循环删除将无法正常工作。.remove()
删除迭代器中的元素;原始数据类型 | 包装类 |
---|---|
byte | Byte |
short | Short |
int | Integer |
long | Long |
float | Float |
double | Double |
boolean | Boolean |
char | Character |
要创建包装器对象,请使用包装器类而不是原始类型。要获取值,您只需打印对象;
以下方法用于获取与对应包装对象关联的值: intValue(), byteValue(), shortValue(), longValue(), floatValue(), doubleValue(), charValue(), booleanValue()
。等于说你用包装类创建对象并赋值后,需要在执行的时候输出值,可以选择使用这个;
可以使用toString()
将包装类中的对象转换为字符串,例如:
Integer myInt = 100;
String myString = myInt.toString();
System.out.println(myString.length());
在Java中,异常可以分为两大类:受检异常(Checked Exceptions)和非受检异常(Unchecked Exceptions)。它们在使用和处理上有一些重要的区别。
非受检异常是指那些不需要强制在代码中进行捕获或声明的异常。它们继承自RuntimeException
类及其子类。非受检异常通常用于表示编程错误,例如逻辑错误或使用不当的API。
NullPointerException
ArrayIndexOutOfBoundsException
ArithmeticException
ClassCastException
IllegalArgumentException
public class Main {
public static void main(String[] args) {
try {
int result = 10 / 0; // 这里会抛出 ArithmeticException
} catch (ArithmeticException e) {
System.out.println("Caught ArithmeticException: " + e.getMessage());
}
}
}
受检异常是指那些需要在代码中显式捕获或声明的异常。它们继承自Exception
类,但不包括RuntimeException
及其子类。受检异常通常用于表示程序的外部条件错误,例如文件未找到或数据库连接失败。
IOException
SQLException
ClassNotFoundException
InterruptedException
import java.io.*;
public class Main {
public static void main(String[] args) {
try {
FileInputStream file = new FileInputStream("test.txt"); // 可能抛出 FileNotFoundException
} catch (FileNotFoundException e) {
System.out.println("Caught FileNotFoundException: " + e.getMessage());
}
}
}
处理要求:
try-catch
)或声明(使用throws
)。用途:
编译器行为:
try
和catch
try
语句定义一段代码块,以便在执行的时候使用其进行错误测试;如果发现错误,执行catch
语句下处理代码块。try {
// 要尝试的代码块
}
catch(Exception e) {
// 处理错误的代码块
}
public class MyClass {
public static void main(String[ ] args) {
try {
int[] myNumbers = {1, 2, 3};
System.out.println(myNumbers[10]);
} catch (Exception e) {
System.out.println("Something went wrong.");
}
}
}
finally
语句允许在try-catch
语句之后照常执行代码,不论后者的执行结果如何。throw
语句允许您创建自定义错误;
throw
语句常与异常类型一起使用;ArithmeticException(算数异常), FileNotFoundException, ArrayIndexOutOfBoundsException, SecurityException
, etc:throw new <ExceptionType>(说明异常的字符串/话)
throw
只是自定义异常,而不是异常类型。如果你想自定义异常类型,需要使用类的继承:class <自定义异常> extends Exception/RuntimeException{
代码块
}
……
throw new <自定义异常>(自定义说明语句)
java.util.regex
包来使用正则表达式。java.util.regex
包提供对正则表达式的支持。这个包中的主要类是Pattern
和Matcher
。complie()
方法,可以在方法中备注 标志(Flag) 改变搜索的执行方式:
Pattern.CASE_INSENSITIVE
- 执行搜索时将忽略英文字母的大小写;Pattern.LITERAL
- 模式中的特殊字符没有任何特殊含义,在执行搜索时将被视为普通字符;Pattern.UNICODE_CASE
- 将它与 CASE_INSENSITIVE
标志一起使用,也可以忽略英文字母表之外的其他字母的大小写;Pattern
类表示一个编译后的正则表达式。它不能直接实例化,而是通过静态方法compile
创建。
Matcher
类是一个引擎,用于解释和匹配输入字符串的模式。可以通过Pattern
对象的matcher
方法创建Matcher
对象。
import java.util.regex.*;
public class RegexExample {
public static void main(String[] args) {
// 创建Pattern对象
Pattern pattern = Pattern.compile("a*b");
// 创建Matcher对象
Matcher matcher = pattern.matcher("aaaaab");
// 检查是否匹配
boolean matches = matcher.matches();
System.out.println("Matches: " + matches); // 输出:Matches: true
}
}
当然可以,以下是根据您提供的图片内容编写的Markdown格式文本:
[abc]
| 从括号内的选项中查找一个字符[^abc]
| 找到一个不在括号内的字符[0-9]
| 从0到9范围内查找一个字符.
| 查找由.
分隔的任意一种模式的匹配项,如:cat.dog
或 fish
?
| 查找任何字符的一个实例^
| 查找作为字符串开头的匹配项,如:^Hello
$
| 在字符串末尾查找匹配项,如:World$
\\d
| 找一个数字\\s
| 查找空白字符\\b
| 在这样的单词开头查找匹配项:\bWORD
,或在这样的单词结尾处查找匹配项:WORD\b
\xxxxx
| 查找十六进制数xxxx
指定的Unicode字符n+
| 匹配任何至少包含一个n的字符串n*
| 匹配包含零次或多次出现n的任何字符串n?
| 匹配包含零次或一次出现n的任何字符串n(x)
| 匹配任何包含一系列Xn的字符串nx,y)
| 匹配任何包含X到Yn序列的字符串n(x,)
| 匹配任何包含至少Xn的序列的字符串请注意,Markdown中的反斜杠\
是转义字符,所以在Markdown中使用时需要使用两个反斜杠\\
来表示一个反斜杠。例如,\d
在Markdown中应写为\\d
。
.
:匹配任意单个字符\d
:匹配数字 [0-9]
\D
:匹配非数字 [^0-9]
( ^
表示取反)\w
:匹配字母或数字 [a-zA-Z_0-9]
\W
:匹配非字母或数字 [^a-zA-Z_0-9]
\s
:匹配空白字符(包括空格、制表符、换页符等)\S
:匹配非空白字符*
:匹配前面的字符零次或多次+
:匹配前面的字符一次或多次?
:匹配前面的字符零次或一次{n}
:匹配前面的字符恰好n次{n,}
:匹配前面的字符至少n次{n,m}
:匹配前面的字符至少n次但不超过m次^
:匹配行的开头$
:匹配行的结尾\b
:匹配单词边界\B
:匹配非单词边界【注意】:对选中区域的匹配,正则表达式字符匹配的先后一般没有影响。每一个匹配符都代表可能存在的一种字符类型。
import java.util.regex.*;
public class EmailValidator {
public static void main(String[] args) {
//由于在字符串中,所以需要使用转义符号\\。
String emailPattern = "^[\\w-\\.]+@([\\w-]+\\.)+[\\w-]{2,4}$";
Pattern pattern = Pattern.compile(emailPattern);
String email = "[email protected]";
Matcher matcher = pattern.matcher(email);
if (matcher.matches()) {
System.out.println(email + " is a valid email address.");
} else {
System.out.println(email + " is not a valid email address.");
}
}
}
import java.util.regex.*;
public class FindDigits {
public static void main(String[] args) {
String text = "My phone number is 123-456-7890 and my zip code is 98765.";
String digitPattern = "\\d+";
Pattern pattern = Pattern.compile(digitPattern);
Matcher matcher = pattern.matcher(text);
while (matcher.find()) {
System.out.println("Found number: " + matcher.group());
}
}
}
compile(String regex)
:编译正则表达式compile(String regex, int flags)
:编译带有指定标志的正则表达式matches()
:尝试将整个区域与模式匹配find()
:扫描输入的序列,查找与该模式匹配的下一个子序列group()
:返回由以前的匹配操作所匹配的输入子序列start()
:返回以前匹配的初始索引end()
:返回最后匹配字符之后的偏移量Java中的正则表达式为文本处理提供了强大的功能,通过Pattern
和Matcher
类,可以灵活地搜索、匹配和操作字符串。掌握正则表达式的基本语法和使用方法,有助于提高字符串处理的效率和灵活性。
结合操作系统的知识,线程即专注于实现程序的某一项功能的部分指令流,它是一个程序中独立执行的最小单位;
它允许程序通过同时执行多项任务来更有效地运行,可以用来在后台执行复杂的任务而不中断主程序(在内核态中分配CPU资源
);
每个Java应用程序至少有一个线程:主线程(mainstream
)。
Java中有两种主要方式创建线程:继承Thread
类,和实现Runnable
接口(后者继承接口后
),具体格式如下:
Thread
类class <线程名> extends Thread{
代码块;
public static void main(String[] args) {
<线程名> 对象名 = new <线程名>();
对象名.start(); // 开启线程
}
}
Runnable
接口class <线程名> implements Runnable{
代码块;
public static void main(String[] args) {
<线程名> 对象名1 = new <线程名>();
//接口的话需要重新实体化成线程,并创建对象使用(殊途同归)
Thread 对象名2 = new Thread(对象名1);
thread.start(); // 开启线程
}
}
操作系统中,线程有五个状态:
当多个线程共享资源时,可能导致资源的不一致性问题。为了解决这个问题,需要进行线程同步。
synchronized
关键字、 volatile
关键字、显示锁(如ReentrantLock
),以及更高级的并发工具(如信号量、栅栏和闭锁)。synchronized
关键字
synchronized
方法是针对整个方法加锁,而synchronized
代码块则可以只对需要同步的部分加锁,从而减少锁的粒度,提高性能。//同步方法
public class Counter {
private int count = 0;
public synchronized void increment() {
count++;
}
public synchronized int getCount() {
return count;
}
}
//同步代码块
public class Counter {
private int count = 0;
public void increment() {
//用this指明需要同步的代码块
synchronized (this) {
count++;
}
}
public int getCount() {
synchronized (this) {
return count;
}
}
}
volatile
关键字
回到操作系统的问题,不同的进程在本关键字的加成下可以共享变量的数据,但是不能保证读写操作之间不会冲突。解决方法是使用其他关键字完成同步,例如synchronized和Atomic
);public class VolatileExample {
private volatile boolean flag = true;
public void stop() {
flag = false;
}
public void run() {
while (flag) {
// do something
}
}
}
显示锁
java.util.concurrent.locks
包中的提供了锁,如ReentranLock
,它们提供了比synchronized
更灵活的同步机制。ReentrantLock
,它是一个可重入锁,类似于synchronized
关键字,但提供了更多的功能和灵活性;具体的使用写在另一份资料里,此处略。
创建线程有几种方法:
Thread
类并覆盖其run()
方法来创建://常见的格式
class <线程名> extends Thread{
public void run() {
System.out.println("Thread is running...");
}
}
Runnable
接口://例如以下格式
public class MyRunnable implements Runnable {
public void run() {
System.out.println("Thread is running...");
}
public static void main(String[] args) {
//建立接口的对象
MyRunnable myRunnable = new MyRunnable();
//将接口对象转变为线程对象
Thread t1 = new Thread(myRunnable);
//启动线程
t1.start();
}
}
Runnable
接口
此处略,在匿名类笔记中有。
//一个很好的例子
public class Main {
public static void main(String[] args) {
Thread t1 = new Thread(() -> System.out.println("Thread is running..."));
t1.start();
}
}
Thread
类,则将其实例化后可以调用.start()
方法来运行线程;Runnable
接口,则可以通过将类的实例传递给Thread
(由变成Thread的对象实例化),并调用线程的.start()
运行它。补充:“继承自类”和“部署接口”的区别:
- 继承自
Thread
类时,除了Thread
之外无法同时继承其他的类;- 部署
Runnable
接口时,也允许从其他的类继承过来(这两个同时发生
);
ReadWriteLock的用武之地
);isAlive()
方法检查线程是否已完成运行。public class MyClass extends Thread {
public static int amount = 0;
public static void main(String[] args) {
MyClass thread = new MyClass();
thread.start();
// 等待线程完成
while(thread.isAlive()) {
System.out.println("Waiting...");
}
//解除循环意味着线程执行完毕
// 更新 amount 并打印其值
System.out.println("Main: " + amount);
amount++;
System.out.println("Main: " + amount);
}
//重写run()方法;
public void run() {
amount++;
}
}
表达式是一小段代码,它接受参数并返回一个值。 Lambda 表达式类似于方法,但它们不需要名称,并且可以直接在方法体中实现;
设置格式:
<para1,para2,···> -> expression;
由于其过于简练,故在表达方式有限的情况下,它需要立即返回一个值,并且不能包含变量、赋值或语句。由此,为了进行更复杂的操作,可以使用花括号包含代码块。如果 lambda 表达式需要返回一个值,那么代码块应该有一个return
语句。
Lambda 表达式通常作为参数传递给方法。而且如果变量类型是只有一个方法的接口,则Lambda
表达式可以存储在变量中,如Consumer
中method接口是Consumer类接口(在java.util.function.Consumer中调用
)
,可以作为存储的容器。
要在方法中使用Lanbda表达式
,该方法应该要有一个参数,其类型为单方法接口。调用这个方法的时候就会运行Lambda
表达式。
实例:
//设置单方法接口
interface StringFunction {
String run(String str);
}
public class MyClass {
public static void main(String[] args) {
//对于main方法,它的参数是StringFunction接口类型的,而这个接口是单方法的,保证了传递Lambda表达式时的唯一性传递。
StringFunction exclaim = (s) -> s + "!";
StringFunction ask = (s) -> s + "?";
printFormatted("Hello", exclaim);
printFormatted("Hello", ask);
}
public static void printFormatted(String str, StringFunction format) {
String result = format.run(str);
System.out.println(result);
}
}
java.io
包中的File
文件类允许我们处理文件;方法 | 类型 | 描述 |
---|---|---|
canRead() |
Boolean | 测试文件是否可读 |
canWrite() |
Boolean | 测试文件是否可写 |
createNewFile() |
Boolean | 创建一个空文件 |
delete() |
Boolean | 删除文件 |
exists() |
Boolean | 测试文件是否存在 |
getName() |
String | 返回文件名 |
getAbsolutePath() |
String | 返回文件的绝对路径名 |
length() |
Long | 返回文件大小(以字节为单位) |
list() |
String[] | 返回目录文件的数组 |
mkdir() |
Boolean | 创建目录 |
createNewFile()
方法,它将返回一个boolean值以表示文件是否创建成功;try...catch
块中。 这是必要的,因为如果发生错误(如果由于某种原因无法创建文件),它会抛出IOException
;//导入File类和IOException类
import java.io.File;
import java.io.IOException;
···
//使用try-catch语块
try {
//新建文件类对象,并声明文件名
File myObj = new File("filename.txt");
//根据返回值判断下一步行动
if (myObj.createNewFile()) {
System.out.println("File created: " + myObj.getName());
} else {
System.out.println("File already exists.");
}
} catch (IOException e) {
System.out.println("An error occurred.");//抛出异常
e.printStackTrace();
}
/Users/name/filename.txt
。FileWriter
类和write()
方法将一些文本写入已经创建的文件中去,如:FileWriter myWriter = new FileWriter("filename.txt");
myWriter.write("Files in Java might be tricky, but it is fun enough!");
之后,如果我们需要关闭我们创建的文本文件,直接使用.close()
方法即可,如:myWriter.close();
FileReader、BufferedReader、Files、Scanner、FileInputStream、FileWriter、BufferedWriter、FileOutputStream
)可用于在Java中读取和写入文件,具体使用那一版取决于需要读取的字节、字符,以及文件/行的大小。Scanner
类来读取我们创建/储存的文本文件的内容:import java.io.File; // 导入 File 文件类
import java.io.FileNotFoundException; // 导入这个类来处理错误
import java.util.Scanner; // 导入 Scanner 类以读取文本文件
···
File myObj = new File("filename.txt");
Scanner myReader = new Scanner(myObj);//生成Scanner类的对象以开始扫描读取工作
while (myReader.hasNextLine()) {
String data = myReader.nextLine();
System.out.println(data);
}//以行为单位逐行输出文件内容
除了.nextLine()
方法以外,要获取更多的文件信息,可以使用之前提到的File
方法。
.delete()
方法,它的返回值是一个布尔值;if (myObj.delete()) {
System.out.println("Deleted the file: " + myObj.getName());
} else {
System.out.println("Failed to delete the file.");
}
如果不为空,delete()方法会返回false,删除操作将失败
):import java.io.File;
public class DeleteDirectory {
public static void main(String[] args) {
// 指定要删除的目录
File directory = new File("path/to/directory");
// 调用递归删除方法
boolean result = deleteDirectory(directory);
if (result) {
System.out.println("目录删除成功");
} else {
System.out.println("目录删除失败");
}
}
// 递归删除目录的方法
public static boolean deleteDirectory(File directory) {
// 如果目录不存在,返回 true
if (!directory.exists()) {
return true;
}
// 如果目录是文件,直接删除并返回删除结果
if (directory.isFile()) {
return directory.delete();
}
// 获取目录中的所有文件和子目录,用文件数组的形式存储
File[] files = directory.listFiles();
if (files != null) {
// 递归删除目录中的所有内容
for (File file : files) {
if (!deleteDirectory(file)) {
return false;
}
}
}
// 删除目录本身
return directory.delete();
}
}
也可以直接使用org.apache.commons.io.FileUtils
库中的方法,使用前要先添加到你的项目中。import org.apache.commons.io.FileUtils;//调用这个特定的库
import java.io.File;
import java.io.IOException;
public class DeleteDirectory {
public static void main(String[] args) {
// 指定要删除的目录
File directory = new File("path/to/directory");
try{
// 调用 FileUtils.deleteDirectory 方法删除目录,一步就搞定了
FileUtils.deleteDirectory(directory);
System.out.println("目录删除成功");
}catch (IOException e) {
e.printStackTrace();
System.out.println("目录删除失败");
}
}
}
Scanner
类从控制台接受用户的输入,并使用PrinterWriter
类将输入的数据写入文件。若之后还要读取文件内容,继续使用Scanner
类对象从文件中读取数据。import java.io.File;//文件的类
import java.io.FileNotFoundException;//解决没找到的文件异常的类
import java.io.PrintWriter;//将输入写入文件的类
import java.io.IOException;//解决文件I/O异常的类
import java.util.Scanner;//接受数据并输出的类
public class FileInputExample {
public static void main(String[] args) {
// 创建一个Scanner对象,用于接收用户输入
Scanner scanner = new Scanner(System.in);
System.out.print("请输入一些文本数据:");
//以行为单位接收数据
String userInput = scanner.nextLine();
// 指定文件路径,创建文件
File file = new File("userInput.txt");
// 将用户输入写入文件,创建写入文件类会返回Boolean值
try (PrintWriter writer = new PrintWriter(file)) {
writer.println(userInput);//将输入写入到写入文件中
} catch (IOException e) {
System.out.println("写入文件时出错:" + e.getMessage());
}
// 读取文件中的数据并输出
try (Scanner fileScanner = new Scanner(file)) {
System.out.println("从文件中读取的数据:");
while (fileScanner.hasNextLine()) {
System.out.println(fileScanner.nextLine());
}//用循环遍历的方式输出文件的全部信息
} catch (FileNotFoundException e) {
System.out.println("读取文件时出错:" + e.getMessage());
}
scanner.close();//关闭文件输出
}
}
String
并用.append()
写入接收文件中,如:1.
Scanner scanner = new Scanner(System.in);
//System.in是标准输入流,可以从控制台读取输入数据。由Scanner类对象接收它,可以很方便地读取各种类型的输入类型。
//除此以外,System.in还是一个Inputstream,它抽象了输入源,除了从键盘读取输入,还可以将其重定向到其他输入源,如文件或者网络流,从而使得程序更具灵活性。
2.
StringBuilder userInput = new StringBuilder();
// 使用StringBuilder来存储多行输入,StringBuilder是标准库的一个类,无需导入,是可以直接使用的。具体情况看附录文件。
3.
String line;//用字符串来存储一行的数据
while (!(line = scanner.nextLine()).equalsIgnoreCase("exit")) {
userInput.append(line).append(System.lineSeparator());//在没有接收到exit的情况下循环接收以行为单位的数据,并添加到StringBuilder类对象中,并自动在每行末尾加上自动换行的特殊符
}
4.
// 将用户输入的数据写入输入文件中
try (PrintWriter writer = new PrintWriter(file)) {
writer.print(userInput.toString());//将输入文件中的内容读入要存储的文件中
} catch (IOException e) {
System.out.println("写入文件时出错:" + e.getMessage());
}//异常处理
在上述接受的过程中,数据的存储类型发生了几次变化,即:
S t r i n g 字符串 → S t r i n g B u i l d e r 字符串流 → P r i n t W r i t e r 输入文件 → F i l e 文件 String字符串\rightarrow StringBuilder字符串流\rightarrow PrintWriter输入文件\rightarrow File文件 String字符串→StringBuilder字符串流→PrintWriter输入文件→File文件
true
,false
,null
虽然不是关键字,但是它们不能用作标识符的文字或者保留字。default
关键字定义的方法只能出现在接口Interface
中,它为接口提供了一种默认选项。你可以选择在实现它的时候重写它也可以选择不重写它。XML 是一种简单的基于文本的语言,旨在以纯文本格式存储和传输数据。
以下是 XML 提供的优势:
与技术无关 − 作为纯文本,XML 与技术无关。 它可以被任何技术用于数据存储和数据传输目的。
人类可读 − XML 使用简单的文本格式。 它是人类可读且易于理解的。
可扩展 − 在 XML 中,可以非常轻松地创建和使用自定义标签。
允许验证 − 使用 XSD、DTD 和 XML 结构可以很容易地进行验证。
XML Parser 提供了一种访问或修改 XML 文档中数据的方法。 Java 提供了多种解析 XML 文档的选项。 以下是通常用于解析 XML 文档的各种类型的解析器:
Dom 解析器 − 通过加载文档的完整内容并在内存中创建其完整的层次树来解析 XML 文档。
SAX 解析器 − 在基于事件的触发器上解析 XML 文档。 不将完整的文档加载到内存中。
JDOM 解析器 − 解析 XML 文档的方式与 DOM 解析器类似,但方式更简单。
StAX 解析器 − 以与 SAX 解析器类似的方式解析 XML 文档,但以更有效的方式。
XPath 解析器 − 基于表达式解析 XML 文档,并广泛与 XSLT 结合使用。
DOM4J 解析器 − 一个使用 Java Collections Framework 解析 XML、XPath 和 XSLT 的 java 库。 它提供对 DOM、SAX 和 JAXP 的支持。
有 JAXB 和 XSLT API 可用于以面向对象的方式处理 XML 解析。
详细内容参考Java XML 教程,此处略。
此处只提供教程链接,在看完教程之后还需要详细信息的可以问ChatGpt:
配合正则表达式使用
)