它是jQuery 1.3中新增的方法,有些时候真的是非常的好用,比如做Ajax聊天的时候。下面是JQuery参考手册中的一个示例:
<p>Click me!</p>
$("p").live("click", function(){
$(this).after("<p>Another paragraph!</p>");
});
Memory of my life and idea.
<p>Click me!</p>
$("p").live("click", function(){
$(this).after("<p>Another paragraph!</p>");
});
var test = function() {
alert(this);
this.f = function() {
alert(this);
};
};
test();
var t =new test();
t.f();
Object Window
Object Object
Object Object
| 同一个类 | 同一个包 | 不同包的子类 | 不同包的非子类 |
Private | √ |
|
|
|
Default | √ | √ |
|
|
Protected | √ | √ | √ |
|
Public | √ | √ | √ | √ |
public class CopyOnWriteArrayList<E> implements List<E>, RandomAccess, Cloneable, java.io.Serializable {
private static final long serialVersionUID = 8673264195747942595L;
/** The lock protecting all mutators */
transient final ReentrantLock lock = new ReentrantLock();
/** The array, accessed only via getArray/setArray. */
private volatile transient Object[] array; //volatile 关键字使其每次被赋值之后都会被其他线程看到,而不会使用线程拷贝的副本。加上赋值操作本身是原子性的所以不会冲突
/**
* Gets the array. Non-private so as to also be accessible
* from CopyOnWriteArraySet class.
*/
final Object[] getArray() {
return array;
}
/**
* Sets the array.
*/
final void setArray(Object[] a) {
array = a;
}
public E get(int index) {
return (E)(getArray()[index]); //不需要同步
}
public E set(int index, E element) {final ReentrantLock lock = this.lock;
lock.lock(); //锁定,如果已被锁定则等待
try {
Object[] elements = getArray();
Object oldValue = elements[index];
if (oldValue != element) { //写入的值与原值不一样int len = elements.length;
Object[] newElements = Arrays.copyOf(elements, len); //拷贝
newElements[index] = element;
setArray(newElements);//赋值} else {// Not quite a no-op; ensures volatile write semantics
setArray(elements);}}
return (E)oldValue;
} finally {
lock.unlock();//解锁
}
public class Singleton {
private static Singleton instance = new Singleton();
public static Singleton getInstance() {
return instance;
}
}
public class SingletonAa {因为初始化依赖于static变量,而这些初始化函数在变量被初始化之前被调用了...所以会跑出NullPointException。
private static SingletonAa singletonAa = new SingletonAa();
public static String name = "sam";
public static String password;
static {
password = "tiger";
}
private SingletonAa(){
if(name.equals("sam")) System.out.println("name is ok");
if(password.equals("tiger")) System.out.println(" password is ok");
}
public static SingletonAa getInstance() {
return singletonAa;
}
}
public class Singleton {
private static Singleton instance = null;
public static synchronized Singleton getInstance() {
if (instance == null) {
instance = new Singletion();
}
return instance;
}
}
public class Singleton {
private static Singleton instance = null;
public static synchronized Singleton getInstance() {
if (instance == null) {
synchronized(Singleton.class) {
if (instance == null) {
Singleton temp = new Singletion(); //防止无序写入导致的多线程问题
instance = temp;
}
}
}
return instance;
}
}
public class Singleton {起初我看到这个代码的时候还认为它不是线程安全的,因为考虑到并发情况下,instance可能被初始化多次,所以得到的并不是单例而是多个。
static class SingletonsHolder {
static Singletons instance = new Singletons();
}
public static Singletons getInstances() {
return SingletonsHolder.instance;
}
}
public class Singletons {上述代码完成的过程是:
public static boolean b = true;
private Singletons() {
System.out.println("In " + b);
if (b) {
System.out.println("Sleeping " + b);
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("Singletons " + b);
}
static class SingletonsHolder {
static Singletons instance = new Singletons();
}
public static Singletons getInstances() {
System.out.println("getInstances");
return SingletonsHolder.instance;
}
public static void main(String[] args) throws InterruptedException {
System.out.println("Start");
new Thread() {
public void run() {
System.out.println(Singletons.getInstances());
}
}.start();
Thread.sleep(1000);
Singletons.b = false;
new Thread() {
public void run() {
System.out.println("Two Start");
System.out.println(Singletons.getInstances());
System.out.println("Two End");
}
}.start();
System.out.println("End ");
}
}
Start
getInstance
In true
Sleeping true
等待了1秒
End
Two Start
getInstance
等待了4秒
Singletons false可见最终得到的单例是同一个。分析我们知道测试的执行过程为:
org.my.test.singleons.Singletons@1a758cb
org.my.test.singleons.Singletons@1a758cb
Two End
Long start = System.currentTimeMillis();
String s = "1";
for (int i=1;i<1000000000;i++) {
if (s.equals("1"))
s = "2"; //11710,11672,11678
s = (s.equals("1"))? "2":s; //11147,11137,11269
s = (s.equals("1"))? "2":"1";//9539,9528,9647
}
Long end = System.currentTimeMillis();
System.out.println(end - start);
转自:http://hxraid.javaeye.com/blog/522167
作者:Java标准类库有几千个类,唯独String不太一样。为什么这么说?就因为每次上网冲杯Java时,都能看到关于String无休无止的争论。还是觉得有必要让这个讨厌又很可爱的String美眉,赤裸裸的站在我们这些Java色狼面前了。嘿嘿....
众所周知,String是由字符组成的串,在程序中使用频率很高。Java中的String是一个类,而并非基本数据类型。 不过她却不是普通的类哦!!!
【镜头1】 String对象的创建
1、关于类对象的创建,很普通的一种方式就是利用构造器,String类也不例外:
String s=new String("Hello world");
问题:参数"Hello world"是什么东西,也是字符串对象吗?莫非用字符串对象创建一个字符串对象???
2、当然,String类对象还有一种大家都很喜欢的创建方式:
String s="Hello world";
问题:有点怪呀,怎么与基本数据类型的赋值操作(int i=1)很像呀???
在开始解释这些问题之前,我们先引入一些必要的知识:
(1) Java class文件结构
我们都知道,Java程序要运行,首先需要编译器将源代码文件编译成字节码文件(也就是.class文件)。然后在由JVM解释执行。
class文件是8位字节的二进制流 。这些二进制流的涵义由一些紧凑的有意义的项 组成。比如class字节流中最开始的4个字节组成的项叫做魔数 (magic),其意义在于分辨class文件(值为0xCAFEBABE)与非class文件。class字节流大致结构如下图左侧。
其中,在class文件中有一个非常重要的项——常量池 。这个常量池专门放置源代码中的常量信息(并且不同的常量存放在不同标志的常量表中)。如上图右侧是HelloWorld代码中的常量表 (HelloWorld代码如下),其中有四个不同类型的常量表(四个不同的常量池入口)。关于常量池的具体细节,请参照《深入Java虚拟机》第二版第 6章。
public class HelloWorld{ void hello(){ System.out.println("Hello world"); } }
显然,HelloWorld代码中的"Hello world"被编译之后,可以清楚的看到存放在了class二进制流的常量池项中(上图右侧红框区域)。并且我们还发现常量池中专门有为String类型设置的常量表 。也就是说,在编译阶段,就已经将代码中的这种("****")形式作为了字符串常量存放在常量池中了 ,这一点和下面代码中出现的整形常量(142),浮点型常量(12.1)等的处理是没有区别的。
String s="Hello world";
int intData=142;
double dblData=12.1;
(2) Java虚拟机运行class文件
当Java虚拟机需要运行一个class文件时,它首先会用类装载器装载进class文件。当然也就需要在内存中存放许多东西。比如class的二进制字 节码。还有需要存储class文件中得到的其他信息,比如程序创建的对象,传递给方法的参数,返回值,局部变量等等。怎么多麻烦的数据当然需要管 理,JVM会把这些东西都组织到几个“运行时数据区 ”中。这些数据区中就有我们动不动就谈到的"堆"呀,"栈"呀什么的?想要详细了解这部分东西可以看《深入Java虚拟机》第二版第5章。
在这里我只谈谈“方法区 ”这个运行时数据区。在Java虚拟机中,关于被装载类型的信息会在一个逻辑上被称为"方法区"的内存中,当虚拟机装载某个类型时,它使用类装载器定位相应的class文件,然后读入该文件。紧接着虚拟机提取其中的类型信息,并将这些信息存储到方法区中。
方法区中的这些类型信息是很有用的,比如:这个类型的全限定名(net.single.HelloWorld);这个类型的直接超类的全限定名;这个类型 是类类型还是接口类型;这个类型的访问修饰符(public,final,static)等。还有两个大家都很熟悉的引用:指向类ClassLoader 的引用和指向Class类的引用。这是Java反射机制能够运行的关键所在。这里我们要提到的是一个非常重要的信息——该类型的常量池 。
上面提到的,class文件结构中的常量池二进制流就被JVM存储在方法区中进行管理。当程序运行时需要使用到常量值的时候,直接在方法区常量池所在的内存中寻找就可以了。
(3) 操作码助忆符指令集
将String s=new String("Hello world");编译成class文件后的指令(由eclipse打开class文件查看的):
0 new java.lang.String [15] 3 dup 4 ldc <String "Hello word"> [17] 6 invokespecial java.lang.String(java.lang.String) [19] 9 astore_1 [s] 10 retur
下面通俗的解释一下这些指令,详细见《深入Java虚拟机》第二版附表:按操作码助忆符排列的指令集。
★ new指令: 在内存的堆区域中为新字符串对象分配足够大的空间,并将对象的实例变量设为默认值。
★ ldc指令:在内存的方法区常量池中找到String类型字面值常量表 的入口,然后定位到的"Hello word"所在内存中的位置。
★ invokespecial指令:调用指定的类构造器(这里调用的是String(String)这一个构造器。将ldc指令所找到的"Hello word"的内容传入到new指令所开辟在堆中的字符串对象中。
★ astore_1:将new指令所开辟堆的内存位置存入局部变量s中 。
将String s="Hello world";编译成class文件后的指令:
0 ldc <String "Hello world"> [15] 2 astore_1 [str] 3 return
★ ldc指令:在内存的方法区常量池中找到String类型字面值常量表 的入口,然后定位到的"Hello word"所在内存中的位置(如果常量池中没有"Hello word",则会在其中添加一个"Hello word")。
★ astore_1:将ldc指令定位到的常量池中的位置存入局部变量s中 。
镜头总结: String类型脱光了其实也很普通。真正让她神秘的原因就在于String类型字面值常量表 的存在。
相关问题解决
(问题1) 代码1 代码2
String sa=new String("Hello world"); String sc="Hello world";
String sb=new String("Hello world"); String sd="Hello world";
System.out.println(sa==sb); // false System.out.println(sc==sd); // true
变量sa,sb中存储的内容是JVM在堆中开辟的两个String对象的内存地址。==比较就是sa,sb变量存储的内容,也就是两个不同的内存地址,当然是false;
变量sc,sd中存储的内容也是地址,但却都是方法区常量池中"Hello word"所在的地址,自然一样。
(问题2) 代码1 代码2
String sa = "ab"; String sc="ab"+"cd";
String sb = "cd"; String sd="abcd";
String sab=sa+sb; System.out.println(sc==sd); //true
String s="abcd";
System.out.println(sab==s); // false
代码1中sa+sb被编译以后使用的是StringBuilder.append(String)方法。JVM会在堆中创建一个 StringBuilder类,将sa所指向常量池中的内容"ab"传入,然后调用append(sb所指向的常量池内容)完成字符串合并功能,最后将堆 中StringBuilder对象的地址赋给变量sab。而s存储的是常量池中"abcd"的地址。sab与s地址当然不一样了。
代码2中"ab"+"cd"会直接在编译阶段就合并成常量"abcd",所以相同的字符串在常量池中的地址也相同了。
【镜头二】 String三姐妹(String,StringBuffer,StringBuilder),谁更性感?
String不用多说,扒的差不多了。但他还有两个妹妹StringBuffer,StringBuilder长的也不错哦!我们首先对这三姐妹下个定义:
String(大姐,出生于JDK1.0时代) 不可变字符序列
StringBuffer(二姐,出生于JDK1.0时代) 线程安全的可变字符序列
StringBuilder(小妹,出生于JDK1.5时代) 非线程安全的可变字符序列
讨论1、StringBuffer与String的可变性问题。
我们先看看这两个类的简要源代码:
//String public final class String { private final char value[]; public String(String original) { // 把原字符串original切分成字符数组并赋给value[]; } } //StringBuffer public final class StringBuffer extends AbstractStringBuilder { char value[]; //继承了父类AbstractStringBuilder中的value[] public StringBuffer(String str) { super(str.length() + 16); //继承父类的构造器,并创建一个大小为str.length()+16的value[]数组 append(str); //将str切分成字符序列并加入到value[]中 } }
很显然,String和StringBuffer中的value[]都用于存储字符序列。但是,
(1) String中的是常量(final)数组,只能被赋值一次。
比如:new String("abc")使得value[]={'a','b','c'},之后这个String对象中的value[]再也不能改变了。这也正是大家常说的,String是不可变的原因 。
注意:这个对初学者来说有个误区,有人说String str1=new String("abc"); str1=new String("cba");不是改变了字符串str1吗?那么你有必要先搞懂对象引用和对象本身的区别。
(2) StringBuffer中的value[]就是一个很普通的数组,而且可以通过append()方法将新字符串加入value[]末尾。这样也就改变了value[]的内容和大小了。
比如:new StringBuffer("abc")使得value[]={'a','b','c','',''...}(注意构造的长度是str.length()+16)。如果再将这个对象append("abc"),那么这个对象中的value[]={'a','b','c','a','b','c',''....}。这也就是为什么大家说 StringBuffer是可变字符串 的涵义了。
从这一点也可以看出,StringBuffer中的value[]完全可以作为字符串的缓冲区功能。
String s1=new String("aaa");
StringBuffer sb1=new StringBuffer(); //一个字符串缓冲区
sb1.append(s1);//将字符串s1加进缓冲区
注意,讨论String和StringBuffer可不可变。本质指对象内部的value[]字符数组可不可变,而不是对象引用可不可变。
讨论2、StringBuffer与StringBuilder的线程安全性问题
StringBuffer和StringBuilder可以算是双胞胎了,这两者的方法没有很大区别。但在线程安全性方面,StringBuffer允许多线程进行字符操作。这是因为在源代码中StringBuffer的很多方法都被关键字synchronized 修饰了,而StringBuilder没有。
有多线程编程经验的程序员应该知道synchronized。这个关键字是为线程同步机制 设定的。我简要阐述一下synchronized的含义:
每一个类对象都对应一把锁,当某个线程A调用类对象O中的synchronized方法M时,必须获得对象O的锁才能够执行M方法,否则线程A阻塞。一旦 线程A开始执行M方法,将独占对象O的锁。使得其它需要调用O对象的M方法的线程阻塞。只有线程A执行完毕,释放锁后。那些阻塞线程才有机会重新调用M方 法。这就是解决线程同步问题的锁机制。
了解了synchronized的含义以后,大家可能都会有这个感觉。多线程编程中StringBuffered比StringBuilder要安全多了 ,事实确实如此。如果有多个线程需要对同一个字符串缓冲区进行操作的时候,StringBuffer应该是不二选择。
注意:是不是String也不安全呢?事实上不存在这个问题,String是字符串常量,不可变。线程对于String对象只能读取,无法修改。试问:还有什么不安全的呢?
讨论3、String和StringBuffer的效率问题(这可是个热门话题呀!)
首先说明一点:StringBuffer和StringBuilder可谓双胞胎,StringBuilder是1.5新引入的,其前身就是StringBuffer。在Core Java editior 7 中文版P611页中有这么一句话: StringBuffer比StringBuilder的效率稍低 。
我们用下面的代码运行1W次字符串的连接操作,测试String,StringBuffer所运行的时间。
//测试代码 public class RunTime{ public static void main(String[] args){ //位置1 long beginTime=System.currentTimeMillis(); for(int i=0;i<10000;i++){ //位置2 } long endTime=System.currentTimeMillis(); System.out.println(endTime-beginTime); } }
(1) String常量与String变量的"+"操作比较
首先声明两个概念:String str="Heart"中"Heart"是String常量(存放在常量池中);而str则是String变量,是String对象的引用。
▲测试①代码: (测试代码位置1) String str="";
(测试代码位置2) str="Heart"+"Raid";
[耗时: 0ms]
▲测试②代码 (测试代码位置1) String s1="Heart";
String s2="Raid";
String str="";
(测试代码位置2) str=s1+s2;
[耗时: 15—16ms]
结论:String常量的“+连接” 稍优于 String变量的“+连接”。
原因:测试①的"Heart"+"Raid"在编译阶段就已经连接起来,并形成了一个新的字符串常量"HeartRaid"。运行阶段只需要 将"HeartRaid"存入常量池。循环1W次也只不过将常量池中"HeartRaid"的地址取出1W次赋值给String对象的引用而已。
测试②在编译阶段,编译器并不知道s1和s2代表什么,它会将这句话编译成几个指令,大体上是这样的。
首先:StringBuilder temp=new StringBuilder(s1),
然后:temp.append(s2)
最后:str=temp.toString();
我们发现,虽然在中间的时候也用到了append()方法,但是在开始和结束的时候分别创建了StringBuilder和String对象。可想而知:调用1W次,是不是就创建了1W次这两中对象呢?不划算。
不言而喻:String变量的"+"连接操作比String常量操作使用的更加广泛。
(2)String对象的"累+"连接操作与StringBuffer对象的append()累和连接操作比较。
▲测试①代码: (代码位置1) String s1="Heart";
String s="";
(代码位置2) s=s+s1;
[耗时: 4200—4500ms]
▲测试②代码 (代码位置1) String s1="Heart";
StringBuffer sb=new StringBuffer();
(代码位置2) sb.append(s1);
[耗时: 0ms(当循环100000次的时候,耗时大概16—31ms)]
结论:StringBuffer的append()累和连接 远好于 String对象的"累+"连接
原因:测试①中s=s+s1我们知道编译器也会使用StringBuilder的append方法,但是首先编译器会将new StringBuilder存放s,然后在通过 StringBuilder.append(s1),最后在new一个新的String对象存放 StringBuilder的连接后的内容 。试想这个过程一次就需要创建一个新的String并赋值给引用s,10000次就需要创建了1W次新String对象。正式因为String中的value[]数组不可变,只能不停的创建新的String来存放变化中的字符串。效率可想而知了。
测试②中sb.append(s1);只需要将自己的value[]数组不停的扩大来存放s1即可。无需在堆中创建一大堆的对象。效率高就不足为奇了。
不言而喻:大规模累加字符串的时候,StringBuffer就比她姐姐不知道强多少倍了。
镜头总结: (1) 在编译阶段就能够确定的字符串常量,完全没有必要创建String或StringBuffer对象。直接使用字符串常量的"+"连接操作效率最高。
(2)否则,StringBuffer对象的append效率要高于String对象的"+"连接操作。
(3) intern()方法很有用。
当调用String 对象的intern方法时,如果池已经包含一个等于此对象的字符串(用 equals(Object) 方法确定),则返回池中的字符串。否则,将此 String 对象添加到池中,并返回此 String 对象的引用。
也就是如果我们需要使用new关键字来创建很多内容重复的字符串的话,使用String.intern()方法可以提高效率。
1. 数据截断:
由于long类型变量的运算(赋值、比较、移位等)产生。long定义在x86上为32bits,而在ia64上为64bits.容易在与int型变量运算时出现异常。
处理方法:尽量避免不同类型变量间的运算,避免将长度较长的变量赋值到较短的变量中,统一变量长度可以解决这个问题。简单的对于32位转移到64位可以将所有long定义转换为int定义。
2. 指针存储:
在x86平台下,习惯使用int来存储指针,并将指针直接参与到int型的运算中,而64位平台下指针大小为64bits,无法存储到int中。对指针的运算也可能会因为数据长度的不一致导致异常。
处理方法:修改用于存储传递指针的变量为intptr_t 类型定义,以保证平台兼容性
3. 不规范的结构:
未命名或为指定数量的成员可能会出现异常
处理方法:命名未命名的成员,声明类型符号,将long型定义转为int型。
4. 代码中的直接数
直接使用16进制数字进行赋值时(一般会隐含假设该变量为32位变量的前提) 可能出现异常。使用数字定义数据大小,进行移位运算时会出现异常。
处理方法:检查代码中的直接数字是否有表示32位有关的各种形式,如4,32, 0x7fffffff等,替换为宏定义。
编写代码时注意可移植化:
1. 在32位与64位下使用同样的头文件
2. 使用严禁的格式定义,如:用off_t定义偏移量,用fpos_t定义文件位置, intptr_t定义指针
3. 使用<intypes.h>中定义的整数类型,不使用int,long,long long的传统定义方式。使用带有整形标示符参数的printf函数,不使用%d,%ld的格式化方式。
4. 使用固定宽度或宏定义的整数类型,数字,掩码
5. 对整形变量作边界检查。
6. 32位与64位进程共享内存操作时,使用64位作为操作字长。
转自:http://www.cnblogs.com/aming/archive/2008/10/27/1320277.html
1、typeof 运算符把类型信息以字符串形式返回。typeof 返回六种可能的值:“数字类型”、“字符串类型”、“布尔型”、“对象类型”、“函数类型”和“未定义类型”。
但对所有的对象和数组类型返回的都是"object",所以它只在区别对象和原始类型的时候才有用。要区一种对象类型和另一种对象类型,必须使用其他的方法。如:instanceof运算符和constructor属性。
2、instanceof运算符。如果 object 是 class 或构造函数的实例,则 instanceof 运算符返回 true。如果 object 不是指定类或函数的实例,或者 object 为 null,则返回 false。
那么,B类由当前Classloader,也就是AClassloader装载。同样的,修改上述的foo方法,其实现改为:
Class clazz = Class.forName("foo.B");
最终获取到的clazz,也是由AClassLoader所装载。
根 (Bootstrap) 装载器:该装载器没有父装载器,它是 JVM 实现的一部分,从 sun.boot.class.path 装载运行时库的核心代码。
扩展 (Extension) 装载器:继承的父装载器为根装载器,不像根装载器可能与运行时的操作系统有关,这个类装载器是用纯 Java 代码实现的,它从 java.ext.dirs ( 扩展目录 ) 中装载代码。
系统 (System or Application) 装载器:装载器为扩展装载器,我们都知道在安装 JDK 的时候要设置环境变量 (CLASSPATH ) ,这个类装载器就是从 java.class.path(CLASSPATH 环境变量 ) 中装载代码的,它也是用纯 Java 代码实现的,同时还是用户自定义类装载器的缺省父装载器。
<div id=”header”>顶部区域</div>
<div id=”mainer”>
<h1>使用负边距创建自适应宽度的流体布局</h1>
<p>随着越来越大的浏览器的出现及普及,网站界面如何能满足不同分辨率浏览器使用者的浏览需求,逐渐成为前端开发工程师必须面对的问题。采 用百分比进行架构是个不错的主意。以往我们进行这类架构都是使用table表格。但,其实使用很小的技术就可以创建出符合WEB标准化的自适应布局。 </p>
</div>
<div id=”sideBar”>
<h2>最新文章</h2>
<ul>
<li>最新文章一</li>
<li>最新文章二</li>
<li>最新文章三</li>
</ul>
</div>
<div id=”footer”>底部区域</div>
body,p,h1,h2,ul {
margin:0;padding:0;
}
#header {
background-color: #A8A754;
}
#footer {
background-color: #A8A754;
clear: both;
}
#mainer {
width: 100%;
margin-right: -250px;
float: left;
}
#sideBar {
float: right;
width: 250px;
}
<div id=”mainer”>
<div id=”main”>
<h1>使用负边距创建自适应宽度的流体布局</h1>
<p>随着越来越大的浏览器的出现及普及,网站界面如何能满足不同分辨率浏览器使用者的浏览需求,逐渐成为前端开发工程师必须面对的问题。采 用百分比进行架构是个不错的主意。以往我们进行这类架构都是使用table表格。但,其实使用很小的技术就可以创建出符合WEB标准化的自适应布局。 </p>
</div>
</div>
#sideBar {
color: #FFF;
background-color: #36361A;
}
#main {
margin-right: 250px;
background-color: #616030;
}
#mainer {
background: url(bj1.jpg) repeat-y right bottom;
}
<div id=”header”>顶部区域</div>
<div id=”wrapper” class=”mid”>
<div id=”mainer”>
<div id=”main”>
<h1>使用负边距创建自适应宽度的流体布局</h1>
<p>随着越来越大的浏览器的出现及普及,网站界面如何能满足不同分辨率浏览器使用者的浏览需求,逐渐成为前端开发工程师必须面对的问题。采 用百分比进行架构是个不错的主意。以往我们进行这类架构都是使用table表格。但,其实使用很小的技术就可以创建出符合WEB标准化的自适应布局。 </p>
<p>随着越来越大的浏览器的出现及普及,网站界面如何能满足不同分辨率浏览器使用者的浏览需求,逐渐成为前端开发工程师必须面对的问题。采 用百分比进行架构是个不错的主意。以往我们进行这类架构都是使用table表格。但,其实使用很小的技术就可以创建出符合WEB标准化的自适应布局。 </p>
<p>随着越来越大的浏览器的出现及普及,网站界面如何能满足不同分辨率浏览器使用者的浏览需求,逐渐成为前端开发工程师必须面对的问题。采 用百分比进行架构是个不错的主意。以往我们进行这类架构都是使用table表格。但,其实使用很小的技术就可以创建出符合WEB标准化的自适应布局。 </p>
<p>随着越来越大的浏览器的出现及普及,网站界面如何能满足不同分辨率浏览器使用者的浏览需求,逐渐成为前端开发工程师必须面对的问题。采 用百分比进行架构是个不错的主意。以往我们进行这类架构都是使用table表格。但,其实使用很小的技术就可以创建出符合WEB标准化的自适应布局。 </p>
</div>
</div>
<div id=”sideBar”>
<h2>最新文章</h2>
<ul>
<li>最新文章一</li>
<li>最新文章二</li>
<li>最新文章三</li>
</ul>
</div>
<div class=”clear”></div>
</div>
<div id=”footer”>底部区域</div>
#wrapper {width: 92%;}
.clearing {clear: both;}
.mid {margin:0 auto;}
<div id=”header” class=”mid”>顶部区域</div>
<div id=”wrapper” class=”mid”>
<div id=”mainer”>
<div id=”main”>
<div id=”leftBar”>
<h2>栏目标题</h2>
<ul>
<li>文章标题</li>
<li>文章标题</li>
<li>文章标题</li>
</ul>
</div>
<div id=”inmain”>
<h1>使用负边距创建自适应宽度的流体布局</h1>
<p>随着越来越大的浏览器的出现及普及,网站界面如何能满足不同分辨率浏览器使用者的浏览需求,逐渐成为前端开发工程师必须面对的问题。采 用百分比进行架构是个不错的主意。以往我们进行这类架构都是使用table表格。但,其实使用很小的技术就可以创建出符合WEB标准化的自适应布局。 </p>
<p>随着越来越大的浏览器的出现及普及,网站界面如何能满足不同分辨率浏览器使用者的浏览需求,逐渐成为前端开发工程师必须面对的问题。采 用百分比进行架构是个不错的主意。以往我们进行这类架构都是使用table表格。但,其实使用很小的技术就可以创建出符合WEB标准化的自适应布局。 </p>
<p>随着越来越大的浏览器的出现及普及,网站界面如何能满足不同分辨率浏览器使用者的浏览需求,逐渐成为前端开发工程师必须面对的问题。采 用百分比进行架构是个不错的主意。以往我们进行这类架构都是使用table表格。但,其实使用很小的技术就可以创建出符合WEB标准化的自适应布局。 </p>
<p>随着越来越大的浏览器的出现及普及,网站界面如何能满足不同分辨率浏览器使用者的浏览需求,逐渐成为前端开发工程师必须面对的问题。采 用百分比进行架构是个不错的主意。以往我们进行这类架构都是使用table表格。但,其实使用很小的技术就可以创建出符合WEB标准化的自适应布局。 </p>
</div>
</div>
</div>
<div id=”sideBar”>
<h2>最新文章</h2>
<ul>
<li>最新文章一</li>
<li>最新文章二</li>
<li>最新文章三</li>
</ul>
</div>
<div class=”clear”> </div>
</div>
<div id=”footer” class=”mid”>底部区域</div>
#main {
margin-right: 250px;
background: url(bj2.jpg) #616030 repeat-y left bottom;
}
#leftBar {
float: left;
width: 150px;
}
#inmain {
margin-left: 150px;
}
1、完全隐藏
在<boby>里加入scroll="no",可隐藏滚动条;
<boby scroll="no">
2、在不需要时隐藏
指当浏览器窗口宽度或高度大于页面的宽或高时,不显示滚动条;反之,则显示;
<boby scroll="auto">
3、样式表方法
在<boby>里加入style="overflow-x:hidden",可隐藏水平滚动条;加入style="overflow-y:hidden",可隐藏垂直滚动条。
这种方法在页面头部为:<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">这种兼容模式下是不行的,其它的没试过,最后终于找到了正确的方法:
被包含页面里加入
<style>
html { overflow-x:hidden; }
</style>
有一段解释是这样说的:body{ overflow-x:hidden; }在标准 DTD 下是不可以的。
4、另一种方法
<style type="text/css">
body {
overflow-x:hidden; /*隐藏水平滚动条*/
overflow-y:hidden; /*隐藏水平滚动条*/
}
</style>
此方法在页面头部为:<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">这种兼容模式下也是不行的。
问题二:如何使firefox始终显示滚动条?<style type="text/css">
html {
overflow-x:hidden; /*隐藏水平滚动条*/
overflow-y:hidden; /*隐藏水平滚动条*/
}
</style>
html {
overflow:-moz-scrollbars-vertical;
}
body, html {
min-height:101%;
}
int indexFor(int h, int length)
{
return h & length;
}
转自:http://www.javaeye.com/topic/401478
理解 Java 的 GC 与 幽灵引用
Java 中一共有 4 种类型的引用 : StrongReference、 SoftReference、 WeakReference 以及 PhantomReference (传说中的幽灵引用 呵呵),
这 4 种类型的引用与 GC 有着密切的关系, 让我们逐一来看它们的定义和使用场景 :
1. Strong Reference
StrongReference 是 Java 的默认引用实现, 它会尽可能长时间的存活于 JVM 内, 当没有任何对象指向它时 GC 执行后将会被回收
@Test public void strongReference() { Object referent = new Object(); /** * 通过赋值创建 StrongReference */ Object strongReference = referent; assertSame(referent, strongReference); referent = null; System.gc(); /** * StrongReference 在 GC 后不会被回收 */ assertNotNull(strongReference); }
2. WeakReference & WeakHashMap
WeakReference, 顾名思义, 是一个弱引用, 当所引用的对象在 JVM 内不再有强引用时, GC 后 weak reference 将会被自动回收
@Test public void weakReference() { Object referent = new Object(); WeakReference<Object> weakRerference = new WeakReference<Object>(referent); assertSame(referent, weakRerference.get()); referent = null; System.gc(); /** * 一旦没有指向 referent 的强引用, weak reference 在 GC 后会被自动回收 */ assertNull(weakRerference.get()); }
WeakHashMap 使用 WeakReference 作为 key, 一旦没有指向 key 的强引用, WeakHashMap 在 GC 后将自动删除相关的 entry
@Test public void weakHashMap() throws InterruptedException { Map<Object, Object> weakHashMap = new WeakHashMap<Object, Object>(); Object key = new Object(); Object value = new Object(); weakHashMap.put(key, value); assertTrue(weakHashMap.containsValue(value)); key = null; System.gc(); /** * 等待无效 entries 进入 ReferenceQueue 以便下一次调用 getTable 时被清理 */ Thread.sleep(1000); /** * 一旦没有指向 key 的强引用, WeakHashMap 在 GC 后将自动删除相关的 entry */ assertFalse(weakHashMap.containsValue(value)); }
3. SoftReference
SoftReference 于 WeakReference 的特性基本一致, 最大的区别在于 SoftReference 会尽可能长的保留引用直到 JVM 内存不足时才会被回收(虚拟机保证), 这一特性使得 SoftReference 非常适合缓存应用
@Test public void softReference() { Object referent = new Object(); SoftReference<Object> softRerference = new SoftReference<Object>(referent); assertNotNull(softRerference.get()); referent = null; System.gc(); /** * soft references 只有在 jvm OutOfMemory 之前才会被回收, 所以它非常适合缓存应用 */ assertNotNull(softRerference.get()); }
4. PhantomReference
作为本文主角, Phantom Reference(幽灵引用) 与 WeakReference 和 SoftReference 有很大的不同, 因为它的 get() 方法永远返回 null, 这也正是它名字的由来
@Test public void phantomReferenceAlwaysNull() { Object referent = new Object(); PhantomReference<Object> phantomReference = new PhantomReference<Object>(referent, new ReferenceQueue<Object>()); /** * phantom reference 的 get 方法永远返回 null */ assertNull(phantomReference.get()); }
诸位可能要问, 一个永远返回 null 的 reference 要来何用, 请注意构造 PhantomReference 时的第二个参数 ReferenceQueue(事实上 WeakReference & SoftReference 也可以有这个参数),
PhantomReference 唯一的用处就是跟踪 referent 何时被 enqueue 到 ReferenceQueue 中.
5. RererenceQueue
当一个 WeakReference 开始返回 null 时, 它所指向的对象已经准备被回收, 这时可以做一些合适的清理工作. 将一个 ReferenceQueue 传给一个 Reference 的构造函数, 当对象被回收时, 虚拟机会自动将这个对象插入到 ReferenceQueue 中, WeakHashMap 就是利用 ReferenceQueue 来清除 key 已经没有强引用的 entries.
@Test public void referenceQueue() throws InterruptedException { Object referent = new Object(); ReferenceQueue<Object> referenceQueue = new ReferenceQueue<Object>(); WeakReference<Object> weakReference = new WeakReference<Object>(referent, referenceQueue); assertFalse(weakReference.isEnqueued()); Reference<? extends Object> polled = referenceQueue.poll(); assertNull(polled); referent = null; System.gc(); assertTrue(weakReference.isEnqueued()); Reference<? extends Object> removed = referenceQueue.remove(); assertNotNull(removed); }
6. PhantomReference vs WeakReference
PhantomReference 有两个好处, 其一, 它可以让我们准确地知道对象何时被从内存中删除, 这个特性可以被用于一些特殊的需求中(例如 Distributed GC, XWork 和 google-guice 中也使用 PhantomReference 做了一些清理性工作).
其二, 它可以避免 finalization 带来的一些根本性问题, 上文提到 PhantomReference 的唯一作用就是跟踪 referent 何时被 enqueue 到 ReferenceQueue 中, 但是 WeakReference 也有对应的功能, 两者的区别到底在哪呢 ?
这就要说到 Object 的 finalize 方法, 此方法将在 gc 执行前被调用, 如果某个对象重载了 finalize 方法并故意在方法内创建本身的强引用, 这将导致这一轮的 GC 无法回收这个对象并有可能
引 起任意次 GC, 最后的结果就是明明 JVM 内有很多 Garbage 却 OutOfMemory, 使用 PhantomReference 就可以避免这个问题, 因为 PhantomReference 是在 finalize 方法执行后回收的,也就意味着此时已经不可能拿到原来的引用, 也就不会出现上述问题, 当然这是一个很极端的例子, 一般不会出现.
7. 对比
taken from http://mindprod.com/jgloss/phantom.html
Soft vs Weak vs Phantom References Type Purpose Use When GCed Implementing Class Strong Reference An ordinary reference. Keeps objects alive as long as they are referenced. normal reference. Any object not pointed to can be reclaimed. default Soft Reference Keeps objects alive provided there’s enough memory. to keep objects alive even after clients have removed their references (memory-sensitive caches), in case clients start asking for them again by key. After a first gc pass, the JVM decides it still needs to reclaim more space. java.lang.ref.SoftReference Weak Reference Keeps objects alive only while they’re in use (reachable) by clients. Containers that automatically delete objects no longer in use. After gc determines the object is only weakly reachable java.lang.ref.WeakReference
java.util.WeakHashMap Phantom Reference Lets you clean up after finalization but before the space is reclaimed (replaces or augments the use offinalize()) Special clean up processing After finalization. java.lang.ref.PhantomReference
8. 小结
一般的应用程序不会涉及到 Reference 编程, 但是了解这些知识会对理解 GC 的工作原理以及性能调优有一定帮助, 在实现一些基础性设施比如缓存时也可能会用到, 希望本文能有所帮助.
王政 于 2009,6,3