Posted in Java, 技术

Java IO 之 InputStream源码

 

Writer      :BYSocket(泥沙砖瓦浆木匠)

微         博:BYSocket

豆         瓣:BYSocket

FaceBook:BYSocket

Twitter    :BYSocket

一、InputStream

InputStream是一个抽象类,即表示所有字节输入流实现类的基类。它的作用就是抽象地表示所有从不同数据源产生输入的类,例如常见的FileInputStream、FilterInputStream等。那些数据源呢?比如:

1) 字节数组(不代表String类,但可以转换)

2) String对象

3) 文件

4) 一个其他种类的流组成的序列化 (在分布式系统中常见)

5) 管道(多线程环境中的数据源)

等等

二者,注意它是属于字节流部分,而不是字符流(java.io中Reader\Writer,下面会讲到)。

FilterInputStream是为各种InputStream实现类提供的“装饰器模式”的基类。因此,可以分为原始的字节流和“装饰”过的功能封装字节流。

 

二、细解InputStream源码的核心

源码如下:

/**
 * 所有字节输入流实现类的基类
 */
public abstract class SInputStream {

    // 缓存区字节数组最大值
    private static final int MAX_SKIP_BUFFER_SIZE = 2048;

    // 从输入流中读取数据的下一个字节,以int返回
    public abstract int read() throws IOException;

    // 从输入流中读取数据的一定数量字节,并存储在缓存数组b
    public int read(byte b[]) throws IOException {
        return read(b, 0, b.length);
    }

    // 从输入流中读取数据最多len个字节,并存储在缓存数组b
    public int read(byte b[], int off, int len) throws IOException {
        if (b == null) {
            throw new NullPointerException();
        } else if (off < 0 || len < 0 || len > b.length - off) {
            throw new IndexOutOfBoundsException();
        } else if (len == 0) {
            return 0;
        }

        int c = read();
        if (c == -1) {
            return -1;
        }
        b[off] = (byte)c;

        int i = 1;
        try {
            for (; i < len ; i++) {
                c = read();
                if (c == -1) {
                    break;
                }
                b[off + i] = (byte)c;
            }
        } catch (IOException ee) {
        }
        return i;
    }

    // 跳过输入流中数据的n个字节
    public long skip(long n) throws IOException {

        long remaining = n;
        int nr;

        if (n <= 0) {
            return 0;
        }

        int size = (int)Math.min(MAX_SKIP_BUFFER_SIZE, remaining);
        byte[] skipBuffer = new byte[size];
        while (remaining > 0) {
            nr = read(skipBuffer, 0, (int)Math.min(size, remaining));
            if (nr < 0) {
                break;
            }
            remaining -= nr;
        }

        return n - remaining;
    }

    // 返回下一个方法调用能不受阻塞地从此读取(或者跳过)的估计字节数
    public int available() throws IOException {
        return 0;
    }

    // 关闭此输入流,并释放与其关联的所有资源

    public void close() throws IOException {}

    // 在此输出流中标记当前位置
    public synchronized void mark(int readlimit) {}

    // 将此流重新定位到最后一次对此输入流调用 mark 方法时的位置。
    public synchronized void reset() throws IOException {
        throw new IOException("mark/reset not supported");
    }

    // 测试此输入流是否支持 mark 和 reset 方法
    public boolean markSupported() {
        return false;
    }

}

其中,InputStream下面三个read方法才是核心方法:

public abstract int read()

抽象方法,没有具体实现。因为子类必须实现此方法的一个实现。这就是输入流的关键方法。

二者,可见下面两个read()方法都调用了这个方法子类的实现来完成功能的。

 

public int read(byte b[])

该方法是表示从输入流中读取数据的一定数量字节,并存储在缓存字节数组b。其效果等同于调用了下面方法的实现:

 read(b, 0, b.length)

如果b的长度为 0,则不读取任何字节并返回 0;否则,尝试读取至少 1 字节。如果因为流位于文件末尾而没有可用的字节,则返回值 -1;否则,至少读取一个字节并将其存储在 b 中。

思考:这时候,怪不得很多时候, b != –1 或者 b != EOF

 

public int read(byte b[], int off, int len)


在输入数据可用、检测到流末尾或者抛出异常前,此方法一直阻塞。

该方法先进行校验,然后校验下个字节是否为空。如果校验通过后,
如下代码:

int i = 1;
try {
    for (; i < len ; i++) {
        c = read();
        if (c == -1) {
            break;
        }
        b[off + i] = (byte)c;
    }
} catch (IOException ee) {
}

将读取的第一个字节存储在元素 b[off] 中,下一个存储在 b[off+1] 中,依次类推。读取的字节数最多等于 len。设 k 为实际读取的字节数;这些字节将存储在 b[off]b[off+k-1] 的元素中,不影响 b[off+k]b[off+len-1] 的元素。

因为有上面两个read的实现,所以这里InputStream设计为抽象类。

三、小结

1. InputSream 对应着 OutputStream

2. 看源码是享受人家写代码中流露的How

3. 泥瓦匠学习的代码都在github上(同步osc git),欢迎大家点star,提意见,一起进步。地址:https://github.com/JeffLi1993

 

Posted in 清文

生来仿徨

两排路灯

穿梭的车流

一条小道

喧嚣的街道

匆匆一别

亦有两日

吾病渐安

思念成疾

无尽的车道

那个转角转弯

人生苦短

无处寻安

笑,自嘲

生来仿徨,死去何安

静,浪过度

才知心里想过的生活

那就去,罢了

– 2015-9-21

Posted in Java, 技术

图解 & 深入浅出Java初始化与清理:构造器必知必会

Writer      :BYSocket(泥沙砖瓦浆木匠)

微         博:BYSocket

豆         瓣:BYSocket

FaceBook:BYSocket

Twitter    :BYSocket

面向对象编程中,编程人员应该在意“资源”。比如

String hello = "hello";

在代码中,我们很在意在内存String类型hello,它是有一个生命周期的。在它生命周期中,初始化(initialization)清理(cleanup)是两个重要的环节。尤其是C程序中,很多bug会出现在对象初始化清理的环节。这样会造成一些程序安全问题。

《Think in Java》中说道:

随着计算机革命的发展,”不安全“的编程方式已逐渐成为编程代价高昂的主因之一。

一、栈与堆

在Java开发中,针对对象会在乎内存的两个区域:对象的生存空间是堆(heap) 方法调用及局部变量(也称变量)的生存空间栈(stack)。如图:

绘图1

 

二、什么是构造器,默认(无参)构造器

为了保证对象成功初始化,Java像C++一样引入了构造器(Constructor),即在创建对象的时候被自动调用的特殊方法。自然,Java额外提供了GC(垃圾回收器),对于不再使用的资源,垃圾回收机制会自动释放资源。Java通过类构造器创建新对象的时候,在使用此对象前,会调用其构造器进行初始化。如:

public class VoidConstructor {
    
    VoidConstructor(){
        // 构造器
    }
    
    void VoidConstructor() {
        // void 方法,不是构造器
    }
}

1、默认构造器

这里,VoidConstructor是一个类,如果需要创建一个VoidConstructor对象只要

VoidConstructor constr = new VoidConstructor();

调用上面的代码就可以通过这个默认构造器(即无参构造器),就生成一个VoidConstuctor对象。代码图解如下

绘图2

2、注意,构造器特殊方法,采取与类名同名。它没有返回值,这与返回值void方法不同。这里,void方法名命名也是不规范,应该是“每个方法首字母小写”,也一般不与类名相同。 

另外,就算你类没有写构造器的话,编译器会生成一个默认构造器。

三、带参构造器

下面跑个带参构造器的例子:

/**
 * @author Jeff Lee
 * @since 2015-9-7 16:54:19
 * 带参数简单构造器的展示
 */
public class SimpleConstructor2 {
    public static void main(String[] args) {
        for (int i = 0; i < 10; i++) {
            new Child2(i);
        }
    }
}

// Child类
class Child2 {
    Child2(int i) {// 带参数的Child类构造器
        System.out.print("Child init " + i + " ");
    }
}

Eclipse中,右键Run asjava application

image

上面Child2(int)是Child类的唯一构造器,此时你先在编译器中通过 new Child2()来创建对象是不行的。

所以,构造器有助于避免开发中出现代码错误

二者,构造器可以用来初始化资源。其中,对象的创建与资源的初始化是捆绑在一起的。

休息一下吧~ 看看小广告:

开源代码都在我的gitHub上哦 — https://github.com/JeffLi1993 作者留言“请手贱,点项目star,支持支持拜托拜托

四、继承中的构造器

在创建一个对象时,所以继承下来的对象都会被调用各构造器。比如说下面这个例子:

/**
 * @author Jeff Lee
 * @since 2015-9-10 08:56:18
 * 继承中的构造器的案例
 */
public class ChildConstructor extends PersonConstructor{
	ChildConstructor() {
 //super(); 
		System.out.println("Making a Child Modle...");
	}
	
	@SuppressWarnings("unused")
	public static void main(String[] args) {
		ChildConstructor childConstructor = new ChildConstructor();
	}
}

class PersonConstructor {
	PersonConstructor() {
		System.out.println("Making a Person Modle...");
	}
}

main函数运行结果如下:

image

 

1. 先分析下类UML,ChildConstructor 继承 PersonConstructor,PersonConstructor 继承 Object:

 

绘图3

2. super关键字,super用来调用父类的构造器。如果子类中没有super关键字,编译器会自动添加默认super()方法。因此,子类通过super()方法调用父类构造器。

3. 然后main函数中,通过new指令,启动了ChildConstructor对象的创建。子类 ChildConstructor 构造器在执行时,第一件事通过super()调用父类,这又会连锁反应到Objerct类。所以,打印控制台先输出 “父类先初始化”。

这过程就是构造器链Constructor Chaining),即Child对象  is-a Person对象同时也 is-a Object对象。如果创建Child对象,也同时创建了Person对象和Object对象的部分。

4. 过程流程图详解(图中是各对象构造器在堆栈块中调用流程 图中引用两字有歧义):

绘图4

五、总结

 

构造器虽小,关键还是非常关键的。

本文小结:

1、默认构造器

2、有参构造器

3、继承中的构造器

还有第一点的知识基础补充

 

欢迎点击我的博客及GitHub — 博客提供RSS订阅哦!

———- http://www.bysocket.com/ ————- https://github.com/JeffLi1993 ———-

Posted in Java, 技术

[好文要转要收藏] UML类图几种关系的总结

在UML类图中,常见的有以下几种关系:泛化(Generalization),  实现(Realization),关联(Association),聚合(Aggregation),组合(Composition),依赖(Dependency)

1.泛化(Generalization)

【泛化关系】:是一种继承关系,它指定了子类如何特化父类的所有特征和行为例如:老虎是动物的一种.

【箭头指向】:带三角箭头的实线,箭头指向父类

2.实现(Realization)

【实现关系】:是一种类与接口的关系,表示类是接口所有特征和行为的实现

【箭头指向】:带三角箭头的虚线,箭头指向接口

3.关联(Association)

关联关系】:是一种拥有的关系,它使一个类知道另一个类的属性和方法;如:老师与学生,丈夫与妻子

关联可以是双向的,也可以是单向的。双向的关联可以有两个箭头或者没有箭头,单向的关联有一个箭头。

【代码体现】:成员变量

【箭头及指向】:带普通箭头的实心线,指向被拥有者

上图中,老师与学生是双向关联,老师有多名学生,学生也可能有多名老师。但学生与某课程间的关系为单向关联,一名学生可能要上多门课程,课程是个抽象的东西他不拥有学生。

上图为自身关联:

4. 聚合(Aggregation)

【聚合关系】:是整体与部分的关系.如车和轮胎是整体和部分的关系.

聚合关系是关联关系的一种,是强的关联关系;关联和聚合在语法上无法区分,必须考察具体的逻辑关系。

【代码体现】:成员变量

【箭头及指向】:带空心菱形的实心线,菱形指向整体

5. 组合(Composition)

【组合关系】:是整体与部分的关系.,没有公司就不存在部门 组合关系是关联关系的一种,是比聚合关系还要强的关系,它要求普通的聚合关系中代表整体的对象负责代表部分的对象的生命周期

【代码体现】:成员变量

【箭头及指向】:带实心菱形的实线,菱形指向整体

6. 依赖(Dependency)

【依赖关系】:是一种使用的关系,所以要尽量不使用双向的互相依赖。

【代码表现】:局部变量、方法的参数或者对静态方法的调用

【箭头及指向】:带箭头的虚线,指向被使用者

各种关系的强弱顺序:

泛化= 实现> 组合> 聚合> 关联> 依赖

下面这张UML图,比较形象地展示了各种类图关系:

原文地址:http://blog.csdn.net/tianhai110/article/details/6339565

 

思考

在Java 各框架源码中,会涉及到各种

继承 = 实现> 组合> 聚合> 关联> 依赖

黑体字为常用

Posted in Java, 技术

图解 & 深入浅出 JavaWeb:Servlet 再说几句

Writer      :BYSocket(泥沙砖瓦浆木匠)

微         博:BYSocket

豆         瓣:BYSocket

FaceBook:BYSocket

Twitter    :BYSocket

上一篇的《 Servlet必会必知 》受到大家一致好评 — (感谢 读者 及 OSC 推荐 每日一’搏’) image

后来觉得还有些东西没点到,这边补充补充。

一、回到 HttpServlet 的 service方法

Servlet 基础接口定义了用于客户端请求处理的service方法。 当请求到达Servlet容器,由Servlet容器路由到一个Servlet实例

比如说 javax.servlet.http.HttpServlet 类 ,其中有一个 protected void service 方法如下:

private static final String METHOD_DELETE = "DELETE";
private static final String METHOD_HEAD = "HEAD";
private static final String METHOD_GET = "GET";
private static final String METHOD_OPTIONS = "OPTIONS";
private static final String METHOD_POST = "POST";
private static final String METHOD_PUT = "PUT";
private static final String METHOD_TRACE = "TRACE";

private static final String HEADER_IFMODSINCE = "If-Modified-Since";

private static final String LSTRING_FILE =
        "javax.servlet.http.LocalStrings";
private static ResourceBundle lStrings =
        ResourceBundle.getBundle(LSTRING_FILE);
/**
 * HTTP状态码304
 */
public static final int SC_NOT_MODIFIED = 304;

/**
 * 接收来自 public service方法的标准HTTP请求,
 * 并将它们分发给此类中定义的doXXX方法。
 */
protected void service(HttpServletRequest req, HttpServletResponse resp) 
		throws ServletException, IOException {
	// 获取请求方法名
	String method = req.getMethod();
	// 如果是GET请求
	if (method.equals(METHOD_GET)) {
		// 上一次修改HttpServletRequest对象的时间
		long lastModified = getLastModified(req);
		// 没有改变
		if (lastModified == -1) {
			doGet(req, resp);
		} else {
			long ifModifiedSince;
			try {
				// 获取请求头中服务器修改时间
				ifModifiedSince = req.getDateHeader(HEADER_IFMODSINCE);
			} catch (IllegalArgumentException iae) {
				// 获取无效
				ifModifiedSince = -1;
			}
			// 如果请求头服务器修改时间迟
			if (ifModifiedSince < (lastModified / 1000 * 1000)) {
				// 设置修改HttpServletResponse对象的时间,重新设置浏览器的参数
                              //maybeSetLastModified(resp, lastModified);
				// 调用doGet方法
                                doGet(req, resp);
			} else {
				// 304 HTTP状态码
				resp.setStatus(SC_NOT_MODIFIED);
			}
		}
	} else if (method.equals(METHOD_HEAD)) {
        long lastModified = getLastModified(req);
      //maybeSetLastModified(resp, lastModified);
        doHead(req, resp);
    } else if (method.equals(METHOD_POST)) {
        doPost(req, resp);
    } else if (method.equals(METHOD_PUT)) {
        doPut(req, resp);
    } else if (method.equals(METHOD_DELETE)) {
        doDelete(req, resp);
    } else if (method.equals(METHOD_OPTIONS)) {
        doOptions(req,resp);
    } else if (method.equals(METHOD_TRACE)) {
        doTrace(req,resp);
    } else {
        // 如果没有被请求到的话
        String errMsg = lStrings.getString("http.method_not_implemented");
        Object[] errArgs = new Object[1];
        errArgs[0] = method;
        errMsg = MessageFormat.format(errMsg, errArgs);
        // 501 HTTP状态码
        resp.sendError(HttpServletResponse.SC_NOT_IMPLEMENTED, errMsg);
    }
}

代码逻辑详解如下:

1、HttpServletprotected void service方法 用于接受 public service接收的标准HTTP请求

也就是说,HttpServlet 重写了父类 GenericServletservice方法。如图显示的是该方法,将从容器获取的 ServletRequestServletResponse 对象强制转化成 用于HTTP处理的 HttpServletRequestHttpServletResponse 对象。然后将两个对象路由给了 HttpServletprotected void service方法(图中代码选中处)

image

2、然后根据请求的方法名,分发到此类定义的doXXX方法。如果没有被请求到的话,则返回501 HTTP 状态码。

这样子仿佛明白了什么,也就是说,如果你在 HelloServlet重写doGet方法,这里分发到就是HttpServlet的子类HelloServlet的doGet方法。

哦~ 还有,501 HTTP 状态码未实现(Not implemented)表示服务器不支持实现请求所需要的功能。例如,客户发出了一个服务器不支持的PUT请求。原来如此,所谓死记硬背这些HTTP 状态码有什么用?这样的记忆才是最有效的。

休息休息,小广告插一下 :(维持生计,O(∩_∩)O~)

涉及到的代码都会在开源项目 servlet-core-learning简介 — Servlet/JSP学习积累的例子,是Java EE初学者及Servlet/JSP核心技术巩固的最佳实践

大致就是这两步骤。这就是service的工作流程

1、接受 public service接收的标准HTTP请求。

2、分发到定义的doXXX方法

二、GET 请求的处理详解

上面对于GET请求代码处理如下:

// 如果是GET请求
if (method.equals(METHOD_GET)) {
	// 上一次修改HttpServletRequest对象的时间
	long lastModified = getLastModified(req);
	// 没有改变
	if (lastModified == -1) {
		doGet(req, resp);
	} else {
		long ifModifiedSince;
		try {
			// 获取请求头中服务器修改时间
			ifModifiedSince = req.getDateHeader(HEADER_IFMODSINCE);
		} catch (IllegalArgumentException iae) {
			// 获取无效
			ifModifiedSince = -1;
		}
		// 如果请求头服务器修改时间迟
		if (ifModifiedSince < (lastModified / 1000 * 1000)) {
			// 设置修改HttpServletResponse对象的时间,重新设置浏览器的参数
          //maybeSetLastModified(resp, lastModified);
			// 调用doGet方法
            doGet(req, resp);
		} else {
			// 304 HTTP状态码
			resp.setStatus(SC_NOT_MODIFIED);
		}
	}
} 

这里,

1、定义了 getLastModified(req) 方法。用于获取上一次修改HttpServletRequest对象的时间。如果lastModified为默认–1L,则总是刷新

这个getLastModified,是HttpServlet定义了用于支持有条件GET操作。即当客户端通过GET请求获取资源时,当资源自第一次获取那个实际点发生更改后才再次发生数据,否则将使用客户端缓存的数据。

在一些适当的场合,实现此方法可以更有效的利用网络资源,减少不必要的数据发送。

2、如果getLastModified方法的返回值是一个正数,那就要分以下两种情况考虑:

    (1)如果请求头没有包含If-Modified-Since头字段(应该是第一次访问资源时候) 或者 其getLastModified返回值比If-Modified-Since头字段指定时间,则调用doGet返回生成 response 设置Last-Modified 消息头

    (2)如果其getLastModified返回值比If-Modified-Since头字段指定时间,则返回一个304状态给客户端,表示让客户端继续使用以前缓存的页面

比如说 304 这个场景我在《 JavaEE 要懂的小事:一、图解Http协议 》文章中提到,第一次访问 百度 首页时,有些资源会成功获取 返回200再次F5,有些资源或直接调用客户端的缓存数据,则返回304

image1_thumb

三、Servlet线程问题

Servlet容器可以并发路由多个请求到 Servlet 的 service方法。为了处理这些请求,Servlet必须在并发及线程安全问题做好处理。上一篇的《 Servlet必会必知 》提到定义全局变量会造成线程安全问题。在开发Servlet时,考虑线程安全问题提出了一下解决

1、实现 SingleThreadModel 接口

    Servlet2.4 已经提出不提倡使用。实现此接口,Servlet容器为每个新的请求创建一个单独的Servlet实例。这会有严重性能问题

2、同步锁

    使用synchronized关键字,虽然可以保证只有一个线程可以访问被保护区段,已达到保证线程安全。但是系统性能及并发量大大降低。不可取~

3、避免使用实例变量,即Servlet中全局变量。使用局部变量 (推荐)

    方法中的局部变量分配在空间,每个线程有私有的栈空间。因此访问是线程安全的。

我想到了以下一个问题:

既然Sevlet的全局变量是线程不安全的,那SpringMVC Controller 也一样。那我们在Controller定义个 XXXService 变量会不会造成线程安全呢?
答:因为这是Spring的一个Service Bean,是线程安全的,所以可以作为单例使用,不会造成线程安全。

四、总结(别忘了点赞哦)

补充文章内容要点:

HttpServlet service 方法详解

深入理解 代码 对HTTP状态码的运用

Servlet的线程安全问题

欢迎点击我的博客及GitHub — 博客提供RSS订阅哦

———- http://www.bysocket.com/ ————- https://github.com/JeffLi1993 ———-

Posted in Working Skills, 技术

Eclipse我常用快捷键

ctrl+shift+r:打开资源

这可能是所有快捷键组合中最省时间的了。节约查找文件的时间,比如你看到报错是 某DAO层报错了,这样就方便。

Control-Shift-T: 打开类型(Open type)。如果你不是有意磨洋工,还是忘记通过源码树(source tree)打开的方式吧。用eclipse很容易打开接口的实现类的,按ctrl+t会列出接口的实现类列表

ctrl+o:快速outline

如果想要查看当前类的方法或某个特定方法,但又不想把代码拉上拉下,也不想使用查找功能的话,就用ctrl+o吧。它可以列出当前类中的所有方法及属性,你只需输入你想要查询的方法名,点击enter就能够直接跳转至你想去的位置。

ctrl+e:快速转换编辑器

这组快捷键将帮助你在打开的编辑器之间浏览。使用ctrl+page downctrl+page up可以浏览前后的选项卡,但是在很多文件打开的状态下,ctrl+e会更加有效率。

shift+enter及ctrl+shift+enter

Shift+enter当前行之下创建一个空白行,与光标是否在行末无关。ctrl+shift+enter则在当前行之前插入空白行。

Alt+方向键

这也是个节省时间的法宝。这个组合将当前行的内容往上或下移动。在try/catch部分,这个快捷方式尤其好使。

ctrl+m

大显示屏幕能够提高工作效率是大家都知道的。Ctrl+m是编辑器窗口最大化的快捷键。

ctrl+.及ctrl+1:下一个错误及快速修改

ctrl+.将光标移动至当前文件中的下一个报错处或警告处。这组快捷键我一般与ctrl+1一并使用,即修改建议的快捷键。新版Eclipse的 修改建议做的很不错,可以帮你解决很多问题,如方法中的缺失参数,throw/catch exception,未执行的方法等等。

Posted in 清文

读书随笔:《人性的弱点》第一章

我常常有一种感觉,总是想着一些没有完成,或者没有头绪的事情。仿佛夸了海口,仿佛熬了这一夜,我就能成功一样。

— 其实呢?

常常是深夜还没睡着,或者是没有熟睡吧。第二天迷糊醒来,还是那个普通的自己。嘲笑一番昨天的想法,起床依旧洗脸睡觉。是自己这阶段缺少看书了,是,我就看起了kindle里面师傅的一本,小小正能量的的书 — 《人性的弱点》

第一章,卡耐基是说了— Fundamental techniques in handling people(与人相处的基本技巧) ,我就纳闷了,这和人性的弱点有什么关系呢?

读完了这一章,我隐约记着

“不要批评 背地说别人” “赞美,恰到好处 别太过”“知道对方需求,需要的东西”

“对自己,学会欣赏别人” “可能现在笑人家太装逼,以后不一定他不成功” “对于自己不喜欢的,止住批评,冷静想想”

一、“如欲采蜜,勿蹴蜂房”

1、批评总是错误的,最错误的事情。恰是当时最能将情绪释放,但请克制

2、了解一个人,比批评好

刚刚看了一则新闻,由于火锅店员与顾客加汤事情出现争执,店员将汤倒向顾客头上。这事显然,店员有错。可是顾客没错吗,有的。肯定有的。这里抛开店员服务态度,我想说,那时候顾客肯定发火了。因为今天晚上我在永和吃饭,店员服务真差,把我的外卖送出去了。其实我在店里吃的,我也当时火了。找他们经理了。

但是我现在想,我也不应该这样。我毕竟点的是外卖,况且车站旁边的永和人实在是多。真多。

里面列举了形形色色的例子,为了就是说明确实 批评不顶用。例如,林肯最喜欢的一句名言“我不论断他人,他人就不会论断你。”是的,在各种战争中,林肯设身处地为犯错的军官们想,如果自己身处那环境会怎么做。而在书信中,也是十分保守委婉的表达批评。

这时候我想到了我的母亲,母亲在我这一生中。总是给我讲故事,让我明白很多道理。她从来没批评过我。总是说,人活着肯干吃苦,啥都能养活自己的。而这辈子也没有多少要求我,记得那次中考失利,一直到高考大学的我,明白了,母亲伟大。同样我那沉默不爱说的老爸也在去年退休了。

伟大的你们,我爱你们~

这辈子,就那么短。没必要花一些时间,去批评,去责怪,去抱怨他人。

做自己该做的事,想自己该想的事。走自己该走的路,路上的批评声很多,但鼓励也不会少的。

松风竹影,它们是一份清香。热血男儿,确实一腔热血。没有什么畏惧,只是我们年轻~

二、真诚的赞赏他人

书上没有规避什么东西,只是把最真实的说给我们听。

大部分人都想要的是:

        健康的身体

        食物

        睡眠

        金钱和金钱可以买来的东西

        未来生活的保障

        性满足

        儿女的幸福

        被人重视的感觉

最后一点,最重要的一点:渴望伟大

在我看来,现实教会我们是现实。没有现实基础在大部分人看来是愚昧。这块确实确实没人从中抛弃几点。最后一点是全部的人都不想抛弃的。因为这是,仿佛来到这个世界莫名的使命。讲讲小孩子的时候,曾经的我。

爱攀比,啥都爱

无论是 练字 体育 跑步 外貌 

无论是 别人体型 身高 力量(爱打架)

现在看来,都是好笑的回忆。那确实我小时候真正的自己。

每个小孩子都一样,我也不例外的爱被夸。我相信以前教育让我,觉得自己成绩好就是最强的人。但现实让我自己现在觉得,强在哪里都可以。

师弟,师弟是我一直在外称呼自己的另一个笔名。因为我觉得三人行,必有我师。我学习的只是人家的好处,那个我很吸收到的好处。同样,我首先得教下这个朋友,交朋友的秘密招数就是

真诚地赞美别人

三、激发他人的强烈需求

2015-8-26号早上写。。。

Posted in Java, 技术

图解 & 深入浅出 JavaWeb:Servlet必会必知

Writer      :BYSocket(泥沙砖瓦浆木匠)

微         博:BYSocket

豆         瓣:BYSocket

FaceBook:BYSocket

Twitter    :BYSocket

“眨眼间,离上一篇写技术博文时隔1个月。怕自己真的生疏了,都是备案太慢惹得。哈哈,继续high~ ”

[JavaEE 要懂的小事] Http相关 ,一直想写点Web开发相关的。最近项目接口开发紧,还有准备新的九月份战斗。JDK IO源码就隔一段落,温故知新看看Servlet & JSP 相关。把自己基础累积回顾一遍,并和大家分享分享一些心得和代码。这里应该涉及到一部分源码,开发思想和一些手工做出的图。喜欢java,或者有一定Java开发经验的多提宝贵意见。

一、Web服务器

从事web开发的人,会很清楚一个东西叫 Web服务器,比如J2EE开发—TomcatJetty,.NET开发—IIS等。HTTP服务器是使用 HTTP(超文本传输协议) 与客户机浏览器进行信息交流。下面就是HTTP服务器简单交互图:(来自[JavaEE 要懂的小事] Http相关 博客)

1_thumb3

HTTP服务器Web服务器的一种,也是开发最常见的,自然还有其他方式进行信息交互,比如FTP文件服务器

Web服务器是可以向发出请求的浏览器提供文档的程序。其核心过程为

连接过程 — 请求过程 — 应答过程 — 关闭连接

这让我想到了Tomcat架构的一张图:

image001

二、Tomcat 简单说几句

如图,Tomcat 包含了核心服务模块:Connector连接模块 和 Container 容器。Tomcat Server 核心是一个 Servlet/JSP Container。对每一个HTTP请求,过程如下

获取连接

Servlet来分析请求HttpServletRequest

— 调用其service方法,进行业务处理

— 产生相应的响应HttpServletResponse

关闭连接

如图:

Tomcat相关

蓝色线指向过程是请求,绿色线指向过程是响应过程。也就是上面Web服务器核心过程:“连接过程 — 请求过程 — 应答过程 — 关闭连接

三、我第一个Servlet

什么是Servlet?(每次都会不停的问自己,这是什么“What”?紧接着应该是什么用“How”吧)

JavaEE 6文档中,介绍如下

Servlet 是运行在Web服务器的Java小程序。Servlet可以获取并针对Web客户端的请求作出响应。一般情况下,通过HTTP,即超文本传输协议,进行传输通信。”

A servlet is a small Java program that runs within a Web server. Servlets receive and respond to requests from Web clients, usually across HTTP, the HyperText Transfer Protocol.

所以,Servlet 是Web服务器核心工作的抽象。它不单单只是实现HttpServlet,可能实现有FtpServlet(这个我猜的)等。相对较多的Web开发,知道的肯定是HttpServlet。

补充,在Servlet规范是这样写道的:

Serlvet是基于Java技术的Web组件,容器托管的,用于生产动态内容。它也是基于平台无关的Java类格式,被编译为平台无关的字节码,可以被基于Java技术的web server动态加载并运行。这里容器,有时候也称为servlet 引擎。

JavaEE 6文档中,是这样介绍HttpServlet

“HttpServlet 提供了一个能被继承后创建一个适应Web网站的Http Servlet的抽象类。”

Provides an abstract class to be subclassed to create an HTTP servlet suitable for a Web site.

光说不练假把式,练一个“Hello,Servlet/JSP World!”:

import java.io.IOException;
import java.io.PrintWriter;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/*
 * Copyright [2015] [Jeff Lee]
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * 
 *   http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

/**
 * @author Jeff Lee
 * @since 2015-6-25 19:46:45
 * 	HelloWrold案例
 */
@WebServlet(urlPatterns = "/helloWorld.html")
public class HelloWorldServletT extends HttpServlet{
	
	private static final long serialVersionUID = 1L;

	@Override
	protected void doGet(HttpServletRequest req, HttpServletResponse resp)
			throws ServletException, IOException{
		// 获取输出打印对象
		PrintWriter out = resp.getWriter();
		out.println("Hello,Servlet/JSP World!");
	}
}

右键该HelloWorldServletT.java文件 — Run As — Run On Server — 选择Tomcat服务器 — Finish即可

image

image

等待片刻,你可看到网页上如下输出。这就是客户端从HttpServlet获取到的响应:

image

休息一下吧~ 看看小广告:

开源代码都在我的gitHub上哦 — https://github.com/JeffLi1993

三、分析源码

 

@WebServlet(urlPatterns = "/helloWorld.html")

@WebServlet 注解用于声明一个HttpServlet的配置。其中,urlPatters = “/helloWorld.html”,urlPatterns复数形式,说明至少一个URL必须被申明。它和另一个value必须存在一个,但不能同时存在。如果要匹配多个URL路径的话,如下:

@WebServlet(urlPatterns = { "/helloWorld01.html", "/helloWorld02.html" }

下面有个@Override,重写了父类HttpServletdoGet方法。我们先看看父类HttpServlet。HttpServlet是一个抽象类,它提供了以下方法:

— doGet  , 服务于 HTPP GET 请求

— doPost , 服务于 HTTP POST 请求

— doPut  , 服务于 HTTP PUT 请求

— doDelete,服务于 HTTP DELETE 请求

如图:

image

对于不同请求,HttpServlet的子类必须相应的实现至少一个方法,通常来说,会是其中一个,这样代码比较清晰。那父类的doGet方法做了什么工作呢?

protected void doGet(HttpServletRequest req, HttpServletResponse resp)
        throws ServletException, IOException
    {
        String protocol = req.getProtocol();
        String msg = lStrings.getString("http.method_get_not_supported");
        if (protocol.endsWith("1.1")) {
            resp.sendError(HttpServletResponse.SC_METHOD_NOT_ALLOWED, msg);
        } else {
            resp.sendError(HttpServletResponse.SC_BAD_REQUEST, msg);
        }
    }

这里就简单的获取了下HTTP协议及Http Local信息,然后可以协议是否是1.1,做出分别是405或者400HTTP状态码的响应。

回到HelloWorldServletT.java 这里:

@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp)
		throws ServletException, IOException {
	// 获取输出打印对象
	PrintWriter out = resp.getWriter();
	out.println("Hello,Servlet/JSP World!");
}

表示该HelloWorldServletT会接受Http GET请求,并OOM到HttpServletRequest,并执行里面的逻辑代码和返回响应。 这里从HttpServletResponse对象中获取到输出打印对象PrintWriter,然后输出了“Hello,Servlet/JSP World!”。

完毕!哦还有一点补充补充补充:

print,这里还好一句话。如果打印个table会很麻烦,因此有一个JSP的东西出现了,是Servlet的HTML化身。

五、深入Servlet 具体过程

又回到这个简单的 Get Servlet代码:

public class HelloWorldServletT extends HttpServlet{
	
	private static final long serialVersionUID = 1L;

	@Override
	protected void doGet(HttpServletRequest req, HttpServletResponse resp)
			throws ServletException, IOException{
		// 获取输出打印对象
		PrintWriter out = resp.getWriter();
		out.println("Hello,Servlet/JSP World!");
	}
}

这过程总结如下:

— 从浏览器(Client)获取连接”/helloWorld.html”

Tomcat Connector模块将请求(Request)传递给 Container模块

Container 模块会做以下事情

—— 分析HTPP请求信息,组装成HttpServletRequest对象

—— 创建新的HttpServletResponse对象

—— 根据路由配置,搜索相应的Servlet,并创建一个线程用于处理本次请求。此时线程会将上面RequestResponse对象的索引传递给Servlet

— 新线程中的Servlet处理逻辑

线程结束后,通过HttpServletResponse对象的PrintWriter,返回浏览器一个信息

过程图如下:

Tomcat相关

蓝色线指向过程是请求,绿色线指向过程是响应过程,橙色线指向过程是内部处理过程。

有些面试题会这样问:

Servlet是线程安全的吗?

不是,一个servlet实现类只会有一个实例对象,多个线程是可能会访问同一个servlet实例对象的,线程安全问题都是由全局变量静态变量引起的。

因此,Servlet对象实例化是在以第一次请求此Servlet时,如果访问后,实例对象存在内存中,只会在服务器停止时,它才会消失。它不会随着各个线程结束而结束。因此下次访问Servlet时,Servlet Container会搜索相应的Servlet,如果不存在Container新建相应的Servlet。这也是我们想要的结果。

再来个恶心的面试题:

Servlet是单例吗?

不一定是,在一个ServeltName情况下是的。在多个ServletName匹配到一个Servlet类时,该Servlet不是单例。

六、小结

下一篇:图解 & 深入浅出 JavaWeb:Servlet 再说几句

发现这一博客写的太多,回头一看。可以写成三个文章了。言归正传本文要点如下

1、简单介绍Web服务器 及 Tomcat容器

2、第一个Sevlet的开发及使用

3、深入源码及api介绍使用

4、总结一次请求及响应的真实过程

5、欢迎点击我的博客及GitHub — 博客提供RSS订阅哦

———- http://www.bysocket.com/ ————- https://github.com/JeffLi1993 ———-

Posted in Java, 技术

图解 Java IO : 二、FilenameFilter源码

Writer      :BYSocket(泥沙砖瓦浆木匠)

微         博:BYSocket

豆         瓣:BYSocket

FaceBook:BYSocket

Twitter    :BYSocket

从上一篇 图解 Java IO : 一、File源码 并没有把所有File的东西讲完。这次讲讲FilenameFilter,关于过滤器文件《Think In Java》中写道:

更具体地说,这是一个策略模式的例子,因为list()实现了基本功能,而按着形式提供了这个策略,完善list()提供服务所需的算法。

java.io.FilenameFilter是文件名过滤器接口,即过滤出符合规则的文件名组。

一、FilenameFilter源码

image

从IO的UML可以看出,FilenameFilter接口独立,而且没有它的实现类。下面就看看它的源码:

public interface FilenameFilter {
    /**
     * 测试指定文件是否应该包含在某一文件列表中。
     *
     * @param   被找到的文件所在的目录。
     * @param   文件的名称
     */
    boolean accept(File dir, String name);
}

从JDK1.0就存在了,功能也很简单:就是为了过滤文件名。只要在accept()方法中传入相应的目录和文件名即可。

深度分析:接口要有真正的实现才能算行为模式中真正实现。所以这里使用的是策略模式,涉及到三个角色:

环境(Context)角色

抽象策略(Strategy)角色

具体策略(Context Strategy)角色

结构图如下:

filenameFilter

其中,FilenameFiler Interface 就是这里的抽象策略角色。其实也可以用抽象类实现。

但是,装饰器模式也有缺点:在编写程序的时候,它提供了相当多的灵活性(容易混合和匹配属性),同时也增加了代码的复杂性

 

 

 

二、使用方法

image

如图 FilenameFiler使用如图所示。上代码吧:(small 广告是要的,代码都在 开源项目java-core-learning。地址https://github.com/JeffLi1993

package org.javacore.io;

import java.io.File;
import java.io.FilenameFilter;
/*
 * Copyright [2015] [Jeff Lee]
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * 
 *   http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

/**
 * @author Jeff Lee
 * @since 2015-7-20 13:31:41
 * 类名过滤器的使用
 */
public class FilenameFilterT {

	public static void main(String[] args) {
		// IO包路径
		String dir = "src" + File.separator +
				"org" + File.separator +
				"javacore" + File.separator +
				"io";
		File file = new File(dir);
		// 创建过滤器文件
		MyFilter filter = new MyFilter("y.java");
		// 过滤
		String files[] = file.list(filter);
		
		// 打印
		for (String name : files) {
			System.err.println(name);
		}
	}
	
	/**
	 *	内部类实现过滤器文件接口
	 */
	static class MyFilter implements FilenameFilter {
		
		private String type;
		
		public MyFilter (String type) {
			this.type = type;
		}

		@Override
		public boolean accept(File dir, String name) {
			return name.endsWith(type);// 以Type结尾
		}
		
	}
}

其中我们用内部类的实现,实现了FilenameFilter Interface。所以当我们File list调用接口方法时,传入MyFilter可以让文件名规则按我们想要的获得。

右键 Run 下,可以看到如图所示的输出:

image

 

补充:

String[] fs = f.list()

File[] fs = f.listFiles()

String []fs = f.list(FilenameFilter filter);;

File[]fs = f.listFiles(FilenameFilter filter);

image


三、总结

1、看源码很简单,看怎么用先,在深入看有什么数据结构,设计模式。理理就清楚了

2、学东西,学一点一点深一点。太深不好,一点就够了

3、泥瓦匠学习的代码都在github上(同步osc git),欢迎大家点star,提意见,一起进步。地址:https://github.com/JeffLi1993

Writer      :BYSocket(泥沙砖瓦浆木匠)

微         博:BYSocket

豆         瓣:BYSocket

FaceBook:BYSocket

Twitter    :BYSocket

Posted in Java, 技术

Servlet & JSP : 一、小姐,来桶全家桶不?(Servlet)

Writer      :BYSocket(泥沙砖瓦浆木匠)

微         博:BYSocket

豆         瓣:BYSocket

FaceBook:BYSocket

Twitter    :BYSocket

一.前言

  小生不才,大二下半学期第二个星期,近11月博客园。星期六闲来看看经典书。重温下Serlvet


二.温故而知新:超文本转移协议HTTP

HTTP协议是通过互联网(internet)或企业内部网(intranet)交换数据。介绍下http请求,http响应的过程。

#KFC点全家桶 – HTTP实现的是资源的请求和传送,类似点单。

1)请求(request):小姐向服务员post请求:“来个全家桶”。(postget是最常用的,下文HttpServlet中会涉及.)

2)回复(response):服务员根据情况,回应顾客的请求。

#多发情况-服务员的响应

1)服务员准备全家桶,交给小姐。(返回 200 成功状态码)

2)服务员今天不销售全家桶,反馈无。(无法找到某资源404

3)服务员将命令转给准备的厨师。(重定向redirect


三.Servlet  API (自顶向下模式讲解)

展示下javax.servlet.http中主要的类型:

                           KFC组织图(Serlvet 重要成员)

javax.servlet.Servlet,先搞个kfc机构出来–引例#MyServlet类

package sedion.jeffli.servlet;

import java.io.IOException;
import java.io.PrintWriter;

import javax.servlet.Servlet;
import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.annotation.WebServlet;

@WebServlet(name = "MyServlet"  , urlPatterns = {"/my"})
public class MyServlet implements Servlet{

    private transient ServletConfig servletConfig;
    @Override
    public void destroy() {
        // TODO Auto-generated method stub
        
    }

    @Override
    public ServletConfig getServletConfig() {
        // TODO Auto-generated method stub
        return servletConfig;
    }

    @Override
    public String getServletInfo() {
        // TODO Auto-generated method stub
        return "M 0333333333y Servlet";
    }

    @Override
    public void init(ServletConfig arg0) throws ServletException {
        // TODO Auto-generated method stub
        this.servletConfig = arg0;
    }

    @Override
    public void service(ServletRequest arg0, ServletResponse arg1)
            throws ServletException, IOException {
        String servletName = servletConfig.getServletName();
        arg1.setContentType("text/html");
        PrintWriter writer = arg1.getWriter();
        writer.print("<html><head></head><body>Helo:"+servletName+"</body></html>");
        
    }

}

然后web工程部署,tomcat 7 注意要7以上。然后火狐敲入:http://localhost:8080/web工程名/my

解剖源码(小生温州医科大学,恶心一下提提神)

@WebServlet(name = “MyServlet” , urlPatterns = {“/my”})

WebServlet注解类型来声明一个Servlet。在声明同时,还可以告诉容器那个URL调用这个Serlvet。name是可选的,urlPattern也是可选的,但几乎都会用到它。urlPattern告诉容器,/my模式可以调用这个MyServlet。(一个通道,方便购买全家桶,小姐你要吗?),自然用web.xml部署描述也是可以的。

MyServlet类,重写了Servlet类中的init→service→destroy方法,属于Servlet生命周期方法。

其中service方法,会根据请求的方法,进行调用下面的方法。

 

#ServletRequest

getParameter最常用的方法。通常用来返回一个html表单域的值。也可以用来获取查询字符串的值。例,利用URL调用:http://localhost:8080/servletTest/my?id=jeffliId 

就可以用下面语句来获取id的值:String id = request.getParameter(“id”);


#ServletResponse

在调用service方法之前,Servlet容器会创建一个ServletResponse。其中定义的getWriter()方法,返回可将文本床给客户端java.io.PrintWriter。默认PrintWriter对象采用ISO-8859-1编码。

#ServletConfig
@WebServlet中以下面这种方式初始化值,值由:键和值。

@WebServlet(
        name = "MyServlet"  ,
        urlPatterns = {"/my"},
        initParams = {
            @WebInitParam(name="jeffliName",value="QiangqiangLi")
        }
    )

#ServletContext
每个web应用只有一个context,在分布式环境下,一个应用可以同时分布到多个容器,并且每个java虚拟机都有一个ServletCotext对象。 
    
javax.servlet.GenericServlet

这个抽象类给我们完成了Servlet接口中所有方法提供默认实现等,方便了我们用。如果MyServlet类继承GenericServlet,我们只需要重写service方法即可。(小姐,你坐着不用走普通流程,我们给你绿色通道。全家桶马上来!)


Http Servlet

Http Servlet两种特性:

1)不覆盖service方法,而是覆盖doGet,doPost。极少情况下,覆盖doHead,doPut,doTrace,doOptions或doDelete。

2)用HttpServletRequest和HttpServletResponse 代替 ServletRequest 和ServletResponse。因为将请求相应对象分别从Servlet容器向下转换。


HttpServletRequest

HttpSession getSession() 返回session对象,没有的话,创建新的session对象。


四.Servlet完成一次请求处理的过程

从客户端(Guest)向服务器发送HTTP请求,该HTTP请求传递给Servlet Container。该Container负责:

分析HTTP请求的信息,并新建request对象,将HTTP请求中的信息放入request对象

    • 新建response对象
    • 根据web.xml(或注释生成),查找URL对应的Servlet对象。如果Servlet对象不存在,则新建相应Servlet对象。
    • 创建新的线程,用于处理本次请求。线程拥有指向request和response对象的引用。

五.参考文献[在此感谢]

http://book.douban.com/doulist/3575997/

http://www.cnblogs.com/vamei/archive/2013/05/12/3073932.html

泥瓦匠学习的代码都在github上(同步osc git),欢迎大家点star,提意见,一起进步。地址:https://github.com/JeffLi1993

Writer      :BYSocket(泥沙砖瓦浆木匠)

微         博:BYSocket

豆         瓣:BYSocket

FaceBook:BYSocket

Twitter    :BYSocket