分类 java 下的文章

HashMap 的实现原理

HashMap 概述

HashMap是基于哈希表的Map接口的非同步实现。此实现提供所有可选的映射操作,并允许使用null值和null键。此类不保证映射的顺序,特别是它不保证该顺序恒定不变。

此实现假定哈希函数将元素适当地分布在各桶之间,可为基本操作(get 和 put)提供稳定的性能。迭代collection视图所需要的时间与HashMap实例的“容量”(桶的数量)及其大小(键-值映射关系数)成比例。所以迭代性能很重要,则不要将初始容量设置得太高将加载因子设置得太低。

需要注意的是: Hashmap 不是同步的, 如果多个线程同时访问一个HashMap, 而其中至少一个线程从结构上(指添加或者删除一个或多个映射关系的任何操作)修改了,则必须保持外部同步,以防止对映射进行意外的非同步访问。

HashMap的数据结构
在Java编程语言中,最基本的结构就是两种,一个是数组,另一个是指针(引用), HashMap就是通过这连个数据结构进行实现。HashMap实际上是一个“链表散列”的数据结构,即数组和链表的结合体。

hashmap1.jpg

- 阅读剩余部分 -

Java 8 默认方法(Default Methods)

Posted by Ebn Zhang on December 20, 2015

Java 8 引入了新的语言特性——默认方法(Default Methods)。

Default methods enable new functionality to be added to the interfaces of libraries and ensure binary compatibility with code written for older versions of those interfaces.

默认方法允许您添加新的功能到现有库的接口中,并能确保与采用旧版本接口编写的代码的二进制兼容性。

默认方法是在接口中的方法签名前加上了 default 关键字的实现方法。

- 阅读剩余部分 -

java float加减运算精度丢失问题

计算货币的时候,使用double和float计算含有小数时,出现的结果不准确。google了一把网上的文档说明。

float和double只能用来作科学计算或者是工程计算,但在商业计算中我们要用java.math.BigDecimal,通过使用BigDecimal类可以解决上述问题,java的设计者给编程人员提供了一个很有用的类BigDecimal,他可以完善float和double类无法进行精确计算的缺憾

- 阅读剩余部分 -

JAVA守护线程(Daemon Thread)

post by Luochengor

在Java中有两类线程:用户线程 (User Thread)守护线程 (Daemon Thread)

所谓守护 线程,是指在程序运行的时候在后台提供一种通用服务的线程,比如垃圾回收线程就是一个很称职的守护者,并且这种线程并不属于程序中不可或缺的部分。因 此,当所有的非守护线程结束时,程序也就终止了,同时会杀死进程中的所有守护线程。反过来说,只要任何非守护线程还在运行,程序就不会终止。

用户线程和守护线程两者几乎没有区别,唯一的不同之处就在于虚拟机的离开:如果用户线程已经全部退出运行了,只剩下守护线程存在了,虚拟机也就退出了。 因为没有了被守护者,守护线程也就没有工作可做了,也就没有继续运行程序的必要了。

将线程转换为守护线程可以通过调用Thread对象的setDaemon(true)方法来实现。在使用守护线程时需要注意一下几点:

(1) thread.setDaemon(true)必须在thread.start()之前设置,否则会跑出一个IllegalThreadStateException异常。你不能把正在运行的常规线程设置为守护线程。
(2) 在Daemon线程中产生的新线程也是Daemon的。
(3) 守护线程应该永远不去访问固有资源,如文件、数据库,因为它会在任何时候甚至在一个操作的中间发生中断。

代码示例:

import java.util.concurrent.TimeUnit; 

/**
 *  守护线程
 */

public class Daemons {

    /**

     * @param args

     * @throws InterruptedException

     */

    public static void main(String[] args) throws InterruptedException {

        Thread d = new Thread(new Daemon());

        d.setDaemon(true); //必须在启动线程前调用

        d.start();

        System.out.println("d.isDaemon() = " + d.isDaemon() + ".");

        TimeUnit.SECONDS.sleep(1);

    }

}
 

class DaemonSpawn implements Runnable {

    public void run() {

        while (true) {

            Thread.yield();

        }

    }

}

class Daemon implements Runnable {

    private Thread[] t = new Thread[10];

    public void run() {

        for (int i=0; i<t.length; i++) {

            t[i] = new Thread(new DaemonSpawn());

            t[i].start();

            System.out.println("DaemonSpawn " + i + " started.");

        }

        for (int i=0; i<t.length; i++) {

            System.out.println("t[" + i + "].isDaemon() = " +

                    t[i].isDaemon() + ".");

        }

        while (true) {

            Thread.yield();

        }
    }
}

运行结果:

d.isDaemon() = true.

DaemonSpawn 0 started.

DaemonSpawn 1 started.

DaemonSpawn 2 started.

DaemonSpawn 3 started.

DaemonSpawn 4 started.

DaemonSpawn 5 started.

DaemonSpawn 6 started.

DaemonSpawn 7 started.

DaemonSpawn 8 started.

DaemonSpawn 9 started.

t[0].isDaemon() = true.

t[1].isDaemon() = true.

t[2].isDaemon() = true.

t[3].isDaemon() = true.

t[4].isDaemon() = true.
t[5].isDaemon() = true.
t[6].isDaemon() = true.
t[7].isDaemon() = true.
t[8].isDaemon() = true.
t[9].isDaemon() = true

以上结果说明了守护线程中产生的新线程也是守护线程。

如果将mian函数中的TimeUnit.SECONDS.sleep(1);注释掉,运行结果如下:

d.isDaemon() = true.

DaemonSpawn 0 started.

DaemonSpawn 1 started.

DaemonSpawn 2 started.

DaemonSpawn 3 started.

DaemonSpawn 4 started.

DaemonSpawn 5 started.

DaemonSpawn 6 started.

DaemonSpawn 7 started.

DaemonSpawn 8 started.

DaemonSpawn 9 started.

以上结果说明了如果用户线程已经全部退出运行了,只剩下守护线程存在了,虚拟机也就退出了。下面的例子也说明了这个问题。

代码示例:

import java.util.concurrent.TimeUnit;

 

/**

 * Finally shoud be always run ?

 */

public class DaemonsDontRunFinally {

 

    /**

     * @param args

     */

    public static void main(String[] args) {

        Thread t = new Thread(new ADaemon());

        t.setDaemon(true);

        t.start();

    }

}

 
class ADaemon implements Runnable {

 

    public void run() {

        try {

            System.out.println("start ADaemon...");

            TimeUnit.SECONDS.sleep(1);

        } catch (InterruptedException e) {

            System.out.println("Exiting via InterruptedException");

        } finally {

            System.out.println("This shoud be always run ?");

        }

    } 

}

运行结果:

start ADaemon...

如果将main函数中的t.setDaemon(true);注释掉,运行结果如下:

start ADaemon...

This shoud be always run ?

Java内存管理

java内存分配策略

三种策略:

a. 静态分配 b.栈式分配 c.堆式分配

对应的内存空间是:静态存储区(方法区)、栈区、堆区

静态存储区:主要放静态数据和常量。类在加载时就分配好,在程序运行期间都存在。
栈区:方法执行时,方法体内的局部变量都在栈上创建,并在方法执行结束时自动释放该占用的内存。

 因为栈内存分配运算内置于处理器的指令集中,效率很高,但是分配的内存容量有限。

堆区:称动态内存分配,通常就是指在程序运行时直接 new 出来的内存。这部分内存在不使用时将会由 Java 垃圾回收器来负 责回收。

- 阅读剩余部分 -