在Java中,Exception
(异常)和Error
(错误)都是继承自Throwable
类的子类,它们用于指示程序中发生了某种不正常的情况。尽管它们有一些相似之处,但它们代表了不同类型的事件,并且通常需要以不同的方式进行处理。
Exception(异常)
- 定义:
Exception
及其子类表示由程序或外部环境导致的条件,这些条件是可以被程序捕获并处理的。 - 特性:
- 可以被捕获并通过适当的异常处理机制进行处理。
- 通常表明程序遇到了一种可以恢复的情形。
- 分为检查性异常(checked exceptions)和非检查性异常(unchecked exceptions)。检查性异常要求必须在代码中显式地处理或者声明抛出,例如
IOException
;非检查性异常则不需要强制处理,通常是编程错误的结果,比如NullPointerException
。
Error(错误)
- 定义:
Error
及其子类表示严重的问题,通常是不可恢复的,这类问题大多数是由JVM抛出的,用来指示一些不应该出现的严重情况。 - 特性:
- 不建议也不应该尝试捕获错误,因为它们通常意味着程序无法继续安全运行。
- 表示系统级的问题,如内存溢出、链接失败等,这些问题通常超出了应用程序的控制范围。
- 常见的例子包括
OutOfMemoryError
、StackOverflowError
等。
总结
- 处理方式:对于
Exception
,我们通常可以通过try-catch语句捕获并处理,从而让程序有机会恢复正常执行。而对于Error
,一般情况下不应试图捕获,因为它们往往指示着非常严重的问题,程序在这种情况下很难或不可能恢复。 - 目的:
Exception
用于指示那些程序设计者认为是正常的可预见的问题,并提供了处理这些问题的机会。而Error
则是为了指出那些严重的、通常是不可恢复的问题,提醒开发者注意并修正潜在的设计或实现缺陷。
在Java中,异常可以分为两大类:检查性异常(Checked Exceptions)和非检查性异常(Unchecked Exceptions)。非检查性异常又通常被称为运行时异常(Runtime Exceptions)。下面详细介绍这三种类型的异常及其区别:
检查性异常(Checked Exceptions)
- 定义:检查性异常是指那些在编译时期就必须处理的异常。它们通常是外部因素导致的异常,例如文件未找到、网络连接失败等。
- 特性:
- 必须在方法签名中使用
throws
关键字声明,或者在方法体内用try-catch
块捕获并处理。 - 表示程序在正常操作过程中可能遇到的可恢复的情况。
- 继承自
Exception
类但不继承自RuntimeException
。 - 常见的例子包括
IOException
、SQLException
等。
- 必须在方法签名中使用
非检查性异常(Unchecked Exceptions)/ 运行时异常(Runtime Exceptions)
- 定义:非检查性异常是指那些在编译时期不需要强制处理的异常,也称为运行时异常。它们通常是由编程错误引起的,比如试图访问空引用的方法或数组越界等。
- 特性:
- 不需要在方法签名中声明,也不强制要求在代码中捕获。
- 表示程序逻辑中的错误,这些错误通常是由于程序员的疏忽造成的。
- 继承自
RuntimeException
类,而RuntimeException
本身是Exception
的一个子类。 - 常见的例子包括
NullPointerException
、ArrayIndexOutOfBoundsException
、ArithmeticException
等。
区别
- 编译器检查:检查性异常在编译阶段会被Java编译器强制要求处理,而非检查性异常则不会。
- 原因:检查性异常通常是由于外部资源不可用等原因造成的,而非检查性异常更多是因为程序内部错误导致的。
- 处理方式:对于检查性异常,你需要决定是否通过
throws
声明将其抛出给调用者处理,还是在当前方法内使用try-catch
来处理。而对于非检查性异常,虽然你也可以选择处理它们,但这不是必须的,并且很多时候你会希望通过改进代码质量来避免这类异常的发生。
thread.sleep(0)有什么作用
在Java中,调用Thread.sleep(0)
意味着请求当前正在执行的线程暂停执行0毫秒,即请求线程放弃剩余的时间片并允许具有相同优先级的其他线程有机会运行。这种技术有时被称为“让出(yield)”,但要注意的是,这与直接调用Thread.yield()
方法的效果并不完全相同。
具体作用
- 调度提示:
Thread.sleep(0)
主要是给线程调度器一个暗示,表示这个线程愿意放弃剩余的时间片。然而,是否真的会有上下文切换取决于底层操作系统的线程调度策略和当前系统负载等因素。 - 促进公平性:在一个多线程环境中,使用
Thread.sleep(0)
可以帮助提高线程间的公平性,尤其是在有多个相同优先级的线程竞争CPU时间时,可以增加其他线程获得执行的机会。 - 不同于
Thread.yield()
:虽然两者的目标相似,都是为了给其他线程提供执行机会,但是它们的行为可能会有所不同。Thread.yield()
是一个本地方法,其行为依赖于具体的JVM实现以及操作系统调度策略。相比之下,Thread.sleep(0)
更明确地依赖于操作系统的线程调度机制来决定是否进行上下文切换。
需要注意的是,使用Thread.sleep(0)
并不能保证任何特定的行为或结果,它只是向调度器发出的一个建议。实际上,是否会发生上下文切换,以及哪个线程将获得执行机会,仍然由操作系统的线程调度算法决定。因此,在大多数情况下,直接依赖这种方法来控制线程的执行顺序并不是最佳实践。对于需要精确控制线程执行的情况,通常推荐使用更高层次的并发控制工具,如锁、信号量或其他同步机制。
ExecutorService.shutdown()
在Java中,Thread
对象本身并没有提供shutdown()
方法。ExecutorService
接口提供的线程池管理功能,特别是当调用其shutdown()
方法后的任务执行行为。
关于 ExecutorService.shutdown()
当你调用一个ExecutorService
的shutdown()
方法时,它会停止接受新的任务提交,但是会继续执行已经提交到服务中的任务。具体的行为如下:
- 拒绝新任务:一旦
shutdown()
被调用,尝试向该ExecutorService
提交新任务将会被拒绝,并抛出RejectedExecutionException
。 - 完成已提交的任务:对于在调用
shutdown()
之前已经提交的任务,包括那些可能还在等待执行的任务,ExecutorService
会继续执行它们直到完成。
如果你希望在调用shutdown()
后立即停止所有正在等待或执行的任务,可以考虑使用shutdownNow()
方法。这个方法会尝试停止所有正在执行的任务,并返回一个列表包含还未开始执行的任务。
示例代码
ExecutorService executor = Executors.newFixedThreadPool(2);
executor.submit(new Task()); // 提交任务A
executor.submit(new Task()); // 提交任务B
executor.shutdown(); // 调用shutdown,此时不能再提交新任务,但A和B将继续执行
// 如果你想尝试立即停止所有任务,可以使用:
// List<Runnable> notExecutedTasks = executor.shutdownNow();
在这个例子中,即使调用了shutdown()
,如果任务A和任务B已经被提交,在它们完成之前,ExecutorService
会确保它们得到执行。然而,任何在shutdown()
调用之后尝试提交的新任务都会被拒绝。
请根据你的实际需要选择合适的方法来控制线程池的行为。如果你的目标是优雅地关闭服务并确保所有已提交的任务完成,那么shutdown()
是你想要的方法;如果你需要立即停止所有活动,那么shutdownNow()
将更适合。
springIOC
Spring IOC(Inversion of Control,控制反转)是Spring框架的核心概念之一。它是一种设计思想,通过这种思想,对象的创建和依赖关系的管理从程序代码中转移到了外部容器(如Spring容器)。这种方法使得代码更加解耦、更易于测试和维护。
核心概念
-
Bean: 在Spring中,由Spring IoC容器管理的对象称为Bean。它们是构成应用程序的基本组件。
-
IoC容器: Spring提供了两种类型的IoC容器,分别是
BeanFactory
和ApplicationContext
。ApplicationContext
是BeanFactory
的子接口,提供了更多企业级功能的支持,如AOP特性、消息资源处理、事件发布等。 -
依赖注入(DI): 依赖注入是实现控制反转的一种方式,指的是对象的依赖关系由外部容器在运行时动态地注入到对象内部,而不是由对象自己创建或查找其依赖。
实现依赖注入的方式
-
构造器注入: 通过类的构造函数来注入依赖。
-
Setter方法注入: 使用JavaBean风格的setter方法注入依赖。
-
字段注入: 直接在字段上使用注解进行注入,这种方式虽然简洁但不如前两种方式推荐,因为它不利于单元测试。
配置Bean的方式
-
基于XML配置: 在早期版本的Spring中广泛使用,通过XML文件定义Bean及其依赖关系。
-
基于注解配置: 使用注解(如
@Component
,@Service
,@Repository
,@Controller
等)标注需要被容器管理的类,并使用@Autowired
等注解自动装配依赖。 -
基于Java配置: 使用配置类和
@Configuration
,@Bean
等注解以编程方式定义Bean及其依赖关系。
Spring IoC的优点
- 降低耦合度:通过将对象之间的依赖关系交由容器管理,减少了代码间的直接依赖。
- 便于测试:由于依赖可以通过配置或注解的形式指定,因此可以很容易地为测试替换真实的依赖。
- 更好地管理Bean生命周期:Spring容器负责Bean的创建、初始化、销毁等生命周期管理,开发者只需关注业务逻辑。
Spring IoC极大地简化了Java EE应用开发中的复杂性,使得开发者能够更加专注于业务逻辑的实现而非基础设施的搭建。通过合理利用Spring IoC,可以构建出更加灵活、可扩展的应用程序。
java 二分查找
public static int binarySearch(int[] arr, int target) {
int left = 0;
int right = arr.length - 1;
while (left <= right) {
int mid = left + (right - left) / 2; // 防止溢出
if (arr[mid] == target) {
return mid; // 找到目标值,返回索引
}
if (arr[mid] < target) {
left = mid + 1; // 调整左边界
} else {
right = mid - 1; // 调整右边界
}
}
return -1; // 未找到目标值
}
base64原理
Base64编码使用64个不同的字符来表示数据,这些字符包括大写字母A-Z、小写字母a-z、数字0-9、加号"+“和斜杠”/"。每个Base64字符代表6位二进制数据,这是因为(2^6 = 64),正好对应这64个字符。
在进行Base64编码时,每3个字节(即24位)(ASCII 编码每个字符代表8位)的二进制数据被分组,并转换为4个Base64字符(每个字符代表6位)。这意味着,Base64编码后的数据长度通常是原数据长度的(4/3)倍。如果原始数据的长度不是3的倍数,则会在编码字符串的末尾添加一个或两个等号“=”作为填充,以确保编码后的字符串长度是4的倍数。
总结来说,虽然我们讨论的是Base64字符(每个代表6位),但没有一个固定的位数可以描述整个Base64编码的数据长度,因为它依赖于输入数据的大小,并且需要考虑到可能存在的填充字符。
java 设计模式中的单例模式的懒汉与饿汉
在Java的设计模式中,单例模式是一种创建型设计模式,它保证一个类仅有一个实例,并提供一个全局访问点来访问这个实例。根据实例化对象的时机不同,单例模式可以分为懒汉式和饿汉式两种实现方式。
饿汉式单例
饿汉式单例的特点是在类加载的时候就创建好实例,如果该实例没有被使用,则会造成资源浪费。这种方式是线程安全的,因为实例在类加载时就已经初始化好了,不存在多线程并发访问的问题。
public class HungrySingleton {
private static final HungrySingleton instance = new HungrySingleton();
// 私有构造函数,防止外部实例化
private HungrySingleton() {}
public static HungrySingleton getInstance() {
return instance;
}
}
懒汉式单例
懒汉式单例则是在第一次调用getInstance()
方法时才创建实例,实现了“延迟加载”,节省了资源。但是,这种实现方式如果不进行额外处理,在多线程环境下可能会导致多个线程同时通过检查并创建多个实例,破坏单例模式。为了解决这个问题,通常需要对getInstance()
方法进行同步处理。
简单懒汉式(非线程安全)
public class LazySingleton {
private static LazySingleton instance;
private LazySingleton() {}
public static LazySingleton getInstance() {
if (instance == null) {
instance = new LazySingleton();
}
return instance;
}
}
同步懒汉式(线程安全)
为了确保在多线程环境下的安全性,可以在getInstance()
方法上加同步锁synchronized
关键字,但这样会降低性能。
public class ThreadSafeLazySingleton {
private static ThreadSafeLazySingleton instance;
private ThreadSafeLazySingleton() {}
public static synchronized ThreadSafeLazySingleton getInstance() {
if (instance == null) {
instance = new ThreadSafeLazySingleton();
}
return instance;
}
}
除此之外,还有更高效的双重检查锁定(Double-Checked Locking)机制和静态内部类等方式来实现线程安全的懒汉式单例,以提高性能和减少资源消耗。例如,使用静态内部类的方式如下:
public class Singleton {
private Singleton() {}
private static class SingletonHolder {
private static final Singleton INSTANCE = new Singleton();
}
public static Singleton getInstance() {
return SingletonHolder.INSTANCE;
}
}
这种方式利用了ClassLoader的机制来保证线程安全,并且实现了延迟加载。
spring 自动配置
1, 在Spring Boot项目中的引导类上有一个注解@SpringBootApplication,这个注解是对三个注解进行了封装,分别是:
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan
2, 其中@EnableAutoConfiguration是实现自动化配置的核心注解。 该注解通过@Import注解导入对应的配置选择器。
内部就是读取了该项目和该项目引用的Jar包的的classpath
路径下META-INF/spring.factories
文件中的所配置的类的全类名。 在这些配置类中所定义的Bean会根据条件注解所指定的条件来决定是否需要将其导入到Spring容器中。
3, 条件判断会有像@ConditionalOnClass
这样的注解,判断是否有对应的class文件,如果有则加载该类,把这个配置类的所有的Bean放入spring容器中使用。
java设计模式
Java 中的设计模式(Design Patterns)是解决软件开发中常见问题的可复用解决方案模板,它们不是具体的代码,而是描述在特定场景下如何组织类和对象的方式。设计模式帮助开发者编写更清晰、可维护、可扩展的代码。
🧩 一、设计模式分类(GoF 23 种经典模式)
根据目的和应用场景,设计模式可以分为三大类:
类型 | 名称 | 描述 |
---|---|---|
创建型(Creational) | 工厂模式、抽象工厂、单例模式、建造者模式、原型模式 | 负责对象的创建与初始化,隐藏对象创建的细节 |
结构型(Structural) | 适配器、代理、装饰器、组合、外观、享元、桥接 | 处理对象与类之间的组合关系,优化系统结构 |
行为型(Behavioral) | 观察者、策略模式、责任链、命令模式、模板方法、迭代器、访问者 | 关注对象之间的通信和职责分配 |
📚 二、Spring 框架中的设计模式应用(举例)
设计模式 | Spring 中的应用 |
---|---|
工厂模式 | BeanFactory / ApplicationContext |
代理模式 | AOP 的底层实现(JDK 动态代理、CGLIB) |
模板方法 | JdbcTemplate 、RestTemplate |
适配器模式 | HandlerAdapter (MVC 请求处理) |
观察者模式 | 事件监听机制(ApplicationEventPublisher ) |
单例模式 | 默认作用域下的 Bean 都是单例的 |
如果你希望我针对某个设计模式进行深入讲解或结合实际项目案例分析,请告诉我!我可以为你详细演示其结构、优缺点以及使用场景。
Spring中@Component和@Bean的区别
在 Spring 框架中,@Component
和 @Bean
注解都是用于将类声明为 Spring 容器管理的 Bean,但它们之间存在一些重要的区别。下面详细解释这两个注解的不同之处。
@Component
-
定义:
@Component
是一个通用的 Spring 容器注解,它指示该类将被 Spring 的类路径扫描机制自动检测,并注册为 Spring 容器中的 Bean。 -
使用场景:适用于你自己的应用程序组件。当你创建了一个新类并希望将其作为 Spring 管理的 Bean 时,可以在类上添加
@Component
注解(或者其更具体的变体如@Service
,@Repository
,@Controller
)。 -
自动发现:Spring 通过类路径扫描来自动发现带有
@Component
注解的类,并且会自动注册这些类为 Spring 容器中的 Bean。 -
示例:
@Component public class MyComponent { public void doSomething() { // 实现细节 } }
@Bean
-
定义:
@Bean
注解用来显式声明一个方法返回的对象应该被注册为 Spring 容器中的 Bean。这个注解通常与@Configuration
注解一起使用,表明这是一个配置类。 -
使用场景:主要用于当你需要对第三方库中的类进行实例化或当你想要对 Bean 的创建过程有更多的控制时。例如,当你不能直接修改某个类以添加
@Component
注解时,你可以通过在一个配置类的方法上使用@Bean
来注册它。 -
显式声明:不同于
@Component
自动扫描的方式,@Bean
需要在配置类中明确地定义方法来创建 Bean。 -
示例:
@Configuration public class MyConfig { @Bean public MyComponent myComponent() { return new MyComponent(); } }
主要区别总结
特性 | @Component | @Bean |
---|---|---|
作用目标 | 类级别注解,用于标记类为 Spring Bean | 方法级别注解,用于指定方法的返回值应注册为 Spring Bean |
使用方式 | 直接在类上标注 | 在 @Configuration 类的方法上标注 |
自动检测 | 支持,通过组件扫描自动注册 | 不支持,需手动配置 |
灵活性 | 较低,适合简单的 Bean 创建 | 更高,允许复杂的初始化逻辑 |
简单来说,如果你正在开发的应用程序中的类可以由你自己控制并且可以直接在类上添加注解,那么 @Component
及其特化版本是很好的选择。但是,如果需要注册第三方库中的类或者需要更多的控制来创建和配置 Bean,则应使用 @Bean
注解在配置类中定义 Bean。
springMVC
1.用户发送出请求到前端控制器DispatcherServlet
2.DispatcherServlet收到请求调用HandlerMapping(处理器映射器)
3.HandlerMapping找到具体的处理器,生成处理器对象及处理器拦截器(如果有),再一起返回给DispatcherServlet。
4.DispatcherServlet调用HandlerAdapter(处理器适配器)
5.HandlerAdapter经过适配调用具体的处理器(Handler/Controller)
6.Controller执行完成返回ModelAndView对象
7.HandlerAdapter将Controller执行结果ModelAndView返回给DispatcherServlet
8.DispatcherServlet将ModelAndView传给ViewReslover(视图解析器)
9.ViewReslover解析后返回具体View(视图)
10.DispatcherServlet根据View进行渲染视图(即将模型数据填充至视图中)
11.DispatcherServlet响应用户
OAuth2授权码
OAuth2.0有四种授权模式:授权码模式、隐式授权模式、用户名密码模式、客户端模式
1.测试公众号申请(获取appID和appsecret)
2…