您的位置:首页 > 职场人生

java面试题(有些是转载)

2016-07-26 16:19 225 查看
一、数据库连接池的作用

数据库连接池技术带来的优势:

1)资源重用

由于数据库连接得到重用,避免了频繁创建、释放连接引起的大量性能开销。在减少系统消耗的基础上,另一方面也增进了系统运行环境的平稳性(减少内存碎片以及数据库临时进程线程的数量)。

2)更快的系统响应速度

数据库连接池在初始化过程中,往往已经创建了若干数据库连接置于池中备用。此时连接的初始化工作均已完成。对于业务请求处理而言,直接利用现有可用连接,避免了数据库连接初始化和释放过程的时间开销,从而缩减了系统整体响应时间。

3)新的资源分配手段

对于多应用共享同一数据库的系统而言,可在应用层通过数据库连接的配置,实现数据库连接池技术,几年钱也许还是个新鲜话题,对于目前的业务系统而言,如果设计中还没有考虑到连接池的应用,那么…….快在设计文档中加上这部分的内容吧。某一应用最大可用数据

库连接数的限制,避免某一应用独占所有数据库资源。

4)统一的连接管理,避免数据库连接泄漏

在较为完备的数据库连接池实现中,可根据预先的连接占用超时设定,强制收回被占用连接。从而避免了常规数据库连接操作中可能出现的资源泄漏。

二、java中有没有内存泄漏?

首先,什么叫内存泄露?就是一个对象已经不再使用,但却仍然占据着内存得不到释放。虽然JVM获得的物理内存有限,但大量的内存泄露会导致Java项目运行时效率下降,还有可能抛出OutOfMemory异常。

举个例子:

Vector v = new  Vector( 10 );
for  ( int  i = 1 ;i < 10 ; i ++ ){
Object o = new  Object();
v.add(o);
o = null ;
}

在这个例子里面,Vector里面装了一些对象,当o=null时,对象o已经不再使用,原来o所在内存区域已经无法取得,但由于v中有o的引用,即使o不再引用那块地址,系统的GC机制也不会释放掉o所在的内存,这就发生了内存泄露。
当然,DTS中物理连接(文件、数据库、网络)未显式关闭,也会造成内存泄露。

三、String,StringBuffer,StringBuilder的区别

(1)都是final的,不能被继承。
(2)String长度不可变,另外两个长度是可变的(例如StringBuffer有append方法)
(3)StringBuffer是线程同步的,里面的每一个API都添加了synchronized修饰,而StringBuilder不是线程同步的,因此拥有更好的性能。

四、String有重写Object的hashCode()和toString()方法吗?如果重写equals不重写hashcode会怎么样?
有。Object.java的hashCode是native方法,应该是调用了C++的代码。

/**
* Returns a hash code for this string. The hash code for a
* {@code String} object is computed as
* <blockquote><pre>
* s[0]*31^(n-1) + s[1]*31^(n-2) + ... + s[n-1]
* </pre></blockquote>
* using {@code int} arithmetic, where {@code s[i]} is the
* <i>i</i>th character of the string, {@code n} is the length of
* the string, and {@code ^} indicates exponentiation.
* (The hash value of the empty string is zero.)
*
* @return  a hash code value for this object.
*/
public int hashCode() {
int h = hash;
if (h == 0 && value.length > 0) {
char val[] = value;

for (int i = 0; i < value.length; i++) {
h = 31 * h + val[i];
}
hash = h;
}
return h;
}

toString()方法返回的是该对象本身。
重写equals不重写hashCode会怎样?
还是看HashMap.java的代码片段:

while ((e = e.next) != null) {
if (e.hash == h &&
((ek = e.key) == key || (ek != null && key.equals(ek))))
return e.val;
}

所以底层判断的原则是先看hashcode是不是相等,再看equals是不是相等。如果重写equals不重写hashcode,就有可能出现两个对象的hashCode不相等,但equals相等,这时HashMap就get不出对应的value,虽然用户重写equals,用自己的逻辑已经判定是“同一对象”。
(如果你认为这两个对象不相同,那么你重写equals令其相同做什么?)

五、Java的序列化
(1)什么是Java的序列化?有什么用?
将一个对象转换成与平台无关的二进制流储存在外存储器中,或在网络中传输。其他程序一旦获得这个二进制流(从文件、从数据库、从网络等)就可以将其转化为Java对象进行操作。
举例:Java的远程方法调用(RMI)就是序列化的具体应用。RMI可以让一个JVM上的对象调用其他JVM上对象的方法。RMI是J2EE的基础。
也可以将一个JavaBean序列化后存储在数据库中。
(2)怎么实现Java的序列化和反序列化:
让一个类实现Serializable接口:

public class Student implements Serializable{
private int id;

public int getId() {
return id;
}

public void setId(int id) {
this.id = id;
}

public Student(int id) {
this.id = id;
}
}

然后使用ObjectOutputStream流写入这个类:

public static void main(String[] args) {
try {
ObjectOutputStream s = new ObjectOutputStream(new FileOutputStream("E:\\1.txt"));
Student s1 = new Student(1);
s.writeObject(s1);
s.close();
} catch (FileNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}

}

运行程序,发现生成了E:\1.txt文件,其中存储的就是s1对象。

接下来反序列化:

public static void main(String[] args) {
try {
ObjectInputStream s = new ObjectInputStream(new FileInputStream("E:\\1.txt"));
Student s1 = (Student)s.readObject();
System.out.println(s1.getId());
s.close();
} catch (FileNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (ClassNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}

}

运行程序,发现s1的学号1已经被读出。
自定义序列化:在特殊情况下,我们有时候不能按照java底层的默认机制对一个对象序列化。这时我们需要在序列化的类中添加readObject()和writeObject()方法,按照自己的逻辑进行序列化和反序列化。如果还需要引用默认序列化方法,则分别调用defaultReadObject()和defaultWriteObject().
有时候我们不能将所有属性都序列化(例如密码等敏感信息),这时需要在不想序列化的属性前面添加transient关键字。
注意:(1)static修饰的属性不能被序列化;(2)如果被序列化对象的属性里面有对象(有点绕),要保证这个对象也是可序列化的。(3)对象的类名,属性会被序列化,而方法不会被序列化。(4)反序列化时要有序列化对象的.class文件,否则强转时会报错。
(5)最好显示声明serialVersionUID:
private static final long serialVersionUID = 7247714666080613254L;

因为不同JVM有可能对同一个类生成的serialVersionUID不同,也可能该类的属性改变,这都会导致反序列化不回去,但如果人为指定了serialVersionUID,就不存在上述情况。
常见的序列化协议:XML,JSON

六、Java如何实现多线程?
这个基本上是必问的了,有3种方式:继承Thread类重写run函数,实现Runnable接口,实现Callable接口。
三种方式有什么区别?
继承Thread类,重写run方法,并new这个类调用start方法。(因为java没有多继承,所以这种方式用的很少)
实现Runnable接口,也重写run方法,并使用new Thread(MyThread).start()执行线程。
实现Callable接口和Runnable差不多,实现的是call方法,但可以有返回值。

七、 线程安全

什么是线程安全?如果一个类在多线程访问的情况下,其行为永远与预期一致,就称为线程安全。
反例:如果一个ArrayList类的addItem方法如下实现:

public void addItem(int item) {
items[Size]=item;
Size++;
}

那么假设有两个线程1和2并发调用这个方法,假设此时数组为空,Size=0。
线程1执行到
items[Size]=item;
的时候系统调度到线程2工作,线程1暂停,那么线程2也执行
items[Size]=item;
语句,那么就会覆盖掉线程1加入的那个数据(因为此时Size都是0),而执行后,Size变成2,数组中却只有1个元素,这就造成了混乱。
如何保证线程安全?
可以对变量使用volatile修饰;也可以对程序段或方法加synchronized修饰。
非线程安全的类(如ArrayList、HashMap等),不能在多线程中共享,但可以在多线程环境中作为某个线程独享的属性。

八、多线程环境中如何进行信息交互?Object类中的wait(),notify(),notifyAll()方法都是干什么用的?
关于这三个方法,JavaAPI是这么解释的(节选自Object.java):

/**
* Causes the current thread to wait until another thread invokes the
* {@link java.lang.Object#notify()} method or the
* {@link java.lang.Object#notifyAll()} method for this object.
* In other words, this method behaves exactly as if it simply
* performs the call {@code wait(0)}.
* <p>
* The current thread must own this object's monitor. The thread
* releases ownership of this monitor and waits until another thread
* notifies threads waiting on this object's monitor to wake up
* either through a call to the {@code notify} method or the
* {@code notifyAll} method. The thread then waits until it can
* re-obtain ownership of the monitor and resumes execution.
*/
public final void wait() throws InterruptedException {
wait(0);
}
/**
* Wakes up a single thread that is waiting on this object's
* monitor. If any threads are waiting on this object, one of them
* is chosen to be awakened. The choice is arbitrary and occurs at
* the discretion of the implementation. A thread waits on an object's
* monitor by calling one of the {@code wait} methods.
* <p>
* The awakened thread will not be able to proceed until the current
* thread relinquishes the lock on this object. The awakened thread will
* compete in the usual manner with any other threads that might be
* actively competing to synchronize on this object; for example, the
* awakened thread enjoys no reliable privilege or disadvantage in being
* the next thread to lock this object.
* @throws  IllegalMonitorStateException  if the current thread is not
*               the owner of this object's monitor.
* @see        java.lang.Object#notifyAll()
* @see        java.lang.Object#wait()
*/
public final native void notify();
/**
* Wakes up all threads that are waiting on this object's monitor. A
* thread waits on an object's monitor by calling one of the
* {@code wait} methods.
* <p>
* The awakened threads will not be able to proceed until the current
* thread relinquishes the lock on this object. The awakened threads
* will compete in the usual manner with any other threads that might
* be actively competing to synchronize on this object; for example,
* the awakened threads enjoy no reliable privilege or disadvantage in
* being the next thread to lock this object.
* <p>
* This method should only be called by a thread that is the owner
* of this object's monitor. See the {@code notify} method for a
* description of the ways in which a thread can become the owner of
* a monitor.
*/
public final native void notifyAll();

这堆E文翻译过来大概就是说,wait()方法使得持有该对象的锁的线程阻塞掉,而notify()则是唤醒一个等待该对象的线程,notifyAll()是唤醒所有等待该对象的线程。
调用wait()方法必须在同步块或者同步方法中进行(synchronized块或者synchronized方法)。
这里要注意一点:notify()和notifyAll()方法只是唤醒等待该对象的monitor的线程,并不决定哪个线程能够获取到monitor。而如果有多个线程等待该对象,则notify()方法唤醒的具体是哪一个则由JVM底层的进程调度决定。

九、 多线程共用一个变量需注意什么?
如果我们实例化了一个实现Runnable接口的类的对象代表一个线程,这个类中定义了全局变量且run方法会修改变量时,如果有多个线程同时修改这个变量,就会出现异常情况。(因为全局信息被并行的修改会造成错误。)
而ThreadLocal解决了这个问题。ThreadLocal类可以封装一个对象进去,被ThreadLocal封装的对象对每个线程来说是独享的,也就是说即使 被ThreadLocal封装的对象是全局的,它也会保证在各个线程间独立。
接下来我们简单的研究一下ThreadLocal.java的源码。
ThreadLocal提供了三个API,get(),set(),remove()。分别用于取出线程本地变量、设置、清空。
ThreadLocal底层维护了一个ThreadLocalMap,它是Thread类的一个属性。所以每个ThreadLocalMap均与当前线程一一对应,再里面则是一个Entry[]数组,数组下标为当前线程的Hash值,对应的Entry对象里面封装了Object value。

public T get() {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null) {
ThreadLocalMap.Entry e = map.getEntry(this);
if (e != null) {
@SuppressWarnings("unchecked")
T result = (T)e.value;
return result;
}
}
return setInitialValue();
}

所以get方法的具体实现逻辑是,先获得当前线程,再把当前的ThreadLocal对象本身(其中包含被封装对象在每个线程中的版本)传进去,如果当前线程的Hash值命中,且对应下标中存有Entry对象,则返回这个对象,再取出封装在里面的value,强制转换并返回。
set方法的实现细节是:

public void set(T value) {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null)
map.set(this, value);
else
createMap(t, value);
}

这个跟get差不多,如果当前线程中有ThreadLocalMap,则把value放进对应的下标中去。当然如果这个下标有可能在数组中不存在或者出现重复,这就要rehash了,在这里不做讨论。
ThreadLocalMap的set实现细节如下:

private void set(ThreadLocal<?> key, Object value) {

// We don't use a fast path as with get() because it is at
// least as common to use set() to create new entries as
// it is to replace existing ones, in which case, a fast
// path would fail more often than not.

Entry[] tab = table;
int len = tab.length;
int i = key.threadLocalHashCode & (len-1);

for (Entry e = tab[i];
e != null;
e = tab[i = nextIndex(i, len)]) {
ThreadLocal<?> k = e.get();

if (k == key) {
e.value = value;
return;
}

if (k == null) {
replaceStaleEntry(key, value, i);
return;
}
}

tab[i] = new Entry(key, value);
int sz = ++size;
if (!cleanSomeSlots(i, sz) && sz >= threshold)
rehash();
}

接下来是remove方法:

public void remove() {
ThreadLocalMap m = getMap(Thread.currentThread());
if (m != null)
m.remove(this);
}

哈哈,写remove的时候好像作者都变懒了,把get和set里面的前两行压缩到了一行。最后调用了map的remove方法,再看看map的remove方法怎么实现的:

private void remove(ThreadLocal<?> key) {
Entry[] tab = table;
int len = tab.length;
int i = key.threadLocalHashCode & (len-1);
for (Entry e = tab[i];
e != null;
e = tab[i = nextIndex(i, len)]) {
if (e.get() == key) {
e.clear();
expungeStaleEntry(i);
return;
}
}
}

挺简单的,就是Hash命中了以后取消对封装的那个value的引用,然后rehash一次。

接下来还有一个volatile关键字要介绍。用volatile修饰的变量,线程在每次使用变量的时候,都会去内存中读取一下该变量最后的值。这样不同的线程访问同一变量,每次都看到的是最后的值。

十、 mysql索引类型,表类型

普通索引 唯一索引 主键索引 组合索引

MySQL为我们提供了很多表类型供选择,有MyISAM、ISAM、HEAP、BerkeleyDB、InnoDB,MERGE表类型,萝卜白菜各有所爱是不假,可是真正选择何种表类型还是要看业务需要啊,每一种表类型都有其自己的属性和优点。

目前MyISAM与InnoDB是最常用的,http://www.youdiancms.com/info/348.html

MyISAM存储引擎
MyISAM是 默认存储引擎。它基于更老的ISAM代码,但有很多有用的扩展。MyISAM存储引擎的一些特征:
所有数据值先存储低字节。这使得数据机和操作系统分离。二进制轻便性的唯一要求是机器使用补码(如最近20年的机器有的一样)和IEEE浮点格式(在主流机器中也完全是主导的)。唯一不支持二进制兼容性的机器是嵌入式系统。这些系统有时使用特殊的处理器。
先存储数据低字节并不严重地影响速度;数据行中的字节一般是未联合的,从一个方向读未联合的字节并不比从反向读更占用更多的资源。服务器上的获取列值的代码与其它代码相比并不显得时间紧。

大文件(达63位文件长度)在支持大文件的文件系统和操作系统上被支持。
当把删除和更新及插入混合的时候,动态尺寸的行更少碎片。这要通过合并相邻被删除的块,以及若下一个块被删除,就扩展到下一块来自动完成。
每个MyISAM表最大索引数是64。 这可以通过重新编译来改变。每个索引最大的列数是16个。
最大的键长度是1000字节。这也可以通过编译来改变。对于键长度超过250字节的情况,一个超过1024字节的的键块被用上。
BLOB和TEXT列可以被索引。
NULL值被允许在索引的列中。这个占每个键的0-1个字节。
所有数字键值以高字节为先被存储以允许一个更高地索引压缩。
当记录以排好序的顺序插入(就像你使用一个AUTO_INCREMENT列之时),索引树被劈开以便高节点仅包含一个键。这改善了索引树的空间利用率。
每表一个AUTO_INCREMEN列的内部处理。MyISAM为INSERT和UPDATE操作自动更新这一 列。这使得AUTO_INCREMENT列更快(至少10%)。在序列顶的值被删除之后就不能再利用。(当AUTO_INCREMENT列被定义为多列索 引的最后一列,可以出现重使用从序列顶部删除的值的情况 )。AUTO_INCREMENT值可用ALTER TABLE或myisamch来重置。
如果数据文件中间的表没有自由块了,在其它线程从表读的同时,你可以INSERT新行到表中。(这被认识为并发操作 )。自由块的出现是作为删除行的结果,或者是用比当前内容多的数据对动态长度行更新的结果。当所有自由块被用完(填满),未来的插入又变成并发。
你可以把数据文件和索引文件放在不同目录,用DATA DIRECTORY和INDEX DIRECTORY选项CREATE TABLE以获得更高的速度,请参阅13.1.5节,“CREATE TABLE语法”。
每个字符列可以又不同的字符集。
在MyISAM索引文件里又一个标志,它表明表是否被正确关闭。如果用--myisam-recover选项启动mysqld,MyISAM表在打开得时候被自动检查,如果被表被不恰当地关闭,就修复表。
如果你用--update-state选项运行myisamchk,它标注表为已检查。myisamchk --fast只检查那些没有这个标志的表。
myisamchk --analyze为部分键存储统计信息,也为整个键存储统计信息。
myisampack可以打包BLOB和VARCHAR列。

MyISAM也支持下列特征:

支持true VARCHAR类型;VARCHAR列以存储在2个字节中的长度来开始。
有VARCHAR的表可以有固定或动态记录长度。
VARCHAR和CHAR列可以多达64KB。
一个被搞乱的已计算索引对可对UNIQUE来使用。这允许你在表内任何列的合并上有UNIQUE。(尽管如此,你不能在一个UNIQUE已计算索引上搜索)。

InnoDB存储引擎
InnoDB给MySQL提供 了具有提交,回滚和崩溃恢复能力的事务安全(ACID兼容)存储引擎。InnoDB锁定在行级并且也在SELECT语句提供一个Oracle风格一致的非 锁定读。这些特色增加 了多用户部署和性能。没有在InnoDB中扩大锁定的需要,因为在InnoDB中行级锁定适合非常小的空间。InnoDB也支持FOREIGN KEY强制。在SQL查询中,你可以自由地将InnoDB类型的表与其它MySQL的表的类型混合起来,甚至在同一个查询中也可以混合。
InnoDB是为处理巨大数据量时的最大性能设计。它的CPU效率可能是任何其它基于磁盘的关系数据库引擎所不能匹敌的。

InnoDB存储引擎被完全与MySQL服务器整合,InnoDB存储引擎为在主内存中缓存数据和索引而维持它自己的缓冲池。 InnoDB存储它的表&索引在一个表空间中,表空间可以包含数个文件(或原始磁盘分区)。这与MyISAM表不同,比如在MyISAM表中每个表被存在 分离的文件中。InnoDB 表可以是任何尺寸,即使在文件尺寸被限制为2GB的操作系统上。

InnoDB默认地被包含在MySQL二进制分发中。Windows Essentials installer使InnoDB成为Windows上MySQL的 默认表。

InnoDB被用来在众多需要高性能的大型数据库站点上产生。著名的Internet新闻站点Slashdot.org运行在 InnoDB上。Mytrix, Inc.在InnoDB上存储超过1TB的数据,还有一些其它站点在InnoDB上处理平均每秒800次插入/更新的负荷。

InnoDB和MyISAM的区别
区别概述:
MyISAM 是MySQL中默认的存储引擎,一般来说不是有太多人关心这个东西。决定使用什么样的存储引擎是一个很tricky的事情,但是还是值我们去研究一下,这里的文章只考虑 MyISAM 和InnoDB这两个,因为这两个是最常见的。

下面先让我们回答一些问题:
你的数据库有外键吗?
你需要事务支持吗?
你需要全文索引吗?
你经常使用什么样的查询模式?
你的数据有多大?

思考上面这些问题可以让你找到合适的方向,但那并不是绝对的。如果你需要事务处理或是外键,那么InnoDB 可能是比较好的方式。如果你需要全文索引,那么通常来说 MyISAM是好的选择,因为这是系统内建的,然而,我们其实并不会经常地去测试两百万行记录。所以,就算是慢一点,我们可以通过使用Sphinx从 InnoDB中获得全文索引。

数据的大小,是一个影响你选择什么样存储引擎的重要因素,大尺寸的数据集趋向于选择InnoDB方式,因为其支持事务处理和故障恢复。数据库的在小 决定了故障恢复的时间长短,InnoDB可以利用事务日志进行数据恢复,这会比较快。而MyISAM可能会需要几个小时甚至几天来干这些事,InnoDB 只需要几分钟。

您操作数据库表的习惯可能也会是一个对性能影响很大的因素。比如: COUNT() 在 MyISAM 表中会非常快,而在InnoDB 表下可能会很痛苦。而主键查询则在InnoDB下会相当相当的快,但需要小心的是如果我们的主键太长了也会导致性能问题。大批的inserts 语句在MyISAM下会快一些,但是updates 在InnoDB 下会更快一些——尤其在并发量大的时候。

所以,到底你检使用哪一个呢?根据经验来看,如果是一些小型的应用或项目,那么MyISAM 也许会更适合。当然,在大型的环境下使用MyISAM 也会有很大成功的时候,但却不总是这样的。如果你正在计划使用一个超大数据量的项目,而且需要事务处理或外键支持,那么你真的应该直接使用InnoDB方 式。但需要记住InnoDB 的表需要更多的内存和存储,转换100GB 的MyISAM 表到InnoDB 表可能会让你有非常坏的体验。

区别总结:
1.InnoDB不支持FULLTEXT类型的索引。
2.InnoDB 中不保存表的具体行数,也就是说,执行select count(*) from table时,InnoDB要扫描一遍整个表来计算有多少行,但是MyISAM只要简单的读出保存好的行数即可。注意的是,当count(*)语句包含 where条件时,两种表的操作是一样的。
3.对于AUTO_INCREMENT类型的字段,InnoDB中必须包含只有该字段的索引,但是在MyISAM表中,可以和其他字段一起建立联合索引。
4.DELETE FROM table时,InnoDB不会重新建立表,而是一行一行的删除。
5.LOAD TABLE FROM MASTER操作对InnoDB是不起作用的,解决方法是首先把InnoDB表改成MyISAM表,导入数据后再改成InnoDB表,但是对于使用的额外的InnoDB特性(例如外键)的表不适用。

另外,InnoDB表的行锁也不是绝对的,如果在执行一个SQL语句时MySQL不能确定要扫描的范围,InnoDB表同样会锁全表,例如update table set num=1 where name like “%aaa%”

提升InnoDB性能的方法:
MyISAM和InnoDB存储引擎性能差别并不是很大,针对InnoDB来说,影响性能的主要是 innodb_flush_log_at_trx_commit 这个选项,如果设置为1的话,那么每次插入数据的时候都会自动提交,导致性能急剧下降,应该是跟刷新日志有关系,设置为0效率能够看到明显提升,当然,同 样你可以SQL中提交“SET AUTOCOMMIT = 0”来设置达到好的性能。另外,还听说通过设置innodb_buffer_pool_size能够提升InnoDB的性能,但是我测试发现没有特别明显 的提升。

基本上我们可以考虑使用InnoDB来替代我们的MyISAM引擎了,因为InnoDB自身很多良好的特点,比如事务支持、存储 过程、视图、行级锁定等等,在并发很多的情况下,相信InnoDB的表现肯定要比MyISAM强很多,当然,相应的在my.cnf中的配置也是比较关键 的,良好的配置,能够有效的加速你的应用。
任何一种表都不是万能的,只用恰当的针对业务类型来选择合适的表类型,才能最大的发挥MySQL的性能优势。

十一、http请求的过程

HTTP通信机制是在一次完整的HTTP通信过程中,Web浏览器与Web服务器之间将完成下列7个步骤:

1. 建立TCP连接
在HTTP工作开始之前,Web浏览器首先要通过网络与Web服务器建立连接,该连接是通过TCP来完成的,该协议与IP协议共同构建Internet,即著名的TCP/IP协议族,因此Internet又被称作是TCP/IP网络。HTTP是比TCP更高层次的应用层协议,根据规则,只有低层协议建立之后才能进行更高层协议的连接,因此,首先要建立TCP连接,一般TCP连接的端口号是80。

2. Web浏览器向Web服务器发送请求命令
一旦建立了TCP连接,Web浏览器就会向Web服务器发送请求命令。例如:GET/sample/hello.jsp HTTP/1.1。

3. Web浏览器发送请求头信息
浏览器发送其请求命令之后,还要以头信息的形式向Web服务器发送一些别的信息,之后浏览器发送了一空白行来通知服务器,它已经结束了该头信息的发送。

4. Web服务器应答
客户机向服务器发出请求后,服务器会客户机回送应答, HTTP/1.1 200 OK ,应答的第一部分是协议的版本号和应答状态码。

5. Web服务器发送应答头信息
正如客户端会随同请求发送关于自身的信息一样,服务器也会随同应答向用户发送关于它自己的数据及被请求的文档。

6. Web服务器向浏览器发送数据
Web服务器向浏览器发送头信息后,它会发送一个空白行来表示头信息的发送到此为结束,接着,它就以Content-Type应答头信息所描述的格式发送用户所请求的实际数据。

7. Web服务器关闭TCP连接
一般情况下,一旦Web服务器向浏览器发送了请求数据,它就要关闭TCP连接,然后如果浏览器或者服务器在其头信息加入了这行代码:Connection:keep-alive

TCP连接在发送后将仍然保持打开状态,于是,浏览器可以继续通过相同的连接发送请求。保持连接节省了为每个请求建立新连接所需的时间,还节约了网络带宽。

TCP建立连接的过程是三次握手

在TCP/IP协议中,TCP协议提供可靠的连接服务,采用三次握手建立一个连接。
第一次握手:建立连接时,客户端发送syn包(syn=j)到服务器,并进入SYN_SEND状态,等待服务器确认;

第二次握手:服务器收到syn包,必须确认客户的SYN(ack=j+1),同时自己也发送一个SYN包(syn=k),即SYN+ACK包,此时服务器进入SYN_RECV状态;

第三次握手:客户端收到服务器的SYN+ACK包,向服务器发送确认包ACK(ack=k+1),此包发送完毕,客户端和服务器进入ESTABLISHED状态,完成三次握手。 完成三次握手,客户端与服务器开始传送数据.



十二、Hibernate数据对象的状态



Hibernate中的对象有3种状态:Transient(瞬时),Persistent(持久)和Detached(托管)。

1 瞬时状态:

由new命令开辟内存空间的java对象,例如Student stu = new Student();如果没有变量对他引用,它将被JVM回收。瞬时对象在内存中孤立存在,他的意义仅是携带信息的载体,不和数据库中的数据有任何的关联。通过session的save()和saveOrUpdate()方法可以把一个瞬时对象与数据库相关联,并把瞬时对象携带的信息通过配置文件所做的映射插入到数据库中,这个瞬时对象就成了持久化对象,拥有和数据库记录相同的id标示(Hibernate自动将id赋予它)

瞬时对象的特点是:

(1)不和Session实例关联

(2)在数据库中没有和瞬时对象关联的记录

2 持久化状态:

持久的实例在数据库中有对应的记录,拥有一个持久化标识。持久对象总是与Session和Transaction相关联,在一个Session中,对持久对象的改变不会马上对数据库进行变更,而必须在Transaction终止,也就是执行commit()之后,才在数据库中真正运行SQL进行变更,持久对象的状态才会与数据库进行同步。在同步之前的持久对象成为脏(dirty)对象。

使用find(),get(),load()和iterater()等方法查询到得数据对象,都是持久化对象;如果一个瞬时对象被持久对象引用,则该对象也会自动变为持久对象。

如果使用delete()方法,它就会变为瞬时对象;当一个Session执行close()或clear()、evict()之后,持久对象就会变为托管对象。

持久对象的特点:

(1)和Session实例关联

(2)在数据库中有和持久对象关联的记录

3 托管状态:

与持久对象关联的Session被关闭后,对象就变为托管的。对托管对象的引用依然有效,对象可继续被修改。托管对象如果重新关联到某个新的Session上,会再次转变为持久的。托管状态期间的改动将会被持久化到数据库。

托管状态拥有数据库标识id,所以它可以通过update()、saveOrUpdate()和lock()等方法,再度与持久层关联。

托管对象的特点:

(1)本质上和瞬时对象相同

(2)只是比瞬时对象多了一个数据库记录标识值id

十三、内存排序和数据库排序分页的优缺点

个人理解,

内存排序可以将数据一下子全取出来,放入内存中进行排序,但是占用内存。

数据库排序,每次换页都要从数据库中重新提取数据,但是不占用太多内存。

十四、transient

Java的serialization提供了一种持久化对象实例的机制。当持久化对象时,可能有一个特殊的对象数据成员,我们不想用 serialization机制来保存它。为了在一个特定对象的一个域上关闭serialization,可以在这个域前加上关键字transient。 当一个对象被序列化的时候,transient型变量的值不包括在序列化的表示中,然而非transient型的变量是被包括进去的。

十五、spring ioc容器配置元数据的方式
http://blog.csdn.net/helloboat/article/details/51199319
基于XML文件进行配置。

基于注解进行配置。 @service @repository @controller 自动扫描

基于Java程序进行配置(Spring 3+)

用@component标注Car Person

package com.lovo.config;

import org.springframework.context.annotation.Bean;

import org.springframework.context.annotation.Configuration;

import com.lovo.bean.Car;

import com.lovo.bean.Person;

@Configuration

public class AppConfig {

@Bean

public Car car() {

return new Car("Benz", 320);

}

@Bean

public Person person() {

return new Person("骆昊", 34);

}

}

package com.lovo.config;

import org.springframework.context.annotation.Bean;

import org.springframework.context.annotation.Configuration;

import com.lovo.bean.Car;

import com.lovo.bean.Person;

@Configuration

public class AppConfig {

@Bean

public Car car() {

return new Car("Benz", 320);

}

@Bean

public Person person() {

return new Person("骆昊", 34);

}

}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: