在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类,该类提供了很多有用的操纵集合对象的方法。