复习参考了Hollis大神的[Java工程师成神之路]:hollischuang.github.io/toBeTopJava…
一、大纲图
二、知识点复习梳理
快捷键
- 格式化代码:command+option+L
- 自动补齐返回类型:option+command+V
Random和Scanner的应用
public class NumberGuess {
public static void main(String[] args) {
Random random = new Random();
int result = random.nextInt(100) + 1; // 生成[1,101)的随机整数
while (true) {
Scanner scanner = new Scanner(System.in);
System.out.println("请输入你要猜测的数字:");
int guessNumer = scanner.nextInt();
if (guessNumer > result) {
System.out.println("你猜测的数字大了");
} else if (guessNumer < result) {
System.out.println("你猜测的数字小了");
} else {
System.out.println("恭喜你猜中了");
break;
}
}
}
工具类的设计思想
- 构造方法用private修饰(防止外界创建对象)
- 成员用public static修饰(可以通过类名调用)
SimpleDateFormat
日期转字符串(格式化):String str = sdf.format(d);
字符串转日期(解析):Date start = sdf.parse(startTime);
public static String Date2String(Date date, String pattern) {
SimpleDateFormat simpleDateFormat = new SimpleDateFormat(pattern);
String result = simpleDateFormat.format(date);
return result;
}
public static Date String2Date(String dateString, String pattern) throws ParseException {
SimpleDateFormat simpleDateFormat = new SimpleDateFormat(pattern);
Date date = simpleDateFormat.parse(dateString);
return date;
}
正则表达式
java.lang.util.regex.*
字符类语法规则:
- [abc]:代表a或者b或者c字符中的一个。
- [^abc]:代表除a,b,c以外的任何字符。
- [a-z]:代表a-z的所有小写字符中的一个。
- [A-Z]:代表A-Z的所有大写字符中的一个。
- [0-9]:代表0-9之间的某一个数字字符。
- [a-zA-Z0-9]:代表a-z或者A-Z或者0-9之间的任意一个字符。
- [a-dm-p]:a 到 d 或 m 到 p之间的任意一个字符。 预定义字符规则:
- "." : 匹配任何字符。
- "\d":任何数字[0-9]的简写;
- "\D":任何非数字[^0-9]的简写;
- "\s": 空白字符:[ \t\n\x0B\f\r] 的简写
- "\S": 非空白字符:[^\s] 的简写
- "\w":单词字符:[a-zA-Z_0-9]的简写
- "\W":非单词字符:[^\w] 数量词:
- X? : 0次或1次
- X* : 0次到多次
- X+ : 1次或多次
- X{n} : 恰好n次
- X{n,} : 至少n次
- X{n,m}: n到m次(n和m都是包含的) String类中的正则表达式:
- public String[] split(String regex) //参数regex就是一个正则表达式。可以将当前字符串中匹配regex正则表达式的符号作为"分隔符"来切割字符串。
- public String replaceAll(String regex,String newStr) //参数regex就是一个正则表达式。可以将当前字符串中匹配regex正则表达式的字符串替换为newStr。
文件夹递归查找文件
public static void getAllFilePath(File srcFile) {
File[] files = srcFile.listFiles();
if (files != null) {
for (File file : files) {
// 判断file是否是目录
if (file.isDirectory()) {
getAllFilePath(file);
} else {
System.out.println(file.getAbsolutePath());
}
}
}
}
IO流分类
- 字节流:可以复制任意文件数据
- 字节输入流(InputStream 抽象类)
- FileInputStream
- BufferedInputStream【缓冲流,一次读写一个字节数组】
- 字节输出流(OutputStream 抽象类)
- FileOutputStream
- BufferedOutputStream【缓冲流,一次读写一个字节数组】
- 字节输入流(InputStream 抽象类)
- 字符流:只能复制文本数据
- 字符输入流(Reader)
- InputStreamReader
- FileReader
- BufferedReader【字符缓冲流,readLine()一次读取一行】
- InputStreamReader
- 字符输出流(Writer)
- OutputStreamWriter
- FileWriter
- BufferedWriter【字符缓冲流,write(String line)一次写一行】
- 字符输入流(Reader)
FileInputStream
// 一次读取一个字节数组数据
byte[] bytes = new byte[1024];
int len;
while ((len = fis.read(bytes)) != -1) {
System.out.print(new String(bytes, 0, len));
}
BufferedInputStream
BufferedOutputStream bos = null;
BufferedInputStream bis = null;
try {
bos = new BufferedOutputStream(new FileOutputStream("bufferfly.txt"));
bos.write("hello \r\nbufferfly".getBytes());
bos.write("\r\nworld".getBytes());
bis = new BufferedInputStream(new FileInputStream("bufferfly.txt"));
int by;
while ((by = bis.read()) != -1) {
System.out.print((char) by);
}
}
BufferedReader
public static void main(String[] args) throws IOException {
ArrayList<String> arrayList = new ArrayList<>();
arrayList.add("hello");
arrayList.add("world");
arrayList.add("java");
arrayList.add("laotie");
BufferedWriter writer = new BufferedWriter(new FileWriter("arraylist.txt"));
for (String string : arrayList) {
writer.write(string);
writer.newLine();
writer.flush();
}
writer.close();
BufferedReader reader = new BufferedReader(new FileReader("arraylist.txt"));
String list;
while ((list = reader.readLine()) != null) {
arrayList.add(list);
}
reader.close();
System.out.println(arrayList);
}
对象序列化流ObjectOutputStream
- ObjectOutputStream(OutputStream out),其中writeObject(Object obj):将指定的对象写入ObjectOutputStream
- ObjectInputStream(InputStream in),其中readObject():从ObjectInputStream读取一个对象
线程生命周期
生产者消费者模式
graph TD
生产者 --> 共享数据区域
消费者 --> 共享数据区域
案例:
/**
* 奶箱类(Box):定义一个成员变量,表示第x瓶奶,提供存储牛奶和获取牛奶的操作
*/
public class Box {
private int milk;
private boolean state = false; // 表示奶箱的状态
public synchronized void put(int milk) {
// 如果有牛奶,就等待消费
if (state) {
try {
wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
// 如果没有牛奶,就生产牛奶
this.milk = milk;
System.out.println("送奶工将第" + this.milk + "瓶牛奶放入奶箱");
// 生产完毕,修改状态,做唤醒操作
state = true;
notifyAll();
}
public synchronized void get() {
// 如果没有牛奶,就等待生产
if (!state) {
try {
wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
// 如果有牛奶,就消费牛奶
System.out.println("用户拿到第" + this.milk + "瓶牛奶");
// 消费完毕,修改状态,做唤醒操作
state = false;
notifyAll();
}
}
/**
* 生产者类(Producer):实现Runnable接口,重写run()方法,调用存储牛奶的操作
*/
public class Producer implements Runnable {
private Box box;
public Producer(Box box) {
this.box = box;
}
@Override
public void run() {
for (int i = 1; i <= 5; i++) {
box.put(i);
}
}
}
/**
* 消费者类(Customer):实现Runnable接口,重写run()方法,调用获取牛奶的操作
*/
public class Customer implements Runnable {
private Box box;
public Customer(Box box) {
this.box = box;
}
@Override
public void run() {
while (true) {
box.get();
}
}
}
/**
* 测试类(BoxDemo):里面有main方法,main方法中的代码步骤如下
* • 创建奶箱对象,这是共享数据区域
* • 创建生产者对象,把奶箱对象作为构造方法参数传递,因为在这个类中要调用存储牛奶的操作
* • 创建消费者对象,把奶箱对象作为构造方法参数传递,因为在这个类中要调用获取牛奶的操作
* • 创建2个线程对象,分别把生产者对象和消费者对象作为构造方法参数传递
* • 启动线程
*/
public class BoxDemo {
public static void main(String[] args) {
Box box = new Box();
Producer producer = new Producer(box);
Customer customer = new Customer(box);
new Thread(producer).start();
new Thread(customer).start();
}
}
UDP协议
用户数据报协议(User Datagram Protocol),无连接通信协议。在数据传输时,数据的发送端和接收端不建立逻辑连接。
通信的两端各建立一个Socket对,在Java中DatagramSocket 类作为基于UDP协议的Socket。
发送数据的步骤:
- 创建发送端的Socket对象(DatagramSocket())
- 创建数据,并把数据打包(DatagramPacket(byte[] buf,int len,InetAddress add,int port))
- 调用DatagramSocket对象的方法发送数据(void send(DatagramPacket p))
- 关闭发送端(void close()) 接收数据的步骤:
- 创建接收端的Socket对象(DatagramSocket(int port)需要指定端口)
- 创建一个数据包,用于接收数据(DatagramPacket(byte[] buf, int len))
- 调用DatagramSocket对象的方法接收数据(void receive(DatagramPacket p))
- 解析数据包,并把数据在控制台显示(byte[] getData())
- 关闭接收端(void close())
案例:
public class SendDemo {
public static void main(String[] args) throws IOException {
DatagramSocket socket = new DatagramSocket();
BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
String line;
while ((line = reader.readLine()) != null) {
if ("886".equals(line)) {
break;
}
byte[] bytes = line.getBytes();
DatagramPacket packet = new DatagramPacket(bytes, bytes.length, InetAddress.getByName("127.0.0.1"), 10090);
socket.send(packet);
}
socket.close();
}
}
public class ReceiveDemo {
public static void main(String[] args) throws IOException {
DatagramSocket socket = new DatagramSocket(10090);
while (true) {
byte[] bytes = new byte[1024];
DatagramPacket packet = new DatagramPacket(bytes, bytes.length);
socket.receive(packet);
System.out.println("解析到的数据是:" + new String(packet.getData(), 0, packet.getLength()));
}
}
}
TCP协议
传输控制协议 (Transmission Control Protocol),是面向连接的通信协议。传输数据之前,在发送端和接收端建立逻辑连接,然后再传输数据。
Java为客户端提供了Socket类,为服务器端提供了ServerSocket类。
发送数据的步骤:
- 创建客户端的Socket对象(Socket)
- 获取输出流,写数据(OutputStream getOutputStream()/write())
- 释放资源 接收数据的步骤:
- 创建服务器端的Socket对象(ServerSocket(int port))
- 监听客户端连接,返回一个Socket对象(Socket accept())
- 获取输入流,读数据(InputStream getInputStream())
- 释放资源
案例:
public class ClientTest1 {
public static void main(String[] args) throws IOException {
Socket client = new Socket("127.0.0.1", 10000);
// 获取字节输出流,写数据
OutputStream outputStream = client.getOutputStream();
outputStream.write("Client-send message".getBytes());
// 接收服务器反馈
InputStream inputStream = client.getInputStream();
byte[] bytes = new byte[1024];
int len = inputStream.read(bytes);
System.out.println("客户端接收来自服务器的数据:" + new String(bytes, 0, len));
client.close();
}
}
public class ServerTest1 {
public static void main(String[] args) throws IOException {
ServerSocket serverSocket = new ServerSocket(10000);
Socket socket = serverSocket.accept();
// 获取字节输入流,读数据
InputStream inputStream = socket.getInputStream();
byte[] bytes = new byte[1024];
int len = inputStream.read(bytes);
System.out.println("服务端接收来自客户端的数据:" + new String(bytes, 0, len));
// 反馈给客户端
OutputStream outputStream = socket.getOutputStream();
outputStream.write("服务端已接收!客户端请回答!".getBytes());
serverSocket.close();
}
}
Lambda
语法格式:
(形式参数) -> { 代码块; } 案例:
// lambda表达式
useAddable((int x, int y) -> {
return x - y;
});
反射
获取Class类对象:
- 类名.class 属性
- 对象名.getClass() 方法
- Class.forName(全路径类名) 方法 获取构造方法:
- Constructor<?>[] getConstructors():返回所有公共构造方法对象的数组
- Constructor<?>[] getDeclaredConstructors():返回所有构造方法对象的数组
- Constructor getConstructor(Class<?>... parameterTypes):返回单个公共构造方法对象
- Constructor getDeclaredConstructor(Class<?>...parameterTypes):返回单个构造方法对象
- Constructor类用于创建对象的方法:T newInstance(Object...initargs) 获取成员变量:
- Field[] getFields():返回所有公共成员变量对象的数组
- Field[] getDeclaredFields():返回所有成员变量对象的数组
- Field getField(String name):返回单个公共成员变量对象
- Field getDeclaredField(String name):返回单个成员变量对象
- Field类用于给成员变量赋值的方法:void set(Object obj,Object value) 获取成员方法:
- Method[] getMethods():返回所有公共成员方法对象的数组,包括继承的
- Method[] getDeclaredMethods():返回所有成员方法对象的数组,不包括继承的
- Method getMethod(String name, Class<?>...parameterTypes):返回单个公共成员方法对象
- Method getDeclaredMethod(String name, Class<?>... parameterTypes):返回单个成员方法对象
- Method类用于执行方法的方法:Object invoke(Object obj,Object... args) 案例1:越过泛型检查
/**
* 通过反射技术,向一个泛型为Integer的集合中添加一些字符串数据
*/
public class ReflectTest1 {
public static void main(String[] args) throws InvocationTargetException, IllegalAccessException, NoSuchMethodException {
ArrayList<Integer> list = new ArrayList<>();
list.add(10);
list.add(20);
Class<? extends ArrayList> listClass = list.getClass();
Method add = listClass.getMethod("add", Object.class);
add.invoke(list, "hello");
add.invoke(list, "world");
System.out.println(list); // [10, 20, hello, world]
}
}
案例2:运行配置文件中指定类的指定方法
/**
* 通过反射运行配置文件中指定类的指定方法
*/
public class ReflectTest2 {
public static void main(String[] args) throws IOException, ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
Properties properties = new Properties();
FileReader reader = new FileReader("class.txt");
properties.load(reader);
reader.close();
String className = properties.getProperty("className");
String methodName = properties.getProperty("methodName");
// 获取类
Class<?> aClass = Class.forName(className);
Constructor<?> constructor = aClass.getConstructor();
// 获取实例
Object instance = constructor.newInstance();
// 获取方法
Method method = aClass.getMethod(methodName);
// 方法调用
method.invoke(instance);
}
}
函数式接口
概述
public class MyInterfaceTest {
public static void main(String[] args) {
MyInterface myInterface = () -> System.out.println("函数式接口-show方法");
myInterface.show(); // 函数式接口-show方法
}
}
@FunctionalInterface
interface MyInterface {
void show();
// 会报错
// void func();
}
函数式接口作为方法的参数:
// 查看Runnable源代码定义
@FunctionalInterface
public interface Runnable {
public abstract void run();
}
public class RunnableDemo {
public static void main(String[] args) {
// 匿名内部类
startThread(new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName() + "启动线程");
}
});
// lambda
startThread(() -> System.out.println(Thread.currentThread().getName() + "启动线程"));
}
private static void startThread(Runnable runnable) {
new Thread(runnable).start();
}
}
函数式接口作为方法的返回值:
// 查看Comparator源代码定义
@FunctionalInterface
public interface Comparator<T> {
}
public class ComparatorDemo {
public static void main(String[] args) {
ArrayList<String> list = new ArrayList();
list.add("cccc");
list.add("aa");
list.add("b");
list.add("ddd");
System.out.println("排序前:" + list);
Collections.sort(list, getComarator());
System.out.println("排序后:" + list);
}
private static Comparator<String> getComarator() {
/*// 匿名内部类实现
return new Comparator<String>() {
@Override
public int compare(String o1, String o2) {
return o1.length() - o2.length();
}
};*/
// lambda实现
return ((o1, o2) -> o1.length() - o2.length());
}
}
Supplier接口
java.util.function.Supplier接口也被称为生产型接口,如果我们指定了接口的泛型是什么类型,那么接口中的get方法就会生产什么类型的数据供我们使用。
常用方法:
- T get():按照某种实现逻辑(由Lambda表达式实现)返回一个数据
Consumer接口
Consumer接口也被称为消费型接口,它消费的数据的数据类型由泛型指定。
常用方法:
- void accept(T t):对给定的参数执行此操作
- default Consumer andThen(Consumer after):返回一个组合的Consumer,依次执行此操作,然后执行after操作
Predicate接口
Predicate接口通常用于判断参数是否满足指定的条件。
常用方法:
- boolean test(T t):对给定的参数进行判断(判断逻辑由Lambda达式实现),返回一个布尔值
- default Predicate negate():返回一个逻辑的否定,对应逻辑非
- default Predicate and(Predicate other):返回一个组合判断,对应短路与
- default Predicate or(Predicate other):返回一个组合判断,对应短路或
Function接口
Function<T,R>接口通常用于对参数进行处理,转换(处理逻辑由Lambda表达式实现),然后返回一个新的值。
常用方法:
- R apply(T t):将此函数应用于给定的参数
- default Function andThen(Function after):返回一个组合函数,首先将该函数应用于输入,然后将after函数应用于结果
Stream流
中间操作:
| 方法名 | 说明 |
|---|---|
| Stream filter(Predicate predicate) | 用于对流中的数据进行过滤 |
| Stream limit(long maxSize) | 返回此流中的元素组成的流,截取前指定参数个数的数据 |
| Stream skip(long n) | 跳过指定参数个数的数据,返回由该流的剩余元素组成的流 |
| static Stream concat(Stream a, Stream b) | 合并a和b两个流为一个流 |
| Stream distinct() | 返回由该流的不同元素(根据Object.equals(Object) )组成的流 |
| Stream sorted() | 返回由此流的元素组成的流,根据自然顺序排序 |
| Stream sorted(Comparatorcomparator) | 返回由该流的元素组成的流,根据提供的Comparator进行排序 |
| Stream map(Function mapper) | 返回由给定函数应用于此流的元素的结果组成的流 |
| IntStream mapToInt(ToIntFunctionmapper) | 返回一个IntStream其中包含将给定函数应用于此流的元素的结果 |
终结操作:
| 方法名 | 说明 |
|---|---|
| void forEach(Consumer action) | 对此流的每个元素执行操作 |
| long count() | 返回此流中的元素数 |
收集操作:
| 方法名 | 说明 |
|---|---|
| R collect(Collector collector) | 把结果收集到集合中 |
工具类Collectors提供了具体的收集方式:
| 方法名 | 说明 |
|---|---|
| public static Collector toList() | 把元素收集到List集合中 |
| public static Collector toSet() | 把元素收集到Set集合中 |
| public static Collector toMap(Function keyMapper, Function valueMapper) | 把元素收集到Map集合中 |
单例设计模式
Singleton模式,指的是一个类、在一个JVM里,只有一个实例存在。三要素:
- 构造方法私有化
- 静态属性指向实例
- public static的 getInstance方法,返回第二步的静态属性
- 饿汉式单例模式 通俗的说,无论如何饿汉模式都会先创建一个实例,占用内存
/*
* 饿汉式单例模式:
* GiantDragon 应该只有一只,通过私有化其构造方法,使得外部无法通过new得到新的实例。
* GiantDragon 提供了一个public static的getInstance方法,外部调用者通过该方法获取定义的对象
* 而且每一次都是获取同一个对象。 从而达到单例的目的。
* 这种单例模式又叫做饿汉式单例模式,无论如何都会创建一个实例
*/
public class GiantDragon {
// 私有化构造方法,外面无法通过new进行实例化
private GiantDragon() {
}
// 类属性,指向一个实例化对象。类属性只有一个,所以每次调用的都相同
private static GiantDragon instance = new GiantDragon();
// public static方法,提供给调用者获取定义的对象
public static GiantDragon getInstance() {
return instance;
}
}
public class TestGiantDragon {
public static void main(String[] args) {
// 报错。
/* GiantDragon dragon = new GiantDragon(); */
GiantDragon dragon1 = GiantDragon.getInstance();
GiantDragon dragon2 = GiantDragon.getInstance();
GiantDragon dragon3 = GiantDragon.getInstance();
// 结果为True
System.out.println(dragon1 == dragon2);
System.out.println(dragon1 == dragon3);
}
}
- 懒汉式单例模式 通俗的说,用到他的时候他才会创建实例,一旦创建后就使用这一个了
注意,懒汉单例设计模式在多线程环境下可能会实例化出多个对象,不能保证单例的状态。
/*
* 懒汉式单例模式与饿汉式单例模式不同
* 只有在调用getInstance的时候,才会创建实例
*/
public class GiantDragon1 {
private GiantDragon1() {
}
// 准备一个类属性,用于指向一个实例化对象,但是暂时指向null
private static GiantDragon1 instance;
public static GiantDragon1 getInstance() {
// 第一次访问的时候,发现instance没有指向任何对象,这时实例化一个对象
if (instance == null) {
instance = new GiantDragon1();
}
return instance;
}
}
public class TestGiantDrsgon1 {
public static void main(String[] args) {
/* GiantDragon1 dragon = new GiantDragon1(); */
GiantDragon1 dragon1 = GiantDragon1.getInstance();
GiantDragon1 dragon2 = GiantDragon1.getInstance();
GiantDragon1 dragon3 = GiantDragon1.getInstance();
System.out.println(dragon1 == dragon2);
System.out.println(dragon1 == dragon3);
}
}
看业务需求,如果业务上允许有比较充分的启动和初始化时间,就使用饿汉式,否则就使用懒汉式。
模板设计模式
思想:将固定流程写到父类中,不同的地方就定义成抽象方法,让不同的子类去重写。模板模式的优势是,模板已经定义了通用架构,使用者只需要关心自己需要实现的功能即可。
// 司机开车的模板类
public abstract class Driver {
public void go() {
System.out.println("开门");
System.out.println("点火");
// 开车姿势不确定?定义为抽象方法
ziShi();
System.out.println("刹车");
System.out.println("熄火");
}
public abstract void ziShi();
}
public class NewDriver extends Driver {
@Override
public void ziShi() {
System.out.println("新司机双手紧握方向盘");
}
}
public class OldDriver extends Driver {
@Override
public void ziShi() {
System.out.println("老司机右手握方向盘左手抽烟...");
}
}
排序算法-冒泡排序
一种排序的方式,对要进行排序的数据中相邻的数据进行两两比较,将较大的数据放在后面,依次对所有的数据进行操作,直至所有数据按要求完成排序。如果有n个数据进行排序,总共需要比较n-1次。每一次比较完毕,下一次的比较就会少一个数据参与。
public class ArrayDemo {
public static void main(String[] args) {
//定义一个数组
int[] arr = {7, 6, 5, 4, 3};
System.out.println("排序前:" + Arrays.toString(arr));
// 这里减1,是控制每轮比较的次数
for (int x = 0; x < arr.length - 1; x++) {
// -1是为了避免索引越界,-x是为了调高比较效率
for (int i = 0; i < arr.length - 1 - x; i++) {
if (arr[i] > arr[i + 1]) {
int temp = arr[i];
arr[i] = arr[i + 1];
arr[i + 1] = temp;
}
}
}
System.out.println("排序后:" + Arrays.toString(arr));
}
}
排序算法-选择排序
一种排序的方式,选中数组的某个元素,其后面的元素依次和选中的元素进行两两比较,将较大的数据放在后面,依次从前到后选中每个元素,直至所有数据按要求完成排序。如果有n个数据进行排序,总共需要比较n-1次。每一次比较完毕,下一次的比较就会少一个数据参与
public class ArrayDemo {
public static void main(String[] args) {
//定义一个数组
int[] arr = {7, 6, 5, 4, 3};
System.out.println("排序前:" + Arrays.toString(arr));
// 这里减1,是控制比较的轮数
for (int x = 0; x < arr.length; x++) {
// 从x+1开始,直到最后一个元素
for (int i = x+1; i < arr.length; i++) {
if (arr[x] > arr[i]) {
int temp = arr[x];
arr[x] = arr[i];
arr[i] = temp;
}
}
}
System.out.println("排序后:" + Arrays.toString(arr));
}
}