了解下JAVA的Java汇合源码分析:Hashtable源码分析
首先java功能强大的背后是其复杂性,就拿web来说,当今流行的框架有很多,什么struts,spring,jQuery等等,而这无疑增加了java的复杂性。Hashtable简介
Hashtable一样是基于哈希表完成的,一样每一个元素是一个key-value对,其外部也是经由过程单链表办理抵触成绩,容量不敷(凌驾了阀值)时,一样会主动增加。
Hashtable也是JDK1.0引进的类,是线程平安的,能用于多线程情况中。
Hashtable一样完成了Serializable接口,它撑持序列化,完成了Cloneable接口,能被克隆。
HashTable源码分析
Hashtable的源码的良多完成都与HashMap差未几,源码以下(到场了对照具体的正文):
packagejava.util;
importjava.io.*;
publicclassHashtable<K,V>
extendsDictionary<K,V>
implementsMap<K,V>,Cloneable,java.io.Serializable{
//保留key-value的数组。
//Hashtable一样接纳单链表办理抵触,每个Entry实质上是一个单向链表
privatetransientEntry[]table;
//Hashtable中键值对的数目
privatetransientintcount;
//阈值,用于判别是不是必要调剂Hashtable的容量(threshold=容量*加载因子)
privateintthreshold;
//加载因子
privatefloatloadFactor;
//Hashtable被改动的次数,用于fail-fast机制的完成
privatetransientintmodCount=0;
//序列版本号
privatestaticfinallongserialVersionUID=1421746759512286392L;
//指定“容量巨细”和“加载因子”的机关函数
publicHashtable(intinitialCapacity,floatloadFactor){
if(initialCapacity<0)
thrownewIllegalArgumentException("IllegalCapacity:"+
initialCapacity);
if(loadFactor<=0||Float.isNaN(loadFactor))
thrownewIllegalArgumentException("IllegalLoad:"+loadFactor);
if(initialCapacity==0)
initialCapacity=1;
this.loadFactor=loadFactor;
table=newEntry;
threshold=(int)(initialCapacity*loadFactor);
}
//指定“容量巨细”的机关函数
publicHashtable(intinitialCapacity){
this(initialCapacity,0.75f);
}
//默许机关函数。
publicHashtable(){
//默许机关函数,指定的容量巨细是11;加载因子是0.75
this(11,0.75f);
}
//包括“子Map”的机关函数
publicHashtable(Map<?extendsK,?extendsV>t){
this(Math.max(2*t.size(),11),0.75f);
//将“子Map”的全体元素都增加到Hashtable中
putAll(t);
}
publicsynchronizedintsize(){
returncount;
}
publicsynchronizedbooleanisEmpty(){
returncount==0;
}
//前往“一切key”的列举对象
publicsynchronizedEnumeration<K>keys(){
returnthis.<K>getEnumeration(KEYS);
}
//前往“一切value”的列举对象
publicsynchronizedEnumeration<V>elements(){
returnthis.<V>getEnumeration(VALUES);
}
//判别Hashtable是不是包括“值(value)”
publicsynchronizedbooleancontains(Objectvalue){
//注重,Hashtable中的value不克不及是null,
//如果null的话,抛出非常!
if(value==null){
thrownewNullPointerException();
}
//从后向前遍历table数组中的元素(Entry)
//关于每一个Entry(单向链表),逐一遍历,判别节点的值是不是即是value
Entrytab[]=table;
for(inti=tab.length;i-->0;){
for(Entry<K,V>e=tab;e!=null;e=e.next){
if(e.value.equals(value)){
returntrue;
}
}
}
returnfalse;
}
publicbooleancontainsValue(Objectvalue){
returncontains(value);
}
//判别Hashtable是不是包括key
publicsynchronizedbooleancontainsKey(Objectkey){
Entrytab[]=table;
//盘算hash值,间接用key的hashCode取代
inthash=key.hashCode();
//盘算在数组中的索引值
intindex=(hash&0x7FFFFFFF)%tab.length;
//找到“key对应的Entry(链表)”,然后在链表中找出“哈希值”和“键值”与key都相称的元素
for(Entry<K,V>e=tab;e!=null;e=e.next){
if((e.hash==hash)&&e.key.equals(key)){
returntrue;
}
}
returnfalse;
}
//前往key对应的value,没有的话前往null
publicsynchronizedVget(Objectkey){
Entrytab[]=table;
inthash=key.hashCode();
//盘算索引值,
intindex=(hash&0x7FFFFFFF)%tab.length;
//找到“key对应的Entry(链表)”,然后在链表中找出“哈希值”和“键值”与key都相称的元素
for(Entry<K,V>e=tab;e!=null;e=e.next){
if((e.hash==hash)&&e.key.equals(key)){
returne.value;
}
}
returnnull;
}
//调剂Hashtable的长度,将长度酿成本来的2倍+1
protectedvoidrehash(){
intoldCapacity=table.length;
Entry[]oldMap=table;
//创立新容量巨细的Entry数组
intnewCapacity=oldCapacity*2+1;
Entry[]newMap=newEntry;
modCount++;
threshold=(int)(newCapacity*loadFactor);
table=newMap;
//将“旧的Hashtable”中的元素复制到“新的Hashtable”中
for(inti=oldCapacity;i-->0;){
for(Entry<K,V>old=oldMap;old!=null;){
Entry<K,V>e=old;
old=old.next;
//从头盘算index
intindex=(e.hash&0x7FFFFFFF)%newCapacity;
e.next=newMap;
newMap=e;
}
}
}
//将“key-value”增加到Hashtable中
publicsynchronizedVput(Kkey,Vvalue){
//Hashtable中不克不及拔出value为null的元素!!!
if(value==null){
thrownewNullPointerException();
}
//若“Hashtable中已存在键为key的键值对”,
//则用“新的value”交换“旧的value”
Entrytab[]=table;
inthash=key.hashCode();
intindex=(hash&0x7FFFFFFF)%tab.length;
for(Entry<K,V>e=tab;e!=null;e=e.next){
if((e.hash==hash)&&e.key.equals(key)){
Vold=e.value;
e.value=value;
returnold;
}
}
//若“Hashtable中不存在键为key的键值对”,
//将“修正统计数”+1
modCount++;
//若“Hashtable实践容量”>“阈值”(阈值=总的容量*加载因子)
//则调剂Hashtable的巨细
if(count>=threshold){
rehash();
tab=table;
index=(hash&0x7FFFFFFF)%tab.length;
}
//将新的key-value对拔出到tab处(即链表的头结点)
Entry<K,V>e=tab;
tab=newEntry<K,V>(hash,key,value,e);
count++;
returnnull;
}
//删除Hashtable中键为key的元素
publicsynchronizedVremove(Objectkey){
Entrytab[]=table;
inthash=key.hashCode();
intindex=(hash&0x7FFFFFFF)%tab.length;
//从table链表中找出要删除的节点,并删除该节点。
//由于是单链表,因而要保存带删省点的前一个节点,才干无效地删除节点
for(Entry<K,V>e=tab,prev=null;e!=null;prev=e,e=e.next){
if((e.hash==hash)&&e.key.equals(key)){
modCount++;
if(prev!=null){
prev.next=e.next;
}else{
tab=e.next;
}
count--;
VoldValue=e.value;
e.value=null;
returnoldValue;
}
}
returnnull;
}
//将“Map(t)”的中全体元素一一增加到Hashtable中
publicsynchronizedvoidputAll(Map<?extendsK,?extendsV>t){
for(Map.Entry<?extendsK,?extendsV>e:t.entrySet())
put(e.getKey(),e.getValue());
}
//清空Hashtable
//将Hashtable的table数组的值全体设为null
//本栏目更多出色内容:http://www.bianceng.cn/Programming/Java/
publicsynchronizedvoidclear(){
Entrytab[]=table;
modCount++;
for(intindex=tab.length;--index>=0;)
tab=null;
count=0;
}
//克隆一个Hashtable,并以Object的情势前往。
publicsynchronizedObjectclone(){
try{
Hashtable<K,V>t=(Hashtable<K,V>)super.clone();
t.table=newEntry;
for(inti=table.length;i-->0;){
t.table=(table!=null)
?(Entry<K,V>)table.clone():null;
}
t.keySet=null;
t.entrySet=null;
t.values=null;
t.modCount=0;
returnt;
}catch(CloneNotSupportedExceptione){
thrownewInternalError();
}
}
publicsynchronizedStringtoString(){
intmax=size()-1;
if(max==-1)
return"{}";
StringBuildersb=newStringBuilder();
Iterator<Map.Entry<K,V>>it=entrySet().iterator();
sb.append({);
for(inti=0;;i++){
Map.Entry<K,V>e=it.next();
Kkey=e.getKey();
Vvalue=e.getValue();
sb.append(key==this?"(thisMap)":key.toString());
sb.append(=);
sb.append(value==this?"(thisMap)":value.toString());
if(i==max)
returnsb.append(}).toString();
sb.append(",");
}
}
//猎取Hashtable的列举类对象
//若Hashtable的实践巨细为0,则前往“空列举类”对象;
//不然,前往一般的Enumerator的对象。
private<T>Enumeration<T>getEnumeration(inttype){
if(count==0){
return(Enumeration<T>)emptyEnumerator;
}else{
returnnewEnumerator<T>(type,false);
}
}
//猎取Hashtable的迭代器
//若Hashtable的实践巨细为0,则前往“空迭代器”对象;
//不然,前往一般的Enumerator的对象。(Enumerator完成了迭代器和列举两个接口)
private<T>Iterator<T>getIterator(inttype){
if(count==0){
return(Iterator<T>)emptyIterator;
}else{
returnnewEnumerator<T>(type,true);
}
}
//Hashtable的“key的汇合”。它是一个Set,没有反复元素
privatetransientvolatileSet<K>keySet=null;
//Hashtable的“key-value的汇合”。它是一个Set,没有反复元素
privatetransientvolatileSet<Map.Entry<K,V>>entrySet=null;
//Hashtable的“key-value的汇合”。它是一个Collection,能够有反复元素
privatetransientvolatileCollection<V>values=null;
//前往一个被synchronizedSet封装后的KeySet对象
//synchronizedSet封装的目标是对KeySet的一切办法都增加synchronized,完成多线程同步
publicSet<K>keySet(){
if(keySet==null)
keySet=Collections.synchronizedSet(newKeySet(),this);
returnkeySet;
}
//Hashtable的Key的Set汇合。
//KeySet承继于AbstractSet,以是,KeySet中的元素没有反复的。
privateclassKeySetextendsAbstractSet<K>{
publicIterator<K>iterator(){
returngetIterator(KEYS);
}
publicintsize(){
returncount;
}
publicbooleancontains(Objecto){
returncontainsKey(o);
}
publicbooleanremove(Objecto){
returnHashtable.this.remove(o)!=null;
}
publicvoidclear(){
Hashtable.this.clear();
}
}
//前往一个被synchronizedSet封装后的EntrySet对象
//synchronizedSet封装的目标是对EntrySet的一切办法都增加synchronized,完成多线程同步
publicSet<Map.Entry<K,V>>entrySet(){
if(entrySet==null)
entrySet=Collections.synchronizedSet(newEntrySet(),this);
returnentrySet;
}
//Hashtable的Entry的Set汇合。
//EntrySet承继于AbstractSet,以是,EntrySet中的元素没有反复的。
privateclassEntrySetextendsAbstractSet<Map.Entry<K,V>>{
publicIterator<Map.Entry<K,V>>iterator(){
returngetIterator(ENTRIES);
}
publicbooleanadd(Map.Entry<K,V>o){
returnsuper.add(o);
}
//查找EntrySet中是不是包括Object(0)
//起首,在table中找到o对应的Entry链表
//然后,查找Entry链表中是不是存在Object
publicbooleancontains(Objecto){
if(!(oinstanceofMap.Entry))
returnfalse;
Map.Entryentry=(Map.Entry)o;
Objectkey=entry.getKey();
Entry[]tab=table;
inthash=key.hashCode();
intindex=(hash&0x7FFFFFFF)%tab.length;
for(Entrye=tab;e!=null;e=e.next)
if(e.hash==hash&&e.equals(entry))
returntrue;
returnfalse;
}
//删除元素Object(0)
//起首,在table中找到o对应的Entry链表
//然后,删除链表中的元素Object
publicbooleanremove(Objecto){
if(!(oinstanceofMap.Entry))
returnfalse;
Map.Entry<K,V>entry=(Map.Entry<K,V>)o;
Kkey=entry.getKey();
Entry[]tab=table;
inthash=key.hashCode();
intindex=(hash&0x7FFFFFFF)%tab.length;
for(Entry<K,V>e=tab,prev=null;e!=null;
prev=e,e=e.next){
if(e.hash==hash&&e.equals(entry)){
modCount++;
if(prev!=null)
prev.next=e.next;
else
tab=e.next;
count--;
e.value=null;
returntrue;
}
}
returnfalse;
}
publicintsize(){
returncount;
}
publicvoidclear(){
Hashtable.this.clear();
}
}
//前往一个被synchronizedCollection封装后的ValueCollection对象
//synchronizedCollection封装的目标是对ValueCollection的一切办法都增加synchronized,完成多线程同步
publicCollection<V>values(){
if(values==null)
values=Collections.synchronizedCollection(newValueCollection(),
this);
returnvalues;
}
//Hashtable的value的Collection汇合。
//ValueCollection承继于AbstractCollection,以是,ValueCollection中的元素能够反复的。
privateclassValueCollectionextendsAbstractCollection<V>{
publicIterator<V>iterator(){
returngetIterator(VALUES);
}
publicintsize(){
returncount;
}
publicbooleancontains(Objecto){
returncontainsValue(o);
}
publicvoidclear(){
Hashtable.this.clear();
}
}
//从头equals()函数
//若两个Hashtable的一切key-value键值对都相称,则判别它们两个相称
publicsynchronizedbooleanequals(Objecto){
if(o==this)
returntrue;
if(!(oinstanceofMap))
returnfalse;
Map<K,V>t=(Map<K,V>)o;
if(t.size()!=size())
returnfalse;
try{
//经由过程迭代器顺次掏出以后Hashtable的key-value键值对
//并判别该键值对,存在于Hashtable中。
//若不存在,则当即前往false;不然,遍历完“以后Hashtable”并前往true。
Iterator<Map.Entry<K,V>>i=entrySet().iterator();
while(i.hasNext()){
Map.Entry<K,V>e=i.next();
Kkey=e.getKey();
Vvalue=e.getValue();
if(value==null){
if(!(t.get(key)==null&&t.containsKey(key)))
returnfalse;
}else{
if(!value.equals(t.get(key)))
returnfalse;
}
}
}catch(ClassCastExceptionunused){
returnfalse;
}catch(NullPointerExceptionunused){
returnfalse;
}
returntrue;
}
//盘算Entry的hashCode
//若Hashtable的实践巨细为0大概加载因子<0,则前往0。
//不然,前往“Hashtable中的每一个Entry的key和value的异或值的总和”。
publicsynchronizedinthashCode(){
inth=0;
if(count==0||loadFactor<0)
returnh;//Returnszero
loadFactor=-loadFactor;//MarkhashCodecomputationinprogress
Entry[]tab=table;
for(inti=0;i<tab.length;i++)
for(Entrye=tab;e!=null;e=e.next)
h+=e.key.hashCode()^e.value.hashCode();
loadFactor=-loadFactor;//MarkhashCodecomputationcomplete
returnh;
}
//java.io.Serializable的写进函数
//将Hashtable的“总的容量,实践容量,一切的Entry”都写进到输入流中
privatesynchronizedvoidwriteObject(java.io.ObjectOutputStreams)
throwsIOException
{
//Writeoutthelength,threshold,loadfactor
s.defaultWriteObject();
//Writeoutlength,countofelementsandthenthekey/valueobjects
s.writeInt(table.length);
s.writeInt(count);
for(intindex=table.length-1;index>=0;index--){
Entryentry=table;
while(entry!=null){
s.writeObject(entry.key);
s.writeObject(entry.value);
entry=entry.next;
}
}
}
//java.io.Serializable的读取函数:依据写进体例读出
//将Hashtable的“总的容量,实践容量,一切的Entry”顺次读出
privatevoidreadObject(java.io.ObjectInputStreams)
throwsIOException,ClassNotFoundException
{
//Readinthelength,threshold,andloadfactor
s.defaultReadObject();
//Readtheoriginallengthofthearrayandnumberofelements
intoriglength=s.readInt();
intelements=s.readInt();
//Computenewsizewithabitofroom5%togrowbut
//nolargerthantheoriginalsize.Makethelength
//oddifitslargeenough,thishelpsdistributetheentries.
//Guardagainstthelengthendingupzero,thatsnotvalid.
intlength=(int)(elements*loadFactor)+(elements/20)+3;
if(length>elements&&(length&1)==0)
length--;
if(origlength>0&&length>origlength)
length=origlength;
Entry[]table=newEntry;
count=0;
//Readthenumberofelementsandthenallthekey/valueobjects
for(;elements>0;elements--){
Kkey=(K)s.readObject();
Vvalue=(V)s.readObject();
//synchcouldbeeliminatedforperformance
reconstitutionPut(table,key,value);
}
this.table=table;
}
privatevoidreconstitutionPut(Entry[]tab,Kkey,Vvalue)
throwsStreamCorruptedException
{
if(value==null){
thrownewjava.io.StreamCorruptedException();
}
//Makessurethekeyisnotalreadyinthehashtable.
//Thisshouldnothappenindeserializedversion.
inthash=key.hashCode();
intindex=(hash&0x7FFFFFFF)%tab.length;
for(Entry<K,V>e=tab;e!=null;e=e.next){
if((e.hash==hash)&&e.key.equals(key)){
thrownewjava.io.StreamCorruptedException();
}
}
//Createsthenewentry.
Entry<K,V>e=tab;
tab=newEntry<K,V>(hash,key,value,e);
count++;
}
//Hashtable的Entry节点,它实质上是一个单向链表。
//也因而,我们才干揣度出Hashtable是由拉链法完成的散列表
privatestaticclassEntry<K,V>implementsMap.Entry<K,V>{
//哈希值
inthash;
Kkey;
Vvalue;
//指向的下一个Entry,即链表的下一个节点
Entry<K,V>next;
//机关函数
protectedEntry(inthash,Kkey,Vvalue,Entry<K,V>next){
this.hash=hash;
this.key=key;
this.value=value;
this.next=next;
}
protectedObjectclone(){
returnnewEntry<K,V>(hash,key,value,
(next==null?null:(Entry<K,V>)next.clone()));
}
publicKgetKey(){
returnkey;
}
publicVgetValue(){
returnvalue;
}
//设置value。若value是null,则抛出非常。
publicVsetValue(Vvalue){
if(value==null)
thrownewNullPointerException();
VoldValue=this.value;
this.value=value;
returnoldValue;
}
//掩盖equals()办法,判别两个Entry是不是相称。
//若两个Entry的key和value都相称,则以为它们相称。
publicbooleanequals(Objecto){
if(!(oinstanceofMap.Entry))
returnfalse;
Map.Entrye=(Map.Entry)o;
return(key==null?e.getKey()==null:key.equals(e.getKey()))&&
(value==null?e.getValue()==null:value.equals(e.getValue()));
}
publicinthashCode(){
returnhash^(value==null?0:value.hashCode());
}
publicStringtoString(){
returnkey.toString()+"="+value.toString();
}
}
privatestaticfinalintKEYS=0;
privatestaticfinalintVALUES=1;
privatestaticfinalintENTRIES=2;
//Enumerator的感化是供应了“经由过程elements()遍历Hashtable的接口”和“经由过程entrySet()遍历Hashtable的接口”。
privateclassEnumerator<T>implementsEnumeration<T>,Iterator<T>{
//指向Hashtable的table
Entry[]table=Hashtable.this.table;
//Hashtable的总的巨细
intindex=table.length;
Entry<K,V>entry=null;
Entry<K,V>lastReturned=null;
inttype;
//Enumerator是“迭代器(Iterator)”仍是“列举类(Enumeration)”的标记
//iterator为true,暗示它是迭代器;不然,是列举类。
booleaniterator;
//在将Enumerator看成迭代器利用时会用到,用来完成fail-fast机制。
protectedintexpectedModCount=modCount;
Enumerator(inttype,booleaniterator){
this.type=type;
this.iterator=iterator;
}
//从遍历table的数组的开端向前查找,直到找到不为null的Entry。
publicbooleanhasMoreElements(){
Entry<K,V>e=entry;
inti=index;
Entry[]t=table;
/*Uselocalsforfasterloopiteration*/
while(e==null&&i>0){
e=t[--i];
}
entry=e;
index=i;
returne!=null;
}
//猎取下一个元素
//注重:从hasMoreElements()和nextElement()能够看出“Hashtable的elements()遍历体例”
//起首,从后向前的遍历table数组。table数组的每一个节点都是一个单向链表(Entry)。
//然后,顺次向后遍历单向链表Entry。
publicTnextElement(){
Entry<K,V>et=entry;
inti=index;
Entry[]t=table;
/*Uselocalsforfasterloopiteration*/
while(et==null&&i>0){
et=t[--i];
}
entry=et;
index=i;
if(et!=null){
Entry<K,V>e=lastReturned=entry;
entry=e.next;
returntype==KEYS?(T)e.key:(type==VALUES?(T)e.value:(T)e);
}
thrownewNoSuchElementException("HashtableEnumerator");
}
//迭代器Iterator的判别是不是存鄙人一个元素
//实践上,它是挪用的hasMoreElements()
publicbooleanhasNext(){
returnhasMoreElements();
}
//迭代器猎取下一个元素
//实践上,它是挪用的nextElement()
publicTnext(){
if(modCount!=expectedModCount)
thrownewConcurrentModificationException();
returnnextElement();
}
//迭代器的remove()接口。
//起首,它在table数组中找出要删除元素地点的Entry,
//然后,删除单向链表Entry中的元素。
publicvoidremove(){
if(!iterator)
thrownewUnsupportedOperationException();
if(lastReturned==null)
thrownewIllegalStateException("HashtableEnumerator");
if(modCount!=expectedModCount)
thrownewConcurrentModificationException();
synchronized(Hashtable.this){
Entry[]tab=Hashtable.this.table;
intindex=(lastReturned.hash&0x7FFFFFFF)%tab.length;
for(Entry<K,V>e=tab,prev=null;e!=null;
prev=e,e=e.next){
if(e==lastReturned){
modCount++;
expectedModCount++;
if(prev==null)
tab=e.next;
else
prev.next=e.next;
count--;
lastReturned=null;
return;
}
}
thrownewConcurrentModificationException();
}
}
}
privatestaticEnumerationemptyEnumerator=newEmptyEnumerator();
privatestaticIteratoremptyIterator=newEmptyIterator();
//空列举类
//当Hashtable的实践巨细为0;此时,又要经由过程Enumeration遍历Hashtable时,前往的是“空列举类”的对象。
privatestaticclassEmptyEnumeratorimplementsEnumeration<Object>{
EmptyEnumerator(){
}
//空列举类的hasMoreElements()一直前往false
publicbooleanhasMoreElements(){
returnfalse;
}
//空列举类的nextElement()抛出非常
publicObjectnextElement(){
thrownewNoSuchElementException("HashtableEnumerator");
}
}
//空迭代器
//当Hashtable的实践巨细为0;此时,又要经由过程迭代器遍历Hashtable时,前往的是“空迭代器”的对象。
privatestaticclassEmptyIteratorimplementsIterator<Object>{
EmptyIterator(){
}
publicbooleanhasNext(){
returnfalse;
}
publicObjectnext(){
thrownewNoSuchElementException("HashtableIterator");
}
publicvoidremove(){
thrownewIllegalStateException("HashtableIterator");
}
}
}几点总结
针对Hashtable,我们一样给出几点对照主要的总结,但要分离与HashMap的对照来总结。
<p>
为什么外国人还要写那些框架进行代码封装,他们不就是为了别人使用时可以更简单么!如果要达到一个企业级项目的不用框架是很难的。小一些的项目还行,大的光是MVC模式的设计的编码量就够大的了。还有性能方面,单轮windows,这个工具是微软写的,。 在全球云计算和移动互联网的产业环境下,Java更具备了显著优势和广阔前景。 关于设计模式的资料,还是向大家推荐banq的网站 http://www.jdon.com/,他把GOF的23种模式以通俗易懂的方式诠释出来,纯Java描述,真是经典中的经典。 象、泛型编程的特性,广泛应用于企业级Web应用开发和移动应用开发。 一直感觉JAVA很大,很杂,找不到学习方向,前两天在网上找到了这篇文章,感觉不错,给没有方向的我指了一个方向,先不管对不对,做下来再说。 你现在最缺的是实际的工作经验,而不是书本上那些凭空想出来的程序。 Pet Store.(宠物店)是SUN公司为了演示其J2EE编程规范而推出的开放源码的程序,应该很具有权威性,想学J2EE和EJB的朋友不要 错过了。 是一种使用者不需花费很多时间学习的语言 其实说这种话的人就如当年小日本号称“三个月拿下中国”一样大言不惭。不是Tomjava泼你冷水,你现在只是学到了Java的骨架,却还没有学到Java的精髓。接下来你得研究设计模式了。
页:
[1]