java的classLoader分析与jettty的WebAppClassLoader
classLoader,从名字就可以知道,用于加载class的东西。
我们知道在Java中,源文件是会被编译成class文件的,我们的程序的运行也是需要依赖这些编译成字节码的class文件,而这些字节码文件就必须要被classLoader加载到内存之后才能使用。。。如果classLoader无法加载到我们要用的类型的class文件,那么将会抛出classnodfound的异常。。。
先用一张图来整体描述一下java标准中定义的classLoader的层次吧:
在整个定义中,一共有两种类型的加载器:
(1)启动类加载器:Bootstrap ClassLoader,这个类加载器是使用c++语言实现的,它本身就是虚拟机的一部分
(2)其他的类加载器,这些类加载器都是用java语言实现的,独立于虚拟机外部,并且他们都继承自抽象类java.lang.ClassLoader
另外按照功能来分的话可以如下:
(1)启动类加载器,也就是Bootstrap ClassLoader,这个类加载器负责将存放在<JAVA_HOME>/lib目录中的类库加载到虚拟机的内存中,而且它还要识别名字,如果名字不符合,就算放在lib目录中,也不会被加载,这个加载器无法被 java程序所引用。。。
(2)扩展类加载器,Extension ClassLoader,这个加载器负责加载<JAVA_HOME>/lib/ext目录中的类库,也就是扩展类库,java程序中可以直接引用这个加载器。。。。
(3)应用程序加载器,Application ClassLoader,它用与负责加载用户类路径上的类库,java程序中可以直接使用这个加载器,而且如果在应用程序中没有自定义过自己的类加载器,那么一般情况下这个就是程序中的默认加载器。。。。也就是loadClass啥的默认都是它了。。。。
另外从上面的图形也可以看成一种层次关系,除了最顶层的启动类加载器之外,其余的加载器都必须要有自己的父加载器(这个是组合实现的,不是继承)。。
这个也就是java中定义的所谓的双亲委派模型:
如果一个类加载器收到了 类加载的请求,那么它首先不会自己去尝试加载这个类,而是将这个请求委派给自己的父加载器去完成,每一层的加载器都是,因此任何一个加载请求都会自动传送到顶层的启动类加载器中,只有当自己的父加载器反馈无法完成这个加载请求的时候,子加载器才会去尝试自己去加载。。。。
这个是一个非常重要的定义,因为它限定了java在api方面的统一,越基础的类那么就由越基础的加载器来加载。。。。
(补充:同一个class文件,如果被不同的加载器加载,那么他们将会不相等)
这种双亲委派模型是java中默认以及推荐的模型,我们一般自己实现classLoader的时候也需要遵守这种规范,但是在web容器中就需要打破这种双亲委派模型了。。。
主流的Javaweb服务器,如tomcat,jetty,weblogic,啥的都有自己定义自己的类加载器。。。
因为在同一个服务器上我们会部署多个web应用程序,那么就需要这些web应用程序代码之间实现相互的隔离,而且web程序的类库还需要与服务器之间隔离,这样web应用程序的部署才不会影响到web服务器。。。
那么在这里就需要打破双亲委派的模型了。。。而且这里会用到线程上下文类加载器。。。(Thread context classLoader)
上面说了这么多,都是对java的classLoader的方面知识的一些普及,因为其实自己在之前也对这方面的概念都了解很少。。。
那么接下来就可开始来分析jetty中定义的WebAppClassLoader了,它在jetty中用于负责加载web应用程序WEB-INF目录下lib以及classes里面的类库。。说白了就是用于加载web应用程序自己的源代码。。。
先来看看它定义的一些比较重要的属性吧:
- private String _name; //名字
- private WebAppContext _context; //其所关联的webcontext
- private ClassLoader _parent; //父classLoader
最开始的是当前loader名字,不知道为啥要有这个名字,感觉也没啥用....第二个参数就是当前classLoader所属的webcontext,这个属性就属于比较重要的了,,...。。。每一个web应用程序都会有一个属于自己的classlaoder,最后一个参数就是前面提到过的父classLoader,仿佛这里看起来是要用于实现上面说过的双亲委派,其实不然。。。
在我们自己定义的web应用程序中,一会用到其余的class代码,例如String,hashmap啥的,那么这里有一个parent,就可以保证可以在parent里面获取他们的class。。。。
那么接下来来看看它的构造函数吧:
- public WebAppClassLoader(WebAppContext context)
- throws IOException {
- this(null,context);
- }
- /* ------------------------------------------------------------ */
- /** Constructor.
- */
- //这里可以看出,如果在构建的时候没有提供父亲classLoader,那么将会默认将当前的线程classLoader作为当前的父classLoader
- public WebAppClassLoader(ClassLoader parent, WebAppContext context)
- throws IOException {
- super(new URL[]{},parent!=null?parent
- :(Thread.currentThread().getContextClassLoader()!=null?Thread.currentThread().getContextClassLoader()
- :(WebAppClassLoader.class.getClassLoader()!=null?WebAppClassLoader.class.getClassLoader()
- :ClassLoader.getSystemClassLoader())));
- _parent=getParent(); //指向当前的parent
- _context=context; //保存当前的context
- if (_parent==null)
- throw new IllegalArgumentException("no parent classloader!");
- if (context.getExtraClasspath()!=null) {
- addClassPath(context.getExtraClasspath());
- }
- }
其实这里一般情况下都是会调用第一个构造函数,第二个构造函数主要是要满足父类URLClassLoader的构造,它是java系统类库中的。。。而且到这里就能够知道其实这里默认是将当前的线程上下文classLoader指定为当前的parent,而这个线程上下文classLoader如果没有用户指定的话默认又将是前面提到过的appClassLoader,也就是用于加载用户代码的classLoader。。。。
接下来来看看它定义的loadClass方法吧:
- //加载类,因为每一个webContext 都会有一个自己的classLoader,所以也就是先了web应用程序代码之间的相互隔离
- protected synchronized Class loadClass(String name, boolean resolve) throws ClassNotFoundException {
- Class c= findLoadedClass(name); //首先在自己这里加载,我们自己定义的代码,引用的jar里面的代码都是在这里加载的
- ClassNotFoundException ex= null;
- boolean tried_parent= false;
- //如果父类加载器优先,或者这里加载的类型是系统类型,例如"java.","javax.servlet.","javax.xml.",那么由父类加载器来加载
- //不过webapp的代码父类加载器肯定是加载不倒的
- if (c == null && _parent!=null && (_context.isParentLoaderPriority() || isSystemPath(name)) ) {
- tried_parent= true;
- try {
- //这里的parent就是appclassLoader
- c= _parent.loadClass(name); //由父类加载,一些系统代码,例如hashMap,set啥的都在这里加载,还有jetty定义的也在这里加载,例如org.mortbay.servlet.NoJspServlet
- if (Log.isDebugEnabled())
- Log.debug("loaded " + c);
- } catch (ClassNotFoundException e) {
- ex= e;
- }
- }
- if (c == null) {
- try {
- c= this.findClass(name); //这里是从当前classLoader的路径下面去加载
- } catch (ClassNotFoundException e) {
- ex= e;
- }
- }
- if (c == null && _parent!=null && !tried_parent && !isServerPath(name) )
- c= _parent.loadClass(name);
- if (c == null)
- throw ex;
- if (resolve)
- resolveClass(c);
- if (Log.isDebugEnabled())
- Log.debug("loaded " + c+ " from "+c.getClassLoader());
- return c;
- }
这段代码其实还算是比较的简单吧。。。这里就可以知道指定parent的重要作用了。。。因为毕竟在我们自己的代码中也会有调用一些类库中的代码。。。而在parent中,却无法加载到我们自己定义的class文件。。。也就实现代码与jetty 服务器本身的隔离。。。。
又因为每一个web应用程序都有自己的classLoader,也就是先了在同一个服务器下,不同的app之间的代码隔离。。。
最后再来补充两个方法吧:
- //添加class或者jar的路径,如果是文件夹路径的话,那么路径应该以/ 结尾
- public void addClassPath(String classPath)
- throws IOException {
- if (classPath == null)
- return;
- StringTokenizer tokenizer= new StringTokenizer(classPath, ",;"); //有可能一次性传入多个classLoader
- while (tokenizer.hasMoreTokens()) {
- /*
- * file:/C:/Users/js/AppData/Local/Temp/Jetty_127_0_0_1_80_manager.war__manager__.24ly2g/webapp/WEB-INF/classes/
- file:/C:/Users/js/AppData/Local/Temp/Jetty_127_0_0_1_80_manager.war__manager__.24ly2g/webapp/WEB-INF/lib/aopalliance-1.0.jar
- file:/C:/Users/js/AppData/Local/Temp/Jetty_127_0_0_1_80_manager.war__manager__.24ly2g/webapp/WEB-INF/lib/commons-collections-3.1.jar
- */
- Resource resource= Resource.newResource(tokenizer.nextToken()); //获取当前路径的resource对象
- if (Log.isDebugEnabled())
- Log.debug("Path resource=" + resource);
- // Resolve file path if possible
- File file= resource.getFile(); //获取当前的文件
- if (file != null) {
- URL url= resource.getURL(); //获取url
- addURL(url); //这个是父类的方法,将会加载这个路径下的class或者jar资源
- } else {
- // Add resource or expand jar/
- if (!resource.isDirectory() && file == null)
- {
- InputStream in= resource.getInputStream();
- File tmp_dir=_context.getTempDirectory();
- if (tmp_dir==null)
- {
- tmp_dir = File.createTempFile("jetty.cl.lib",null);
- tmp_dir.mkdir();
- tmp_dir.deleteOnExit();
- }
- File lib= new File(tmp_dir, "lib");
- if (!lib.exists())
- {
- lib.mkdir();
- lib.deleteOnExit();
- }
- File jar= File.createTempFile("Jetty-", ".jar", lib);
- jar.deleteOnExit();
- if (Log.isDebugEnabled())
- Log.debug("Extract " + resource + " to " + jar);
- FileOutputStream out = null;
- try
- {
- out= new FileOutputStream(jar);
- IO.copy(in, out);
- }
- finally
- {
- IO.close(out);
- }
- URL url= jar.toURL();
- addURL(url);
- } else {
- URL url= resource.getURL();
- addURL(url); //添加class的搜索路径
- }
- }
- }
- }
- //添加jar包或压缩文件
- public void addJars(Resource lib) {
- if (lib.exists() && lib.isDirectory())
- {
- String[] files=lib.list(); //获取当前文件夹下面的所有文件
- //遍历下面的所有文件
- for (int f=0;files!=null && f<files.length;f++) {
- try {
- Resource fn=lib.addPath(files[f]); //获取这个文件的resource
- String fnlc=fn.getName().toLowerCase();
- //判断文件的扩展名是否正确
- if (fnlc.endsWith(".jar") || fnlc.endsWith(".zip")) {
- String jar=fn.toString();
- jar=StringUtil.replace(jar, ",", "%2C");
- jar=StringUtil.replace(jar, ";", "%3B");
- addClassPath(jar); //调用addClassPath来将jar包加载
- }
- } catch (Exception ex) {
- Log.warn(Log.EXCEPTION,ex);
- }
- }
- }
- }
这两个方法的定义应该从名字就能够知道是干嘛的。。用于加载jar包或者class文件的。。最终是通过父类的addUrl的方法来实现这些源文件的加载。。。
好了,到这里jetty中定义的webappclassLoader就算是比较清楚了。。不过比较遗憾没有去分析它的父类URLClassLoader,从而对java的整个classLoader有更深的了解。。以后有机会的话看看吧。。
这样子就可以开始看jetty中如何创建webContext以及启动这些web应用程序了。。。
classLoader,从名字就可以知道,用于加载class的东西。
我们知道在Java中,源文件是会被编译成class文件的,我们的程序的运行也是需要依赖这些编译成字节码的class文件,而这些字节码文件就必须要被classLoader加载到内存之后才能使用。。。如果classLoader无法加载到我们要用的类型的class文件,那么将会抛出classnodfound的异常。。。
先用一张图来整体描述一下java标准中定义的classLoader的层次吧:
在整个定义中,一共有两种类型的加载器:
(1)启动类加载器:Bootstrap ClassLoader,这个类加载器是使用c++语言实现的,它本身就是虚拟机的一部分
(2)其他的类加载器,这些类加载器都是用java语言实现的,独立于虚拟机外部,并且他们都继承自抽象类java.lang.ClassLoader
另外按照功能来分的话可以如下:
(1)启动类加载器,也就是Bootstrap ClassLoader,这个类加载器负责将存放在<JAVA_HOME>/lib目录中的类库加载到虚拟机的内存中,而且它还要识别名字,如果名字不符合,就算放在lib目录中,也不会被加载,这个加载器无法被 java程序所引用。。。
(2)扩展类加载器,Extension ClassLoader,这个加载器负责加载<JAVA_HOME>/lib/ext目录中的类库,也就是扩展类库,java程序中可以直接引用这个加载器。。。。
(3)应用程序加载器,Application ClassLoader,它用与负责加载用户类路径上的类库,java程序中可以直接使用这个加载器,而且如果在应用程序中没有自定义过自己的类加载器,那么一般情况下这个就是程序中的默认加载器。。。。也就是loadClass啥的默认都是它了。。。。
另外从上面的图形也可以看成一种层次关系,除了最顶层的启动类加载器之外,其余的加载器都必须要有自己的父加载器(这个是组合实现的,不是继承)。。
这个也就是java中定义的所谓的双亲委派模型:
如果一个类加载器收到了 类加载的请求,那么它首先不会自己去尝试加载这个类,而是将这个请求委派给自己的父加载器去完成,每一层的加载器都是,因此任何一个加载请求都会自动传送到顶层的启动类加载器中,只有当自己的父加载器反馈无法完成这个加载请求的时候,子加载器才会去尝试自己去加载。。。。
这个是一个非常重要的定义,因为它限定了java在api方面的统一,越基础的类那么就由越基础的加载器来加载。。。。
(补充:同一个class文件,如果被不同的加载器加载,那么他们将会不相等)
这种双亲委派模型是java中默认以及推荐的模型,我们一般自己实现classLoader的时候也需要遵守这种规范,但是在web容器中就需要打破这种双亲委派模型了。。。
主流的Javaweb服务器,如tomcat,jetty,weblogic,啥的都有自己定义自己的类加载器。。。
因为在同一个服务器上我们会部署多个web应用程序,那么就需要这些web应用程序代码之间实现相互的隔离,而且web程序的类库还需要与服务器之间隔离,这样web应用程序的部署才不会影响到web服务器。。。
那么在这里就需要打破双亲委派的模型了。。。而且这里会用到线程上下文类加载器。。。(Thread context classLoader)
上面说了这么多,都是对java的classLoader的方面知识的一些普及,因为其实自己在之前也对这方面的概念都了解很少。。。
那么接下来就可开始来分析jetty中定义的WebAppClassLoader了,它在jetty中用于负责加载web应用程序WEB-INF目录下lib以及classes里面的类库。。说白了就是用于加载web应用程序自己的源代码。。。
先来看看它定义的一些比较重要的属性吧:
- private String _name; //名字
- private WebAppContext _context; //其所关联的webcontext
- private ClassLoader _parent; //父classLoader
最开始的是当前loader名字,不知道为啥要有这个名字,感觉也没啥用....第二个参数就是当前classLoader所属的webcontext,这个属性就属于比较重要的了,,...。。。每一个web应用程序都会有一个属于自己的classlaoder,最后一个参数就是前面提到过的父classLoader,仿佛这里看起来是要用于实现上面说过的双亲委派,其实不然。。。
在我们自己定义的web应用程序中,一会用到其余的class代码,例如String,hashmap啥的,那么这里有一个parent,就可以保证可以在parent里面获取他们的class。。。。
那么接下来来看看它的构造函数吧:
- public WebAppClassLoader(WebAppContext context)
- throws IOException {
- this(null,context);
- }
- /* ------------------------------------------------------------ */
- /** Constructor.
- */
- //这里可以看出,如果在构建的时候没有提供父亲classLoader,那么将会默认将当前的线程classLoader作为当前的父classLoader
- public WebAppClassLoader(ClassLoader parent, WebAppContext context)
- throws IOException {
- super(new URL[]{},parent!=null?parent
- :(Thread.currentThread().getContextClassLoader()!=null?Thread.currentThread().getContextClassLoader()
- :(WebAppClassLoader.class.getClassLoader()!=null?WebAppClassLoader.class.getClassLoader()
- :ClassLoader.getSystemClassLoader())));
- _parent=getParent(); //指向当前的parent
- _context=context; //保存当前的context
- if (_parent==null)
- throw new IllegalArgumentException("no parent classloader!");
- if (context.getExtraClasspath()!=null) {
- addClassPath(context.getExtraClasspath());
- }
- }
其实这里一般情况下都是会调用第一个构造函数,第二个构造函数主要是要满足父类URLClassLoader的构造,它是java系统类库中的。。。而且到这里就能够知道其实这里默认是将当前的线程上下文classLoader指定为当前的parent,而这个线程上下文classLoader如果没有用户指定的话默认又将是前面提到过的appClassLoader,也就是用于加载用户代码的classLoader。。。。
接下来来看看它定义的loadClass方法吧:
- //加载类,因为每一个webContext 都会有一个自己的classLoader,所以也就是先了web应用程序代码之间的相互隔离
- protected synchronized Class loadClass(String name, boolean resolve) throws ClassNotFoundException {
- Class c= findLoadedClass(name); //首先在自己这里加载,我们自己定义的代码,引用的jar里面的代码都是在这里加载的
- ClassNotFoundException ex= null;
- boolean tried_parent= false;
- //如果父类加载器优先,或者这里加载的类型是系统类型,例如"java.","javax.servlet.","javax.xml.",那么由父类加载器来加载
- //不过webapp的代码父类加载器肯定是加载不倒的
- if (c == null && _parent!=null && (_context.isParentLoaderPriority() || isSystemPath(name)) ) {
- tried_parent= true;
- try {
- //这里的parent就是appclassLoader
- c= _parent.loadClass(name); //由父类加载,一些系统代码,例如hashMap,set啥的都在这里加载,还有jetty定义的也在这里加载,例如org.mortbay.servlet.NoJspServlet
- if (Log.isDebugEnabled())
- Log.debug("loaded " + c);
- } catch (ClassNotFoundException e) {
- ex= e;
- }
- }
- if (c == null) {
- try {
- c= this.findClass(name); //这里是从当前classLoader的路径下面去加载
- } catch (ClassNotFoundException e) {
- ex= e;
- }
- }
- if (c == null && _parent!=null && !tried_parent && !isServerPath(name) )
- c= _parent.loadClass(name);
- if (c == null)
- throw ex;
- if (resolve)
- resolveClass(c);
- if (Log.isDebugEnabled())
- Log.debug("loaded " + c+ " from "+c.getClassLoader());
- return c;
- }
这段代码其实还算是比较的简单吧。。。这里就可以知道指定parent的重要作用了。。。因为毕竟在我们自己的代码中也会有调用一些类库中的代码。。。而在parent中,却无法加载到我们自己定义的class文件。。。也就实现代码与jetty 服务器本身的隔离。。。。
又因为每一个web应用程序都有自己的classLoader,也就是先了在同一个服务器下,不同的app之间的代码隔离。。。
最后再来补充两个方法吧:
- //添加class或者jar的路径,如果是文件夹路径的话,那么路径应该以/ 结尾
- public void addClassPath(String classPath)
- throws IOException {
- if (classPath == null)
- return;
- StringTokenizer tokenizer= new StringTokenizer(classPath, ",;"); //有可能一次性传入多个classLoader
- while (tokenizer.hasMoreTokens()) {
- /*
- * file:/C:/Users/js/AppData/Local/Temp/Jetty_127_0_0_1_80_manager.war__manager__.24ly2g/webapp/WEB-INF/classes/
- file:/C:/Users/js/AppData/Local/Temp/Jetty_127_0_0_1_80_manager.war__manager__.24ly2g/webapp/WEB-INF/lib/aopalliance-1.0.jar
- file:/C:/Users/js/AppData/Local/Temp/Jetty_127_0_0_1_80_manager.war__manager__.24ly2g/webapp/WEB-INF/lib/commons-collections-3.1.jar
- */
- Resource resource= Resource.newResource(tokenizer.nextToken()); //获取当前路径的resource对象
- if (Log.isDebugEnabled())
- Log.debug("Path resource=" + resource);
- // Resolve file path if possible
- File file= resource.getFile(); //获取当前的文件
- if (file != null) {
- URL url= resource.getURL(); //获取url
- addURL(url); //这个是父类的方法,将会加载这个路径下的class或者jar资源
- } else {
- // Add resource or expand jar/
- if (!resource.isDirectory() && file == null)
- {
- InputStream in= resource.getInputStream();
- File tmp_dir=_context.getTempDirectory();
- if (tmp_dir==null)
- {
- tmp_dir = File.createTempFile("jetty.cl.lib",null);
- tmp_dir.mkdir();
- tmp_dir.deleteOnExit();
- }
- File lib= new File(tmp_dir, "lib");
- if (!lib.exists())
- {
- lib.mkdir();
- lib.deleteOnExit();
- }
- File jar= File.createTempFile("Jetty-", ".jar", lib);
- jar.deleteOnExit();
- if (Log.isDebugEnabled())
- Log.debug("Extract " + resource + " to " + jar);
- FileOutputStream out = null;
- try
- {
- out= new FileOutputStream(jar);
- IO.copy(in, out);
- }
- finally
- {
- IO.close(out);
- }
- URL url= jar.toURL();
- addURL(url);
- } else {
- URL url= resource.getURL();
- addURL(url); //添加class的搜索路径
- }
- }
- }
- }
- //添加jar包或压缩文件
- public void addJars(Resource lib) {
- if (lib.exists() && lib.isDirectory())
- {
- String[] files=lib.list(); //获取当前文件夹下面的所有文件
- //遍历下面的所有文件
- for (int f=0;files!=null && f<files.length;f++) {
- try {
- Resource fn=lib.addPath(files[f]); //获取这个文件的resource
- String fnlc=fn.getName().toLowerCase();
- //判断文件的扩展名是否正确
- if (fnlc.endsWith(".jar") || fnlc.endsWith(".zip")) {
- String jar=fn.toString();
- jar=StringUtil.replace(jar, ",", "%2C");
- jar=StringUtil.replace(jar, ";", "%3B");
- addClassPath(jar); //调用addClassPath来将jar包加载
- }
- } catch (Exception ex) {
- Log.warn(Log.EXCEPTION,ex);
- }
- }
- }
- }
这两个方法的定义应该从名字就能够知道是干嘛的。。用于加载jar包或者class文件的。。最终是通过父类的addUrl的方法来实现这些源文件的加载。。。
好了,到这里jetty中定义的webappclassLoader就算是比较清楚了。。不过比较遗憾没有去分析它的父类URLClassLoader,从而对java的整个classLoader有更深的了解。。以后有机会的话看看吧。。
这样子就可以开始看jetty中如何创建webContext以及启动这些web应用程序了。。。
java的classLoader分析与jettty的WebAppClassLoader的更多相关文章
- 【JVM】深度分析Java的ClassLoader机制(源码级别)
原文:深度分析Java的ClassLoader机制(源码级别) 为了更好的理解类的加载机制,我们来深入研究一下ClassLoader和他的loadClass()方法. 源码分析 public abst ...
- java的classLoader原理理解和分析
java的classLoader原理理解和分析 学习了:http://blog.csdn.net/tangkund3218/article/details/50088249 ClassNotFound ...
- [Java类加载器]Java中classLoader浅析.
本文为在公司内部TD上写的一篇小文, 主要讲解java中classLoader基础知识, 现在拿来这里分享一下. 一.问题 请在Eclipse中新建如下类,并运行它: 1 package java.l ...
- 常用 Java 静态代码分析工具的分析与比较
常用 Java 静态代码分析工具的分析与比较 简介: 本文首先介绍了静态代码分析的基 本概念及主要技术,随后分别介绍了现有 4 种主流 Java 静态代码分析工具 (Checkstyle,FindBu ...
- Java线程问题分析定位
Java线程问题分析定位 分析步骤: 1.使用top命令查看系统资源占用情况,发现Java进程占用大量CPU资源,PID为11572: 2.显示进程详细列表命令:ps -mp 11572 -o THR ...
- java内存溢出分析(二)
我们继续java内存溢出分析(一)的分析,点击Details>按钮,显示如下图,我们发现有一个对象数量达到280370216个,再点击其中的List objects 点击后,显示下图 至此,我们 ...
- 性能分析之-- JAVA Thread Dump 分析综述
性能分析之-- JAVA Thread Dump 分析综述 一.Thread Dump介绍 1.1什么是Thread Dump? Thread Dump是非常有用的诊断Java应用问题的工 ...
- java初学的分析
java初学的分析第一阶段:入门阶段学习目标:简单项目开发学习内容:1.Java入门书籍,Java基础知识.关于Java入门级的书,给大家推荐过<Java编程思想>.<Java核心技 ...
- Java基础—ClassLoader的理解
##默认的三个类加载器 Java默认是有三个ClassLoader,按层次关系从上到下依次是: - Bootstrap ClassLoader - Ext ClassLoader - System C ...
随机推荐
- 第84讲:Scala中List和ListBuffer设计实现思考
今天来学习了scala中的list和ListBuffer scala list 内部很多操作是listbuffer做的,因为改变元素,listbuffer非常高效,tl是var类型的 ,但是他属于s ...
- hdu 4542 打表+含k个约数最小数
http://acm.hdu.edu.cn/showproblem.php?pid=4542 给出一个数K和两个操作 如果操作是0,就求出一个最小的正整数X,满足X的约数个数为K. 如果操作是1,就求 ...
- Excel函数vlookup
最近整理业务文档,需要用到excel,按照教程,操作了20来分钟,却得不到结果. 看了视频,才知道,vlookup仅限关联选中区域的第一列关联,把要关联的行拷贝到第一列,解决. https://www ...
- Android-Kotlin-抽象类与多态的表现
选择包名,然后右键: 选择Class类型,会有class: 选择File类型,不会自动有class: 目录结构: 定义描述抽象类 Person人类: package cn.kotlin.kotlin ...
- Matlab中函数句柄@的作用及介绍
问:f=@(x)acos(x)表示什么意思?其中@代表什么?答:表示f为函数句柄,@是定义句柄的运算符.f=@(x)acos(x) 相当于建立了一个函数文件:% f.mfunction y=f(x) ...
- SharePoint 列表中增加列编辑功能菜单
需求描述 在企业的部署中,经常将SharePoint和TFS集成在一起,两个系统之间相互读取数据,展现开发进度.在TFS 2018之前版本中,由于TFS的门户定制功能有限,用户比较喜欢使用ShareP ...
- jQuery的addClass,removeClass和toggleClass方法
jQuery的addClass,removeClass和toggleClass方法,最后一个方法在某一情形之下,可以替代前面2个方法. 第一个方法addClass为元素添加一个class. 第二个方法 ...
- Class和普通js构造函数的区别
Class 在语法上更加贴合面向对象的写法 Class 实现继承更加易读.易理解 更易于写 java 等后端语言的使用 本质还是语法糖,使用 prototype Class语法 typeof Math ...
- win10安装Ubuntu双系统
1.软碟通做启动盘,不要用easyBCD,比较麻烦 2.windows10中取消选择"启用快速启动(推荐)" 3.压缩出空白卷 4.重启时按F12 5.在bios中将boot pr ...
- centoos 安装hadoop集群
环境准备 两台centoos系统服务器 H30(192.168.3.238) H31(192.168.3.237) H30为master,H31为slave,slave后续还可以再加机器: 先通过xs ...