一些Java的知识点扫盲

来源:互联网 发布:python进阶书籍推荐 编辑:程序博客网 时间:2024/05/29 19:15

关于Java的一些知识点扫盲

fat-jar

fat-jar 或者叫uber-jar

the fat-jar is the jar,which contains classes and resources from all the libraries, on which your project depends

fat-jar 其实也是一个jar,他本省包含了当前工程中所有依赖包中的类,

            <plugin>                <groupId>org.apache.maven.plugins</groupId>                <artifactId>maven-jar-plugin</artifactId>                <configuration>                    <archive>                        <manifest>                            <mainClass>com.volshell.Starter</mainClass>                        </manifest>                    </archive>                </configuration>            </plugin>

如果只添加上面的配置会出现,“java.lang.NoClassDefFoundError”的错误日志。
需要添加onejar-plugin的配置。

            <plugin>                <groupId>com.jolira</groupId>                <artifactId>onejar-maven-plugin</artifactId>                <version>1.4.4</version>                <executions>                    <execution>                        <configuration>                            <onejarVersion>0.97</onejarVersion>                            <classifier>onejar</classifier>                        </configuration>                        <!--会在上面指定生成的jar的名称添加one-jar的后缀-->                        <goals>                            <goal>one-jar</goal>                        </goals>                    </execution>                </executions>            </plugin>

REFERENCES

  1. Packing your java application as one (or fat) jar

jar 下面的MATA-INF文件夹

该文件夹线面定义了关于本jar的配置信息,其中包括配置 application, extension, classloaders and services.

【They are used as building blocks for applications and extensions,is used to store package and extension configuration data, including security, versioning, extension and services.】

  • MANIFEST.MF
    在这里会标明包的一些版本信息等
    • Manifest-Version
    • Archiver-Version
    • Created-By
    • Built-By
    • Build-Jdk 版本
    • INDEX.LIST used by class loaders to speed up their class loading process。加快加载器对类的加载。
    • x.SF jar signature file for the jar.
    • x.DSA 对x.SF 签名文件的数字签名。
    • services/

补充说明

可以将静态文件添加到META-INF/目录下面:

META-INF/resources/pngs/logo.png
引用: /localhost/app_name/logo.png


References

  1. Java docs about jar
  2. StackOverFlow Q&A

关于类加载和初始化的一点随笔

在类编译之后,会产生一个Class对象,该对象存在于.class字节码中。接下来JVM的ClassLoader会对该.class字节码文件进行加载。实际上,ClassLoader并不是一个人在战斗,而是一个ClassLoader链。这种链是通过树状结构来组织的,而加载机制被称之为【父类委托加载机制】,简单地说,就是一个类的加载,首先要要交由父类加载器加载,这种父类加载器会沿着该树状结构向上追溯,直到顶层。父类加载器无法加载的时候,再交由子类加载器加载,直到找到相应的加载器。否则出现类无法加载。


  • 初始化过程

    加载 -> 链接(验证,准备,解析) -> 初始化

    • 加载(Loading),由类加载器执行,查找字节码,并创建一个Class对象(只是创建)生成字节码.class文件,执行static{}块;
    • 链接(Linking),验证字节码,为静态域分配存储空间(只是分配,并不初始化该存储空间),解析该类创建所需要的对其它类的应用;
    • 初始化(Initialization),首先执行 【静态初始化块static{},初始化静态变量,执行静态方法(如构造方法)】
  • 链接阶段

    • 验证(Verification),验证是保证二进制字节码在结构上的正确性,具体来说,工作包括检测类型正确性,接入属性正确性(public、private),检查final class 没有被继承,检查静态变量的正确性等。
    • 准备(Preparation),准备阶段主要是【创建静态域,分配空间,给这些域设默认值】,需要注意的是两点:一个是在准备阶段不会执行任何代码,仅仅是设置默认值,二个是这些默认值是这样分配的,原生类型全部设为0,如:float:0f,int 0, long 0L, boolean:0(布尔类型也是0),其它引用类型为null。
    • 解析(Resolution),解析的过程就是对类中的接口、类、方法、变量的符号引用进行解析并定位,解析成直接引用(符号引用就是编码是用字符串表示某个变量、接口的位置,直接引用就是根据符号引用翻译出来的地址),并保证这些类被正确的找到。【解析的过程可能导致其它的类被加载】。需要注意的是,根据不同的解析策略,这一步不一定是必须的,有些解析策略在解析时递归的把所有引用解析,这是early resolution,要求所有引用都必须存在;还有一种策略是late resolution,这也是Oracle 的JDK所采取的策略,即在类只是被引用了,还没有被真正用到时,并不进行解析,只有当真正用到了,才去加载和解析这个类。
  • 初始化
    所有java虚拟机实现必须在每个类或接口被java程序首次【主动使用】时才初始化。
    主动使用的场景:

    1. 创建类实例 new
    2. 访问类/接口的静态变量,或者对静态变量赋值的时候。访问常量(final)的时候,不会触发初始化,因为在编译器已经确定值。
    3. 调用静态方法。
    4. 反射 Class.forName()
    5. 初始化一个类的子类(相当于对父类的主动使用),会触发父类的初始化。
    6. 启动类 main所在类。

注:
1. 并不是所有的加载机制都是父类委托加载机制。

CompletionService 简介

  • 前言

当向Executor提交批处理任务时,并且希望在它们完成后获得结果,如果用FutureTask,你可以循环获取task,并用future.get()去获取结果,但是如果这个task没有完成,你就得阻塞在这里,这个实效性不高,其实在很多场合,其实你拿第一个任务结果时,此时结果并没有生成并阻塞,其实在阻塞在第一个任务时,第二个task的任务已经早就完成了,显然这种情况用future task不合适的,效率也不高。

  • 高效方法的实现

    首先创建一个list,保存所有的FutureTask,从list中遍历的每个Future对象并不一定处于完成状态,这时调用get()方法就会被阻塞住,如果系统是设计成每个线程完成后就能根据其结果继续做后面的事,这样对于处于list后面的但是先完成的线程就会增加了额外的等待时间。

  • JDK帮我们实现了

    CompletionService的实现是维护一个保存Future对象的BlockingQueue。只有当这个Future对象状态是结束的时候,才会加入到这个Queue中,take()方法其实就是Producer-Consumer中的Consumer。它会从Queue中取出Future对象,如果Queue是空的,就会阻塞在那里,直到有完成的Future对象加入到Queue中。
    CompletionService采取的是BlockingQueue

package com.volshell.concurrent;import java.util.Random;import java.util.concurrent.Callable;import java.util.concurrent.CompletionService;import java.util.concurrent.ExecutionException;import java.util.concurrent.ExecutorCompletionService;import java.util.concurrent.ScheduledExecutorService;import java.util.concurrent.ScheduledThreadPoolExecutor;import org.apache.commons.lang3.concurrent.BasicThreadFactory;/** * @author huiyao.wh created at 17/5/10 */public class CompletionServiceDemo {    private static class Task implements Callable<Integer> {        private int i;        Task(int i) {            this.i = i;        }        @Override        public Integer call() throws Exception {            Thread.sleep(new Random().nextInt(5000));            System.out.println(Thread.currentThread().getName() + "   " + i);             return i;        }    }    public void run() {        //至于为什么时候下面的方式实现线程池,文章最后有解释。        ScheduledExecutorService pool = new ScheduledThreadPoolExecutor(1, new BasicThreadFactory.Builder().namingPattern("example-schedule-pool-%d").daemon(true).build());        CompletionService<Integer> completionService = new ExecutorCompletionService<Integer>(pool);        try {            for (int i = 0; i < 10; i++) {                if (i % 3 == 0) {                    i = i * i;                }                completionService.submit(new CompletionServiceDemo.Task(i));            }            for (int i = 0; i < 10; i++) {                System.out.println(completionService.take().get());            }        } catch (InterruptedException e) {            e.printStackTrace();        } catch (ExecutionException e) {            e.printStackTrace();        } finally {            pool.shutdown();        }    }    public static void main(String[] args) {        new CompletionServiceDemo().run();    }}

为什么会这样

ScheduledExecutorService pool = new ScheduledThreadPoolExecutor(1, new BasicThreadFactory.Builder().namingPattern("example-schedule-pool-%d").daemon(true).build());CompletionService<Integer> completionService = new ExecutorCompletionService<Integer>(pool);
  • 之前的使用方式

    Executors.newFixedThreadPool/newSingleThreadPool/newCachedThreadPool/newScheduledThreadPool

  • 为什这样可以?

    说明:Executors各个方法的弊端:

    1.newFixedThreadPool和newSingleThreadExecutor:
      主要问题是堆积的请求处理队列可能会耗费非常大的内存,甚至OOM。
      
      
    2.newCachedThreadPool和newScheduledThreadPool:
      主要问题是线程数最大数是Integer.MAX_VALUE,可能会创建数量非常多的线程,甚至OOM。
      
      

结尾

对于最后提出的两种新的实现方式,不一定要完全使用这种方式来实现,在线程数量很少的情况下,使用上面的方式,并不会体现出他们的弊端,但是在大型应用中,线程数量动辄上千上万个,这样就不妙了。

2 0
原创粉丝点击