Java提供了一种序列化机制,可以将一个对象的类型和数据等信息写入到文件(序列化作用于对象),这个机制就是序列化机制。之后可以通过反序列化机制将其读取出来并用其新建对象,该过程基于JVM,因此可以在一个平台上序列化之后在另一平台上反序列化出对象。有时候因业务需要(如银行卡号、密码等敏感信息)并不会将所有的信息都序列化,这时需要使用transient关键字标识该属性不会序列化到文件中。

User类:实现了Seriazable

一个类要被序列化必须满足2个条件:

1、该类必须实现 java.io.Serializable对象:ArrayList、HashMap都实现了都可以被序列化

2、该类的所有属性都可以被序列化,如果不可被序列化则该属性必须用transient注明是短暂的

import java.io.Serializable;
/**
 * Created by m@zhangguoli.cn on 07-11-011 <br >
 */
public class User implements Serializable {

    public String uuid;
    public String username;
    public transient String password;
}

序列化

Java序列化后生成的文件后缀为.ser

- 阅读剩余部分 -

学习JDK的动态代理前需要了解静态代理的实现,假设我们有一个接口Moveable,一个类BMW,通过静态代理的方式获取汽车行驶的时间。

静态代理

(1)Moveable接口,有个move()方法

package guoli.proxy;
public interface Moveable {
    void move();
}

(2)类BMW实现了Moveable接口

- 阅读剩余部分 -

java.sql下的StatementPreparedStatementCallableStatement是用来执行SQL查询的API。其中Statement用于执行普通查询,PreparedStatement用于执行参数化查询,CallableStatement则用于存储过程。他们之间的继承关系:PreparedStatement extends StatementCallableStatement extends PreparedStatement

本文主要介绍的是PreparedStatement的简单使用、相比Statement的优势,以及PreparedStatement的局限性。

PreparedStatement示例

String url = "jdbc:mysql://127.0.0.1:3306/dbname";
//获取一个连接
Connection conn = DriverManager.getConnection(url, "username", "password");
//PreparedStatement 为预编译对象
PreparedStatement pst = conn.prepareStatement("select uuid from test limit ?");
pst.setInt(1, 1);
//结果对象
ResultSet result = pst.executeQuery();
while (result.next()) {
    System.out.println(result.getString("uuid"));
}
//依次关闭
result.close();
pst.close();
connection.close();

PreparedStatement优势

- 阅读剩余部分 -

本文基于jdk1.9,且只分析了put()的实现

initialCapacity 初始化容量
loadFactor 负载因子

HashMap底层基于数组+链表数组+红黑树实现


put(K key, V value)

Object key;
Object value;
static final int hash(Object key) {
    int hash;
    if(key == null){
        hash = 0;
    }else{
        int h = key.hashCode();
        hash = h^(h>>>16);
    }
}
 public V put(K key, V value) {
    return putVal(hash(key), key, value, false, true);
}
final V putVal(int hash, K key, V value, boolean onlyIfAbsent,
               boolean evict) {
    Node<K,V>[] tab; 
    Node<K,V> p;
    int i;
    tab = table;
    int n = tab.length; //HashMap的长度/大小
    i = (n-1) & hash;//计算所在的数组的下标index
    p = tab[i]; //+++保存所在位置的已经存在的Node数据+++
    if ((tab == null || n == 0){
        tab = resize();//调整Node大小,扩容每次增加一倍
        n = tab.length;
    }
    if ((p == null){//如果要插入的位置Node为null,则在此处直接增加Node
        tab[i] = newNode(hash, key, value, null);//<=new Node<>(hash, key, value, next);
    }
    else {//
        Node<K,V> e; //【代表了?】
        K k = p.key;
        if (p.hash == hash && ((k == key || (key != null && key.equals(k)))){//【?】
            e = p;
        }
        else if (p instanceof TreeNode)//【使用treeNode保存了,详细代码后面再看】
            e = ((TreeNode<K,V>)p).putTreeVal(this, tab, hash, key, value);
        else {
            for (int binCount = 0; ; ++binCount) {//在index位置循环链表,在链表的最后插入新数据
                e = p.next;
                if (e == null) {//++ 最后一个Node
                    p.next = newNode(hash, key, value, null);//将新Node插入到此处链表的最后
                    if (binCount >= TREEIFY_THRESHOLD - 1) // -1 for 1st   TREEIFY_THRESHOLD = 8 
                        treeifyBin(tab, hash);//链表长度大于8则转为红黑树TreeNode
                    break;
                }
                k = e.key;
                if (e.hash == hash && (k == key || (key != null && key.equals(k))))//【?】
                    break;
                p = e;
            }
        }
        if (e != null) { // existing mapping for key
            V oldValue = e.value;
            if (!onlyIfAbsent || oldValue == null)//如果onlyIfAbsent为true,则不更新此处的值;
                e.value = value;
            afterNodeAccess(e);//Node访问之后执行的操作,更新链表,把最近访问的放到最后
            return oldValue;
        }
    }
    ++modCount;
    if (++size > threshold)
        resize();//调整大小
    afterNodeInsertion(evict);//节点插入之后执行的操作
    return null;
}

一、生命周期 init()、service()、destory()

  1. 在第一次创建Servlet的时候通过init()初始化,之后不再执行此方法
  2. 通过service()处理HTTP请求,常用doGet()、doPost()
  3. destory()在Servlet结束时执行
    最后由JVM进行垃圾回收.

二、实现方式 implements Servlet、extends GenericServlet、extends HttpServlet

HttpServlet本身就是通过implements Servlet实现的.


三、代码示例: 通过继承HttpServlet实现Servlet


- 阅读剩余部分 -

实现Runnable接口并编写run()方法

public class FirstThread implements Runnable {

    @Override
    public void run() {
        System.out.println("implement the Runnable interface and write the run () method");
    }
}

new Thread(new FirstThread().start())

继承Thread类

Thread类本身就是实现的Runnable接口: Thread implements Runnable

public class SecondThread extends Thread {

    @Override
    public void run() {
        System.out.println("second thread run...");
    }
}
new Thread(new SecondThread().start())

实现Callable接口来创建Thread线程

- 阅读剩余部分 -

package cn.gl.work;

/**
 * Created by guoli on 2017/9/9 0009.
 * YXV0aG9yOiB6aGFuZ2d1b2xpLmNu

 */
public class StringIndexOf {

    public int IndexOf(String source, String target) {
        char[] sources = source.toCharArray();
        char[] targets = target.toCharArray();
        int sourceCount = sources.length;
        int targetCount = targets.length;
        char first = targets[0];
        int max = sourceCount - targetCount;
        for (int i = 0; i <= max; i++) {
            //1. 子符第一个字符与母串第一个字符相等
            if (sources[i] != first) {
                while (++i < max && sources[i] != first) {
                    ;
                }
            }
            //2、匹配2到子串
            if (i < max) {//未到最后
                int j = i + 1;
                int end = j + targetCount - 1;//注意end位置
                for (int k = 1; j < end && sources[j] == targets[k]; j++, k++) {
                    ;//子串和原串截取的部分在循环对比
                }
                if (j == end) {//找到了
                    return i;
                }
            }
        }
        return -1;
    }
}

Linux下启动Tomcat报错:

ERROR:java.lang.InternalError: Can't connect to X11 window server using 'localhost:11.0' as the value of the DISPLAY variable.

查询得知是因为linux上没有启动x server。

解决办法

1、 修改tomcat/bin/catalina.sh

在所有的-Dcatalina.home="$CATALINA_HOME" /后面增加-Djava.awt.headless=true \

 -Dcatalina.home="\"$CATALINA_HOME\"" \
 -Djava.awt.headless=true  \

2、export DISPLAY=:0

通俗的解释Java的回调机制:A类调用了B类中的方法C,方法C又反过来调用了A类中的方法D,方法D就是回调方法,这个机制就叫做CallBack.

假设存在计算器类Cal、学生小明类XiaoMing以及老师测试类Tea。

处理流程

  1. 老师布置作业给小明 Question(a,b)
  2. 小明调用计算器 new Cal().add(a,b,this)
  3. 计算器计算 int result = a + b;
  4. 计算器回调小明的Answer(result)方法

1、学生类XiaoMing

public class XiaoMing {

    void Question(int a ,int b){
        new Cal().add(a,b,this);
    }
    void Answer(int result){
        System.out.println("答案是" + result);
    }
}

2、计算器类Cal

public class Cal {
    void add(int a,int b, XiaoMing xiaoMing){
        int result = a + b;
        xiaoMing.Answer(result);
    }
}

3、老师测试类Tea

public class Tea{
    public static void main(String args[]){
        new XiaoMing().Question(2,3);
    }
}

上述例子中,回调函数是Answer(),为了好理解,把学生类和计算器类耦合在一起了,假设小红XiaoHong也需要使用计算器Cal,现在的代码是无法实现的,所以在实际开发中,学生小明类XiaoMing一般是继承某个接口,且回调函数常用接口作为参数,下面的例子是改进后的。

- 阅读剩余部分 -

1、什么Class类,什么是反射

在Java中,任何一个类都是java.lang.Class类的实例对象,Class类只有Java虚拟机可以new出来。

反射指Java运行时,任意一个类都可以知道这个类的所有属性和方法;对于任意一个对象,都可以调用他的方法和属性,这种动态获取信息和动态调用对象方法的功能机制成为反射机制。

2、Class类的3种动态加载

2.1 getClass()

xxObject obj;
Class c1 = obj.getClass();

2.2 Class.forName("className")

Class c2 = Class.forName("java.util.HashMap");

注意className是类的完全限定名,且可能抛出ClassNotFoundException

2.3 T.class

Class c3 = java.util.HashMap.class;

参考资料:getClass()forName()T.class

- 阅读剩余部分 -