日志框架
。日志规范接口:Commons Logging,Simple Logging Facade for Java(Slf4j)
Logback日志框架
SpringBoot默认集成Slf4j,可以直接注解Slf4j
三个技术模块
。logback-core:入口,必须有
。logback-classic
。logback-access
😄快速入门:
步骤:
1.新建lib文件夹,把logback需要的jar包导入该文件夹下
logback的jar包
logback-classic-1.2.3.jarlogback-core-1.2.3.jarslf4j-api-1.7.26.jar
2.把logback核心配置文件,拷贝到scr目录下
logback.xml
3.在代码中获取日志对象,使用API方法
private static Logger logger = LoggerFactory.getLogger("类名");
logger.debug("写控制台输出语句")
//API方法
TRACE < DEBUG < INFO < WARN < ERROR级别大小
TRACE:用于追踪程序的详细执行情况,通常用于输出非常详细的调试信息,适用于排查问题时需要更深层次的信息。
DEBUG:用于调试信息,输出一些对调试有帮助的信息,通常在开发和测试阶段使用。
INFO:用于输出一般信息,标识程序运行中的关键事件,以便了解应用程序的整体运行状态。
WARN:用于警告信息,表示潜在的问题,不影响程序继续运行,但可能需要关注。
ERROR:用于错误信息,表示程序发生了可恢复或不可恢复的错误,可能导致程序异常终止。
异常
😄异常概述:
程序出现不正常情况,导致JVM的非正常停止
😄存在形式:
- 已检查异常(Checked Exception):
- 这些异常是在编译时强制处理的异常,必须通过捕获或声明抛出来处理。
- 通常继承自 Exception 类(不是 RuntimeException 类)。
- 例如:IOException、SQLException。
- 未检查异常(Unchecked Exception):
- 也称为运行时异常(RuntimeException),是一种不需要显式捕获或声明的异常。
- 通常继承自 RuntimeException 类。
- 例如:NullPointerException、ArrayIndexOutOfBoundsException。
- 错误(Error):
- 表示严重问题,通常是 JVM 自身无法解决的问题。
- 通常继承自 Error 类。
- 例如:OutOfMemoryError、StackOverflowError。
- 自定义异常:
- 为了满足特定需求,可以创建自定义异常类。
- 继承自 Exception 或其子类,根据具体情况选择合适的基类
// 自定义异常类
class 自定义异常类 extends Exception {
public MyCustomException() {
super("This is a custom exception");
}
public MyCustomException(String message) {
super(message);
}
}
// 在代码中抛出自定义异常
class CustomExceptionExample {
public static void main(String[] args) {
try {
// 某些条件下抛出自定义异常
throw new MyCustomException("Something went wrong");
} catch (MyCustomException e) {
System.out.println("Caught custom exception: " + e.getMessage());
}
}
}
😄JVM处理异常方法:
打印异常位置原因,JVM停止
😄处理异常:
main()方法中只能用方法二
。自己不处理,交给别人处理
修饰符号 返回类型 方法名(参数类型 参数)throws 异常类1,异常类2{
}
面试题:throws和throw的区别:
- throws:
- throws 关键字用于在方法声明中指定可能会抛出的异常类型,表示该方法可能会抛出指定类型的异常。
- 当一个方法可能会抛出某种异常时,可以使用 throws 关键字将异常类型列在方法签名后面,通知调用者可能需要处理这些异常。
- throws 是一种声明式的方式,用于告知编译器该方法可能会抛出哪些异常,但实际的异常抛出和处理是在方法内部进行的。
- throw:
- throw 关键字用于手动抛出异常,即在代码块中创建异常对象并将其抛出给调用者或上层方法进行处理。
- 通过 throw 可以触发异常,并将控制权交给异常处理机制,让程序按照异常处理流程执行。
- throw 是一个操作符,用于抛出特定的异常实例,可以是 Java 内置异常类或自定义异常类的实例。
public static void main(String[] args) {
try {//处理异常
throwCustomException();
} catch (CustomException e) {
System.out.println("Caught custom exception: " + e.getMessage());
}
}
public static void throwCustomException() throws CustomException {
// 创建自定义异常对象并抛出
throw new CustomException("This is a custom exception");
}
try {
// 可能会引发异常的代码
} catch (ExceptionType1 e1) {//子异常
// 处理 ExceptionType1 类型的异常
} catch (ExceptionType2 e2) {//父异常,父异常一定在后面
// 处理 ExceptionType2 类型的异常
} finally {
// 可选的 finally 块,用于执行无论是否发生异常都需要执行的代码
}
😄Throwable成员方法:
- getMessage():
- 用于获取异常的详细描述信息,通常是异常发生的原因。
- printStackTrace():
- 将异常信息和异常发生时的调用栈信息输出到标准错误流,可用于调试或日志记录。
- getCause():
- 获取导致当前异常的异常原因,即该异常的根本原因。
- 获取导致当前异常的异常原因,即该异常的根本原因。
Lambda表达式
。简化程序中匿名内部类的书写
public class LambdaExample {
public static void main(String[] args) {
// 使用Lambda表达式实现Runnable接口
Runnable runnable = () -> {
System.out.println("This is a lambda expression");
};
// 使用Lambda表达式作为参数传递给Thread构造函数
Thread thread = new Thread(runnable);
thread.start();
}
}
😄Lambda表达式:
只对函数接口进行代码编写
。函数接口:接口中只有一个抽象方法
@FunctionalInterface
public interface MyFunctionInterface {
void myMethod();
}
。1.匿名内部类基本实现:
public static void main(String[] args) {
// 使用匿名内部类继承类
Animal animal = new Animal() {
@Override
void sound() {
System.out.println("Dog barks");
}
};
animal.sound();
}
。2.用Lambda表达式简化
public static void main(String[] args) {
// 使用Lambda表达式继承类
Animal animal = () -> System.out.println("Dog barks");
animal.sound();
}
。3.标准语法:
Lambda表达式的基本语法如下:
Copy Code(parameters) -> expression
或
(parameters) -> { statements; }
其中:
- parameters:表示Lambda表达式的参数列表,可以是空参数、单个参数或多个参数。如果没有参数,则使用空括号 ();如果只有一个参数,可以省略参数类型和括号,比如 a -> a*a;如果有多个参数,使用逗号分隔,比如 (a, b) -> a + b。
- ->:称为箭头符号,用于分隔参数列表和Lambda表达式的主体。
- expression:表示Lambda表达式的返回值,可以是一个表达式。
- {}:用于定义多行代码块,如果Lambda表达式包含多条语句,使用这对大括号 {} 将语句包裹起来。
下面是一些Lambda表达式的示例:
- 无参数的Lambda表达式:
CodeRunnable runnable = () -> System.out.println("Hello, Lambda!");
- 单个参数的Lambda表达式:
CodeConsumer<String> consumer = message -> System.out.println("Message: " + message);
- 多个参数的Lambda表达式:
CodeComparator<Integer> comparator = (a, b) -> Integer.compare(a, b);
- 包含多条语句的Lambda表达式:
CodeFunction<Integer, Integer> square = n -> {
int result = n * n;
return result;
};
。4.匿名内部类和Lambda表达式的区别:
匿名内部类:可以是接口,抽象类,还可以是具体类,产生一个单独的.class字节码文件
Lambda表达式: 只能是函数接口,编译时字节码动态生成
Stream流
😄作用:
针对集合功能简化开发
😄常用功能及特点:
- 惰性求值:
- Stream 是惰性求值的,只有当终端操作被调用时才会执行中间操作,这样可以提高效率。
- 流水线:
- 可以将多个中间操作和一个终端操作连接起来形成流水线,以便高效地对数据进行处理。
- 函数式编程:
- Stream API 鼓励使用函数式编程的思想,通过Lambda表达式来传递操作,使代码更简洁、易读。
- 操作步骤:
- 流的创建
- Stream的操作可以分为中间操作(Intermediate Operations)和终端操作(Terminal Operations)两类。
- 中间操作:如 filter、map、sorted 等,用于对数据进行处理和转换。
- 终端操作:如 forEach、collect、reduce 等,用于触发流的计算并生成最终结果。
- 示例操作:
- filter: 过滤符合条件的元素。
- map: 对每个元素进行操作/转换。
- forEach: 对每个元素执行操作。
- collect: 将流元素收集到集合中。
- reduce: 对流中的元素进行累积操作。
- distinct:去重
- limit:截取参数个数
😄使用:可以用Lambda简化
- 创建流对象
2. 中间操作:
filter:过滤元素。
Codestream.filter(e -> e.startsWith("a"))//过滤以a开始的字符串
map:对每个元素进行操作/转换。
Codestream.map(user -> new SuperMan(user.getName()))
sorted:排序元素。
Codestream.sorted()
- 终端操作:使用后流就关闭了
forEach:对每个元素执行操作。
Codestream.forEach(System.out::println)
collect:将流元素收集到集合中。
CodeList<String> resultList = stream.collect(Collectors.toList())
reduce:对流中的元素进行累积操作。
CodeOptional<String> result = stream.reduce((s1, s2) -> s1 + s2)
count:返回流中元素的数量。
Codelong count = stream.count()
- 流的收集:把流处理过后的数据返回给一个集合
//将流中的元素收集到一个List集合中
List<String> list = stream.collect(Collectors.toList());
//将流中的元素收集到一个Set集合中
Set<String> set = stream.collect(Collectors.toSet());
//收集为指定类型的集合
LinkedList<String> linkedList = stream.collect(Collectors.toCollection(LinkedList::new));
//收集为映射
Map<Integer, String> map =
stream.collect(Collectors.toMap(Function key ,Function value));
Optional类
😄用处:
简化非空操作
😄常用API:
orElse:
T orElse(T other)
如果 Optional 对象中包含值,则返回该值,否则返回指定的默认值 other。
ofNullable:
static <T> Optional<T> ofNullable(T value)
创建一个包含指定值的 Optional 对象,该值可以为空。
线程
😄什么是线程:
简单来说,一个进程可以包含多个线程,可以并发地执行不同的任务。在操作系统中,每个进程都有自己的地址空间、资源和执行状态,而线程是进程中的实际执行单位,它共享进程的资源。多个线程可以共享相同的内存区域,这使得线程之间更容易进行数据共享和通信,也提高了程序的运行效率。
线程实现
😄线程的使用:
使用 Thread 类创建线程:
- 创建 Thread 子类:创建一个继承自 Thread 类的子类,并重写 run() 方法,该方法包含线程的执行逻辑。
Codeclass MyThread extends Thread {
public void run() {
// 线程执行的逻辑
System.out.println("Hello, I am a thread!");
}
}
- 实例化线程对象:实例化 Thread 子类的对象。
CodeMyThread myThread = new MyThread();
- 启动线程:调用线程对象的 start() 方法启动线程的执行。
CodemyThread.start();
使用 Runnable 接口创建线程:
- 创建实现 Runnable 接口的类:创建一个实现 Runnable 接口的类,并实现 run() 方法。
Codeclass MyRunnable implements Runnable {
public void run() {
// 线程执行的逻辑
System.out.println("Hello, I am a thread!");
}
}
- 实例化线程对象:创建 Runnable 对象,并将其作为参数传递给 Thread 构造方法。
CodeMyRunnable myRunnable = new MyRunnable();
Thread thread = new Thread(myRunnable);
- 启动线程:调用线程对象的 start() 方法启动线程的执行。
Codethread.start();
线程的常用方法:
- start():启动线程,使其进入就绪状态并开始执行。
- run():线程的执行逻辑,需要在子类中重写。
- sleep(long milliseconds):使当前线程暂停执行指定的毫秒数。
- join():等待线程终止。
- interrupt():中断线程的执行。
注意事项:
- 线程的启动是通过调用 start() 方法而不是 run() 方法。
- 线程共享数据时需要注意线程安全性,避免出现数据竞争和并发问题。
- 确保合理地管理线程的生命周期,避免资源泄露和性能问题。
线程安全
😄线程安全问题:多个线程对同一个数据进行操作
常见的线程安全问题包括:
- 数据竞争(Race Condition):多个线程同时对共享数据进行读写操作,导致数据的最终状态不确定或错误。
- 死锁(Deadlock):两个或多个线程因为相互持有对方所需的资源而无法继续执行的状态。
- 活锁(Livelock):线程在互相响应对方的动作而无法继续执行的状态,类似于死锁但是线程仍然在不断地改变状态。
- 并发修改异常(Concurrent Modification Exception):在迭代集合的过程中,同时对集合进行修改导致的异常。
线程安全解决方案:
- 加锁机制:通过使用 synchronized 关键字或 Lock 接口等机制,在关键代码段内部对共享资源进行加锁,使得同一时刻只有一个线程可以访问共享资源,从而避免数据竞争。
- 使用线程安全的数据结构:例如 ConcurrentHashMap、CopyOnWriteArrayList 等,这些数据结构内部实现了对共享资源的安全访问。
- 原子操作:使用原子操作类(Atomic 类)来进行原子性操作,保证多个线程对共享变量的操作是原子性的。
- 避免不必要的共享:尽量避免多个线程对同一份数据进行并发访问,可以通过拷贝、副本等方式减少共享数据的访问。
- 使用线程安全的设计模式:如单例模式中的双重检查锁定等,确保在多线程环境下单例对象的正确创建和访问。
- 并发工具类的使用:Java 提供了诸如 CountDownLatch、Semaphore、CyclicBarrier 等并发工具类,可以帮助开发人员更安全地管理多线程之间的协作和同步。
😄代码实现:
//方法一。同步代码块
synchronized(任意对象){
共享的代码块
}
//方法二。同步方法
public synchronized void increment() {
count++;
System.out.println("Incremented: " + count);
}
Thread thread1 = new Thread(() -> counter.increment());
//方法三。Lock锁实现线程同步
//Lock是接口,不能实例化,采用实现类,ReentrantLock来实例化
//void lock():获得锁,void unlock():释放锁
Lock l = new ReentrantLock();
l.lock();//上锁
l.unlock();//解锁
线程通讯
😄什么是线程通讯:
在多线程编程中,线程通讯是指多个线程之间通过共享变量或其他机制来实现信息交换和协作的过程。线程通讯常用于实现线程之间的同步和协作,以达到某种共同的目标。以下是一些常见的线程通讯方式
。等待
wait();//无限等待,其他线程唤醒
wait(long 毫秒);//计时等待,时间到了,自己醒来
。唤醒
void notify();//随机唤醒此对象监视器上等待的单线程
void notifyAll();//唤醒所有进程
不会导致当前线程释放掉锁资源
😄注意事项:
wait()和notify()都必须绑定在对象锁上
Object lock = new Object();
lock.wait();
lock.notify();
线程池
主要优点:
- 降低资源消耗:线程池可以控制并发线程的数量,避免因为创建过多线程而导致系统资源耗尽。
- 提高响应速度:由于线程池中的线程已经预先创建好,当任务到达时可以立即执行,减少了任务等待的时间。
- 提高性能:通过重复利用线程,可以减少线程的创建和销毁开销,从而提高系统的整体性能。
- 可以灵活管理线程:线程池可以根据实际需求调整线程数量,动态适应不同的场景。
需要考虑:
- 线程池大小:线程池应该设定合适的线程数量,既不能过多导致资源浪费,也不能过少影响任务处理效率。
- 任务队列:线程池通常会有一个任务队列,用于存放待执行的任务,线程会从队列中取出任务并执行。
- 线程池类型:不同的编程语言或框架可能提供不同类型的线程池,如固定大小线程池、可缩放线程池等,根据实际需求选择合适的类型。
文件处理
打开指定文件
- 打开应用程序
Runtime.getRuntime().exec(路径)
- 打开文件和文件夹
runtime.getRuntime().exec("cmd /c start " + file.getAbsolutePath());
- 实例
public static void searchFile(File dir,String filename) throws IOException {
if(dir == null || !dir.exists() || dir.isFile()){
return;
}
File []files = dir.listFiles();
if(files != null && files.length > 0){
for (File file : files) {
if(file.isFile()){
if(file.getName().contains(filename)){
System.out.println("找到了");
Runtime runtime = Runtime.getRuntime();
runtime.getRuntime().exec("cmd /c start " + file.getAbsolutePath());
}else{
searchFile(file,filename);
}
}
}
}
删除文件
public static void deleteDir(File dir){
if(dir == null || !dir.exists()){//为空或不存在
return;
}
if(dir.isFile()){//是文件
dir.delete();
return;
}
File []files = dir.listFiles();
for (File file : files) {//遍历一级文件
if(file.isFile()){
file.delete();
}else{
deleteDir(file);
}
}
}
读取文件
FileInputStream
文件读入程序,读一个字节
public static void main(String[] args) throws IOException {
/*
1.读过之后,is就不再有该字符
*/
InputStream is = new FileInputStream("..\\demon\\aa.txt");//输入流管道
//读第一个字节;
int b = is.read();
System.out.println(b);//-1表示没有数据
//一次读多个
byte []butter = new byte[3];
is.read(butter);
System.out.println(new String(butter));
//读所有字节
byte []bu = is.readAllBytes();
System.out.println(new String(bu));
is.close();//流必须关闭
}
FileReader
读取一个字符,可以读汉字
BufferedInputStream
使用的原因:自带8kb的缓冲池,提高性能
InputStream re = new FileInputStream("..\\demon\\aa.txt");
InputStream res = new BufferedInputStream(re);
InputStream ress = new BufferedInputStream(re , 8192 * 2);//扩大缓冲
BufferedReader
新增功能
res.readLine();//读取一行
写入文件
FileOutputStream
程序写入文件,写一个字节
public static void main(String[] args) throws IOException {
FileOutputStream op = new FileOutputStream("..\\demon\\aa.txt");//覆盖数据
FileOutputStream op1= new FileOutputStream("..\\demon\\aa.txt",true);//在文件后加数据
//写字节,只能写一个
op.write(97);
op.write('9');
//写多个数据
byte[]bytes = "我带你".getBytes();
op.write(bytes);
//换行
op.write("\r\n".getBytes());
op.close();
}
FileWrite
写一个字符,可以是汉字
BufferedOutputStream
BufferedWrite
新增功能
res.nextLine();//换行
释放资源
try-catch-finally
- 类代码
try{
System.out.println(10 / 2);
}catch (Exception e){
e.printStackTrace();
}finally {
/*
注:
1.先跑的finally,然后在运行try中,所以finally最好不要return
2.除非jvm结束,finally都要跑一次
3.finally可以进行资源释放
*/
System.out.println("finally里的东西至少跑一次");
}
- 实例
public static void main(String[] args) throws IOException {
OutputStream op = null;//注意OutputStream的作用域
try {
op = new FileOutputStream("..\\demon\\aa.txt");
} catch (IOException e) {
throw new RuntimeException(e);
} finally {
op.close();//关闭流才能刷数据
}
}
try-with-resource
- 类代码
try (//流的定义,会自动关闭){
} catch (IOException e) {
throw new RuntimeException(e);
}
- 实例
try (OutputStream op = new FileOutputStream("..\\demon\\aa.txt");){
} catch (IOException e) {
throw new RuntimeException(e);
}
文件乱码处理
InputStreamReader
控制按什么类型字符读取
try (
InputStream is = new FileInputStream("..\\demon\\aa.txt");
//把原始的按照GBK转换成字符输入流,把GBK转成当前所需的UTF-8
Reader isr = new InputStreamReader(is,"GBK");
BufferedReader br = new BufferedReader(isr);
) {
String line;
while((line = br.readLine()) != null){
System.out.println(line);
}
}
OutputStreamWriter
try (
OutputStream is = new FileOutputStream("..\\demon\\aa.txt");
//把原始的UTF-8转成GBK,写入aa.txt
Writer isr = new OutputStreamWriter(is,"GBK");
BufferedWriter br = new BufferedWriter(isr);
) {
br.write("我是中国人");
br.newLine();
}
打印数据
打印什么就写什么,可以取代写入流
数据输出
特殊文件
properties文件
- 内容
主要是用户信息, 一些属性
- 读取
采用键值对来读取
public static void main(String[] args) throws IOException {
//创建一个properties对象,类似于Map(键值对集合)
Properties properties = new Properties();
System.out.println(properties);
properties.load(new FileReader("..\\demon\\src\\user.properties"));
System.out.println(properties);
}
- 写入
public static void main(String[] args) throws IOException {
Properties properties = new Properties();
properties.setProperty("bb","djska");
properties.setProperty("aa","adsd");
properties.store(new FileWriter("..\\demon\\src\\user.properties"),"写入注解");
}
XML文件
可扩展标记语言
- 作用
存储复杂的数据结构
用来做配置文件
- 读取
Dom4J