在Log4J中存在幾個概念首先介紹一下,最最重要的兩個是Logger和Appender(請參考Log4J手冊),其實是繼承層次以及輸出控制。
首先Log4J中總是存在一個rootLogger,即使沒有顯示配置也是存在的,并且默認輸出級別為DEBUG。
其它的logger都繼承自這個rootLogger(如果其他logger未單獨定義其輸出級別)。
Log4J中的層次是用'.'來分隔的,如log4j.logger.com.example.test,這里并不是說log4j.logger后面一定是具體的包名乃至類名,
這個名稱可以自定義,我們甚至可以定義一個log4j.logger.A.B.C, 在com.example.test中的類里取名稱為A.B的logger,如
Logger logger = Logger.getLogger("A.B")
上例中我們建立了3個logger實例,它們分別是"A"、"A.B"、"A.B.C"。每次我們在系統中取得logger時,并不是新建實例,這些實例是
系統啟動的時候就按照配置文件初始化好的(也可能時第一次引用的時候建立的,然后緩存其實例供以后使用,這部分還沒有時間研究)。
限制appender疊加性
例1:
log4j.rootLogger=DEBUG, Console
log4j.logger.A=DEBUG, Console
log4j.logger.A.B=INFO, Console
對于logger A.B輸出的任何日志會往控制臺輸出三次,原因是A.B繼承A的以及A的父logger的所有appender,
這種繼承關系僅僅是把父logger的appender添加到自己的appender列表中,父logger的輸出level不會影響
子logger的輸出。
例2:限制appender疊加
log4j.rootLogger=DEBUG, Console
log4j.logger.A=DEBUG, Console
log4j.logger.A.B=INFO, Console
log4j.additivity.A.B=false
logger A.B的日志僅會輸出到自己Console中,不會繼承任何父logger的appender。
控制appender的輸出級別
若想對輸出到appender中的日志級別進行限制的話,就需要用到threshold來控制。
log4j.threshold=ERROR 用來控制所有的appender,即輸出到所有appender的日志,
不管原來是什么級別的,都不能低于threshold所規定的級別。
log4j.appender.Console.threshold=ERROR 用來控制指定的appender的輸出級別。
本貼將不定期更新,將集合JavaScript使用中常見的問題。
約定:瀏覽器名稱IE/FF(通指Windows平臺上的IE、FF)、IE5.5+/FF2+(后面的數字表示版本,甚至可以有更詳細的版本信息)
目前只測試IE和FF,其他瀏覽器不在測試范圍內,如果我只提及IE的,則說明在IE中有問題,而在FF中沒有問題。
- IE中Class不能跨窗口引用,即在父窗口定義的SomeClass不能在子窗口中實例化,如new opener.SomeClass()。請注意在這里我沒有提到frame的情況,因為我還沒有時間測試,如果哪位有時間測試請告訴我結果。
- IE中delete操作只能刪除JavaScript或自定義對象的屬性,而不能刪除HTML Element對象的屬性,如果想刪除Element的屬性,請使用removeAttribute()方法。
- IE中delete操作只能刪除非var定義的變量。
- IE中如果在一個窗口引用一個對象,如果這個對象是在其他窗口建立的,但是那個窗口已經關閉了,那么對這個對象的引用將會出錯。
如在窗口A中
opener.someClassObj = new SomeClass();
window.close();
窗口B中
typeof someClassObj 結果是 'object', 但是實際引用該對象卻會出錯。
在些JavaScript類定義的時候,大家很可能都寫過下面的代碼:
function A() {}
function B() {}
B.prototype = new A()
上面這樣寫是為了讓instanceof語句能起作用。舉個例子:
1.不重寫子類的prototype屬性
b = new B();
b instanceof B //return true
b instanceof A //
return false
b instanceof Object //return true
2.寫子類的prototype屬性
b = new B();
b instanceof B //return true
b instanceof A //
return true
b instanceof Object //return true
另外,prototype的作用是可以用來模擬繼承,使我們在父類里添加的屬性方法在子類里能訪問。
但是我們可以使用一種其他的方法來變通。
function A(x) {
this.x = x;
this.method1 = functioni () {};
}
function B(x,y) {
A.call(this,x);
this.y = y;
}
b = new B(1, 2)
這時b中絕對有x,并且x 等于1,但是我們為什么還要使用prototype呢?
主要是為了向父類原型動態添加的屬性和方法可以出現在子類的實例對象中。
接著上面的例子
A.prototype.z = function () {}
如果我們沒有設置B.prototype = new A(),則b不會動態添加方法z 。
注:部分摘自《
JavaScript: The Definitive Guide, 5th Edition》
字符型轉換為數值型可以使用下列方法
parseInt(stringVar); //parseInt還可以指定二進制、八進制、或16進制
parseFloat(stringVar);
Number(stringVar);
for example:
parseInt("ff") //will throw a error
parseInt("ff", 16) == parseInt("0xff") // return 255
parseInt("77") //return 77
parseInt("077") == parseInt("77", 8) // return 63
注:加"0"或"0x"前綴會自動檢測并轉換為相應的數制所表示的值
If parseInt( ) or parseFloat( ) cannot convert the specified
string to a number, it returns NaN
數值型轉換為字符型
numberVar + "";
numberVar.toString(); //可以指定二進制、八進制、或16進制
String(numberVar);
other useful method
var n = 123456.789;
n.toFixed(0); // "123457"
n.toFixed(2); // "123456.79"
n.toExponential(1); // "1.2e+5"
n.toExponential(3); // "1.235e+5"
n.toPrecision(4); // "1.235e+5"
n.toPrecision(7); // "123456.8"
其實還有一種方法就是使用new操作符,如
new String(numberVar);
new Numer(stringVar);
但是這種方法返回的結果是object類型,而不是原始數據類型,大家可以酌情使用。
另外,在相等判斷時使用' == '會自動轉型(具體轉換情況請參考其他資料),如果想精確判斷請使用' === '。
如 1 == '1' //return true
1 === '1' //return false
1 == new Number(1) //return true
1 === new Number(1) //return false
數值連接等相關操作
"21" + "2" == "21" + 2 //return 212
2 + "21" //return 221
"21" * "2" == "21" * 2 == 2 * "21" //return 42
"21" / "3" == "21" / 3 == 21 / "3" //return 7
"21" - "2" == "21" - 2 == 21 - "2" == 21 - " 2 " //return 19
正如和Java中一樣,new Number(3) == new Number(3)返回false,同理推廣到其他類型,new操作符總是建立一個新對象,
而==只是比較其引用,并不比較對象本身,所以兩個new的對象的引用總是不同的。所以在通常的邏輯判斷中(如if或while等),
最好避免使用Primitive Datatype Wrapper,而直接使用Primitive Datatype。
From Wrapper to Primitive, for example:
new Number(3).valueOf()
new String("str").valueOf()
new Date().valueOf() //convert a Date to millisecond representation
[any other object].valueOf() //The primitive value associated with the
object,
if any. If there is no value associated with
object, returns the
object itself.
Finally, note that any number, string, or boolean value can be
converted to its corresponding wrapper object with the Object( )
function:
var number_wrapper = Object(3);
Automatic datatype conversions
Value
|
Context in which value is used
|
|
String
|
Number
|
Boolean
|
Object
|
Undefined value
|
"undefined"
|
NaN
|
false
|
Error
|
null
|
"null"
|
0
|
false
|
Error
|
Nonempty string
|
As is
|
Numeric value of string or NaN
|
TRue
|
String object
|
Empty string
|
As is
|
0
|
false
|
String object
|
0
|
"0"
|
As is
|
false
|
Number object
|
NaN
|
"NaN"
|
As is
|
false
|
Number object
|
Infinity
|
"Infinity"
|
As is
|
true
|
Number object
|
Negative infinity
|
"-Infinity"
|
As is
|
TRue
|
Number object
|
Any other number
|
String value of number
|
As is
|
true
|
Number object
|
true
|
"true"
|
1
|
As is
|
Boolean object
|
false
|
"false"
|
0
|
As is
|
Boolean object
|
Object
|
toString( )
|
valueOf( ), toString( ), or
NaN
|
true
|
As is
|
利用上面的表,我們可以知道if ("") 、if (0)、if (undefined)、if (null)、if (NaN),這些if語句的條件都為false.
- 靜態方法要想范化,需要指定其類型參數
- 非范化類型中的實例方法要想范化,也需要制定其類型參數。
- 范化類型中的實力方法可以直接使用其類型本身的類型參數。
- 范型不是協變的,即List<Integer>不是List<Number>的子類。
- 不能實例化范型類型的數組,即
new List<String>[3]
是不合法的,除非類型參數是一個未綁定的通配符,即new List<?>[3]
是合法的。
- 構造延遲,在代碼編寫時我們不能通過類型參數來構造一個該類型的實例,原因是我們不知道如何構造,類型參數的實際類型是在運行時確定的。
- 對于注意5所描述的問題我們有一個解決方法是List<String> list = (List<String>[]) new Object[3];但是如果是運行時建立數組呢,類型信息是運行時確定的,那就換種寫法T[] tarray = (T[]) new Object[3];
- 上面的數組構造是不被推薦的,最好的方法是將類型信息傳遞到方法中,如method(Class<V> type) { V[] array = (V[])Array.newInstance(type, length); },可以參考ArrayList類的toArray(T[] a)方法的實現。
- 構造通配符引用,如果set是一個Set<?>類型,則Set<?> set2 = new HashSet<?>(set);是不合法的,改成Set<?> set2 = new HashSet<Object>(set);就合法了。
最后,推薦三篇IBM上的文章
IBM開發者樂園上的教程(需注冊才能瀏覽,建議注冊一個帳號,IBM網站上有很多好文章),很詳細的介紹了3種粒度模型(對象、屬性、事件)。
Java事件傳遞技術
Map接口常用的實現類有:
1.HashMap
2.Hashtable
3.TreeMap
4.LinkedHashMap
討論1:底層機制
HashMap與Hashtable基于數組實現,TreeMap基于樹型結構,底層存儲結構是典型的鏈表結構。LinkedHashMap繼承自HashMap,所以也是基于數組實現的。
討論2:繼承關系
HashMap與TreeMap繼承自AbstractMap,Hashtable繼承自Dictionary,LinkedHashMap繼承自HashMap。
討論3:同步關系
Hashtable是同步的,而HashMap與TreeMap以及LinkedHashMap不是同步的,可以使用Collections中提供的方法轉換為同步的。
討論4:迭代器
迭代器都是快速失敗的(注:參考本系列第一篇List篇)
討論5:不可修改
通過使用Collections.unmodifiableMap(Map map)來轉換
List的有用實現
1.ArrayList
2.LinkedList
3.Vector
4.Stack
討論1:底層機制(牽扯到的數據結構的知識請讀者自行復習)
ArrayList與Vector都是基于數組實現的,這就說明ArrayList與Vector適合做遍歷而不適合做頻繁的插入和刪除。
LinkedList是基于鏈表實現的,所以它生來就是為了頻繁插入與刪除對象。
討論2:特殊功能
Stack是一個后進先出(LIFO)對象堆棧,而LinkedList除可以被用作堆棧外,還可以被用作隊列或雙端隊列。
不同的是Stack繼承自Vector,也就是說它也是基于數組實現的。
討論3:內存占用
基于數組實現的List,在動態擴展時會產生新的數組,然后把舊數組里的內容復制到新數組里,
這會產生大量的不再被使用的對象引用變量等待系統回收。而基于鏈表實現的List就不會有這種問題。
討論4:同步問題
Vector與Stack生來就是同步的,
而ArrayList與LinkedList需要使用Collections.synchronizedList(List list)方法來轉換成同步List。
從它們的對象上返回的迭代器是快速失敗的,也就是說在使用迭代器進行迭代的時候,必須使用迭代器本身的remove、add、set
方法來添加或更改List元素,如果在迭代的同時,在其他線程中
從結構上修改了List(結構上的修改是指任何添加或刪除一個或多個元素的操作,或者顯式調整底層數組的大小;僅僅設置元素的值不是結構上的修改),快速失敗迭代器會盡最大努力拋出ConcurrentModificationException。
討論5:使用策略
如果數據被從數據源提取,數據量不確定,該數據一經被提取后就幾乎不會再添加或刪除,那么應該建立一個LinkedList來保存從數據源中取出的數據,然后將該LinkedList轉換成ArrayList來優化遍歷操作。反過來,數據量確定的數據從數據源取出可以先建立一個ArrayList來保存,根據需要如需頻繁增刪,就轉換為LinkedList,如頻繁遍歷就不需轉換。
轉換的方法就是使用對應的List類來封裝目標List對象。如
ArrayList al = new ArrayList();
LinkedList ll = new LinkedList(al);
同理反過來也可以
LinkedList ll = new LinkedList();
ArrayList al = new ArrayList(ll);
討論6:toArray()方法
基于數組實現的List會直接返回一個底層數組的拷貝(使用了System.arraycopy方法),基于鏈表實現的List會新生成一個數組。
討論7:不可修改
通過使用Collections.unmodifiableList(List list)來生成一個不可修改的List,試圖修改返回的列表,不管是直接修改還是通過其迭代器進行修改,都將導致拋出UnsupportedOperationException。
討論8:遍歷器
請盡量使用Iterator,Enumeration已不被鼓勵使用。
最后,請參考java.util.Collections類,該類提供了很多有用的操縱集合對象的方法。