高级特性 (Java)
异常处理
捕获异常
***try … catch:***捕获异常
**多 ctach 语句:**从上到下匹配,匹配成功后不再继续匹配(子类必须写在前面)
***finally:***最后执行(可不写)
1 | public static void main(String[] args) { |
抛出异常
**printStackTrace():**从下往上看,看调用层次,来调试错误
抛出异常:
创建
Exception
实例throw
抛出1
2
3
4
5void process2(String s) {
if (s==null) {
throw new NullPointerException();
}
}
**防止异常覆盖:**传入 Exception
实例
1 | static void process1() { |
**防止异常屏蔽:**用 origin
保存异常,调用 Throwable.addSuppressed()
1 | // exception |
自定义异常
标准库常用异常:
1 | Exception |
自定义根异常:BaseException
1 | public class BaseException extends RuntimeException { |
反射
Class
JVM为每个加载的 class
及 interface
创建 Class
实例来保存信息
**反射:**通过 Class
实例获取 class
信息的方法(Class
实例在JVM中是唯一的)
获取 Class 实例:
Class cls = String.class;
String s = "Hello";
Class cls = s.getClass();
Class s = Class.forName("java.lang.String");
**动态加载:**执行程序时,用到JVM才加载 class
访问字段
获取字段:
Class.getField("...")
:根据字段名获取 public 字段(包括父类)Class.getDeclaredFiedl("...")
:根据字段名获取当前类的字段(不包括父类)Class.getFields()
:获取所有 public 的字段(包括父类)Class.getDeclaredFields()
:获取当前类的所有字段(不包括父类)
Field 对象:
Field.getName()
:返回字段名Field.getType()
:返回字段类型Field.getModifiers()
:返回字段修饰符1
2
3
4
5
6
7
8
9
10
11
12public final class String {
private final byte[] value;
}
Field f = String.class.getDeclaredField("value");
f.getName(); // "value"
f.getType(); // class [B 表示byte[]类型
int m = f.getModifiers();
Modifier.isFinal(m); // true
Modifier.isPublic(m); // false
Modifier.isProtected(m); // false
Modifier.isPrivate(m); // true
Modifier.isStatic(m); // false
访问 private 字段:Field.setAccessible(true);
设置字段:Field.set(Object, Object)
(实例,待修改的值)
1 | // reflection |
调用方法
获取方法:
Class.getMethod("..",Class...)
:获取某个 public 方法(包括父类)Class.getDeclaredMethod("..",Class...)
:获取当前类的方法(不包括父类)Class.getMethods()
:获取所有 public 方法(包括父类)Class.getDeclaredMethods()
:获取当前类的所有方法(不包括父类)
Method 对象:
Method.getName()
:返回方法名Method.getReturnType()
:返回方法返回类型Method.getParameterTypes()
:返回方法参数类型(Class数组)Method.getModifiers()
:返回方法修饰符
调用方法:
Method.invoke(Object,...)
(实例,方法参数)Method.invoke(null,...)
:调用静态方法
访问非 public 字段:Method.setAccessible(true)
1 | // reflection |
**多态:**调用实际类型的覆写方法
调用构造方法
通过反射创建新实例:Person p = Person.class.newInstance();
(只能调用该类的public无参数构造方法)
**Constructor 对象:**获取当前类的构造方法(不是父类)
getConstructor(Class...)
:获取某个public
的构造方法getDeclaredConstructor(Class...)
:获取某个构造方法getConstructors()
:获取所有public
的构造方法getDeclaredConstructors()
:获取所有构造方法
访问非 public 构造方法:Constructor.setAccessible(true)
1 | import java.lang.reflect.Constructor; |
获取继承关系
获取父类的 Class:Class.getSuperclass()
获取接口:Class.getInterfaces();
(不包括父类接口)
1 | // reflection |
集合
泛型
IO
**InputStream / OutputStream:**IO流(字节流),以 byte
(字节)为最小单位
**Reader / Writer:**字符流,以 char
为最小单位
同步 vs 异步:
- 同步:读写IO时代码必须等待数据返回后才继续执行后续代码 (
java.io
) - 异步:读写IO时仅发出请求,然后立刻执行后续代码 (
java.nio
)
File 对象
既可以表示文件,也可以表示目录
**创建:**需要传入路径
绝对路径:
- Windows:
File f = new File("C:\\Windows\\notepad.exe");
- Linux:
File f = new File("/usr/bin/javac");
- Windows:
相对路径:
.
表示当前目录,..
表示上级目录1
2
3
4// 假设当前目录是C:\Docs
File f1 = new File("sub\\javac"); // 绝对路径是C:\Docs\sub\javac
File f3 = new File(".\\sub\\javac"); // 绝对路径是C:\Docs\sub\javac
File f3 = new File("..\\sub\\javac"); // 绝对路径是C:\sub\javac
文件和目录
是否是已存在的文件:isFile()
是否是已存在的目录:isDirectory()
是否可读:canRead()
是否可写:canWrite()
是否可执行:canExecute()
文件字节大小:length()
创建和删除文件
创建文件:File.createNewFile()
删除文件:File.delete()
临时文件:
1 | import java.io.*; |
遍历文件和目录
列出目录下文件和子目录:
File.list()
File.listFiles()
File 对象表示目录:
mkdir()
:创建当前File对象表示的目录mkdirs()
:创建当前File对象表示的目录(必要时将不存在的父目录也创建出来)delete()
:删除当前File对象表示的目录(当前目录必须为空才能删除成功)
日期与时间
正则表达式
正则表达式:构成搜索模式的字符序列。在文本中搜索数据时,可以使用此搜索模式来描述要搜索的内容。
正则表达式可以是单个字符,也可以是更复杂的模式
则表达式可用于执行所有类型的文本搜索和文本替换 操作。
导入 java.util.regex
包来使用正则表达式,该软件包包括一下类:
Pattern
类:定义模式(用于搜索)Matchaer
类:用于搜索模式PatternSyntaxException
类:异常类 - 指示正则表达式模式中的语法错误
1 | import java.util.regex.Matcher; |
在句子中搜索单词 “hello”:
Pattern.compile()
:创建模式。第一个参数指示正在搜索的模式,第二个参数有一个标志,指示搜索应不区分大小写。第二个参数是可选的。
matcher()
:在字符串中搜索模式。它返回一个 Matcher 对象,其中包含有关所执行搜索的信息如果在字符串中找到模式,则
find()
方法返回 true,如果未找到模式,则返回 false
flags
compile()
方法中的标志会更改搜索的执行方式。以下是其中的一些:
Pattern.CASE_INSENSITIVE
- 执行搜索时,将忽略字母的大小写。Pattern.LITERAL
- 模式中的特殊字符将没有任何特殊含义,在执行搜索时将被视为普通字符。Pattern.UNICODE_CASE
- 将它与CASE_INSENSITIVE
标志一起使用,也可以忽略英文字母表以外的字母的大小写
正则表达式模式
Pattern.compile()
方法的第一个参数是 pattern。它描述了正在搜索的内容。
方括号用于查找字符范围:
表示 | 描述 |
---|---|
[abc] | 匹配括号内的任意字符 |
[^abc] | 匹配非括号内的字符 |
[0-9] | 匹配0-9之间的字符 |
元字符是具有特殊含义的字符:
元字符 | 描述 |
---|---|
| | 匹配多个模式中的任意一个,cat|dog|fish只要它们中的任意一个出现在文本中,就算匹配成功 |
. | 匹配任何单个字符,除了换行符 \n |
^ | 匹配字符串的开始位置,如^Hello |
$ | F匹配字符串的结束位置,如World$ |
\d | 匹配任何数字字符 |
\s | 匹配任何空白字符(如空格、制表符、换行符等) |
\b | 匹配单词边界,\bWORD 匹配以 “WORD” 开头的单词,WORD\b 匹配以 “WORD” 结尾的单词 |
\uxxxx | 匹配由四个十六进制数字表示的 Unicode 字符,\u0041 匹配 Unicode 字符 ‘A’ |
量词定义数量:
量词 | 描述 |
---|---|
n+ | 匹配包含至少一个 n 的字符串(即 n 至少出现一次) |
n* | 匹配包含零个或多个 n 的字符串(即 n 可以出现任意次,包括零次) |
n? | 匹配包含零个或一个 n 的字符串(即 n 要么出现一次,要么不出现) |
n{x} | 匹配包含恰好 X 个 n 的字符串 |
n{x,y} | 匹配包含 X 到 Y 个 n 的字符串 |
n{x,} | 匹配包含至少 X 个 n 的字符串 |
线程
线程允许程序通过同时执行多项事务来更高效地运行,可用于在后台执行复杂的任务,而不会中断主程序。
创建:
扩展
Thread
类并覆盖其run()
1
2
3
4
5public class Main extends Thread {
public void run() {
System.out.println("This code is running in a thread");
}
}实现
Runnable
接口:1
2
3
4
5public class Main implements Runnable {
public void run() {
System.out.println("This code is running in a thread");
}
}
运行:
扩展
Thread
类:创建该类的实例并调用start()
方法来运行1
2
3
4
5
6
7
8
9
10public class Main extends Thread {
public static void main(String[] args) {
Main thread = new Main();
thread.start();
System.out.println("This code is outside of the thread");
}
public void run() {
System.out.println("This code is running in a thread");
}
}实现
Runnable
接口:将类的实例传递给Thread
对象的构造函数,然后调用线程的start()
方法1
2
3
4
5
6
7
8
9
10
11public class Main implements Runnable {
public static void main(String[] args) {
Main obj = new Main();
Thread thread = new Thread(obj);
thread.start();
System.out.println("This code is outside of the thread");
}
public void run() {
System.out.println("This code is running in a thread");
}
}
extends 和 implements 的区别:
当一个类扩展 Thread 类时,你不能扩展任何其他类。但通过实现 Runnable 接口,也可以从另一个类扩展,例如: class MyClass extends OtherClass implements Runnable
.
并发
由于线程与程序的其他部分同时运行,因此无法知道代码将按什么顺序运行。当线程和主程序读取和写入相同的变量时,这些值是不可预测的。由此产生的问题称为并发问题。
例如:
1 | public class Main extends Thread { |
有时会输出 0 2
,有时会输出 0 1
为避免并发问题,最好在线程之间共享尽可能少的属性。如果需要共享属性,一种可能的解决方案是使用 isAlive()
方法检查线程是否已经运行完毕,然后再使用任何 线程可以更改的属性。
1 | public class Main extends Thread { |
Lambda
Java 8 中添加,lambda 表达式是一个简短的代码块,它接受参数并返回一个值。Lambda 表达式类似于方法,但它们不需要名称,并且可以直接在方法的主体中实现
语法:
一个参数
1
parameter -> expression
多个参数
1
(parameter1, parameter2) -> expression
为了执行更复杂的作,可以将代码块与大括号一起使用。如果 lambda 表达式需要返回一个值,则代码块应具有
return
语句。1
(parameter1, parameter2) -> { code block }
使用:
作为方法的参数:
在
ArrayList
的forEach()
方法中使用 lambda 表达式来打印列表中的每一项1
2
3
4
5
6
7
8
9
10
11
12import java.util.ArrayList;
public class Main {
public static void main(String[] args) {
ArrayList<Integer> numbers = new ArrayList<Integer>();
numbers.add(5);
numbers.add(9);
numbers.add(8);
numbers.add(1);
numbers.forEach( (n) -> { System.out.println(n); } );
}
}当方法参数类型是接口时,Lambda 表达式可以存储在变量中。Lambda 表达式需要匹配接口方法的参数数量和返回类型
1
2
3
4
5
6
7
8
9
10
11
12
13
14import java.util.ArrayList;
import java.util.function.Consumer;
public class Main {
public static void main(String[] args) {
ArrayList<Integer> numbers = new ArrayList<Integer>();
numbers.add(5);
numbers.add(9);
numbers.add(8);
numbers.add(1);
Consumer<Integer> method = (n) -> { System.out.println(n); };
numbers.forEach( method );
}
}Java 内置了许多此类接口,例如列表使用的
Consumer
接口(位于java.util
包中)作为函数式接口的实现:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16interface StringFunction {
String run(String str);
}
public class Main {
public static void main(String[] args) {
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);
}
}
高级排序
Comparator
和 Comparable
接口允许您指定用于对对象进行排序的规则
Comparator
Comparator
接口:使用compare()
方法创建一个类,来比较两个对象
compare()
方法:返回一个数字
- 如果第一个对象应位于列表中的第一个,则为负数
- 如果第二个对象应位于列表中的第一个,则为正数
- 如果 顺序 无关紧要,则为零
1 | // Sort Car objects by year |
**使用:**将其作为参数传递给排序方法
1 | // Use a comparator to sort the cars |
例子:
1 | import java.util.ArrayList; |
Lambda:
将比较器替换为 lambda 表达式,该表达式具有与 compare()
方法相同的参数和返回值(代码更短)
1 | Collections.sort(myCars, (obj1, obj2) -> { |
Comparable
Comparable
接口:使用 compareTo()
方法指定自己的排序规则
compareTo()
方法:将对象作为参数,把 comparable 与参数进行比较,返回一个数字
- 如果 Comparable 排在列表的首位,则为负数
- 如果另一个对象应该在列表中排在第一位,则为整数
- 如果 顺序 无关紧要,则为零
1 | class Car implements Comparable { |
使用:
Comparable
是接口而不是比较器
许多Java类实现了 Comparable
接口,如String
和 Integer
,所以它们不需要比较器排序
1 | class Car implements Comparable { |
例子:
1 | import java.util.ArrayList; |
技巧
正向:
1 | return a.year - b.year; |
反向:
1 | return b.year - a.year; |
区别
comparator
:具有一种方法的对象comparable
:将自身与其他对象进行比较的对象
使用 Comparable
接口会更容易,但 Comparator
接口功能更强大,因为它允许您对任何类型的对象进行排序,即使您无法更改其代码
文件操作
java.io
包中的 File
类允许我们处理文件
**使用 file
类:**创建该类的对象,并指定文件名或目录名称
1 | import java.io.File; // Import the File class |
File
类的方法:
方法 | 类型 | 描述 |
---|---|---|
canRead() |
Boolean | 测试文件是否可读 |
canWrite() |
Boolean | 测试文件是否可写 |
createNewFile() |
Boolean | 创建一个空的文件。如果文件已经存在,则返回 false ;如果文件成功创建,则返回 true 。 |
delete() |
Boolean | 删除文件。如果文件成功删除,则返回 true ;如果删除失败或文件不存在,则返回 false 。 |
exists() |
Boolean | 测试文件是否存在 |
getName() |
String | 返回文件的名称(即不包括路径部分) |
getAbsolutePath() |
String | 返回文件的绝对路径 |
length() |
Long | 返回文件的大小,单位是字节(byte) |
list() |
String[] | 如果文件是一个目录,返回该目录下所有文件和子目录的名称数组 |
mkdir() |
Boolean | 创建一个目录 |
创建:createNewFile()
文件成功创建: true
,文件已存在: false
。注意:该方法必须包含在 try...catch
块,因为如果发生错误(如果由于某种原因无法创建文件),它会抛出 IOException
1 | import java.io.File; // Import the File class |
在特定目录中创建:
1 | File myObj = new File("C:\\Users\\MyName\\filename.txt"); |
Mac、Linux可直接编写路径
写入文件:
用 FileWriter
,write()
,完成写入后用 close()
关闭。
1 | import java.io.FileWriter; // Import the FileWriter class |
读取:Scanner
1 | import java.io.File; // Import the File class |
获取文件信息:File
方法
1 | import java.io.File; // Import the File class |
注意:Java API 中有许多可用的类可用于在 Java 中读取和写入文件: FileReader, BufferedReader, Files, Scanner, FileInputStream, FileWriter, BufferedWriter, FileOutputStream
等。使用哪一个取决于你正在使用的 Java 版本,你是否需要读取字节或字符,以及文件/行的大小等
删除文件:delete()
1 | import java.io.File; // Import the File class |
**删除文件夹:**删除文件夹的前提是 文件夹必须为空
1 | import java.io.File; |