都工作3年了,怎么能不懂双亲委派呢?(带你手把手断点源码)

news2025/7/31 23:40:21

💗推荐阅读文章💗

  • 🌸JavaSE系列🌸👉1️⃣《JavaSE系列教程》
  • 🌺MySQL系列🌺👉2️⃣《MySQL系列教程》
  • 🍀JavaWeb系列🍀👉3️⃣《JavaWeb系列教程》
  • 🌻SSM框架系列🌻👉4️⃣《SSM框架系列教程》

🎉本博客知识点收录于🎉👉🚀《JavaSE系列教程》🚀—>✈️18【枚举、类加载器、动态代理】✈️

文章目录

  • 二、类加载器
    • 2.1 类加载时机
    • 2.3 类加载器
      • 2.3.1 类加载器的种类
      • 2.3.2 双亲委派机制
      • 2.3.3 双亲委派的好处
      • 2.3.4 URLClassLoader类加载器
        • 1)加载本地磁盘上的类:
        • 2)加载网络上的类:
      • 2.3.5 自定义类加载器
      • 2.3.6 打破双亲委派
    • 2.2 类的加载过程
      • 2.2.1 类的生命周期
        • 1)加载
        • 2)连接
        • 3)初始化

二、类加载器

2.1 类加载时机

我们知道,所有的代码都是运行在内存中的,我们必须把类加载到内存中才能运行;在Java中,所有的Java类都是通过类加载器加载到内存进行执行的;

  • 一个类何时被加载?
    • 1)main方法所在的类总是被首先初始化
    • 2)创建该类对象时,首先会将内加载到内存(如果该类存在父类,那么首先加载父类到内存,创建父类的对象(super))
    • 3)访问该类的静态成员时,会将类加载到内存(该静态成员不能被fianl修饰)
    • 4)class.forName(“类的全包名”)
package com.dfbz.demo01;
import org.junit.Test;
/**
 * @author lscl
 * @version 1.0
 * @intro:
 */
public class Demo01_类何时被加载 {
    @Test
    public void test1() throws ClassNotFoundException {
//        new B();          // 先加载A再加载B
//        Integer num = B.num;        // 先加载A再加载B
        Class<?> clazz = Class.forName("com.dfbz.demo01.B");        // 先加载A再加载B
    }
}
class A {
    public static Integer num = 10;
    static {
        System.out.println("A loader...");
    }
}
class B extends A {
    public static Integer num = 20;
    static {
        System.out.println("B loader...");
    }
}

Tips:不管是用什么方法加载,类从始至终只会加载一次;

2.3 类加载器

2.3.1 类加载器的种类

  • 启动类加载器Bootstrap ClassLoader: 是嵌在JVM内核中的加载器,该加载器是用C++语言写的,主要负则加载JAVA_HOME/lib下的类库,启动类加载器无法被应用程序直接使用。
  • **扩展类加载器Extension ClassLoader:**该加载器器是用JAVA编写,且它的父加载器是Bootstrap,是由sun.misc.Launcher$ExtClassLoader实现的,主要加载JAVA_HOME/lib/ext目录中的类库。开发者可以这几使用扩展类加载器。
  • **系统类加载器App ClassLoader:**系统类加载器,也称为应用程序类加载器,负责加载应用程序classpath目录下的所有jar和class文件(第三方jar)。它的父加载器为Ext ClassLoader。

Tips:这里的父加载器并非是Java中的继承关系,而是我们后面学习双亲委派过程中向上委派的加载器,我们将其称为父加载器;


测试类:

package com.dfbz.demo01;
import com.dfbz.demo02.Demo02;
import com.sun.java.accessibility.AccessBridge;
import org.junit.Test;
/**
 * @author lscl
 * @version 1.0
 * @intro:
 */
public class Demo02_类加载器的种类 {
    @Test
    public void test1(){
        // Bootstrap类加载器是获取不到的,为null
        System.out.println("Bootstrap ClassLoader: "+ String.class.getClassLoader());
        // jre\lib\ext\access-bridge-64.jar
        System.out.println("ExtClassLoader ClassLoader: "+ AccessBridge.class.getClassLoader());
        System.out.println("AppClassLoader ClassLoader: "+ Demo02.class.getClassLoader());
    }
}

2.3.2 双亲委派机制

从JDK1.2开始,类的加载过程采用双亲委派机制,它是一种任务委派模式。即把加载类的请求交由父加载器处理,一直到顶层的父加载器(BootstrapClassLoader);如果父加载器能加载则用父加载器加载,否则才用子加载器加载该类;

  • 示例代码:
package com.dfbz.demo01;
import org.junit.Test;
/**
 * @author lscl
 * @version 1.0
 * @intro:
 */
public class Demo03_类加载的过程 {
    @Test
    public void test1() {
        Class<T> tClass = T.class;
        System.out.println(tClass);
    }
    class T {
    }
}

JVM在加载类时,会调用类加载器(ClassLoader)的loadClass方法进行加载;
ClassLoader类加载源码:

protected Class<?> loadClass(String name, boolean resolve)
    throws ClassNotFoundException
{
    synchronized (getClassLoadingLock(name)) {
        // 检查该类是否被加载过
        Class<?> c = findLoadedClass(name);
        if (c == null) {
            long t0 = System.nanoTime();
            try {
                if (parent != null) {
                    // 使用父加载器加载
                    c = parent.loadClass(name, false);
                } else {
                    
                    // 如果没有父加载器则使用BootstrapClassLoader加载
                    c = findBootstrapClassOrNull(name);
                }
            } catch (ClassNotFoundException e) {
                // ClassNotFoundException thrown if class not found
                // from the non-null parent class loader
            }
            if (c == null) {
                // 如果依旧没有加载,则调用自身的findClass方法进行加载
                long t1 = System.nanoTime();
                c = findClass(name);
                // this is the defining class loader; record the stats
                sun.misc.PerfCounter.getParentDelegationTime().addTime(t1 - t0);
                sun.misc.PerfCounter.getFindClassTime().addElapsedTimeFrom(t1);
                sun.misc.PerfCounter.getFindClasses().increment();
            }
        }
        if (resolve) {
            resolveClass(c);
        }
        return c;
    }
}

findClass方法源码:

protected Class<?> findClass(String name) throws ClassNotFoundException {
    throw new ClassNotFoundException(name);
}

可以看到,默认情况下ClassLoader的findClass方法只是抛出了一个异常而已(这个方法是留给我们写的)


  • 双亲委派机制流程图:

  • 1)从上图我们可以分析,当一个Demo.class这样的文件要被加载时,首先会在AppClassLoader中检查是否加载过,如果有那就无需再加载了。

  • 2)如果没有加载,那么会拿到父加载器(向上委派),然后调用父加载器的loadClass方法进行加载。AppClassLoader的父加载器为ExtClassLoader,而ExtClassLoader并没有重写loadClass方法,因此还是调用ClassLoader类的loadClass方法,相当于是一个递归的操作;

  • 3)父类中同理也会先检查自己是否已经加载过,如果没有再往上。注意是个递归的过程,直到到达Bootstrap classLoader之前,都是在检查是否加载过,并不会选择自己去加载。直到BootstrapClassLoader,已经没有父加载器了,这时候开始考虑自己是否能加载了,如果自己无法加载,会下沉到子加载器去加载,一直到最底层,如果没有任何加载器能加载,就会抛出ClassNotFoundException。


BootstrapClassLoader不能加载该类,因此还是为null,然后调用本类的findClass方法;这里需要注意两点:

  • 1)本类还是ExtClassLoader
  • 2)ExtClassLoader是自身没有findClass方法,但ExtClassLoader继承与URLClassLoader,并且URLClassLoader提供有findClass方法;


接下来调用到URLClassLoader类中的findClass方法来加载该类:

URLClassLoader类中的findClass方法无法加载我们传递的类,然后向上抛出了一个异常;这里需要注意:

  • 1)URLClassLoader类中的findClass方法是通过ExtClassLoader调用findClass方法进去的,因此向上抛出异常后,findClass方法后面的代码将不会执行了,并且触发的异常继续往上抛给调用者(调用loadClass的对象)
  • 2)ExtClassLoader的loadClass方法是在AppClassLoader中,通过parent.loadClass()调用进去的,因此异常被抛到了这里;


异常被抛到了AppClassLoader中的loadClass方法中,接着尝试使用AppClassLoader的findClass()方法来加载类;

最终交给AppClassLoader完成类的加载:

2.3.3 双亲委派的好处

我们已经了解了Java中类加载的双亲委派机制,**即加载类时交给父加载器加载,如果父加载器不能加载,再交给子加载器加载;**这样做有何好处呢?

  • 1)避免类的重复加载:当父类加载器已经加载了该类时,就没有必要子 ClassLoader 再加载一次。
  • 2)安全问题:有了双亲委派机制,当有人想要替换系统级别的类或者篡改他的实现时,在双亲委派机制下,在任何的Java代码运行之前,会将所有要用到的系统类提前使用BootstrapClassLoader加载进内存(而当一个类需要被加载时必定会轮到BootstrapClassLoader来加载,只是是否能加载的问题,不能加载的必定不是系统级别的类),所以其他类加载器并没有机会再去加载,从一定程度上防止了危险代码的植入。

在指定的系统包下建立指定的类(由BootstrapClassLoader、ExtClassLoader加载的系统类):

  • Object:
package java.lang;
/**
 * @author lscl
 * @version 1.0
 * @intro:
 */
public class Object {
    static {
        System.out.println("自定义的Object类被加载了....");
    }
}
  • AccessBridge:
package com.sun.java.accessibility;
/**
 * @author lscl
 * @version 1.0
 * @intro:
 */
public class AccessBridge {
    static {
        System.out.println("自定义的AccessBridge类被加载了.....");
    }
}
  • 测试类:
package com.dfbz.demo01;
import com.sun.java.accessibility.AccessBridge;
import org.junit.Test;
/**
 * @author lscl
 * @version 1.0
 * @intro:
 */
public class Demo04_双亲委派的好处 {
    @Test
    public void test1() {
        // java.lang.Object 类在JVM启动时就已经被加载过了,因此不会再被加载了
        Class<Object> clazz = Object.class;
        // com.sun.java.accessibility.AccessBridge 类在JVM启动时就已经被加载过了,因此不会再被加载了
        Class<AccessBridge> accessBridgeClass = AccessBridge.class;
    }
}

Tips:根据双亲委派机制,我们自定义的Object、AccessBridge类不可能被加载;

另外,JVM的类加载器对包名的定义也有限制;不允许我们自定义系统包名
在系统包名下创建任意一个类:

@Test
public void test2() {
    //  不允许用户将类定义在受限包名下 ,Prohibited package name: java.lang
    Class<AA> clazz = AA.class;
}

运行结果:

2.3.4 URLClassLoader类加载器

在 java.net 包中,JDK提供了一个更加易用的类加载器URLClassLoader,它扩展了 ClassLoader,能够从本地或者网络上指定的位置加载类,我们可以使用该类作为自定义的类加载器使用。
URLClassLoader的构造方法:

  • public URLClassLoader(URL[] urls):指定要加载的类所在的URL地址,父类加载器默认为系统类加载器
  • public URLClassLoader(URL[] urls, ClassLoader parent):指定要加载的类所在的URL地址,并指定父类加载器。

1)加载本地磁盘上的类:

在指定目录下准备一个Java文件并把它编译成class文件:

  • Show.java:
package com.dfbz.demo01;
/**
 * @author lscl
 * @version 1.0
 * @intro:
 */
public class Show {
    public Show(){
        System.out.println("new Show....");
    }
}
  • 编译文件:
D:\000\com\dfbz\demo01>javac Show.java
D:\000\com\dfbz\demo01>

  • 测试代码:
package com.dfbz.demo01_类加载器的功能;
import org.junit.Test;
import java.io.File;
import java.net.URI;
import java.net.URL;
import java.net.URLClassLoader;
/**
 * @author lscl
 * @version 1.0
 * @intro:
 */
public class Demo05_URLClassLoader {
    @Test
    public void test() throws Exception{
        File file = new File("D:\\000");
        // file   --->  URI
        URI uri = file.toURI();
        // URI    --->  URL
        URL url = uri.toURL();
        // 根据URL构建一个类加载器
        URLClassLoader classLoader = new URLClassLoader(new URL[]{url});
        System.out.println("父类加载器:" + classLoader.getParent());             // 默认父类加载器是系统类加载器
        Class clazz = classLoader.loadClass("com.dfbz.demo01.Show");
        // 实例化这个类
        clazz.newInstance();
    }
}

运行结果:

2)加载网络上的类:

@Test
public void test2() throws Exception{
    // 构建一个网络地址
    URL url = new URL("http://www.baidu.com/class/");
    
    URLClassLoader classLoader = new URLClassLoader(new URL[]{url});
    System.out.println("父类加载器:" + classLoader.getParent());         // 默认父类加载器是系统类加载器
    Class clazz = classLoader.loadClass("com.baidu.demo.Show");
    
    // 实例化这个类
    clazz.newInstance();
}

Tips:关于加载网络上的类,等我们以后学习了服务器编程再来体验!

2.3.5 自定义类加载器

我们如果需要自定义类加载器,只需要继承ClassLoader,并覆盖掉findClass方法即可。

Tips:我们自定义的类加载器的父加载器为AppClassLoader;

  • 自定义类加载器:
package com.dfbz.demo02;
import java.io.ByteArrayOutputStream;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
// 1. 继承 ClassLoader
// 2. 覆盖 findClass方法
public class MyClassLoader extends ClassLoader {
    // 被加载类所在的目录
    private String dir;
    public MyClassLoader(String dir) {          // 默认父类加载器就是系统类加载器 AppClassLoader
        this.dir = dir;
    }
    public MyClassLoader(ClassLoader parent, String dir) {
        super(parent);
        this.dir = dir;
    }
    /**
     *
     * @param name
     * @return 重写findClass方法
     * @throws ClassNotFoundException
     */
    @Override
    protected Class<?> findClass(String name) throws ClassNotFoundException {
        try {
            // 把类名转换为目录  --->  D:/000/com/dfbz/demo01/Show.class
            String file = dir + "/" + name.replace(".", "/") + ".class";
            // 从文件中读取这个Class文件
            InputStream in = new FileInputStream(file);
            // 构建一个内存输出流(将读取到的Class文件写在内存中)
            ByteArrayOutputStream baos = new ByteArrayOutputStream();
            byte[] buf = new byte[1024];
            int len ;
            while ((len = in.read(buf)) != -1) {
                baos.write(buf, 0, len);
            }
            // 读取到的流的二进制数据
            byte[] data = baos.toByteArray();
            in.close();
            baos.close();
            /*
            defineClass: 根据类的全包名和内存中的数据流来加载一个类
             - 参数1: 需要加载类的全包名
             - 参数2: 已经加载到内存中的数据流
             - 参数3: 从指定的数据下表开始读取
             - 参数4: 读取到什么位置
             */
            return defineClass(name, data, 0, data.length);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }
}
  • 测试类:
package com.dfbz.demo02;
/**
 * @author lscl
 * @version 1.0
 * @intro:
 */
public class Demo01_自定义类加载器的使用 {
    public static void main(String[] args) throws Exception{
        
        // 创建我们自己的类加载器
        MyClassLoader classLoader = new MyClassLoader("d:/000");
        
        // 使用loadClass加载类
        Class<?> clazz = classLoader.loadClass("com.dfbz.demo01.Show");
        
        clazz.newInstance();
    }
}

2.3.6 打破双亲委派

我们前面自定义了类加载器,观察下面代码:

package com.dfbz.demo02_自定义类加载器;
/**
 * @author lscl
 * @version 1.0
 * @intro:
 */
public class Demo02_打破双亲委派_01 {
    public static void main(String[] args) throws Exception {
        MyClassLoader classLoader = new MyClassLoader("d:/000");
        MyClassLoader classLoader2 = new MyClassLoader("d:/000");
        Class<?> clazz = classLoader.loadClass("com.dfbz.demo01.Show");
        Class<?> clazz2 = classLoader2.loadClass("com.dfbz.demo01.Show");
        System.out.println(clazz == clazz2);                // true
        System.out.println(clazz.getClassLoader());         // sun.misc.Launcher$AppClassLoader@18b4aac2
        System.out.println(clazz2.getClassLoader());        // sun.misc.Launcher$AppClassLoader@18b4aac2
    }
}

运行结果:

根据我们之前学习双亲委派机制,上面两个类加载器在加载Show类时,都会判断有没有加载这个类,没有加载则使用父加载器加载,MyClassLoader的父加载器是AppClassLoader,而AppClassLoader正好可以加载这个类;所以其实这两次的加载都是由AppClassLoader来加载的,而AppClassLoader在加载时会判断是否已经加载过,加载过了则不加载;因此Show类只会加载一次;


但是需要注意的是,双亲委派机制的逻辑是写在ClassLoader类的loadClass方法中的,通过一系列逻辑判断最终执行findClass方法来加载类;如果我们加载类直接使用findClass方法呢?那就相当于避开了双亲委派;(当然也可以重写loadClass方法,重新自定义loadClass规则)

  • 测试类:
package com.dfbz.demo02;
/**
 * @author lscl
 * @version 1.0
 * @intro:
 */
public class Demo03_打破双亲委派_02 {
    public static void main(String[] args) throws Exception{
        MyClassLoader classLoader = new MyClassLoader("d:/000");
        MyClassLoader classLoader2 = new MyClassLoader("d:/000");
        // 不使用loadClass来加载类,直接使用findClass方法去加载类,每一次调用findClass都相当于是加载一次新的类
        Class<?> clazz = classLoader.findClass("com.dfbz.demo01.Show");
        Class<?> clazz2 = classLoader2.findClass("com.dfbz.demo01.Show");
        System.out.println(clazz == clazz2);                // false
        System.out.println(clazz.getClassLoader());         // com.dfbz.demo02.MyClassLoader@135fbaa4
        System.out.println(clazz2.getClassLoader());        // com.dfbz.demo02.MyClassLoader@330bedb4
    }
}

运行结果:

2.2 类的加载过程

2.2.1 类的生命周期

一个Java类从开始到结束整个生命周期会经历7个阶段:加载(Loading)、验证(Verification)、准备(Preparation)、解析(Resolution)、初始化(Initialization)、使用(Using)和卸载(Unloading)。
其中验证、准备、解析三个部分又统称为连接(Linking)。

1)加载

加载过程就是把class字节码文件载入到虚拟机中,至于从哪儿加载,虚拟机设计者并没有限定,你可以从文件、压缩包、网络、数据库等等地方加载class字节码。
类加载的方式有:

  • 1)通过类的全限定名来获取定义此类的二进制字节流
  • 2)将此二进制字节流所代表的静态存储结构转化成方法区的运行时数据结构(加载到内存)
  • 3)在内存中生成代表此类的java.lang.Class对象(在堆中),作为该类访问入口;

2)连接

连接阶段的开始,并不一定等到加载阶段结束。加载阶段与连接阶段的部分内容(如一部分字节码文件格式验证动作)是交叉进行的,加载阶段尚未完成,连接阶段可能已经开始,但这些夹杂在加载阶段之中的动作任然属于连接阶段,加载和连接这两个阶段的开始顺序是固定的。

  • 验证:验证节点主要确保Class文件的格式正确,运行时不会危害JVM的安全;包含文件格式验证、元数据验证、字节码验证、符号引用验证等;
  • 准备:准备阶段会为类变量(被static修饰的变量)分配内存并设置类变量的初始值,这些变量所使用的内存都将在方法区中分配。
    假如有一个变量private static int value = 123;那么value在准备阶段过后值是0,而不是123;因为这个时候尚未执行任何java方法,而把value赋值为123的动作在初始化阶段才会执行。
    但是如果上面的变量被final修饰,变为:private static final int value = 123;编译时javac会为value生成ConstantValue属性,在准备阶段虚拟机就会根据ConstantValue的设置将value赋值为123。
  • 解析:解析阶段是虚拟机将常量池内的符号引用替换为直接引用的过程。

Tips:

  • 符号引用:在编译的时候每个java类都会被编译成一个class文件,但在编译的时候虚拟机并不知道所引用类的地址,所以就用符号引用来代替,而在这个解析阶段就是为了把这个符号引用转化成为真正的地址的阶段。
  • 直接引用:直接引用可以直接指向目标的指针,如果有了直接引用,那引用的目标必定已经在内存中存在。

解析动作主要针对类或接口、字段、类方法、接口方法、方法类型方法句柄和调用点限定符7类符号引用进行。

3)初始化

类初始化是类加载过程的最后一步,这一步会真正开始执行类中定义的Java程序代码(或者说字节码)。在准备阶段,变量已经被赋过一次系统要求的初始值,在初始化阶段,变量会再次赋值为程序员设置的值。比如变量:private static int value = 123;那么value在准备阶段过后值是0,初始化阶段后值是123。
会导致 类加载 的情况

  • 1)main方法所在的类总是被首先初始化
  • 2)创建该类对象时,首先会将内加载到内存(如果该类存在父类,那么首先加载父类到内存,创建父类的对象(super))
  • 3)访问该类的静态成员时,会将类加载到内容(该静态常量不能被final修饰的基本类型和字符型)
  • 4)class.forName(“类的全包名”)

不会导致 类加载 的情况

  • 1)访问 类的 static final 静态变量(基本类型和字符型)不会触发初始化
  • 2)类对象.class 不会触发初始化
  • 3)创建该类的数组不会触发初始化
  • 4)Class.forName 的参数2 为 false 时

测试类:

package com.dfbz.demo03_类的初始化流程;
/**
 * @author lscl
 * @version 1.0
 * @intro:
 */
public class Demo01_测试类 {
    public static void main(String[] args) {
        System.out.println(A.B);            // 访问类中的final成员类并不会初始化
        System.out.println(A.OBJ);          // 访问非基本数据类型和String时将会初始化A
        Class<A> aClass = A.class;      	// 类已经初始化过一次了,并不会再次初始化
        System.out.println(aClass);
    }
}
class A {
    static {
        System.out.println("A加载了");
    }
    public static final String B = "1";
//    public static final String B = new String();      // 如果访问的是堆内存中的String,那么A将会被加载
    public static final Obj OBJ = new Obj();
}
class Obj{}

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

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

相关文章

c盘怎么清理到最干净?有什么好的清理方法

c盘怎么清理到最干净?有什么好的清理方法&#xff1f;清理C盘空间是电脑维护的重要步骤之一。C盘是Windows操作系统的核心部分&#xff0c;保存了许多重要的系统文件&#xff0c;因此空间不足会影响计算机的性能和稳定性。下面是一些清理C盘空间的方法 一.清理临时文件 在使用…

【Java学习笔记】10.条件语句 - if...else及switch case 语句

前言 本章介绍Java的条件语句 - if…else和switch case 语句。 Java 条件语句 - if…else Java 中的条件语句允许程序根据条件的不同执行不同的代码块。 一个 if 语句包含一个布尔表达式和一条或多条语句。 语法 if 语句的语法如下&#xff1a; if(布尔表达式) {//如果布…

实验二:动态规划

1.双11的红包雨 问题描述 双11到了&#xff0c;据说这2天会下红包雨&#xff0c;每个红包有不同的价值&#xff0c;小k好开心&#xff0c;但有个规则&#xff0c;就只能接掉落在他身旁的10米范围内的红包&#xff08;0-10这11个位置&#xff09;。小k想尽可能的多抢红包&…

评价提高分子对接性能的组合策略

评价提高分子对接性能的组合策略 相关背景&#xff1a; 分子对接可能是应用于基于结构的药物设计和虚拟筛选活动中最快速、成本最低的计算技术&#xff0c;它可以从巨大的化合物文库中找到潜在的活性分子&#xff0c;用于先导发现。 在分子对接中&#xff0c;配体(通常是小分…

LIME: Low-light Image Enhancement viaIllumination Map Estimation

Abstract当人们在低光条件下拍摄图像时&#xff0c;图像通常会受到低能见度的影响。除了降低图像的视觉美感外&#xff0c;这种不良的质量还可能显著降低许多主要为高质量输入而设计的计算机视觉和多媒体算法的性能。在本文中&#xff0c;我们提出了一种简单而有效的微光图像增…

2023年最新的站内SEO指南:如何通过关键词优化提高网站排名

SEO或搜索引擎优化是指通过改善网站的内部和外部元素&#xff0c;以获得更好的自然搜索引擎排名和更多的网站流量。 链接建设和外链是SEO的重要组成部分&#xff0c;因为它们可以提高网站的权威性和可信度&#xff0c;从而使其在搜索引擎中排名更高。 在此指南中&#xff0c;…

MySQL三范式

1、查询语句写的烂2、索引失效&#xff08;数据变更&#xff09;3、关联查询太多join&#xff08;设计缺陷或不得已的需求&#xff09;4、服务器调优及各个参数设置&#xff08;缓冲、线程数等&#xff09; 通常SQL调优过程&#xff1a; 观察&#xff0c;至少跑1天&#xff0…

CF756div3 vp

又被薄纱了&#xff0c;rk就不放了&#xff0c;好丢人QwQDashboard - Codeforces Round 756 (Div. 3) - CodeforcesA. Make Even小分类讨论题意&#xff1a;给定一个数&#xff0c;每次操作可以选取其前缀然后翻转其前缀&#xff0c;问你最少操作几次可以把该数变为偶数思路&am…

基于深度学习的轴承寿命预测实践,开发CNN、融合LSTM/GRU/ATTENTION

关于轴承相关的项目之前做的大都是故障识别诊断类型的&#xff0c;少有涉及回归预测的&#xff0c;周末的时候宅家发现一个轴承寿命加速实验的数据集就想着拿来做一下寿命预测。首先看下数据集如下&#xff1a;直接百度即可搜到&#xff0c;这里就不再赘述了。Learning_set为训…

什么是蜕变测试?

文章目录1.传统测试2.蜕变测试2.1.蜕变测试的理解2.2.蜕变测试的步骤2.2.1.生成蜕变关系2.2.2.生成蜕变用例2.2.3.执行蜕变用例2.2.4.校验蜕变关系参考文献1.传统测试 在没有蜕变测试的时代&#xff0c;传统软件测试的原理是&#xff1a;给定输入&#xff0c;观察被测软件的输…

你把骑行当什么? 它就是你需要的

1.骑行是一种有活力的运动&#xff0c;尝试一下你一定会喜欢上它的&#xff01;2.把骑行当作一种娱乐&#xff0c;让自己快乐地体验自然的美&#xff01;3.骑行可以帮助你改变心态&#xff0c;让你的心情变得更加愉悦&#xff01;4.让骑行成为你每天的计划&#xff0c;看看骑行…

项目实战典型案例27——单表的更新接口有9个之多

单表的更新接口有9个之多一&#xff1a;背景介绍环境准备引入pom依赖配置数据库连接mybatis配置文件Mybatis的配置类编写通用的更新语句可以覆盖的更新接口暂时无法覆盖的接口测试四&#xff1a;总结五&#xff1a;升华一&#xff1a;背景介绍 本篇博客是对项目开发中出现的单…

如何分析sql性能

1、前言 提到sql性能分析&#xff0c;可能都会想到explain&#xff0c;它在mysql里被称为执行计划&#xff0c;也就是说可以通过该命令看出mysql在通过优化器分析之后如何执行sql。mysql的内置优化器十分强大&#xff0c;它能帮我们把sql再次优化&#xff0c;以最低的成本去执…

记录自己开发一款小程序中所遇到的问题(uniapp+uview)(持续更新)

每次开发小程序中&#xff0c;都会遇到各种各样的问题。但是有的问题已经遇到过了&#xff0c;但是遇到的时候还是要各种的问度娘。 特此出这篇文章&#xff0c;方便自己也是方便大家。 仅供参考 1. u-collapse的样式在h5中正常&#xff0c;但是运行到微信小程序中样式就乱了…

滑动窗口求最大和最小

滑动窗口 要区分最小和最大滑窗&#xff0c;内层while循环的条件和更新结果的地方 核心&#xff1a; 关键的区别在于&#xff0c;最大滑窗是在迭代右移右边界的过程中更新结果&#xff0c;而最小滑窗是在迭代右移左边界的过程中更新结果。 最小滑窗 给定数组 nums&#xff0…

成都待慕电商:抖音极速版商品卡免佣扶持政策规则

新规&#xff0c;抖音极速版推出商品卡免佣扶持政策规则&#xff0c;本次抖音规则如何规定&#xff1f;具体往下看&#xff1a;一、政策简介1.1政策介绍为了更好地满足用户消费需求&#xff0c;丰富商家经营模式&#xff0c;降低商家经营成本&#xff0c;现平台针对商品卡场景推…

【数据结构】模拟实现 堆

堆数据结构是一种数组对象&#xff0c;它可以被看作一颗完全二叉树的结构&#xff08;数组是完全二叉树&#xff09;&#xff0c;堆是一种静态结构。堆分为最大堆和最小堆。最大堆&#xff1a;每个父结点都大于孩子结点。最小堆&#xff1a;每个父结点都小于孩子结点。堆的优势…

前端脚手架搭建(part 1)

本篇主要介绍如何搭建前端脚手架&#xff0c;一步一步地实现通过搭建的脚手架下载对应的项目模板。通过脚手架的创建&#xff0c;可以快速搭建项目的基础配置和模板&#xff0c;在部门项目开发的规范中尤其总要。初始化项目&#xff1a;创建一个文件夹&#xff0c;命名随便&…

【LeetCode】螺旋矩阵 [M](数组)

54. 螺旋矩阵 - 力扣&#xff08;LeetCode&#xff09; 一、题目 给你一个 m 行 n 列的矩阵 matrix &#xff0c;请按照 顺时针螺旋顺序 &#xff0c;返回矩阵中的所有元素。 示例 1&#xff1a; 输入&#xff1a;matrix [[1,2,3],[4,5,6],[7,8,9]] 输出&#xff1a;[1,2,3,…

OceanBase 生态产品:时序数据库CeresDB 正式发布 1.0 版本

欢迎访问OceanBase官网获取更多信息&#xff1a;https://www.oceanbase.com/ CeresDB 是一款拥有计算存储分离架构的分布式时序数据库&#xff0c;其存储层可以基于 OceanBase KV、OSS 等。经过近一年的开源研发工作&#xff0c;CeresDB 1.0 现已正式发布&#xff0c;达到生产…