加载类_JVM类加载详解

类的加载器

概述

类加载器是JVM执行类加载机制的前提。

ClassLoader的作用:ClassLoader是Java的核心组件,所有的Class都是由ClassLoader进行加载的,ClassLoader负责通过各种方式将Class信息的二进制数据流读入JVM内部,转换为一个与目标类对应的java.lang.Class对象实例。然后交给Java虚拟机进行链接、初始化等操作。因此,ClassLoader在整个装载阶段,只能影响到类的加载,而无法通过ClassLoader去改变类的链接和初始化行为。至于它是否可以运行,则由Execution Engine决定。

1ca3b98fc6a51f1a8f00012e940cc4ce.png
jvm

类加载分类

类的加载分类:显式加载 vs 隐式加载

class文件的显式加载与隐式加载的方式是指JVM加载class文件到内存的方式。

  • 显式加载指的是在代码中通过调用ClassLoader加载class对象,如直接使用Class.forName(name)或this.getClass().getClassLoader().loadClass()加载class对象。
  • 隐式加载则是不直接在代码中调用ClassLoader的方法加载class对象,而是通过虚拟机自动加载到内存中,如在加载某个类的class文件时,该类的class文件中引用了另外一个类的对象,此时额外引用的类将通过JVM自动加载到内存中。

代码示例:

User user=new User();//隐式加载
Class clazz=Class.forName("com.atguigu.java.User");//显式加载并初始化
ClassLoader.getSystemClassLoader().loadClass("T1.Parent"); //显式加载,但不初始化

类加载器的必要性

一般情况下,Java开发人员并不需要在程序中显式地使用类加载器,但是了解类加载器的加载机制却显得至关重要。从以下几个方面说:

  • 避免在开发中遇到java.lang.ClassNotFoundException异常或java.lang.NoClassDefFoundError异常时,手足无措。只有了解类加载器的 加载机制才能够在出现异常的时候快速地根据错误异常日志定位问题和解决问题
  • 需要支持类的动态加载或需要对编译后的字节码文件进行加解密操作时,就需要与类加载器打交道了。
  • 开发人员可以在程序中编写自定义类加载器来重新定义类的加载规则,以便实现一些自定义的处理逻辑。

命名空间

什么是类的唯一性?

对于任意一个类,都需要由加载它的类加载器和这个类本身一同确认其在Java虚拟机中的唯一性。每一个类加载器,都拥有一个独立的类名称空间:比较两个类是否相等,只有在这两个类是由同一个类加载器加载的前提下才有意义。否则,即使这两个类源自同一个Class文件,被同一个虚拟机加载,只要加载他们的类加载器不同,那这两个类就必定不相等。

命名空间

  • 每个类加载器都有自己的命名空间,命名空间由该加载器及所有的父加载器所加载的类组成
  • 在同一命名空间中,不会出现类的完整名字(包括类的包名)相同的两个类
  • 在不同的命名空间中,有可能会出现类的完整名字(包括类的包名)相同的两个类

类加载机制的基本特征

  • 双亲委派模型。但不是所有类加载都遵守这个模型,有的时候,启动类加载器所加载的类型,是可能要加载用户代码的,比如JDK内部的ServiceProvider/ServiceLoader机制,用户可以在标准API框架上,提供自己的实现,JDK也需要提供些默认的参考实现。例如,Java中JNDI、JDBC、文件系统、Cipher等很多方面,都是利用的这种机制,这种情况就不会用双亲委派模型去加载,而是利用所谓的上下文加载器。
  • 可见性,子类加载器可以访问父加载器加载的类型,但是反过来是不允许的。不然,因为缺少必要的隔离,我们就没有办法利用类加载器去实现容器的逻辑。
  • 单一性,由于父加载器的类型对于子加载器是可见的,所以父加载器中加载过的类型,就不会在子加载器中重复加载。但是注意,类加载器“邻居”间,同一类型仍然可以被加载多次,因为互相并不可见。

类的加载器分类

JVM支持两种类型的类加载器,分别为引导类加载器(Bootstrap ClassLoader)自定义类加载器(User-Defined ClassLoader)

从概念上来讲,自定义类加载器一般指的是程序中由开发人员自定义的一类类加载器,但是Java虚拟机规范却没有这么定义,而是将所有派生于抽象类ClassLoader的类加载器都划分为自定义类加载器。无论类加载器的类型如何划分,在程序中我们最常见的类加载器结构主要是如下情况:

6521f939e45f9395fa64f656e98101ba.png
类加载器

除了顶层的启动类加载器外,其余的类加载器都应当有自己的“父类”加载器。

不同类加载器看似是继承(Inheritance)关系,实际上是包含关系。在下层加载器中,包含着上层加载器的引用。

引导类加载器(Bootstrap ClassLoader)

  • 这个类加载使用C/C++语言实现的,嵌套在JVM内部。
  • 它用来加载Java的核心库(JAVAHOME/jre/lib/rt.jar或sun.boot.class.path路径下的内容)。用于提供JVM自身需要的类。
  • 不继承自java.lang.ClassLoader,没有父加载器。
  • 出于安全考虑,Bootstrap启动类加载器只加载包名为java、javax、sun等开头的类
  • 加载扩展类和应用程序类加载器,并指定为他们的父类加载器。

扩展类加载器(Extension ClassLoader)

  • Java语言编写,由sun.misc.Launcher$ExtClassLoader实现。
  • 继承于ClassLoader类
  • 父类加载器为启动类加载器
  • 从java.ext.dirs系统属性所指定的目录中加载类库,或从JDK的安装目录的jre/lib/ext子目录下加载类库。如果用户创建的JAR放在此目录下,也会自动由扩展类加载器加载。
952a8d9c061d9ce0e7d5129975234b98.png

代码示例:

public static void main(String[] args) {

    //获取BootstrapcLassLoader能够加载的api的路径
    URL[] urLs = sun.misc.Launcher.getBootstrapClassPath().getURLs();
    for (URL element : urLs) {
        System.out.println(element.toExternalForm());
    }
    //        file:/D:/java/jre/lib/resources.jar
    //        file:/D:/java/jre/lib/rt.jar
    //        file:/D:/java/jre/lib/sunrsasign.jar
    //        file:/D:/java/jre/lib/jsse.jar
    //        file:/D:/java/jre/lib/jce.jar
    //        file:/D:/java/jre/lib/charsets.jar
    //        file:/D:/java/jre/lib/jfr.jar
    //        file:/D:/java/jre/classes


    //引导类加载
    ClassLoader classloader = java.security.Provider.class.getClassLoader();
    System.out.println(classloader); // null


    System.out.println("***********扩展类加载器*************");
    String extDirs = System.getProperty("java.ext.dirs");
    for (String path : extDirs.split(";")) {
        System.out.println(path);
    }
    //        D:\Java\jre\lib\ext
    //        C:\Windows\Sun\Java\lib\ext

    //扩展类加载器
    ClassLoader classLoader1 = sun.security.ec.CurveDB.class.getClassLoader();
    System.out.println(classLoader1);// sun.misc.Launcher$ExtClassLoader@6e0be858
}

系统类加载器(AppClassLoader)

  • java语言编写,由sun.misc.Launcher$AppClassLoader实现
  • 继承于ClassLoader类
  • 父类加载器为扩展类加载器
  • 它负责加载环境变量classpath或系统属性java.class.path 指定路径下的类库
  • 应用程序中的类加载器默认是系统类加载器。
  • 它是用户自定义类加载器的默认父加载器
  • 通过ClassLoader的getSystemClassLoader()方法可以获取到该类加载器
cde4221f62621db7353e67b3f8f25051.png

用户自定义类加载器

  • 在Java的日常应用程序开发中,类的加载几乎是由上述3种类加载器相互配合执行的。在必要时,我们还可以自定义类加载器,来定制类的加载方式。
  • 体现Java语言强大生命力和巨大魅力的关键因素之一便是,Java开发者可以自定义类加载器来实现类库的动态加载,加载源可以是本地的JAR包,也可以是网络上的远程资源。
  • 通过类加载器可以实现非常绝妙的插件机制,这方面的实际应用案例举不胜举。例如,著名的OSGI组件框架,再如Eclipse的插件机制。类加载器为应用程序提供了一种动态增加新功能的机制,这种机制无须重新打包发布应用程序就能实现。
  • 同时,自定义加载器能够实现应用隔离,例如Tomcat,Spring等中间件和组件框架都在内部实现了自定义的加载器,并通过自定义加载器隔离不同的组件模块。这种机制比C/C++程序要好太多,想不修改C/C++程序就能为其新增功能,几乎是不可能的,仅仅一个兼容性便能阻挡住所有美好的设想。
  • 自定义类加载器通常需要继承ClassLoader。

ClassLoader源码分析

ClassLoader与现有类加载器的关系:

3bf83b87df56354cc4aac94f97607f9d.png

ClassLoader的主要方法

  • public final ClassLoader getParent() :返回该类加载器的超类加载器

  • **public Class> loadClass(String name) **:加载名称为name的类,返回结果为java.lang.Class类的实例。如果找不到类,则返回ClassNotFoundException异常。该方法中的逻辑就是使用双亲委派机制实现的。

    protected Class> loadClass(String name, boolean resolve) //resolve = false 不进行解析
            throws ClassNotFoundException
        {
            synchronized (getClassLoadingLock(name)) {
                // First, check if the class has already been loaded
                // 查看该类是否被加载过
                Class> c = findLoadedClass(name);
                if (c == null) {
                    long t0 = System.nanoTime();
                    try {
                        //获取当前类父类的加载器
                        if (parent != null) {
                            //使用父类的加载器加载
                            c = parent.loadClass(name, false);
                        } else {
                            //说明父类加载器为引导类加载器
                            c = findBootstrapClassOrNull(name);
                        }
                    } catch (ClassNotFoundException e) {
                        // ClassNotFoundException thrown if class not found
                        // from the non-null parent class loader
                    }

                    if (c == null) {//当前类的加载器父类加载器未加载此类 or 当前类的加载器未加载此类
                        // If still not found, then invoke findClass in order
                        // to find the class.
                        long t1 = System.nanoTime();
                        //调用当前Classloader的findClass
                        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;
            }
    }
  • **protected Class > findClass(String name) **:查找二进制名称为name的类,返回结果为java.lang.Class类的实例。这是一个受保护的方法,JVM鼓励我们重写此方法,需要自定义加载器遵循双亲委托机制,该方法会在检查完父类加载器之后被loadClass()方法调用。

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

    我们找到对应重写该方法的地方。URLClassLoader类。

    protected Class> findClass(final String name)
            throws ClassNotFoundException
        {
            final Class> result;
            try {
                result = AccessController.doPrivileged(
                    new PrivilegedExceptionAction>() {public Class> run() throws ClassNotFoundException {//全路径替换
                            String path = name.replace('.', '/').concat(".class");
                            Resource res = ucp.getResource(path, false);if (res != null) {try {//调用defineClass生成Class对象return defineClass(name, res);
                                } catch (IOException e) {throw new ClassNotFoundException(name, e);
                                }
                            } else {return null;
                            }
                        }
                    }, acc);
            } catch (java.security.PrivilegedActionException pae) {throw (ClassNotFoundException) pae.getException();
            }if (result == null) {throw new ClassNotFoundException(name);
            }return result;
    }
  • protected final Class>defineclass(String name,byte[]b,int off,int len):根据给定的字节数组b转换为Class的实例,off和len参数表示实际Class信息在byte数组中的位置和长度,其中byte数组b是ClassLoader从外部获取的。这是受保护的方法,只有在自定义ClassLoader子类中可以使用。

    defineClass()方法是用来将byte字节流解析成JVM能够识别的Class对象(ClassLoader中已实现该方法逻辑),通过这个方法不仅能够通过class文件实例化class对象,也可以通过其他方式实例化Class对象,如通过网络接收一个类的字节码,然后转换为byte字节流创建对应的Class对象。

    defineClass()方法通常与findClass()方法一起使用,一般情况下,在自定义类加载器时,会直接覆盖ClassLoader的findClass()方法并编写加载规则,取得要加载类的字节码后转换成流,然后调用defineClass()方法生成类的Class对象

    代码示例:

    @Override
    protected Class>findClass(String name)throws ClassNotFoundException{
        //获取类的字节数组
        byte[]classData=getClassData(name);
        if(classData==null){
            throw new ClassNotFoundException();
        }else{
            //使用defineclass生成class对象
            return defineclass(name,classData,0,classData.length);
        }
    }
  • protected final void resolveClass(Class>c):链接指定的一个Java类。使用该方法可以使用类的Class对象创建完成的同时也被解析。前面我们说链接阶段主要是对字节码进行验证,为类变量分配内存并设置初始值同时将字节码文件中的符号引用转换为直接引用。

  • protected final Class>findLoadedClass(String name):查找名称为name的已经被加载过的类,返回结果为java.lang.Class类的实例。这个方法是final方法,无法被修改。

  • **private final ClassLoader parent:**它也是一个ClassLoader的实例,这个字段所表示的ClassLoader也称为这个ClassLoader的双亲。在类加载的过程中,ClassLoader可能会将某些请求交予自己的双亲处理。

SecureClassLoader 与 URLClassLoader

SecureClassLoader扩展了ClassLoader,新增了几个与使用相关的代码源(对代码源的位置及其证书的验证)和权限定义类验证(主要指对class源码的访问权限)的方法,一般我们不会直接跟这个类打交道,更多是与它的子类URLClassLoader有所关联。

前面说过,ClassLoader是一个抽象类,很多方法是空的没有实现,比如findClass()、findResource()等。而URLClassLoader这个实现类为这些方法提供了具体的实现。并新增了URLClassPath类协助取得Class字节码流等功能。在编写自定义类加载器时,如果没有太过于复杂的需求,可以直接继承URLClassLoader类,这样就可以避免自己去编写findClass()方法及其获取字节码流的方式,使自定义类加载器编写更加简洁。

双亲委派模型

如果一个类加载器在接到加载类的请求时,它首先不会自己尝试去加载这个类,而是把这个请求任务委托给父类加载器去完成,依次递归,如果父类加载器可以完成类加载任务,就成功返回。只有父类加载器无法完成此加载任务时,才自己去加载。

本质

规定了类加载的顺序是:引导类加载器先加载,若加载不到,由扩展类加载器加载,若还加载不到,才会由系统类加载器或自定义的类加载器进行加载。

19408389f22ded6a04001ac005b03afb.png
双亲委派机制
f919cd0978303dc0167047ddce0d6de1.png
类加载流程

优势与劣势

优势

  • 避免类的重复加载,确保一个类的全局唯一性
    • Java类随着它的类加载器一起具备了一种带有优先级的层次关系,通过这种层级关可以避免类的重复加载,当父亲已经加载了该类时,就没有必要子ClassLoader再加载一次。
  • 保护程序安全,防止核心API被随意篡改

问题

如果在自定义的类加载器中重写java.lang.ClassLoader.loadClass(String)或java.lang.ClassLoader.loadclass(String,boolean)方法,抹去其中的双亲委派机制,那么是不是就能够加载核心类库了呢?这也不行!因为JDK还为核心类库提供了一层保护机制。不管是自定义的类加载器,还是系统类加载器抑或扩展类加载器,最终都必须调用 java.lang.ClassLoader.defineclass(String,byte[],int,int,ProtectionDomain)方法,而该方法会执行preDefineClass()接口,该接口中提供了对JDK核心类库的保护。

劣势

  • 检查类是否加载的委托过程是单向的,这个方式虽然从结构上说比较清晰,使各个ClassLoader的职责非常明确,但是同时会带来一个问题,即顶层的ClassLoader无法访问底层的ClassLoader所加载的类。
  • 通常情况下,启动类加载器中的类为系统核心类,包括一些重要的系统接口,而在应用类加载器中,为应用类。按照这种模式,应用类访问系统类自然是没有问题,但是系统类访问应用类就会出现问题。比如在系统类中提供了一个接口,该接口需要在应用类中得以实现,该接口还绑定一个工厂方法,用于创建该接口的实例,而接口和工厂方法都在启动类加载器中。这时,就会出现该工厂方法无法创建由应用类加载器加载的应用实例的问题。

自定义类加载器

为什么要自定义类加载器?

  • 隔离加载类
    • 在某些框架内进行中间件与应用的模块隔离,把类加载到不同的环境。比如:阿里内某容器框架通过自定义类加载器确保应用中依赖的jar包不会影响到中间件运行时使用的jar包。再比如:Tomcat这类Web应用服务器,内部自定义了好几种类加载器,用于隔离同一个Web应用服务器上的不同应用程序。
  • 修改类加载的方式
    • 类的加载模型并非强制,除Bootstrap外,其他的加载并非一定要引入,或者根据实际情况在某个时间点进行按需进行动态加载
  • 扩展加载源
    • 比如从数据库、网络、甚至是电视机机顶盒进行加载
  • 防止源码泄漏
    • Java代码容易被编译和篡改,可以进行编译加密。那么类加载也需要自定义,还原加密的字节码。

常见的场景

  • 实现类似进程内隔离,类加载器实际上用作不同的命名空间,以提供类似容器、模块化的效果。例如,两个模块依赖于某个类库的不同版本,如果分别被不同的容器加载,就可以互不干扰。这个方面的集大成者是JavaEE和OSGI、JPMS等框架。
  • 应用需要从不同的数据源获取类定义信息,例如网络数据源,而不是本地文件系统。或者是需要自己操纵字节码,动态修改或者生成类型。

注意

在一般情况下,使用不同的类加载器去加载不同的功能模块,会提高应用程序的安全性。但是,如果涉及Java类型转换,则加载器反而容易产生不美好的事情。在做Java类型转换时,只有两个类型都是由同一个加载器所加载,才能进行类型转换,否则转换时会发生异常。

实现方式

  • Java提供了抽象类java.lang.ClassLoader,所有用户自定义的类加载器都应该继承ClassLoader类。

  • 在自定义ClassLoader的子类时候,我们常见的会有两种做法:

    • 方式一:重写loadClass()方法
    • 方式二:重写findclass()方法(推荐)
  • 这两种方法本质上差不多,毕竟loadClass()也会调用findClass(),但是从逻辑上讲我们最好不要直接修改loadClass()的内部逻辑。建议的做法是只在findClass()里重写自定义类的加载方法,根据参数指定类的名字,返回对应的Class对象的引用。

  • loadclass()这个方法是实现双亲委派模型逻辑的地方,擅自修改这个方法会导致模型被破坏,容易造成问题。因此我们最好是在双亲委派模型框架内进行小范围的改动,不破坏原有的稳定结构。同时,也避免了自己重写loadClass()方法的过程中必须写双亲委托的重复代码,从代码的复用性来看,不直接修改这个方法始终是比较好的选择。

  • 当编写好自定义类加载器后,便可以在程序中调用loadClass()方法来实现类加载操作。

代码示例:

public class MyClassLoad extends ClassLoader {

    private String byteCodePath;

    public MyClassLoad(String byteCodePath) {
        this.byteCodePath = byteCodePath;
    }

    @Override
    protected Class> findClass(String name) throws ClassNotFoundException {
        BufferedInputStream bufferedInputStream = null;
        ByteArrayOutputStream byteOutputStream = null;
        try {
            // 获取完整的字节码文件路径+class文件名
            String fileName = byteCodePath + name + ".class";

            // 获取一个输入流
            bufferedInputStream = new BufferedInputStream(new FileInputStream(fileName));

            // 获取一个输出流
            byteOutputStream = new ByteArrayOutputStream();
            // 具体读取数据并写出的过程
            int len;
            byte[] data = new byte[1024];
            while ((len = bufferedInputStream.read(data)) != -1) {
                byteOutputStream.write(data, 0, len);
            }
            // 将输出流转成数组
            byte[] byteCode = byteOutputStream.toByteArray();

            // 调用defineClass(),将字节数组转成Class实列
            Class> aClass = defineClass(null, byteCode, 0, byteCode.length);
            return aClass;

        } catch (IOException e) {
            e.printStackTrace();
        } finally {

            try {
                if (bufferedInputStream != null) {
                    bufferedInputStream.close();
                }

            } catch (IOException e) {
                e.printStackTrace();
            }
            try {
                if (byteOutputStream != null) {
                    byteOutputStream.close();
                }

            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        return null;
    }
}

测试类:

public class MyClassLoadTest {
    public static void main(String[] args) throws ClassNotFoundException {
        MyClassLoad myClassLoad = new MyClassLoad("D:/");
        Class> myClassLoadClass = myClassLoad.findClass("Demo1");
        System.out.println(myClassLoadClass.getClassLoader().getClass().getName());  System.out.println(myClassLoadClass.getClassLoader().getParent().getClass().getName()); 
    }
}

Java9新特性

为了保证兼容性,JDK9没有从根本上改变三层类加载器架构和双亲委派模型,但为了模块化系统的顺利运行,仍然发生了一些值得被注意的变动。

变化

  1. 扩展机制被移除,扩展类加载器由于向后兼容性的原因被保留,不过被重命名为平台类加载器(platform class loader)。可以通过classLoader的新方法getPlatformClassLoader()来获取。

    JDK9时基于模块化进行构建(原来的rt.jar和tools.jar被拆分成数十个JMOD文件),其中的Java类库就已天然地满足了可扩展的需求,那自然无须再保留\lib\ext目录,此前使用这个目录或者java.ext.dirs系统变量来扩展JDK功能的机制已经没有继续存在的价值了。

  2. 平台类加载器和应用程序类加载器都不再继承自java.net.URLClassLoader。

    现在启动类加载器、平台类加载器、应用程序类加载器全都继承于jdk.internal.loader.BuiltinClassLoader

d16877f0fedb80f156158adec3dc618d.png
jdk9类加载器
  1. 在Java9中,类加载器有了名称。该名称在构造方法中指定,可以通过getName()方法来获取。平台类加载器的名称是platform,应用类加载器的名称是app。类加载器的名称在调试与类加载器相关的问题时会非常有用。
  2. 启动类加载器现在是在jvm内部和java类库共同协作实现的类加载器(以前是C++实现),但为了与之前代码兼容,在获取启动类加载器的场景中仍然会返回null,而不会得到BootClassLoader实例。
  3. 类加载的委派关系也发生了变动。当平台及应用程序类加载器收到类加载请求,在委派给父加载器加载前,要先判断该类是否能够归属到某一个系统模块中,如果可以找到这样的归属关系,就要优先委派给负责那个模块的加载器完成加载。
56abb2369e498742e266774c3e49c2b3.png
不同jdk版本对比

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

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

相关文章

group by用法多个字段_select的用法

select的用法 --每个员工的所有信息 select * from emp; --每个人的部门编号,姓名,薪水 select empno, ename, sal from emp; --每个人的年薪 select ename, sal*12 from emp; --计算2*3的值 select 2*3 from emp; --计算2*3的值(dual) select 2*3 from …

计算机考试打字小作文,打字练习作文(通用5篇)

打字练习作文(通用5篇)导语:随着计算机在人们的生活中普及,敲键盘打字的速度就变成了人们努力的方向。下面是小编为大家整理的打字练习作文(通用5篇),欢迎阅读,希望大家能够喜欢。打字练习作文 篇1今天过得有些无聊,爸…

eclipse创建pojo_使用Eclipse Hibernate插件逐步为POJO域Java类和hbm自动生成代码

eclipse创建pojo概述: 在本教程中,我们将使用Eclipse Hibernate工具自动生成域对象和相应的hbm xml文件。 如果您正在处理大型或中型项目,并且开始时有超过5个以上的表,那么您可能会发现此插件是自动生成映射域对象java文件和相应…

c语言中x的n次方怎么表示_线性代数的本质及其在AI中的应用

线性代数是 AI 专家必须掌握的知识,这已不再是个秘密。如果不掌握应用数学这个领域,你永远就只能是「门外汉」。当然,学习线性代数道阻且长。数学,尤其是线性代数常与枯燥、复杂和毫无意义的事物联系起来。不过你还可以另辟蹊径。…

解码base64_linux C++ Base64编解码

Base64的由来目前Base64已经成为网络上常见的传输8Bit字节代码的编码方式之一。在做支付系统时,系统之间的报文交互都需要使用Base64对明文进行转码,然后再进行签名或加密,之后再进行(或再次Base64)传输。那么&#xf…

java的for循环取出数据只是拿到最后一个_新兴大数据分析榆中百合

新兴大数据分析榆中百合大数据流程从流程角度上看,整个大数据处理可分成4个主要步骤。处理流程图分为三层(数据采集层,数据存储与计算处理层,数据可视化):表2-1 系统环境系统版本Windows 10专业版(建议)LinuxCentOS 6.8 or CentO…

c++冒泡排序代码_【开源推荐】数据结构和算法必知必会的50个代码实现

最近GitHub上发现了个非常不错的项目,目前star 4000,项目主要讲数据结构和算法,有多种语言 50个代码实现。实现语言有c,c#,go,java,javascript,object-c,python&#xff…

android 抽屉_Android Studio之路,我们来了解一下Google官方Android开发工具

记得我的第一篇博客就是写Android Studio,但是现在看来还是有些粗糙了,所有重构了一下思路,覆写了一篇Google主推-Android开发利器——Android Studio,这可能是最全的AS教程!Android Studio,自Google2013年发布以来,就…

karaf osgi_在OSGi中为Karaf构建Camel-CXF REST服务–组播和聚合

karaf osgi请查看我在Karaf的OSGi中构建普通CXF服务(无Camel)的其他文章 。 这是有关如何 创建一个CXF REST服务 使用骆驼多播(并并行化)传入的请求 来自两个不同服务的源数据 汇总响应并 最后将合并结果作为JSON返回给最终…

酒店wifi代理服务器没有响应,wn10连接酒店wifi的登录界面无法弹出如何处理

通常情况下,我们在连接酒店wifi后都会出现登录验证界面。不过,最近一位windows10系统用户在连接wifi后打开网页却遇到无法显示登陆界面情况,该怎么办呢?接下来,就随小编一起看看wn10连接酒店wifi的登录界面无法弹出问题…

python词云安装什么库_python词云安装什么库

python词云需要安装wordcloud库。 安装方法: 在cmd使用pip install wordcloud命令即可安装。 wordcloud库把词云当作一个WordCloud对象:wordcloud.WordCloud()代表一个文本对应的词云。 可以根据文本中词语出现的频率等参数绘制词云。 示例:f…

怎么wps解除合并单元格_wps表格怎么锁定单元格

wps表格怎么锁定单元格呢?很多用户对此还不是很清楚,小编这里就给大家带来有关wps表格怎么锁定单元格的回答,希望能够对大家有所帮助。一、整个表格进行锁定1、同时按住CtrlA,选中整个单元格,如图2、选中以后&#xff…

挡土墙计算软件_广联达软件如何计算钢板止水带?

原创作者:张向荣1、什么是钢板止水带?混凝土结构中,地下室墙体施工中,如果底板和墙体分开浇筑,就需要留置施工缝。施工缝:施工缝并不是一种真实存在的“缝”,它只是因先浇筑混凝土超过初凝时间&…

网站图片多服务器选多大,网站上的图片一般多大合适

网站上的图片一般多大合适 内容精选换一换安装了SSL证书后,访问网站时,HTTPS比HTTP要多几次握手的时间,HTTPS协议握手阶段比较费时,同时还要进行RSA校验,因此使用了SSL证书后,相较于HTTP访问,访…

怎么在自己的网站上显示其它网站_自己做网站要怎么选域名?

域名是一种无形的资产注册一个好的域名能让你的网站更加容易取得成功,那么什么样的域名才是好域名呢?自己做网站域名要怎么选呢?1.好记的域名如果一个喜欢你网站的网友,电脑硬盘坏了丢失了收藏夹里所有的网址,或者在他人机器上上网&#xf…

微信朋友圈删除后服务器还有吗,删了的朋友圈还可以找回来吗

演示工具:手机型号:iphone12系统版本:ios14软件版本:微信7.0.20删除的朋友圈动态不能找回来。删除是从网络服务器上面把此信息删除了,是无法找回的,而已删除掉以后,好友那边此条动态也是同步删除…

mysql drop_mysql恢复drop掉的表

手贱drop了几个表,以为能从昨天的备份中恢复,结果发现最近两个月的备份都是空文件,因为备份脚本在两个月前改错了!难道就这样丢失两个月的数据?镇定镇定——先看看mysql的配置文件cat /etc/my.cnf发现如下字样说明有救…

修改网站首页批处理_几个神奇的批处理,万事不求人!

文字 | 配图 | 排版 |©老Y网站:www.youquhome.cn小白可能会问啥事批处理?批处理(Batch),也称为批处理脚本。是对某对象进行批量的处理,通常被认为是一种简化的脚本语言,它应用于DOS和Windows系统中。批处理文件的…

全境封锁服务器维护 2018,全境封锁全域事件2018年8月活动什么时候开始_全域事件怎么玩...

本文给大家带来全境封锁全域事件2018年8月活动时间介绍,全境封锁全域事件三进击即将到来,想要刷面具的不要错过哦。全域事件三:进击重新回归了!开始时间:8 月 27 日周一结束时间:9 月 2 日周日参加这一次的全域事件将会…

linux下部署mysql数据库连接_Linux远程部署MySQL数据库详细步骤

Linux远程部署MySQL数据库,供大家参考,具体内容如下1.0 安装 yum install lrzsz -y 命令(导入外界压缩包插件(已下载无视即可))1.1 将mysql压缩包拷贝到 /usr/local 文件目录下进行解压:1.2 将导入的Mysql压缩包进行解压,输入:tar…