设计模式 单例模式(创建型)

news2025/1/11 20:44:36

一、前言

学习设计模式我们关注的是什么,如何实现么?是也不是。我认为比了解如何实现设计模式更重要的是这些设计模式的应用场景,什么场景下我们该用这种设计模式;以及这些设计模式所包含的思想,最终帮助我们把代码写“好”。

设计模式的分类

在这里插入图片描述
而对于单例模式的创建型模式来说,主要关注点是“怎样创建对象?”,它的主要特点是“将对象的创建与使用分离”。这样可以降低系统的耦合度,使用者不需要关注对象的创建细节。

二、什么是单例模式

单例模式(Singleton Pattern),是最简单的一个模式。

单例模式基本定义:程序运行时,在java虚拟机中只存在该类的一个实例对象。

单例模式指的是全局只有一个实例,并且它负责创建自己的对象。单例模式不仅有利于减少内存开支,还有减少系统性能开销、防止多个实例产生冲突等优点。

主要解决:一个全局使用的类频繁地创建与销毁。

因为单例模式保证了实例的全局唯一性,而且只被初始化一次,所以比较适合全局共享一个实例,且只需要被初始化一次的场景,例如数据库实例、全局配置、全局任务池等。

单例模式又分为饿汉方式和懒汉方式。

  • 饿汉方式:指该类初始化的时候就创建实例对象,线程是安全的。
  • 懒汉方式:指首次使用单例实例的时候创建,之后使用时再判断单例实例是否已创建,如果没有则创建实例。

三、单例模式的实现

3.1 饿汉模式—静态变量方式

代码示例

/**
 * @Author: 公众号:技术能量站
 * @desc: 1. 饿汉模式—静态变量的方式
 * @create: 2019-10-20-20:20
 */
public class HungrySingleton {

    //保存该类对象的实例,饿汉式的做法:在声明的同时初始化该对象
    private static final HungrySingleton instance = new HungrySingleton();

    //将构造函数私有化,不对外提供构造函数
    private HungrySingleton() {
    }

    //对外提供访问该类对象的方法
    public static HungrySingleton getInstance() {
        return instance;
    }
}

该方式在成员位置声明 Singleton 类型的静态变量,并创建 Singleton 类的对象 instance。instance 对象是随着类的加载而创建的。如果该对象足够大的话,而一直没有使用就会造成内存的浪费。

3.2 饿汉模式—枚举类

枚举类实现单例模式是极力推荐的单例实现模式,因为枚举类型是线程安全的,并且只会装载一次,设计者充分的利用了枚举的这个特性来实现单例模式,枚举的写法非常简单,而且枚举类型是所用单例实现中唯一一种不会被破坏的单例实现模式。
代码示例

/**
 * @author 公众号:技术能量站
 * @desc: 枚举实现单例模式
 * @date: 2021/7/20
 */
public enum EnumSingleton {

    INSTANCE;

    public static EnumSingleton getInstance() {
        return INSTANCE;
    }
}

3.3 懒汉模式—线程不安全

代码示例

/**
 * @Author: 公众号:技术能量站
 * @desc: 懒汉模式——线程不安全
 * @create: 2019-10-20-20:53
 * @detail:
 */
public class ThreadUnSafeLazySingleton {

    private static ThreadUnSafeLazySingleton instance;  // ----> 注释1

    private ThreadUnSafeLazySingleton() {
    }

    public static ThreadUnSafeLazySingleton getInstance() {
        if (instance == null) {
            //  ----> 注释2
            instance = new ThreadUnSafeLazySingleton(); // ----> 注释3
        }
        return instance;
    }
}

从上面代码我们可以看出该方式在成员位置声明 ThreadUnSafeLazySingleton 类型的静态变量,并没有进行对象的赋值操作,那么什么时候赋值的呢?

当调用 getInstance() 方法获取 ThreadUnSafeLazySingleton 类的对象的时候才创建 Singleton 类的对象,这样就实现了懒加载的效果。但是,如果是多线程环境,会出现线程安全问题。

3.4 懒汉模式—线程安全

示例代码

/**
 * @author 公众号:技术能量站
 * @desc: 懒汉模式——线程安全
 * @date: 2021/7/22
 */
public class ThreadSafeLazySingleton {

    private static ThreadSafeLazySingleton instance;

    // 私有构造方法,防止被实例化
    private ThreadSafeLazySingleton() {
    }

    // 静态get方法,加了synchronized独占锁
    public static synchronized ThreadSafeLazySingleton getInstance() {
        if (instance == null) {
            instance = new ThreadSafeLazySingleton();
        }
        return instance;
    }
}

这种方式也实现了懒加载效果,同时又解决了线程安全问题。但是在 getInstance() 方法上添加了 synchronized 关键字,导致该方法的执行效果特别低。从上面代码我们可以看出,其实就是在初始化 instance 的时候才会出现线程安全问题,一旦初始化完成就不存在了。

3.5 懒汉模式—双重检测

再来讨论一下懒汉模式中加锁的问题,对于 getInstance() 方法来说,绝大部分的操作都是读操作,读操作是线程安全的,所以我们没必要让每个线程必须持有锁才能调用该方法,我们需要调整加锁的时机。由此也产生了一种新的实现模式:双重检查锁模式。
代码示例

/**
 * @Author: 公众号:技术能量站
 * @desc: 双重检测 DCL
 * @create: 2019-10-20-20:47
 */
public class DoubleCheckSingleton {

    private static DoubleCheckSingleton singleton;

    private DoubleCheckSingleton() {
    }

    public static DoubleCheckSingleton getInstance() {
        // 检查是否已经被创建——第一个 if 可以避免频繁加锁,如果没有第一个 if,它就会直接尝试获取锁资源
        if (singleton == null) {
            // 同步块
            synchronized (DoubleCheckSingleton.class) {
                // 再次检测是否被创建----双重检测

                // 第二个if是避免重复创建线程,破坏单例

                // 现假设有两个 T1 和 T2,T1 执行到同步块时,CPU 的执行权被 T2 抢夺走,T2 执行完成之后创建了一个对象实例,
                // 并且释放 Java 的类锁。这个时候 T1 又重新获得了 CPU 的执行权,并且获得了类锁。
                // 如果没有第二个 if 的判断,T1 又会重新创建一个 实例对象,这样就破坏了单例。
                if (singleton == null) {
                    // 标记3
                    singleton = new DoubleCheckSingleton();
                }
            }
        }
        return singleton;
    }
}

双重检查锁模式是一种非常好的单例实现模式,解决了单例、性能、线程安全问题,上面的双重检测锁模式看上去完美无缺,其实是存在问题;

在多线程的情况下,可能会出现空指针问题,出现问题的原因是 JVM 在实例化对象的时候会进行优化和指令重排序操作

要解决双重检查锁模式带来空指针异常的问题,只需要使用 volatile 关键字, volatile 关键字可以保证可见性和有序性。
代码示例

/**
 * @Author: 公众号:技术能量站
 * @desc: 双重检测 DCL
 * @create: 2019-10-20-20:47
 */
public class DoubleCheckSingleton{

    // volatile 存在是保证内存的可见性,禁止指令重排序
    // 原因:在Java指令中创建对象和赋值操作是分开进行的,也就是说instance = new Singleton();语句是分两步执行的。
    // 但是JVM并不保证这两个操作的先后顺序,也就是说有可能JVM会为新的Singleton实例分配空间,然后直接赋值给instance成员,
    // 然后再去初始化这个Singleton实例

    // 举例: 重排序会 导致 step2 和 step3 执行的顺序颠倒
    // 创建对象的步骤:step1:在内存中分配一块空间。step2:对内存空间进行初始化。step3:把对象在内存中的位置指向 instance。
    // 现在假设有两个线程T1、T2,T1 线程执行完重排序后的 step3 ,CPU 的执行权被 T2 获得。这个时候,instance 已经不为 null 了,
    // 他指向了内存中的一块地址。T2 执行到第一个 if 的时候,发现 instance 不为 null,就直接返回,但是这个 instance 并没有被初始化,
    // 这就会导致 T2 在执行的过程中发生不可预知的错误。
    private volatile static DoubleCheckSingleton singleton;

    private DoubleCheckSingleton() {
    }

    public static DoubleCheckSingleton getInstance() {
        // 检查是否已经被创建——第一个 if 可以避免频繁加锁,如果没有第一个 if,它就会直接尝试获取锁资源
        if (singleton == null) {
            // 同步块
            synchronized (DoubleCheckSingleton.class) {
                // 再次检测是否被创建----双重检测

                // 第二个if是避免重复创建线程,破坏单例

                // 现假设有两个 T1 和 T2,T1 执行到同步块时,CPU 的执行权被 T2 抢夺走,T2 执行完成之后创建了一个对象实例,
                // 并且释放 Java 的类锁。这个时候 T1 又重新获得了 CPU 的执行权,并且获得了类锁。
                // 如果没有第二个 if 的判断,T1 又会重新创建一个 实例对象,这样就破坏了单例。
                if (singleton == null) {
                    // 标记3
                    singleton = new DoubleCheckSingleton();
                }
            }
        }
        return singleton;
    }
}

添加 volatile 关键字之后的双重检查锁模式是一种比较好的单例实现模式,能够保证在多线程的情况下线程安全也不会有性能问题。

3.6 懒汉模式—静态内部类

静态内部类单例模式中实例由内部类创建,由于 JVM 在加载外部类的过程中, 是不会加载静态内部类的,只有内部类的属性/方法被调用时才会被加载,并初始化其静态属性。静态属性由于被 static 修饰,保证只被实例化一次,并且严格保证实例化顺序。
代码示例

/**
 * @Author: 公众号:技术能量站
 * @desc: 静态内部类的单例
 * @create: 2019-10-20-21:01
 */
public class InnerClassSingleton {

    // 私有化构造方法
    private InnerClassSingleton() {
    }

    // 获取单例
    public static InnerClassSingleton getInstance() {
        return SingletonFactory.INSTANCE;
    }

    /* 此处使用一个内部类来维护单例 */
    private static final class SingletonFactory {
        private static final InnerClassSingleton INSTANCE = new InnerClassSingleton();
    }
}

第一次加载 InnerClassSingleton 类时不会去初始化 INSTANCE,只有第一次调用 getInstance,虚拟机加载 SingletonFactory 并初始化 INSTANCE,这样不仅能确保线程安全,也能保证 Singleton 类的唯一性。

静态内部类单例模式是一种优秀的单例模式,是开源项目中比较常用的一种单例模式。在没有加任何锁的情况下,保证了多线程下的安全,并且没有任何性能影响和空间的浪费。

四、存在的问题

破坏单例模式:使上面定义的单例类(Singleton)也可以通过序列化或者反射创建多个对象,枚举方式除外。所以序列化和反射技术会破坏单例

枚举方式不会出现这两个问题。

下面以双重检测实现单例模式的实现为例:

/**
 * @Author: 公众号:技术能量站
 * @desc: 双重检测 DCL
 * @create: 2019-10-20-20:47
 */
public class DoubleCheckSingleton {

    // volatile 存在是保证内存的可见性,禁止指令重排序
    // 原因:在Java指令中创建对象和赋值操作是分开进行的,也就是说instance = new Singleton();语句是分两步执行的。
    // 但是JVM并不保证这两个操作的先后顺序,也就是说有可能JVM会为新的Singleton实例分配空间,然后直接赋值给instance成员,
    // 然后再去初始化这个Singleton实例

    // 举例: 重排序会 导致 step2 和 step3 执行的顺序颠倒
    // 创建对象的步骤:step1:在内存中分配一块空间。step2:对内存空间进行初始化。step3:把对象在内存中的位置指向 instance。
    // 现在假设有两个线程T1、T2,T1 线程执行完重排序后的 step3 ,CPU 的执行权被 T2 获得。这个时候,instance 已经不为 null 了,
    // 他指向了内存中的一块地址。T2 执行到第一个 if 的时候,发现 instance 不为 null,就直接返回,但是这个 instance 并没有被初始化,
    // 这就会导致 T2 在执行的过程中发生不可预知的错误。
    private volatile static DoubleCheckSingleton singleton;

    private DoubleCheckSingleton() {
    }

    public static DoubleCheckSingleton getInstance() {
        // 检查是否已经被创建——第一个 if 可以避免频繁加锁,如果没有第一个 if,它就会直接尝试获取锁资源
        if (singleton == null) {
            // 同步块
            synchronized (DoubleCheckSingleton.class) {
                // 再次检测是否被创建----双重检测

                // 第二个if是避免重复创建线程,破坏单例

                // 现假设有两个 T1 和 T2,T1 执行到同步块时,CPU 的执行权被 T2 抢夺走,T2 执行完成之后创建了一个对象实例,
                // 并且释放 Java 的类锁。这个时候 T1 又重新获得了 CPU 的执行权,并且获得了类锁。
                // 如果没有第二个 if 的判断,T1 又会重新创建一个 实例对象,这样就破坏了单例。
                if (singleton == null) {
                    // 标记3
                    singleton = new DoubleCheckSingleton();
                }
            }
        }
        return singleton;
    }
}

4.1 反序列化—破坏单例模式

破坏原因:是反序列化的时候通过反射newInstance 重新生成了一个类 所以和原有的类不是同一个

    /**
     * implements Serializable
     * 注意:测试序列化时 需要被序列化的类 实现序列化接口
     */
    @SneakyThrows
    private static void test_serialize() {
        // 1、序列化
        DoubleCheckSingleton checkSingleton01 = DoubleCheckSingleton.getInstance();
        FileOutputStream fileOutputStream = new FileOutputStream("single");
        ObjectOutputStream oos = new ObjectOutputStream(fileOutputStream);
        oos.writeObject(checkSingleton01);
        oos.flush();
        oos.close();

        // 2、反序列化
        // 破坏原因:反序列化的时候通过反射newInstance 重新生成了一个类 所以和原有的类不是同一个
        FileInputStream inputStream = new FileInputStream("single");
        ObjectInputStream oosInput = new ObjectInputStream(inputStream);
        DoubleCheckSingleton checkSingleton02 = (DoubleCheckSingleton)oosInput.readObject();

        // 打印输出
        System.out.println("checkSingleton01 = " + checkSingleton01);
        System.out.println("checkSingleton02 = " + checkSingleton02);
        System.out.println(checkSingleton02 == checkSingleton01);
    }

4.2 反射—破坏单例模式

通过反射获得单例类的构造函数,由于该构造函数是private的,通过setAccessible(true)指示反射的对象在使用时应该取消;

setAccessible(true) 方法可以在Java 语言访问检查,使得私有的构造函数能够被访问,这样使得单例模式失效。

代码示例

    /**
     * 测试
     * 反射 可以修改单例类/破坏单例
     * 解决办法:
     *   通过一个变量统计创建对象的次数,如果count>0 则抛出异常
     */
    @SneakyThrows
    private static void test_reflect() {
        DoubleCheckSingleton checkSingleton01 = DoubleCheckSingleton.getInstance();
        Constructor<DoubleCheckSingleton> constructor = DoubleCheckSingleton.class.getDeclaredConstructor();
        // 通过反射获得单例类的构造函数,由于该构造函数是private的,通过setAccessible(true)指示反射的对象在使用时应该取消
       
        // 取消访问检查
        constructor.setAccessible(true);
        DoubleCheckSingleton checkSingleton02 = constructor.newInstance();

        System.out.println(checkSingleton01.hashCode()); // 1625635731
        System.out.println(checkSingleton02.hashCode()); // 1580066828
    }

五、如何避免破坏单例

5.1 反序列化方式破坏单例的解决方案

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

代码示例

import java.io.Serializable;

/**
 * @Author: 公众号:技术能量站
 * @desc: 双重检测 DCL
 * @create: 2019-10-20-20:47
 */
public class DoubleCheckSingleton implements Serializable {

    // volatile 存在是保证内存的可见性,禁止指令重排序
    // 原因:在Java指令中创建对象和赋值操作是分开进行的,也就是说instance = new Singleton();语句是分两步执行的。
    // 但是JVM并不保证这两个操作的先后顺序,也就是说有可能JVM会为新的Singleton实例分配空间,然后直接赋值给instance成员,
    // 然后再去初始化这个Singleton实例

    // 举例: 重排序会 导致 step2 和 step3 执行的顺序颠倒
    // 创建对象的步骤:step1:在内存中分配一块空间。step2:对内存空间进行初始化。step3:把对象在内存中的位置指向 instance。
    // 现在假设有两个线程T1、T2,T1 线程执行完重排序后的 step3 ,CPU 的执行权被 T2 获得。这个时候,instance 已经不为 null 了,
    // 他指向了内存中的一块地址。T2 执行到第一个 if 的时候,发现 instance 不为 null,就直接返回,但是这个 instance 并没有被初始化,
    // 这就会导致 T2 在执行的过程中发生不可预知的错误。
    private volatile static DoubleCheckSingleton singleton;

    private DoubleCheckSingleton() {
    }

    public static DoubleCheckSingleton getInstance() {
        // 检查是否已经被创建——第一个 if 可以避免频繁加锁,如果没有第一个 if,它就会直接尝试获取锁资源
        if (singleton == null) {
            // 同步块
            synchronized (DoubleCheckSingleton.class) {
                // 再次检测是否被创建----双重检测

                // 第二个if是避免重复创建线程,破坏单例

                // 现假设有两个 T1 和 T2,T1 执行到同步块时,CPU 的执行权被 T2 抢夺走,T2 执行完成之后创建了一个对象实例,
                // 并且释放 Java 的类锁。这个时候 T1 又重新获得了 CPU 的执行权,并且获得了类锁。
                // 如果没有第二个 if 的判断,T1 又会重新创建一个 实例对象,这样就破坏了单例。
                if (singleton == null) {
                    // 标记3
                    singleton = new DoubleCheckSingleton();
                }
            }
        }
        return singleton;
    }

    /**
     * 解决序列化和反序列化对单例的破坏,
     *  当进行反序列化时,会自动调用该方法,将该方法的返回值直接返回
     * @return
     */
    private Object readResolve() {
        return singleton;
    }

}

源码解析—ObjectInputStream类

    public final Object readObject()
        throws IOException, ClassNotFoundException
    {
        ...
        // if nested read, passHandle contains handle of enclosing object
        int outerHandle = passHandle;
        try {
            Object obj = readObject0(false);//重点查看readObject0方法
        ...
    }

    private Object readObject0(boolean unshared) throws IOException {
        ...
        try {
            switch (tc) {
                ...
                case TC_OBJECT:
                    return checkResolve(readOrdinaryObject(unshared));//重点查看readOrdinaryObject方法
                ...
            }
        } finally {
            depth--;
            bin.setBlockDataMode(oldMode);
        }    
    }

    private Object readOrdinaryObject(boolean unshared) 
        throws IOException
    {
        ...
        // isInstantiable 返回true,执行 desc.newInstance(),通过反射创建新的单例类,
        obj = desc.isInstantiable() ? desc.newInstance() : null; 
        ...
        // 在Singleton类中添加 readResolve 方法后 desc.hasReadResolveMethod() 方法执行结果为true
        if (obj != null && 
            handles.lookupException(passHandle) == null && 
            desc.hasReadResolveMethod()) 
        {
            // 通过反射调用 Singleton 类中的 readResolve 方法,将返回值赋值给rep变量
            // 这样多次调用ObjectInputStream类中的readObject方法,继而就会调用我们定义的readResolve方法,所以返回的是同一个对象。
            Object rep = desc.invokeReadResolve(obj);
            ...
        }
        return obj;
    }

5.2 反射方式破解单例的解决方案

这种方式比较好理解。当通过反射方式调用构造方法进行创建创建时,直接抛异常。不运行此中操作。

public class Singleton {

    // 私有构造方法
    private Singleton() {
        /*
         * 反射破解单例模式需要添加的代码
         */
        if (instance != null) {
            throw new RuntimeException("不能创建多个对象");
        }
    }
    
    private static volatile Singleton instance;

    // 对外提供静态方法获取该对象
    public static Singleton getInstance() {

        if (instance != null) {
            return instance;
        }

        synchronized (Singleton.class) {
            if (instance != null) {
                return instance;
            }
            instance = new Singleton();
            return instance;
        }
    }
}

六、应用实践

6.1 JDK源码解析-Runtime类

Runtime 类就是使用的单例设计模式
(1)通过源代码查看使用的是哪种单例模式

public class Runtime {
    // 饿汉式初始化
    private static Runtime currentRuntime = new Runtime();

    /**
     * Returns the runtime object associated with the current Java application.
     * Most of the methods of class <code>Runtime</code> are instance
     * methods and must be invoked with respect to the current runtime object.
     *
     * @return  the <code>Runtime</code> object associated with the current
     *          Java application.
     */
    public static Runtime getRuntime() {
        return currentRuntime;
    }

    /** Don't let anyone else instantiate this class */
    private Runtime() {}
    ...
}

从上面源代码中可以看出 Runtime 类使用的是饿汉式(静态属性)方式来实现单例模式的。

(2)使用 Runtime 类中的方法

public class RuntimeDemo {
    public static void main(String[] args) throws IOException {
        // 获取Runtime类对象
        Runtime runtime = Runtime.getRuntime();

		// 调用runtime的方法
        // 返回 Java 虚拟机中的内存总量
        //System.out.println(runtime.totalMemory());
        // 返回 Java 虚拟机试图使用的最大内存量
        //System.out.println(runtime.maxMemory());

        // exec方法,创建一个新的进程执行指定的字符串命令,返回进程对象
        Process process = runtime.exec("ipconfig");
        // 获取命令执行后的结果,通过输入流获取
        InputStream inputStream = process.getInputStream();
        byte[] arr = new byte[1024 * 1024* 100];
        // 读取数组 存到arr字节数组中
        int b = inputStream.read(arr); // 返回读到的字节的个数
        // 将字节数组转换为字符串输出到控制台
        System.out.println(new String(arr, 0, b, "gbk"));
    }
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/547907.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

继承 + 多态 + final + 权限修饰符

目录 继承 多态 final 权限修饰符 继承 继承定义&#xff1a; 可以让类跟类之间产生子父的关系继承的好处 可以把多个子类中重复的代码抽取到父类中&#xff0c;子类可以直接使用&#xff0c;减少代码几余&#xff0c;提高代码的复用性子类继承内容 非私有private构造方法…

#机器学习--深度学习中的正则化

#机器学习--深度学习中的正则化 引言1、参数范数惩罚2、 L 2 L^{2} L2 正则化3、 L 1 L^{1} L1 正则化4、显式约束和重投影5、参数绑定和参数共享6、Bagging7、Dropout 引言 本系列博客旨在为机器学习(深度学习)提供数学理论基础。因此内容更为精简&#xff0c;适合二次学习的…

uniapp实现条码扫描 可开闪光灯,原生H5调用,不需要任何sdk。

主要思路 使用QuaggaJs这个库。调用摄像头使用的 navigator.mediaDevices.getUserMedia 这个H5的api。通过 video 和 canvas 把摄像头获取到的数据展现到页面上&#xff0c;同时调用监听Quagga解析。 获取设备摄像头权限,用于后续开启摄像头。创建video元素显示摄像头画面,和ca…

AcWing算法提高课-1.3.10混合背包问题

宣传一下算法提高课整理 <— CSDN个人主页&#xff1a;更好的阅读体验 <— 本题链接&#xff08;AcWing&#xff09; 点这里 题目描述 有 N N N 种物品和一个容量是 V V V 的背包。 物品一共有三类&#xff1a; 第一类物品只能用1次&#xff08;01背包&#xff0…

opencv相机标定

当你把摄像机放在一个特定的位置&#xff0c;在它的后面放一个目标图像&#xff0c;或者是把摄像机放到某个物体上&#xff0c;摄像机周围的物体是什么形状&#xff0c;你需要知道这些信息。 当你在计算机上处理图像时&#xff0c;会使用以下三个参数&#xff1a; 1.像素坐标&a…

软件测试5年了,薪资25K,我还怕00后卷吗?

前言 沅哥在这个行业爬摸滚打5年了&#xff0c;从最开始点点点的功能测试到现在到现在成为高级测试&#xff0c;工资也翻了几倍&#xff0c;到了25k。他和我说&#xff0c;最开始他是想躺平的&#xff0c;后来也是被迫卷的&#xff0c;但好在这个结果他很满意。 之所以改变的…

一文3000字从0到1使用Selenium进行自动化测试

对于很多刚入门的测试新手来说&#xff0c;大家都将自动化测试作为自己职业发展的一个主要阶段。可是&#xff0c;在成为一名合格的自动化测试工程师之前&#xff0c;我们不仅要掌握相应的理论知识&#xff0c;还要进行大量的实践&#xff0c;积累足够的经验&#xff0c;以便快…

【重新定义matlab强大系列七】利用matlab函数ischange查找数据变化点

&#x1f517; 运行环境&#xff1a;matlab &#x1f6a9; 撰写作者&#xff1a;左手の明天 &#x1f947; 精选专栏&#xff1a;《python》 &#x1f525; 推荐专栏&#xff1a;《算法研究》 #### 防伪水印——左手の明天 #### &#x1f497; 大家好&#x1f917;&#x1f91…

为了入职字节测试岗准备半年,上岸拿个30K应该不算高吧?

历时近半年的面试终于告一段落&#xff0c;终于有时间整理下自己这次的换工作心得&#xff0c;分享给大家。 面试经历 我的基本情况是这样&#xff1a;中下等 985 学校本科毕业&#xff0c;非计算机专业&#xff0c;在北京工作五年&#xff0c;之前一直在中小私企、国企工作。…

程序语言易错题

程序语言易错题 包含8个成员的开发小组的沟通路径最多有&#xff08;&#xff09;条。 A、28 B、32 C、56 D、64 解析 软件开发小组的沟通路径受到小组组织形式和规模的影响。若任意小组成员之间均可能有沟通路径&#xff0c;则可用完全连通图来对开发小组的沟通路径建模&#…

Anaconda-labelimg的使用

文章目录 一、创建Anaco的虚拟环境并激活二、下载labelimg以及使用1.下载2.使用 在这里我是创建了一个虚拟环境&#xff0c;虚拟环境名字为labelimg 之后将labelimg下载到该虚拟环境中 一、创建Anaco的虚拟环境并激活 conda create -n labelimg conda activate labelimg二、下…

Smoothieware_best-for-pnp 工具链的升级尝试

文章目录 Smoothieware_best-for-pnp 工具链的升级尝试概述实验工具链安装的思路更换工具链的工作备注END Smoothieware_best-for-pnp 工具链的升级尝试 概述 正在迁移Smoothieware_best-for-pnp到MCUXPresso的失败实验中徘徊. 现在已知2者的工具链版本是不一样的. 通过2进制…

MAC免密登录服务器

文章目录 1.Mac本机打开终端生成公私钥2.找打刚才生成的公钥3.上传公钥到远程 Linux 服务器4.远程登录到Linux系统服务器里面执行如下操作5.设置后在退出终端,输入如下命令即可免密登录6. 禁止 Linux 使用账号密码登录 1.Mac本机打开终端生成公私钥 输入ssh-keygen&#xff0c…

python获取某电商平台口红数据并制作词云

目录标题 前言开发环境:模块使用数据来源分析代码展示获取数据制作词云 尾语 &#x1f49d; 前言 嗨喽~大家好呀&#xff0c;这里是魔王呐 ❤ ~! 开发环境: Python 3.8 Pycharm 模块使用 requests jieba 结巴分词 wordcloud 词云 第三方模块安装&#xff1a; win R 输…

Apache Kafka - 重识Kafka

文章目录 概述一、Kafka的概念二、Kafka的特点三、Kafka的使用场景导图 概述 Kafka是一个高性能、分布式的消息队列系统&#xff0c;它的出现为大规模的数据处理提供了一种可靠、快速的解决方案。我们先初步了解Kafka的概念、特点和使用场景。 一、Kafka的概念 Kafka是由Apac…

《设计模式》状态模式

《设计模式》状态模式 定义&#xff1a; 状态模式也称为状态机模式&#xff0c;允许对象在内部状态发生改变时改变它的行为&#xff0c;对象看起来好像修改了它的类。属于行为型模式。 状态模式的角色组成&#xff1a; State(抽象状态类)&#xff1a;定义一个接口用来封装与…

文本三剑客正则表达式3

文章目录 文本三剑客&正则表达式31 awk工作原理2 awk的基本格式及其内置变量2.1 基本格式2.2 内置变量2.3 示例2.3.1 直接打印所有内容2.3.2 取每一行的第一列2.3.3 打印行号&#xff0c;及所有内容2.3.4 打印第三行2.3.5 打印2-4行2.3.6 打印第2行和第4行2.3.7 用正则表达…

面试面麻了,别再难为测试人了····

去面试吧 面不被录用的试 面hr为了完成任务的试 面一轮二轮没有下文试 面需要通勤2小时的试 面随时加班的试 ...... 今年的“金三银四”被网友们称为“铜三铁四”&#xff0c;招聘软件上的岗位都能背下来了&#xff0c;简历却依然石沉大海。 好不容易等来个回复&#xff0c;还不…

手写西瓜书bp神经网络 mnist10 c#版本

本文根据西瓜书第五章中给出的公式编写&#xff0c;书中给出了全连接神经网络的实现逻辑&#xff0c;本文在此基础上编写了Mnist10手写10个数字的案例&#xff0c;网上也有一些其他手写的例子参考。demo使用unity进行编写&#xff0c;方便且易于查错。 该案例仅作为学习&#x…

Linux网络——shell编程之免交互

Linux网络——shell编程之shell编程之免交互 一、概述1.常用的交互程序&#xff1a;2.语法格式&#xff1a; 二、Here Document常规用法1.read 命令的读取2.wc -l 的内容行数统计3.passwd用户密码的修改4.cat 查看内容并输出到文件中5.cat 查看交互内容并输出到新的文件中6.交互…