Cloneable接口

基本类型:存放在栈空间中。
引用类型:存放在堆空间中。

 

如果类没有实现Cloneable接口,调用类对象的clone方法会抛出CloneNotSupportedException,Object提供的clone方法是浅度复制的。

Demo:

输出结果:name is: john, age is: 20

因为clone后,clonePerson的name是引用类型,所以当原name的值变化后,clonePerson的值也会变,age是基本类型,clone后不变。(引用类似于指针,变化后指向堆中不同的地方。)

前端开发

 前端开发环境

环境搭建


知识储备

  • velocity——java后端模板
  • fis3——百度开发的,前端工程化工具
  • jello——基于Java的前端集成解决方案,就当他是fis3好了
  • jQuery——js基础api
  • bootstrap——主题+组件、帮助迅速开发样式
  • less——css预处理语言

前端目录介绍

目录/文件名 作用
page 所有页面入口模板
resource 运营资源目录,本地测试用
static 静态、公用资源目录
test 本地测试用,模拟后端数据(静态 + ajax接口)
widget 页面片断,用于组成页面的组件 + 模块
.gitignore git忽略文件配置,如果用的svn此文件没用
fis-conf.js fis3配置文件,js/css/图片优化打包、服务器部署等
README.md 说明文档,可能也没写什么有用的东西
server.conf 本地测试用,路由配置

一、fis3环境配置

参考:http://fis.baidu.com/fis3/docs/beginning/install.html

  1. 安装node(包含npm),版本号:4.x(5.x不兼容)
    http://nodejs.cn/
  2. 安装fis3
    1. npm install -g fis3

    如果国外源安装不上,试试淘宝的源

    1. npm install -g fis3 --disturl=http://registry.npm.taobao.org/mirrors/node --registry=http://registry.npm.taobao.org
  3. 执行 fis3 -v 看看是否安装成功,如果你看到一个老漂亮的FIS你就成功了
  4. 安装项目fis插件
    1. npm install -g fis3-jello@1.0.8
    2. npm install -g fis3-server-jello
    3. npm install -g fis-parser-less@0.1.3
    4. npm install -g fis3-hook-amd@0.1.14
    5. npm install -g fis3-parser-reactjs@1.0.0
    6. npm install -g fis3-preprocessor-extlang@1.1.7
    7. npm install -g fis-parser-babel-5.x
    • fis3-parser-less是网友贡献的插件,有bug,不要安装

二、本地调试

参考:https://github.com/fex-team/jello

  1. 命令行cd到前端开发目录web
  2. 启动fis3本地服务器,使用8081端口
    1. fis3 server start -p 8081 --type jello
  3. 执行发布命令
    1. fis3 release -w
  4. 打开http://127.0.0.1:8081

三、与后端联调开发(已经不用这种方式了)

  1. 配置前端开发目录web根目录下的fis-conf.js文件,找到以下一坨配置代码
    1. fis.media('rd').match('*', {
    2. deploy: fis.plugin('http-push', {
    3. receiver: 'http://127.0.0.1:8080/upload',
    4. // receiver: 'http://172.20.198.179:8080/upload',
    5. to: '/Users/erin/new-work/laosiji/chosengamer/backend/spring-mongo-demo/target/demo-1.0.0' // 注意这个是指的是测试机器的路径,而非本地机器
    6. })
    7. });

    receiver改成 http://<后端局域网ip>:8080/upload
    to改成后端 spring-mongo-demo/target/demo-1.0.0 目录,后端是window机器记得加盘符

  2. 在确保后端项目启动的前提下,在web目录下执行命令行
    1. fis3 release rd
  3. 访问http://<后端局域网ip>:8080/

其他——前端推荐使用sublime编辑器

  1. 安装sublime3
  2. 从菜单 ViewShow Console 或者 ctrl + ~ 快捷键,调出console。将以下 Python 代码粘贴进去并 enter 执行
    1. import urllib.request,os; pf = 'Package Control.sublime-package'; ipp = sublime.installed_packages_path(); urllib.request.install_opener( urllib.request.build_opener( urllib.request.ProxyHandler()) ); open(os.path.join(ipp, pf), 'wb').write(urllib.request.urlopen( 'http://sublime.wbond.net/' + pf.replace(' ','%20')).read())
  3. ctrl + shift + p 并在输入框输入 ip (install package的缩写), enter 安装
  4. 在输入框输入 velocityenter 安装默认插件,用于高亮.vm文件
  5. 重复步骤3,在输入框输入 lessenter 安装默认插件,用于高亮.less文件

前端页面开发流程


本文以一个todo-list页面为?:
页面url为/demo/todo-list,实现对list增删改操作,布局使用老司机的通用页头页尾。

1. 创建本地路由

修改根目录下的server.conf文件,增加路由条目

  1. rewrite ^\/demo\/todo-list$ /page/demo/todo-list

2. 创建页面vm

根据路由配置,在/page/demo目录下创建todo-list.vm

Java反射

Demo:

输出结果:

name=audi,color = red,price = 1,000

 

类装载器ClassLoader

类装载器工作机制

类装载器就是寻找类的节码文件并构造出类在JVM内部表示对象的组件。在Java中,类装载器把一个类装入JVM中,要经过以下步骤:

      [1.]装载:查找和导入Class文件;
      [2.]链接:执行校验、准备和解析步骤,其中解析步骤是可以选择的;
          [2.1]校验:检查载入Class文件数据的正确性;
          [2.2]准备:给类的静态变量分配存储空间;
          [2.3]解析:将符号引用转成直接引用;
    [3.]初始化:对类的静态变量、静态代码块执行初始化工作。

 

ClassLoader重要方法

在Java中,ClassLoader是一个抽象类,位于java.lang包中。下面对该类的一些重要接口方法进行介绍:

    •   Class loadClass(String name)
    • name参数指定类装载器需要装载类的名字,必须使用全限定类名,如com.baobaotao. beans.Car。该方法有一个重载方法loadClass(String name ,boolean resolve),resolve参数告诉类装载器是否需要解析该类。在初始化类之前,应考虑进行类解析的工作,但并不是所有的类都需要解析,如果JVM只需要知道该类是否存在或找出该类的超类,那么就不需要进行解析。

    • Class defineClass(String name, byte[] b, int off, int len)
    • 将类文件的字节数组转换成JVM内部的java.lang.Class对象。字节数组可以从本地文件系统、远程网络获取。name为字节数组对应的全限定类名。

    •   Class findSystemClass(String name)
    • 从本地文件系统载入Class文件,如果本地文件系统不存在该Class文件,将抛出ClassNotFoundException异常。该方法是JVM默认使用的装载机制。

    •   Class findLoadedClass(String name)
    • 调用该方法来查看ClassLoader是否已装入某个类。如果已装入,那么返回java.lang.Class对象,否则返回null。如果强行装载已存在的类,将会抛出链接错误。

  •   ClassLoader getParent()
  • 获取类装载器的父装载器,除根装载器外,所有的类装载器都有且仅有一个父装载器,ExtClassLoader的父装载器是根装载器,因为根装载器非Java编写,所以无法获得,将返回null。

 

Java反射机制

Class反射对象描述类语义结构,可以从Class对象中获取构造函数、成员变量、方法类等类元素的反射对象,并以编程的方式通过这些反射对象对目标类对象进行操作。这些反射对象类在java.reflect包中定义,下面是最主要的三个反射类:

    • Constructor:类的构造函数反射类,通过Class#getConstructors()方法可以获得类的所有构造函数反射对象数组。在JDK5.0中,还可以通过getConstructor(Class… parameterTypes)获取拥有特定入参的构造函数反射对象。Constructor的一个主要方法是new Instance(Object[] initargs),通过该方法可以创建一个对象类的实例,相当于new关键字。在JDK5.0中该方法演化为更为灵活的形式:newInstance (Object… initargs)。
    •  Method:类方法的反射类,通过Class#getDeclaredMethods()方法可以获取类的所有方法反射类对象数组Method[]。在JDK5.0中可以通过getDeclaredMethod(String name, Class… parameterTypes)获取特定签名的方法,name为方法名;Class…为方法入参类型列表。Method最主要的方法是invoke(Object obj, Object[] args),obj表示操作的目标对象;args为方法入参。在JDK 5.0中,该方法的形式调整为invoke(Object obj, Object… args)。此外,Method还有很多用于获取类方法更多信息的方法:
    • 1)Class getReturnType():获取方法的返回值类型;

            2)Class[] getParameterTypes():获取方法的入参类型数组;
            3)Class[] getExceptionTypes():获取方法的异常类型数组;
          4)Annotation[][] getParameterAnnotations():获取方法的注解信息,JDK 5.0中的新方法;

  •  Field:类的成员变量的反射类,通过Class#getDeclaredFields()方法可以获取类的成员变量反射对象数组,通过Class#getDeclaredField(String name)则可获取某个特定名称的成员变量反射对象。Field类最主要的方法是set(Object obj, Object value),obj表示操作的目标对象,通过value为目标对象的成员变量设置值。如果成员变量为基础类型,用户可以使用Field类中提供的带类型名的值设置方法,如setBoolean(Object obj, boolean value)、setInt(Object obj, int value)等。

摘录:http://www.iteye.com/topic/1123081

Singleton单例模式

Singleton(单例模式):

单例模式用来保证整个程序中某个实例有且只有一个。

单例模式有两种构建方式:

  • 饿汉方式:用static修饰,在类装载时构建。不适用于初始化时构造器非常耗时的类。
  • 懒汉方式:指全局的单例实例只有在第一次被使用时才构建,延迟初始化。

1.饿汉式单例类

public class TestSingleton
 
    private static TestSingleton instance = new TestSingleton();
 
    //把默认的构造方法设置为私有的,这样外界就无法通过构造方法来创建实例
    private TestSingleton() {}
 
    //静态方法,用来返回类的唯一实例
public static TestSingleton getInstance(){
return instance;
}
}
  • 饿汉模式因为是用static修饰,在类装载时构建,所以会导致加载类时比较慢,如果构造器内的方法比较耗时,则加载过程会比较长。
  • 但饿汉模式是线程安全的,运行时获取对象速度比较快。

2.懒汉式单例类

public class TestSingleton {
    //把默认的构造方法设置为私有的,这样外界就无法通过构造方法来创建实例
    private TestSingleton() {
    }

    private static TestSingleton instance = null;

    //静态方法,用来返回类的唯一实例,因为用synchronized加锁力度太大,所以使用double check的方式。
    public static TestSingleton getInstance() {
        if (instance == null) {
            synchronized (TestSingleton.class) {
                if (instance == null) {

                    //instance在初始化过程中,可能已经不为null,所以加一临时变量t,确保初始化完成后再赋值给instance。
                     TestSingleton t = new TestSingleton();
                     instance = t;
                 }
            }
        }
        return instance;
        }
}

其中instance = new TestSingleton()这句,这并非是一个原子操作,事实上在 JVM 中这句话大概做了下面 3 件事情:

  1. 给 instance 分配内存
  2. 调用TestSingleton 的构造函数来初始化成员变量,形成实例
  3. 将instance对象指向分配的内存空间(执行完这步 instance才是非 null 了)

但是在 JVM 的即时编译器中存在指令重排序的优化。也就是说上面的第二步和第三步的顺序是不能保证的,最终的执行顺序可能是 1-2-3 也可能是 1-3-2。如果是后者,则在 3 执行完毕、2 未执行之前,被线程二抢占了,这时 instance 已经是非 null 了(但却没有初始化),所以线程二会直接返回 instance,然后使用,就会报错,所以我们使用一个临时变量,确保初始化完成后再赋值给instance。

  • 懒汉模式的特点是加载类时比较快,但是在运行时获取对象的速度比较慢,线程不安全, 懒汉式如果在创建实例对象时不加上synchronized则会导致对象的访问不是线程安全的。

ArrayList和Vector ArrayList和LinkedList

ArrayList、Vector和LinkedList都实现了List类。

 

ArrayList和Vector:

  •  Vector的方法都是同步的(Synchronized),是线程安全的(thread-safe),而ArrayList的方法不是;
  • ArrayList性能比Vector好(因为Vector是线程安全的)
  • 两者都是采用的线性连续空间存储元素,但是当Vector或ArrayList中的元素超过它的初始大小时,Vector会将它的容量翻倍,而ArrayList只增加50%的大小。

ArrayList和LinkedList:

  • ArrayList的内部实现是基于内部数组Object[],所以从概念上讲,它更象数组,适合随机访问和遍历;
  • LinkedList是用链表结构存储数据的,很适合数据的动态插入和删除,随机访问和遍历速度比较慢。

Hash(散列)

  • 数组与链表数据结构存储数据的方式:
因为数组删除、插入操作时,同时需要操作后面的元素,所以增、删效率较低,而链表的结构导致了查找的效率较低,我们是否可以构造一个关系,使得待存储的集合{a,b,c,d,e,f}中的任意一个元素通过这个关系都能找到一个指定的索引值,所以现在假设有一种方法f,可以将各个元素和存储位置对应起来,那么这个方法f就是hash函数(散列函数)。

根据上图我们知道原有集合中的元素被散列之后所得到的有序对的集合为{(d,0),(b,2),(c,2),(a,3),(f,4),(e,5)}。其中,散列表中的1号槽为空,表示经过散列函数作用之后,原集合中没有任何元素被散列至该位置,而2号槽中则存在两个元素,将两个不同元素散列至相同位置的情形我们称为碰撞(Collision)。【hashMap中碰撞发生时,用链表进行存储,存储结构中包含key和value,当进行查找时,先查找hashCode找到对应槽(桶?bucket?),然后在链表中根据key进行查找value】

 

  • 常用的散列函数:
    1、除余法
    顾名思义,除余法就是用关键码x除以M(往往取散列表长度),并取余数作为散列地址。除余法几乎是最简单的散列方法,散列函数为: h(x) = x mod M。

 

 

  • 冲突解决的策略

尽管散列函数的目标是使得冲突最少,但实际上冲突是无法避免的。因此,我们必须研究冲突解决策略。冲突解决技术可以分为两类:开散列方法( open hashing,也称为拉链法,separate chaining )和闭散列方法( closed hashing,也称为开地址方法,open addressing )。这两种方法的不同之处在于:开散列法把发生冲突的关键码存储在散列表主表之外,而闭散列法把发生冲突的关键码存储在表中另一个槽内。

开散列方法:

1、拉链法

开散列方法的一种简单形式是把散列表中的每个槽定义为一个链表的表头。散列到一个特定槽的所有记录都放到这个槽的链表中。图9-5说明了一个开散列的散列表,这个表中每一个槽存储一个记录和一个指向链表其余部分的指针。这7个数存储在有11个槽的散列表中,使用的散列函数是h(K) = K mod 11。数的插入顺序是77、7、110、95、14、75和62。有2个值散列到第0个槽,1个值散列到第3个槽,3个值散列到第7个槽,1个值散列到第9个槽。

 

哈希表有多种不同的实现方法,最常用的一种方法就是 拉链法,可以理解为“链表的数组” ,如图:

从上图我们可以发现哈希表是由数组+链表组成的,一个长度为16的数组中,每个元素存储的是一个链表的头结点。那么这些元素是按照什么样的规则存储到数组中呢。一般情况是通过hash(key)%len获得,也就是元素的key的哈希值对数组长度取模得到。比如上述哈希表中,12%16=12,28%16=12,108%16=12,140%16=12。所以12、28、108以及140都存储在数组下标为12的位置。

HashMap其实也是一个线性的数组实现的,所以可以理解为其存储数据的容器就是一个线性数组。这可能让我们很不解,一个线性的数组怎么实现按键值对来存取数据呢?这里HashMap有做一些处理。

首先HashMap里面实现一个静态内部类Entry,其重要的属性有 key , value, next,从属性key,value我们就能很明显的看出来Entry就是HashMap键值对实现的一个基础bean,我们上面说到HashMap的基础就是一个线性数组,这个数组就是Entry[],Map里面的内容都保存在Entry[]里面。

 

2、桶式散列

桶式散列方法的基本思想是把一个文件的记录分为若干存储桶,每个存储桶包含一个或多个页块,一个存储桶内的各页块用指针连接起来,每个页块包含若干记录。散列函数h把关键码值K转换为存储桶号,即h(K)表示具有关键码值K的记录所在的存储桶号。 图9-6表示了一个具有B个存储桶的散列文件组织。有一个存储桶目录表,存放B个指针,每个存储桶一个,每个指针就是所对应存储桶的第一个页块的地址。

有些存储桶仅仅由一个页块组成,如下图中的1号存储桶。有的存储桶由多个页块组成,每一个页块的块头上有一个指向下一个页块的指针,例如,如下图中的第B-1号存储桶由b4,b5,b6三个页块组成,每个存储桶中最后一个页块的头上为空指针。

 

摘录自:http://blog.csdn.net/koself/article/details/7869453

http://blog.csdn.net/jn1158359135/article/details/7205688

http://blog.csdn.net/vking_wang/article/details/14166593

equals和==

  • ==用来比较两个对象的引用(地址)是否相同,用来比较基本数据类型时,用来比较值是否相等。
  • equals用来比较两个引用的对象的值是否相同。

 

  • 默认Object类的equals方法是比较两个对象的地址,跟==的结果一样。
  • 两个对象equals相等,则hashCode一定相等
  • 两个对象equasl不等,但hashCode有可能相等

 

在集合操作的时候有如下规则:

将对象放入到集合中时,首先判断要放入对象的hashcode值与集合中的任意一个元素的hashcode值是否相等,如果不相等直接将该对象放入集合中。如果hashcode值相等,然后再通过equals方法判断要放入对象与集合中的任意一个对象是否相等,如果equals判断不相等,直接将该元素放入到集合中,否则不放入。

 

  • 覆盖equals时总要覆盖hashCode 方法。

HashMap和HashTable

  • HashMap:

public class HashMap<K,V> extends AbstractMap<K,V> implements Map<K,V>, Cloneable, Serializable

hashmap的键和值都是对象,并且不能包含重复键,但可以包含重复值。HashMap允许null key和null value,但是hashtable不允许。

HashMap是Hashtable的轻量级实现,但是HashMap不是线程安全的,由于非线程安全,效率上可能高于Hashtable。

 

hashMap工作原理:

  • 通过put()方法保存键和值时,先对key调用hashCode()方法,通过返回的hashCode用来找到bucket位置来存储Entry对象(Entry对象类似于链表的节点),而在bucket中存储的是keyvalue,作为map.Entry。
  • 当两个对象的hashCode相同时,bucket位置相同,这时就会发生碰撞,因为HashMap使用链表存储对象,所以会去找是否存在该key,如果存在则替换其value,不存在则将这个Entry存储在链表中。
  • 当用get查找时,会根据key计算hashCode找到对应bucket,然后根据key查找对应的Entry( 找到bucket位置之后,会调用keys.equals()方法去找到链表中正确的节点,最终找到要找的值对象),找到后将value返回。
  • 如果HashMap的大小超过了负载因子(load factor)定义的容量(默认的负载因子大小为0.75),也就是说,当一个map填满了75%的bucket时候,和其它集合类(如ArrayList等)一样,将会创建原来HashMap大小的两倍的bucket数组,来重新调整map的大小,并将原来的对象放入新的bucket数组中。这个过程叫作rehashing。
  • 当重新调整HashMap大小时,在多线程的情况下,可能产生条件竞争(race condition)。因为如果两个线程都发现HashMap需要重新调整大小了,它们会同时试着调整大小。在调整大小的过程中,存储在链表中的元素的次序会反过来,因为移动到新的bucket位置的时候,HashMap并不会将元素放在链表的尾部,而是放在头部,这是为了避免尾部遍历(tail traversing)。如果条件竞争发生了,那么就死循环了。

HashMap源代码:

//初始容量为16
static final int DEFAULT_INITIAL_CAPACITY = 1 << 4; // aka 16

//最大容量
static final int MAXIMUM_CAPACITY = 1 << 30;

//负载因子
static final float DEFAULT_LOAD_FACTOR = 0.75f;

因为负载因子为0.75,16*0.75=12,所以当保存第12个元素的时候会进行rehashing,会导致输出的结果顺序有变化。

例子:

for(int i = 0;i<20;i++){
    hashMap.put(i+"", i);
}

Iterator iterator = hashMap.entrySet().iterator();
while(iterator.hasNext()){
    System.out.println(iterator.next());
}

输出结果:

11=11
12=12
13=13
14=14
15=15
16=16
17=17
18=18
19=19
0=0
1=1
2=2
3=3
4=4
5=5
6=6
7=7
8=8
9=9
10=10

总结:Set对每个对象只接受一次,并使用自己内部的排序方法(通常,你只关心某个元素是否属于 Set,而不关心它的顺序–否则应该使用List)。Map同样对每个元素保存一份,但这是基于”键”的,Map也有内置的排序,因而不关心元素添加的 顺序。如果添加元素的顺序对你很重要,应该使用 LinkedHashSet或者LinkedHashMap.

HashMap遍历使用Demo:

HashMap<String,Integer> hashMap = new HashMap<>();
long startTime = System.currentTimeMillis();
for(int i = 0;i<TIME;i++){
    hashMap.put(i+"", i);
}
Iterator<Entry<String,Integer>> iterator = hashMap.entrySet().iterator();
while(iterator.hasNext()){
    Entry<String,Integer> entry = iterator.next();
    System.out.println("key is:"+entry.getKey());
    System.out.println("value is:"+entry.getValue());
}

 

  • HashTable:

public class Hashtable<K,V> extends Dictionary<K,V> implements Map<K,V>, Cloneable, Serializable

HashTable的许多方法都是用synchronized修饰的。

总结:

HashMap 线程不安全 运行有null的键和值 运行速度快 实现了Map接口
HashTable 线程安全 不允许有null的键和值 占用系统资源多,速度稍慢 Dictionary是过时的了,所以在后面的版本也实现了Map接口

 

 

MessageFormat类

java.text.MessageFormat类可以格式化字符串。

public class MessageFormat extends Format

常用方法:

public static String format(String pattern,Object… arguments)这个方法,后面接受可变参数。

其中可以用占位符表示变化部分,占位符的格式为{ ArgumentIndex , FormatType , FormatStyle }。

使用实例:

System.out.println(MessageFormat.format("My name is:{0},age is:{1,number,integer}",new Object[]{"hy",22}));

输出结果:My name is:hy,age is:22