Entity:这是最基本的空接口,用来标注实体类,.表示该实体类能够有JPA管理.
MappedSuperclass:用于标注父类,将实体类中的公共属性抽取到一个父类汇总,用于减少实体类的复杂度.
Embeddable:用于标注嵌入式对象,讲一个或多个非实体类的对象嵌入到实体类对象中,以此简化实体对象的设计.
EntityListeners:用于标注监听器类,是一种AOP的实现方式,可以在实体类的生命周期中回调指定的方法.
Converter:用于将实体属性进行转化,其中的converToDatabaseColumn方法将实体属性转化为数据库字段值,converToEntityAttribute方法将数据库字段值转为实体属性.
AttributeConverter:是Converter的子接口,用于将实体类中较为复杂的属性转为数据库可以接受的简单类型,如JSON转为 VARCHAR
BIO是一种同步的I/O操作,它同一时间只能处理一个请求,如果在处理一个请求时,又接受到了的多个请求,那其他请求会等待当前请求处理完毕.
NIO是一种异步的I/O操作,它在接受到请求之后会建立连接并且将这个请求注册到Selector中,Selector会轮询注册通道,如果发现可操作的通道之后,会返回通道集合,然后进行I/O操作.
AIO也是一种异步的I/O操作,AIO在读取数据或发送数据时,不会等到数据准备完毕或数据传输完毕,而是会向操作系统注册一个回调函数,当数据传输完成之后操作系统会调用回调函数,这样就可以实现I/O的异步操作.
区别:
在Java中,集合主要分为两类,分别是Map Collection,这两个是集合框架中的顶级接口,Collection下面有三个子接口List Set Queue.
Collection下面主要分为三个子接扣,List 有序集合, Set 无序集合 Queue 队列.
List有序集合,即存入和取出数据有序,可以根据索引去确定某一个数据在集合中的位置,也可以将其取出,或者对指定位置的数据做修改或删除操作,也可以在指定位置添加数据.
ArrayList
ArrayList是Java中的动态数组,它是由一个可调整大小的数组实现的集合.它允许在任何时候通过索引访问列表中的元素,并提供了在列表末尾添加,插入和删除元素的操作.
LinkedList
LinkedList是Java中的一个链表实现,它可以用来存储一个序列的元素,其中有一个指向下一个元素的引用.和ArrayList不同,LinkedList的元素不是被存储在一个连续的数组中,而是散步在内存中的不同位置上.链表的每个元素都包含一个指向下一个元素的引用.
Vector
Vector和ArrayList类似,也是基于数组实现的,并且具有相似的动态扩容机制,但是和ArrayList相比,还有一些特点:
Set是Java中的一种集合,它以无序和唯一的方式存储对象,即不能在Set中重复添加同一个对象.
HashSet
HashSet是Java中最常用的一种Set实现,它基于HashMap实现,不保证元素的顺序,但是能够实现快速的插入,查找和删除操作
TreeSet
TreeSet是实现SortedSet接口的一个集合类,它是一个基于红黑树的有序集合.能够实现常数时间的查找,插入和删除操作,
Map是以key-value的形式存储并操作数据的.
HashMap
HashMap是Java中最常用的Map实现类之一,它基于哈希表实现
HashTable
HashTable和HashMap类似,也是使用哈希表存储键值对,相比较于HashMap.HashTable也有一些特点:
TreeMap
TreeMap基于红黑树数据结构,可以根据键值的自然排序或自定义比较器排序.
索引是数据库的一种优化技术,用于增加数据库的查询操作,如果一个数据表中的数据量较大时,查询某一条数据所消耗的时间就会变多.用查字典举例,我们传统的查询方式就是一页页去找,直到找到我们想要的数据为止,如果增加了索引之后,就类似于我们在查字典的时候按照音序或者部首偏旁的方式去查找,其效率会更高.但是字典中部首偏旁和音序占用了字典的前面一部分,也是会占用一定页数的.同样,数据库中的索引也会占用一定的磁盘空间.
如果我们想要创建索引,可以使用 create index 索引名 on 表名(库名);
根据逻辑维度,索引可以分为以下几类:
在使用索引的情况下,某些特定场景会导致索引失效,这样我们的查询效率会大大下降,主要有以下几个场景:
针对以上索引失效的问题,可以采用这些方式去解决:
尽量避免使用不等于操作符:可以使用其他方式代替,如使用IN()或NOT()等。
针对大表格进行查询操作时,可以使用“分页”或“分区”技术,在不读取所有记录的情况下快速返回结果。
如果数据分布不均匀,可以使用数据分析工具进行分析,优化访问计划,并使用多列索引或覆盖索引来优化查询。
单例设计模式可以保证一个类只有一个实例对象,并且可以在项目中全局访问.
1.饿汉式,饿汉式单例模式是最基本的实现方式之一,通过在类加载时初始化单例实例,可以确保线程安全。
public class EagerSingleton {
private static final EagerSingleton instance = new EagerSingleton();
private EagerSingleton() {}
public static EagerSingleton getInstance() {
return instance;
}
}
2.懒汉式,懒汉式单例模式与饿汉式单例模式的区别在于单例实例的初始化时机。懒汉式单例模式不会在类的加载阶段创建单例实例,而是在第一次调用时才进行初始化,这样可以减少系统启动时间。
public class LazySingleton {
private static volatile LazySingleton instance = null;
private LazySingleton() {}
public static synchronized LazySingleton getInstance() {
if (instance == null) {
instance = new LazySingleton();
}
return instance;
}
}
3.静态内部类单例模式,与懒汉式单例模式不同,静态内部类单例模式的单例实例的创建是在第一次调用时进行的,但是,由于他是在内部类的加载时进行初始化,所以线程安全也得到了保障.
public class StaticInnerClassSingleton {
private StaticInnerClassSingleton() {}
private static class SingletonHolder {
private static final StaticInnerClassSingleton instance = new StaticInnerClassSingleton();
}
public static StaticInnerClassSingleton getInstance() {
return SingletonHolder.instance;
}
}
4.双重校验锁单例模式,这是一种懒汉式单例模式的优化,在多线程的环境下更加安全和高效,它也是在第一次调用时才进行单例模式的初始化,在后续的访问中直接访问单例实例.
public class DoubleCheckSingleton {
private static volatile DoubleCheckSingleton instance = null;
private DoubleCheckSingleton() {}
public static DoubleCheckSingleton getInstance() {
if (instance == null) {
synchronized (DoubleCheckSingleton.class) {
if (instance == null) {
instance = new DoubleCheckSingleton();
}
}
}
return instance;
}
}
5.枚举单例模式,他说一种相对简单但非常强大的实现方式,它可以解决大多数单例实现中可能存在的序列化,反射,线程安全等问题.
public enum EnumSingleton {
INSTANCE;
public void method() {
}
}
6.注册式单例模式,它提供了单例验证的功能,为单例实例提供了确保线程安全和正确性的线程安装件结构.实现方式可以借助于容器中的注册机制,spring容器的bean注入等,具体的视线方式于技术选型相关.
public class RegistrySingleton {
private static Map registry = new HashMap<>();
static {
RegistrySingleton singleton = new RegistrySingleton();
registry.put(singleton.getClass().getName(), singleton);
}
protected RegistrySingleton() {}
public static RegistrySingleton getInstance(String name) {
if (name == null) {
name = RegistrySingleton.class.getName();
}
if (!registry.containsKey(name)) {
try {
registry.put(name, (RegistrySingleton) Class.forName(name).newInstance());
} catch (Exception e) {
e.printStackTrace();
}
}
return registry.get(name);
}
}
7.ThreadLocal单例模式,一种多线程环境下非常安全和高效的单例实现方式,它通过为每个线程提供一个独立的实力,从而保证了线程安全性和正确性,在大量线程的并发情况下非常可靠.
通过ThreadLocal.withInitial()方法创建ThreadLocal实例,每个线程访问该实例都会返回各自独立的副本.这种方式不仅保证了线程安全性和可靠性,也减少了线程间的同步开销.
public class ThreadLocalSingleton {
private static final ThreadLocal threadLocalInstance = ThreadLocal.withInitial(ThreadLocalSingleton::new);
private ThreadLocalSingleton() {}
public static ThreadLocalSingleton getInstance() {
return threadLocalInstance.get();
}
}
8.容器单例模式,一种灵活的单例实现方式,它通过容器来管理各种单例实例的创建和访问,可以在容器中灵活地管理单例实例的初始化,销毁等操作,同时也提供了更好的扩展性和灵活性,而且这种方式可以通过Spring的集成等方式实现.
public class ContainerSingleton {
private static Map singletonMap = new HashMap<>();
private ContainerSingleton() {}
public static void putInstance(String key, Object instance) {
if (key != null && !"".equals(key.trim()) && instance != null && !singletonMap.containsKey(key)) {
singletonMap.put(key, instance);
}
}
public static Object getInstance(String key) {
return singletonMap.get(key);
}
}
1.开闭原则
开闭原则是指类,模块,方法等实体是可扩展的,而不是不可修改的.用扩展类的方式实现新功能,而不是修改原有的代码.这样对现有接口的扩展不会影响其原有的代码结构.从而保证了程序的可维护性,可扩展性和可复用性.
2.单一职责原则
单一职责原则是指一个类或方法应该只有一个单一的职责.每个类都应该只有一项职责.如果一个类中又多重职责,不同的职责可能会引起类的变化,降低程序的可扩展性和可维护性.单一职责原则可以保证类的内聚性,使得类的功能更加专一,更加易于理解和维护.
3.里氏替换原则
里氏替换原则是指子类对象应该能够完全替换父类对象.即,在程序中使用父类对象的地方,可以使用其子类对象来代替,而程序的逻辑不应该发生改变.保证了程序的正确性.里氏替换原则保证了程序的可扩展性和可维护性,使得程序具有更好的灵活性和可靠性.
4.依赖倒置原则
依赖倒置原则是指高层模块不应该依赖于低层模块,而应该通过抽象接口来解耦.即任意两个类之间都应该通过抽象接口来相互通信,而不是直接依赖于具体的实现.依赖倒置原则可以保证程序的可扩展性和可维护性,使得程序具有更好的灵活性和可靠性.
5.接口隔离原则
接口隔离原则是指应该将不同的功能性接口分开,避免将过多的功能集中到同一个接口中.这样可以避免客户端依赖于不需要的接口,也可以使得功能更加模块化和易于维护.该原则可以避免冗余代码和接口.
6.合成复用原则
合成复用原则是指尽量使用合成/聚合关系,而不是继承关系,来实现代码的复用.因为击沉关系会有很强的耦合性,容易造成代码的膨胀和混乱,而合成/聚合关系能够更好的解耦,实现代码的复用和扩展.
设计模式的六大原则是设计模式中最基础的原则,这些原则并不是互相孤立的,他们之间互相补充和交织,能够更好地知道和规范软件开发的实践.
创建型
创建模式的目的是尽可能的抽象对象的创建过程.并根据需求灵活的创建.例如:工厂模式,单例模式等.
结构型
结构型和对象之间的耦合有关,主要是为了提高系统的效率和灵活性,降低耦合度,例如:桥接模式,装饰模式等.
行为型
行为型模式主要关注对象之间的通信及职责分配,达到解耦和复用的目的,实现松耦合.比如:观察者模式,访问者模式等.
工厂模式是将对象的创建和使用进行了分离,定义一个接口用于创建对象,但让子类决定要实例化哪个类,从而可以在不改变其他相关类的情况下添加新的产品类.这种模式实现的解耦合,增强了系统的可扩展性.实现过程中可以使用抽象类和接口来定义工厂类和产品类.
抽象工厂模式提供了一种接口,可以创建一些列相互依赖的对象.通常在一个产品族中定义一组产品,不同的产品族有不同的工厂类,从而实现了不同产品族的解耦.这种模式的有点是能够保证同一产品族的产品在一起使用,并且可以在不修改客户端代码的前提下新增新产品.
单例模式是保证一个类仅有一个实例,并通过一个全局访问点.这种模式的优点是可以全员访问单例对象,而不需要将对象的引用传递给其他对象,从而简化了对该对象的管理和维护.
建造者模式讲一个复杂对象的构建过程分解为多个简单的对象构造过程,从而依次组装起来,最终构建成一个复杂对象.它将构建过程和表示分离,从而使得相同的构建过程可以创建不同的表示,增强了系统化的灵活性和可扩展性.建造者模式适用于一个对象有复杂的内部结构,要求把生成过程封装起来以及生成的对象具有复杂的属性等.
原型模式通过拷贝一个现有对象来创建一个新的对象,而不是使用new关键字实现.这种方式可以避免复杂类的创建过程,提高创建对象的效率,同时可以保证对象的类型和属性的完整性,适用于对象的创建过程比较复杂或对象的类型不确定的情况.
适配器模式通过重新定义一个适配器类将现有类的接口转化为客户端需要的接口,从而实现两个不兼容接口之间的通信.适配器模式 分为类适配器模式和对象适配器模式,在对象适配器模式中,适配器和它所适配id喜爱你个相互协作,将请求转发给该对象.
桥接模式是将一个大类或一系列紧密相连的类分为两个独立的等级结构,从而是它们可以独立变化,相互之间减少耦合,提高系统的灵活性.这种模式的核心是将抽象部分和实现部分分离,实现部分可以在运行时动态切换,而不影响抽象部分.
组合模式是将对象组合成树形结构以表示"整体/结构"的层次结构关系,是的客户端可以将单个对象和组合对象同等对待,从而简化代码的开发和维护,组合模式分为透明模式和安全模式两种,在透明模式中,组合中的所有对象都符合同一接口,客户端不需要区分不同的对象类型.
装饰模式动态地给一个对象添加一些额外的职责,而不会改变其原有的接口和实现,从而使得类的功能更加灵活和可扩展.装饰模式实现方法是通过装饰类和被装饰类实现同一接口,从而实现透明的装饰.
外观模式为客户端提供了一组简单的接口,从而隐藏了复杂的系统结构和细节,使得客户端使用起来更加方便.外观模式通常将大量的逻辑和操作分解到不同的类中取,通过外观类的同一接口暴露给客户端,从而提高系统的易用性和可维护性.
享元模式通过共享对象来减少系统中的对象数量.提高系统的性能和效率.享元对象通常具有内部状态和外部状态,其中内部状态可以被共享,外部状态需要由客户端来管理.享元模式适用于需要大量的对象,而这些对象使用相同的数据进行初始化,从而达到复用的目的.
代理模式在不改变原有代码的情况下,提供了一个代理对象来控制对某个对象的访问,代理模式分为静态代理和动态代理两种,静态代理需要手动编写代理类,而动态代理使用Java反射机制实现动态代理,使得代理类更加灵活和易于维护.
职责亮模式使得请求的发送者和接收者都不需要对方的存在,而是通过一条链处理请求.请求沿着链的传递,直到有对象处理该请求或没有对象可处理为止.职责链模式适用于存在多个处理对象且处理对象不确定的情况.
命令模式讲一个请求封装为一个对象,使得请求的发送者和接收者可以分别处理请求,从而将两者解耦.命令模式的优点是可以方便地实现撤销,重做和队列操作等功能,适用于将请求和处理对象解耦的情况.
解释器模式定义一个语言的语法,并定义一个解释器来解释该语言中的句子,即根据规定的语法解释输入的内容.解释器模式适用于需要对复杂的语言或语法进行处理的情况.
迭代器模式提供一种无需直到内部结构的方式来访问集合对象中的元素,从而使得集合对象与访问对象解耦.迭代器模式的优点是增加了带阿米的复用性和灵活性,同时提高了系统的可扩展性和易用性.
中介者模式定义了一个中介对象来封装对象之间的交互关系.使得每个对象都只需知道如何与中介对象进行交互,而不需要了解其他对象的信息.中介者模式适用于存在大量对象之间耦合的情况.
备忘录模式提供了一种在不破坏封装范围内恢复对象状态的方法.备忘录模式将对象状态封装到备忘录中,从而可以对对象的状态进行备份,并在需要时进行回复,备忘录模式的有点是可以有效地解决对象状态管理问题,从而提高系统的可靠性和灵活性.
观察者模式定义了一种一对多的依赖关系,当一个对象的状态发生改变时,它的所有依赖对象都会自动收到通知并进行更新.观察者模式适用于存在多个对象之间关系复杂,难以进行可靠管理的情况,具有减少耦合.增强对象之间的协作和可维护性等优点.
状态模式定义了一种状态机,将对象的状态从复杂的条件逻辑中分离出来,并将每一个状态封装成一个类.状态之间可以相互转移,通过调用不同状态类的方法来改变对象的状态.状态模式的优点是降低了对象之间的耦合度和复杂性,提高了系统的可扩展性和可维护性.
策略模式定义了一系列算法,将每个算法封装起来并使它们之间可以相互替换,从而使得算法可以独立于客户端而变化.策略模式的优点是可以动态地改变算法的视线,而不需要修改具体的实现类,提高了代码的可维护性和灵活性.
模板方法模式定义了一个操作中的算法框架,而将一些步骤延迟到子类中实现.模板方法模式的优点是可以在不影响算法框架的情况下更改某些步骤的实现,提高了系统的可扩展性和灵活性.
访问者模式定义了一组访问方法,可以在不修改具体元素的类的情况下访问和操作元素中的部分或全部内容.访问者模式适用于数据结构相对稳定的情况,且需要增加新的操作但不希望影响到各个具体元素.访问者模式的优点是可以方便地增加访问和操作对象的功能,而不需要修改已有元素类的代码,提高了系统的可扩展性和灵活性.
静态代理
public interface Subject {
void doSomething();
}
public class RealSubject implements Subject {
@Override
public void doSomething() {
System.out.println("RealSubject do something");
}
}
public class ProxySubject implements Subject {
private Subject subject;
public ProxySubject(Subject subject) {
this.subject = subject;
}
@Override
public void doSomething() {
System.out.println("Before invoke doSomething()");
subject.doSomething();
System.out.println("After invoke doSomething()");
}
}
// 使用静态代理
Subject subject = new ProxySubject(new RealSubject());
subject.doSomething();
JDK代理
public interface Subject {
void doSomething();
}
public class RealSubject implements Subject {
@Override
public void doSomething() {
System.out.println("RealSubject do something");
}
}
public class JDKProxy implements InvocationHandler {
private Object target;
public JDKProxy(Object target) {
this.target = target;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("Before invoke " + method.getName() + "()");
Object result = method.invoke(target, args);
System.out.println("After invoke " + method.getName() + "()");
return result;
}
}
// 使用JDK代理
Subject subject = new RealSubject();
Subject proxy = (Subject) Proxy.newProxyInstance(
Subject.class.getClassLoader(),
new Class[] { Subject.class },
new JDKProxy(subject));
proxy.doSomething();
CGLIB代理
public class RealSubject {
public void doSomething() {
System.out.println("RealSubject do something");
}
}
public class CGLIBProxy implements MethodInterceptor {
private Object target;
public CGLIBProxy(Object target) {
this.target = target;
}
public Object createProxy() {
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(target.getClass());
enhancer.setCallback(this);
return enhancer.create();
}
public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
System.out.println("Before invoke " + method.getName() + "()");
Object result = proxy.invokeSuper(obj, args);
System.out.println("After invoke " + method.getName() + "()");
return result;
}
}
// 使用CGLIB代理
RealSubject subject = new RealSubject();
CGLIBProxy proxy = new CGLIBProxy(subject);
RealSubject proxySubject = (RealSubject) proxy.createProxy();
proxySubject.doSomething();