当前位置: 首页 > news >正文

【设计模式】- 创建者模式

单例模型

饿汉式

静态方法创建对象

public class Singleton {// 私有构造方法private Singleton(){}private static Singleton instance = new Singleton();// 提供一个外界获取的方法public static Singleton getInstance(){return instance;}
}

静态代码块创建对象

public class Singleton {private Singleton(){}private static Singleton instance;static {instance = new Singleton();}public static Singleton getInstance(){return instance;}
}

懒汉式

synchronized关键字

public class Singleton {private Singleton(){}private static Singleton instance;public static Singleton getInstance(){if(instance == null){instance = new Singleton();}return instance;}
}

问题】上边的代码是存在线程不安全的情况的,当线程1进来,判读instance==null成立,准备创建对象;但是线程1还没创建对象完毕时,线程2来了,线程2也判断成立,也去创建对象,此时就会创建两个不同的对象。
解决】:给getInstance方法上添加synchronized关键字

public class Singleton {private Singleton(){}private static Singleton instance;public static synchronized Singleton getInstance(){if(instance == null){instance = new Singleton(); // 下边简称:“写操作”}return instance; // 下边简称:“读操作”}
}

双重检查锁

问题】:对于getInstance()方法,其实大部分的操作都是读操作,读操作是线程安全的,如果直接给getInstance方法上加锁,其实会造成大量的线程等待。
解决】:调整加锁的时机,双重检查锁

public class Singleton {private Singleton(){}private static volatile Singleton instance; // volatile:保证指令的有序性和可见性public static Singleton getInstance(){// 第一次判断,如果instance的值不为null,不需要抢占锁,直接返回对象if(instance == null){synchronized (Singleton.class){// 第二次判断if(instance == null){instance = new Singleton();}}}return instance;}
}

静态内部类

public class Singleton {private Singleton(){}// 定义一个静态内部类private static class SingletonHolder{// 在内部类中声明并初始化外部类的对象private static final Singleton INSTANCE = new Singleton();}public static Singleton getInstance(){return SingletonHolder.INSTANCE;}
}

JVM加载外部类的过程是不会加载静态内部类的,只有内部类的属性、方法被调用时才会加载并初始化静态属性。静态属性被static修饰,所以只会被实例化一次。

枚举类

枚举类也是线程安全的,只会被加载一次,也是所有单例实现中唯一一种不会被破坏的单例模式

public enum Singleton {INSTANCE
}

枚举方式是属于饿汉式的方式,在不考虑浪费内存空间的情况下,首选枚举方式

存在的问题

问题】:破坏单例模式(让单例模式可以创建多个对象,枚举方式除外)

通过序列化和反序列化破坏单例模式

public class Client {public static void main(String[] args) throws Exception {writeObject2File();readObjectFromFile(); // Singleton@27bc2616readObjectFromFile(); // Singleton@3941a79c}// 从文件中读取对象private static void readObjectFromFile() throws Exception {// 1. 创建输入流对象ObjectInputStream ois = new ObjectInputStream(new FileInputStream("D:\\Desktop\\a.txt"));// 2. 读取对象Singleton instance = (Singleton) ois.readObject();System.out.println(instance);// 3. 释放资源ois.close();}// 从文件中写对象public static void writeObject2File() throws Exception {// 1. 获取Singleton对象Singleton instance = Singleton.getInstance();// 2. 将对象写入文件ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("D:\\Desktop\\a.txt"));oos.writeObject(instance);// 3. 释放资源oos.close();}
}

上面的代码生成的两个对象不是同一个对象,破坏了单例模式

通过反射破坏单例模式

public class Client {public static void main(String[] args) throws Exception {// 1. 获取Singleton的字节码对象Class clazz = Singleton.class;// 2. 获取无参构造方法对象Constructor cons = clazz.getDeclaredConstructor();// 3. 取消访问检查(暴力反射)cons.setAccessible(true);// 4. 创建对象Singleton s1 = (Singleton) cons.newInstance();Singleton s2 = (Singleton) cons.newInstance();System.out.println(s1 == s2); // false - 破坏单例模式}
}

上边代码返回的是false,说明s1和s2不是同一个对象,破坏了单例模式

问题解决

序列化、反序列化破坏单例模式解决方法

在Singleton类种添加readResolve()方法,在反序列化时被反射调用。如果定义了这个方法,就返回这个方法的值,如果没有定义,则返回new出来的对象。

public class Singleton implements Serializable {private Singleton(){}// 定义一个静态内部类private static class SingletonHolder{// 在内部类中声明并初始化外部类的对象private static final Singleton INSTANCE = new Singleton();}public static Singleton getInstance(){return SingletonHolder.INSTANCE;}// 当进行反序列化时,自动调用该方法,将该方法的返回值直接返回public Object readResolve(){return SingletonHolder.INSTANCE;}
}

反射方式破坏单例模式解决方法

其实反射破坏的原理是:通过反射获取Singleton的私有构造方法,然后通过这个私有的构造方法去创建对象。
因此我们只需要在构造方法里添加一个判断即可

public class Singleton implements Serializable {private static boolean flag = false;private Singleton(){synchronized (Singleton.class){if(flag) {throw new RuntimeException("不能创建多个对象");}flag = true;}}// 定义一个静态内部类private static class SingletonHolder{// 在内部类中声明并初始化外部类的对象private static final Singleton INSTANCE = new Singleton();}public static Singleton getInstance(){return SingletonHolder.INSTANCE;}
}

JDK源码解析 - Runtime类

饿汉式:
在这里插入图片描述

工厂模式

引例:点咖啡

现在有美式咖啡、拿铁咖啡,顾客可以选择咖啡的种类,咖啡都需要进行加糖加奶。

原本的写法:

咖啡类:

public abstract class Coffee {public abstract String getName();// 加糖public void addsugar() {System.out.println("加糖");}// 加奶public void addmilk() {System.out.println("加奶");}
}

美式咖啡:

public class AmericanCoffee extends Coffee {@Overridepublic String getName() {return "美式咖啡";}
}

拿铁咖啡:

public class LatteCoffee extends Coffee {@Overridepublic String getName() {return "拿铁咖啡";}
}

咖啡店:

public class CoffeeStore {public Coffee orderCoffee(String coffeeType) {// 声明Coffee类型的变量,根据不同的类型创建不同的子类对象Coffee coffee = null;if("american".equals(coffeeType)) {coffee = new AmericanCoffee();}else if("latte".equals(coffeeType)) {coffee = new LatteCoffee();}else {throw new RuntimeException("对不起的,您所点的咖啡没有");}// 加配料coffee.addmilk();coffee.addsugar();return coffee;}
}

测试方法:

public class Client {public static void main(String[] args) {// 创建咖啡店类CoffeeStore store = new CoffeeStore();// 点咖啡Coffee coffee = store.orderCoffee("latte");System.out.println(coffee.getName());}
}

存在问题】:如果需要更换对象,那么所有new对象的地方都要修改一遍,这就违背了软件设计的开闭原则。
解决】:工厂模式

简单工厂模式

角色:

  • 抽象产品:定义了产品的规范(咖啡类)
  • 具体产品:是现货集成抽象产品的子类(美式咖啡、拿铁咖啡)
  • 具体工厂:提供了创建产品的方法,调用者通过该方法来获取产品

简单咖啡工厂类,用来生产咖啡:

public class SimpleCoffeeFactory {public Coffee createCoffee(String coffeeType) {// 声明Coffee类型的变量,根据不同的类型创建不同的子类对象Coffee coffee = null;if("american".equals(coffeeType)) {coffee = new AmericanCoffee();}else if("latte".equals(coffeeType)) {coffee = new LatteCoffee();}else {throw new RuntimeException("对不起的,您所点的咖啡没有");}return coffee;}
}

咖啡店:

public class CoffeeStore {public Coffee orderCoffee(String coffeeType) {SimpleCoffeeFactory factory = new SimpleCoffeeFactory();// 调用生产咖啡的方法Coffee coffee = factory.createCoffee(coffeeType);// 加配料coffee.addmilk();coffee.addsugar();return coffee;}
}

解除了CoffeeStore和具体的咖啡的耦合
优势】:工厂类的客户端可能有很多,这样只需要去修改SimpleCoffeeFactory的代码,可以省去其他的修改操作。
劣势】:如果要再加新的品种的咖啡,就必须要修改SimpleCoffeeFactory的代码,这违反了开闭原则。

工厂方法模式

角色:

  • 抽象产品:定义了产品的规范(咖啡类)
  • 具体产品:是现货集成抽象产品的子类(美式咖啡、拿铁咖啡)
  • 抽象工厂:提供创建产品的接口,调用者通过访问它具体工厂的工厂方法来创建产品
  • 具体工厂:提供了创建产品的方法,调用者通过该方法来获取产品

抽象工厂:

public interface CoffeeFactory {// 创建咖啡对象的方法Coffee createCoffee();
}

具体工厂:

  • 拿铁咖啡工厂对象 - 用来生产拿铁咖啡
public class LatteCoffeeFactory implements CoffeeFactory {@Overridepublic Coffee createCoffee() {return new LatteCoffee();}
}
  • 美式咖啡工厂对象 - 用来生产美式咖啡
public class AmericanCoffeeFactory implements CoffeeFactory {@Overridepublic Coffee createCoffee() {return new AmericanCoffee();}
}

咖啡店:

public class CoffeeStore {private CoffeeFactory factory;public void setFactory(CoffeeFactory factory) {this.factory = factory;}// 点咖啡public Coffee orderCoffee() {// 创建咖啡Coffee coffee = factory.createCoffee();// 加配料coffee.addmilk();coffee.addmilk();return coffee;}
}

测试方法:

public class Client {public static void main(String[] args) {// 创建咖啡店类CoffeeStore store = new CoffeeStore();store.setFactory(new AmericanCoffeeFactory()); // 生产美式咖啡// 点咖啡Coffee coffee = store.orderCoffee();System.out.println(coffee.getName());}
}

优势】:用户只要知道具体工程的类名就可以得到产品;系统增加新的产品只需要新增具体产品类和对应的具体工厂类即可。
劣势】:每增加一个产品就需要增加一个具体产品类和具体工厂类, 增加了系统的复杂度

抽象工厂模式

抽象工厂模式和工厂方法模式的区别:

  • 工厂方法模式:只生产一个等级的产品
  • 抽象工厂模式:可以创建多个不同等级的产品

需求变更】:现在咖啡店不仅需要生产咖啡,还需要生产甜品

甜品抽象类:

public abstract class Dessert {abstract void show();
}

提拉米苏类:

public class Trimisu extends Dessert{@Overridevoid show() {System.out.println("提拉米苏");}
}

抹茶慕斯类:

public class MatchaMousse extends Dessert{@Overridevoid show() {System.out.println("抹茶慕斯");}
}

甜品抽象工厂:

public interface DessertFactory {// 生产咖啡的功能Coffee createCoffee();// 生产甜品的功能Dessert createDessert();
}

意大利风味甜品工厂(生产拿铁咖啡和提拉米苏甜品):

public class ItaltyDessertFactory implements DessertFactory{ // 意大利风味甜品工厂(生产拿铁咖啡和提拉米苏甜品)@Overridepublic Coffee createCoffee() {return new LatteCoffee(); // 拿铁咖啡}@Overridepublic Dessert createDessert() {return new Trimisu(); // 提拉米苏}
}

美式咖啡的甜品工厂(生产美式咖啡和抹茶慕斯):

public class AmericanDessertFactory implements DessertFactory{ // 美式咖啡的甜品工厂 - 生产美式咖啡和抹茶慕斯@Overridepublic Coffee createCoffee() {return new AmericanCoffee(); // 美式咖啡}@Overridepublic Dessert createDessert() {return new MatchaMousse(); // 抹茶慕斯}
}

测试类:

public class Client {public static void main(String[] args) {// 创建意大利风味的工厂ItaltyDessertFactory it = new ItaltyDessertFactory();Coffee coffee = it.createCoffee(); // 拿铁咖啡Dessert dessert = it.createDessert(); // 提拉米苏System.out.println(coffee.getName());dessert.show();}
}

如果要加一个产品族,只需要再加一个对应的工厂类,不需要修改其他类
优点】:客户端只能使用同一个产品族中的对象
缺点】:产品族种需要新增一个新的产品,所有的工厂类都需要进行修改。

适用场景:

  • 需要创建的对象是一系列相互关联或相互依赖的产品族(电器工厂中的电视机、洗衣机、空调)
  • 系统种有多个产品族每次只用其中一种产品(有人只喜欢穿一个品牌的衣服和裤子)
  • 系统提供了产品的类库,且所有产品的接口相同,客户端不依赖产品实例的创建细节和内部结构
  • 如:搜狗输入法换皮肤(一套一起换)

模式扩展(Spring框架底层)

bean.properties文件:

american = com.itheima.pattern02factory.factory_04_config.AmericanCoffee
latte = com.itheima.pattern02factory.factory_04_config.LatteCoffee

工厂类:

public class CoffeeFactory {// 1. 定义容器对象存储咖啡对象private static HashMap<String, Coffee> map = new HashMap<>();// 2. 加载配置文件,并创建该配置文件里类的对象并进行存储(只需要加载一次)static {// 2.1 创建Properties对象Properties p = new Properties();// 2.2 调用p对象中的load方法进行配置文件的加载InputStream is = CoffeeFactory.class.getClassLoader().getResourceAsStream("bean.properties");try {p.load(is);// 2.3 从p集合中获取全类名并创建对象Set<Object> keys = p.keySet();for (Object key : keys) {String className = p.getProperty((String) key);// 2.4 通过反射技术创建对象Class clazz = Class.forName(className);Coffee coffee = (Coffee) clazz.newInstance();// 2.5 将名称和对象存储到容器中map.put((String) key, coffee);}} catch (Exception e) {throw new RuntimeException(e);}}// 根据名称获取对象public static Coffee createCoffee(String name) {return map.get(name);}
}

JDK源码解析 - Collection.iterator()

在这里插入图片描述
在这里插入图片描述

Collection是抽象工厂;ArrayList是具体工厂
【补】:DateForamt类中的getInstance()、Calendar类中的getInstance()也是工厂模式

原型模式

用一个已经创建的对象作为原型,复制这个原型对象来创建一个和原型对象相同的新对象。

包含的角色:

  • 抽象原型类:规定了具体原型对象必须实现的clone()方法
  • 具体原型类:实现抽象原型类中的clone()方法,他是可以被复制的对象

浅克隆:创建一个新对象,新对象的属性和原来对象完全相同。对于非基本类型属性,克隆对象和源对象指向的是同一块内存空间
深克隆:创建一个新对象,属性中的引用的其他对象也会被克隆,不会指向原有对象地址。

引例1:克隆对象

具体原型对象

public class Relizetype implements Cloneable { // 必须实现Cloneable接口:否则调用clone()会抛出CloneNotSupportedExceptionpublic Relizetype() {System.out.println("具体的原型类创建成功");}@Overridepublic Relizetype clone() throws CloneNotSupportedException { // clone()方法是在Object类里的System.out.println("具体原型复制成功");return (Relizetype) super.clone();}
}

测试类

public class Client {public static void main(String[] args) throws CloneNotSupportedException {// 创建原型对象Relizetype relizetype = new Relizetype();// 调用原型类中的clone()方法进行对象的克隆Relizetype clone = relizetype.clone();System.out.println(relizetype == clone); // false}
}

必须实现Cloneable接口:否则调用clone()会抛出CloneNotSupportedException。
重写clone()方法:通常需将其改为public访问权限,并返回具体类型。
clone() 方法创建了一个新的对象,而不是返回原对象的引用,因为java的Object.clone()方法是一个native方法,不会调用构造方法,而是直接分配内存并复制数据

引例2.1:三好学生奖状分发(浅克隆)

奖状类:

@Data
public class Citation implements Cloneable {// 三好学生上的姓名private String name;@Overridepublic Citation clone() throws CloneNotSupportedException {return (Citation) super.clone();}public void show() {System.out.println(name + "同学被评为三好学生");}
}

测试类:

public class CitationTest {public static void main(String[] args) throws CloneNotSupportedException {// 1. 创建原型对象Citation citation = new Citation();// 2. 克隆奖状对象Citation citation1 = citation.clone();citation.setName("张三");citation1.setName("李四");citation.show(); // 张三同学被评为三好学生citation1.show(); // 李四同学被评为三好学生}
}

使用场景

  1. 对象的创建非常复杂,可以使用原型模式快捷创建对象(原型对象的所属类必须实现clone()方法)
  2. 性能和安全的要求比较高

引例2.2:三好学生奖状分发(深克隆)

学生类:

@Data
@Accessors(chain = true)
public class Student {// 学生姓名private String name;
}

奖状类:

@Data
public class Citation implements Cloneable {private Student student;@Overridepublic Citation clone() throws CloneNotSupportedException {return (Citation) super.clone();}public void show() {System.out.println(student.getName() + "同学被评为三好学生");}
}

测试类:

public class CitationTest {public static void main(String[] args) throws CloneNotSupportedException {// 1. 创建原型对象Citation citation = new Citation();Student stu = new Student().setName("张三");citation.setStudent(stu);// 2. 克隆奖状对象Citation citation1 = citation.clone();Student stu1 = citation1.getStudent();stu1.setName("李四");citation.show(); // 李四同学被评为三好学生citation1.show(); // 李四同学被评为三好学生}
}

问题】由上边测试结果可知,修改了citation1成员变量的值的同时,也修改了citation对象的值
产生原因】这是因为stu和stu1此时是一个对象(这就是浅克隆),此时将stu1的属性改成“李四”,导致stu的属性也变成“李四”

修改】:把浅克隆变成深克隆(使用序列化和反序列化实现)

public class CitationTest1 {public static void main(String[] args) throws Exception {// 1. 创建原型对象Citation citation = new Citation();Student stu = new Student().setName("张三");citation.setStudent(stu);// 把对象写入文件中ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("a.txt")); // 对象输出流对象oos.writeObject(citation); // 写对象oos.close(); // 释放资源// 从文件中读取对象 (2. 克隆奖状对象)ObjectInputStream ois = new ObjectInputStream(new FileInputStream("a.txt"));Citation citation1 = (Citation) ois.readObject();citation1.getStudent().setName("李四");ois.close();citation.show(); // 张三同学被评为三好学生citation1.show(); // 李四同学被评为三好学生}
}

Citation类和Student类都必须实现Serializable接口,否则会抛NotSerializableException异常

修改】:把浅克隆变成深克隆(在重写clone()方法的时候调用属性的clone()方法)

奖状类:

@Data
public class Citation implements Cloneable, Serializable {private Student student;@Overridepublic Citation clone() throws CloneNotSupportedException {Citation clone = (Citation) super.clone();clone.setStudent(student.clone()); // 深拷贝return clone;}public void show() {System.out.println(student.getName() + "同学被评为三好学生");}
}

学生类:

@Data
@Accessors(chain = true)
public class Student implements Serializable, Cloneable {// 学生姓名private String name;@Overridepublic Student clone() throws CloneNotSupportedException {return (Student) super.clone();}
}

测试类:

public class CitationTest {public static void main(String[] args) throws CloneNotSupportedException {// 1. 创建原型对象Citation citation = new Citation();Student stu = new Student().setName("张三");citation.setStudent(stu);// 2. 克隆奖状对象Citation citation1 = citation.clone();citation1.getStudent().setName("李四");citation.show(); // 张三同学被评为三好学生citation1.show(); // 李四同学被评为三好学生}
}

建造者模式

将复杂对象的构建和表示分离,使同样的构建过程可以创建不同的表示。

建造者建造的产品一般需要有较多的共同点,组成部分需要相似(如果产品之间差异比较大,不适合使用建造者模式)

  • 产品类(Product):要创建的复杂对象
  • 抽象建造者类(Builder):这个接口规定要实现复杂对象那部分的创建,不涉及具体的对象创建
  • 具体建造者类(ConcreteBuilder):实现Builder接口,完成复杂产品的各个部件的具体创建方法(强调装配的过程)
  • 指挥者类(Director):调用具体建造者来创建复杂对象的各个部分,不涉及具体产品信息,只保证对象各个部分完整创建或按照某种顺序创建
    在这里插入图片描述

引例:创建共享单车

需求】:自行车包含了车架、车座等组件的生产;车架又有碳纤维,铝合金等材质;车座有橡胶、真皮的材质
在这里插入图片描述

Bike类:产品类(车架、车座组件)
Builder:抽象建造者(MobikeBuilder、OfoBuilder是具体的建造者)
Director:指挥者

产品类:

@Data
public class Bike {/*车架*/private String frame;/*车座*/private String seat;
}

抽象构建者:

public abstract class Builder {/*Bike对象*/protected Bike bike = new Bike(); // 目前还没有组装组件(指挥者做)/*构建车架*/public abstract void buildFrame();/*构建车座*/public abstract void buildSeat();/*构建自行车*/public abstract Bike createBike();
}

具体构建者1(摩拜单车):

public class MobileBuilder extends Builder{@Overridepublic void buildFrame() {bike.setFrame("碳纤维车架");}@Overridepublic void buildSeat() {bike.setSeat("真皮车座");}@Overridepublic Bike createBike() {return bike;}
}

具体构建者2(ofo单车):

public class OfoBuilder extends Builder{@Overridepublic void buildFrame() {bike.setFrame("铝合金车架");}@Overridepublic void buildSeat() {bike.setSeat("橡胶车座");}@Overridepublic Bike createBike() {return bike;}
}

指挥者类:

public class Director {private Builder builder;public Director(Builder builder) {this.builder = builder;}/*组装自行车*/public Bike construct() {builder.buildFrame();builder.buildSeat();return builder.createBike();}
}

测试类:

public class Client {public static void main(String[] args) {// 1. 创建指挥者对象Director director = new Director(new MobileBuilder());// 2. 让指挥者进行自行车的组装Bike bike = director.construct();System.out.println(bike.getFrame());System.out.println(bike.getSeat());}
}

Director指挥者类在建造者模式中很重要,是由指挥者类来指导具体的建造者应该如何构建产品,控制调用的先后顺序,向调用者返回完整的产品类。

改进】:指挥者类也可以和抽象建造者进行结合:

public abstract class Builder {/*Bike对象*/protected Bike bike = new Bike(); // 目前还没有组装组件(指挥者做)/*构建车架*/public abstract void buildFrame();/*构建车座*/public abstract void buildSeat();/*构建自行车*/public abstract Bike createBike();/*组装自行车*/public Bike construct() {this.buildFrame();this.buildSeat();return this.createBike();}
}

这样做虽然可以不用写指挥者类,但是也加重了建造者类的职责,也不符合单一职责原则,如果construct()过于复杂,还是建议封装到Director中。

模式扩展

当一个类的构造方法需要传入很多参数,如果创建这个类的实例,代码的可读性就会很差,就可以使用建造者模式进行重构。
手机类:

@Data
public class Phone {private String cpu;private String screen;private String memory;private String mainboard;/*私有构造方法*/private Phone(Builder builder) {this.cpu = builder.cpu;this.screen = builder.screen;this.memory = builder.memory;this.mainboard = builder.mainboard;}public static final class Builder {private String cpu;private String screen;private String memory;private String mainboard;public Builder cpu(String cpu) {this.cpu = cpu;return this; // 为了链式编程}public Builder screen(String screen) {this.screen = screen;return this;}public Builder memory(String memory) {this.memory = memory;return this;}public Builder mainboard(String mainboard) {this.mainboard = mainboard;return this;}/*使用构建者创建Phone对象*/public Phone build() {return new Phone(this);}}
}

测试类:

public class Client {public static void main(String[] args) {/*创建手机对象 - 通过构建者对象获取手机对象*/Phone phone = new Phone.Builder().cpu("intel").screen("三星").memory("金士顿内存条").mainboard("华硕").build();System.out.println(phone); // Phone(cpu=intel, screen=三星, memory=金士顿内存条, mainboard=华硕)}
}

将构建的顺序交给客户,这个相当于lombok里的@Builder注解

构建者模式对比

工厂方法模式 vs 建造者模式

  1. 工厂方法模式:整体对象的创建方式
  2. 建造者模式:部件构建的过程

抽象工厂模式 vs 建造者模式

  1. 抽象工厂模式:实现对产品家族的创建,不需要关心建造过程,只关心什么产品由什么工厂生产
  2. 建造者模式:按照指定的蓝图建造产品,通过组装零件而产生一个新产品

抽象工厂模式:汽车配件生产工厂(生产一个产品族的产品)
建造者模式:骑车组装工厂(通过对配件的组装可以返回一个完整的骑车)

相关文章:

  • 【AI面试秘籍】| 第9期:Transformer架构中的QKV机制深度解析:从原理到实践实现
  • SparkSQL操作MySQL
  • 【C语言指针超详解(六)】--sizeof和strlen的对比,数组和指针笔试题解析,指针运算笔试题解析
  • 深入解析JVM字节码解释器执行流程(OpenJDK 17源码实现)
  • 小程序 存存上下滑动的页面
  • BMS工具箱用来执行贝叶斯模型平均(BMA)计算模块
  • 中国版Cursor | 我用CodeBuddy Craft 3分钟复刻NFC经典游戏
  • 性能比拼: Nginx vs. Envoy
  • 《Python星球日记》 第69天:生成式模型(GPT 系列)
  • web第三次课后作业--基于JDBC对mysql数据库的增删查改操作
  • 主题切换方案
  • 智能手表项目风险评估与应对计划书
  • Linux程序设计--期末复习
  • 【ROS2】报错记录及对应解决方案
  • matlab提取脑电数据的五种频域特征指标数值
  • Jmeter元件 CSV Data Set Config详解
  • Python笔记:c++内嵌python,c++主窗口如何传递给脚本中的QDialog,使用的是pybind11
  • Java 框架配置自动化:告别冗长的 XML 与 YAML 文件
  • [Linux] vim及gcc工具
  • 本地部署小红书FireRedASR
  • 怎样给网站换空间/在线识图
  • 邯郸做网站推广/运营培训班有用吗
  • 网站建设要准备什么/关键词优化公司
  • 比较著名的网站用javaweb做的/关键词优化的最佳方法
  • 南城做网站/运营网站
  • wordpress 密码忘了/优化网站排名如何