String http://blog.csdn.net/uyu2yiyi/article/details/6275808 1. 首先String不属于8种基本数据类型,String是一个对象。因为对象的默认值是null,所以String的默认值也是null;但它又是一种特殊的对象,有其它对象没有的一些特性。 2. Java代码 new String() 和 new String(“”)都是申明一个新的空字符串,是空串不是null; 3. String str=”kvill”; String str=new String (“kvill”); 的区别: 在这里,我们不谈堆,也不谈栈,只先简单引入常量池这个简单的概念。 常量池(constant pool)指的是在编译期被确定,并被保存在已编译的.class文件中的一些数据。它包括了关于类、方法、接口等中的常量,也包括字符串常量。 看例1: String s0=”kvill”; String s1=”kvill”; String s2=”kv” + “ill”; System.out.println( s0==s1 ); System.out.println( s0==s2 ); 结果为: true true 首先,我们要知道Java会确保一个字符串常量只有一个拷贝。 因为例子中的s0和s1中的”kvill”都是字符串常量,它们在编译期就被确定了,所以s0==s1为true;而”kv”和”ill”也都是字符串常量,当一个字符串由多个字符串常量连接而成时,它自己肯定也是字符串常量,所以s2也同样在编译期就被解析为一个字符串常量,所以s2也是常量池中”kvill”的一个引用。 所以我们得出s0==s1==s2; 用new String() 创建的字符串不是常量,不能在编译期就确定,所以new String() 创建的字符串不放入常量池中,它们有自己的地址空间。 看例2: String s0=”kvill”; String s1=new String(”kvill”); String s2=”kv” + new String(“ill”); System.out.println( s0==s1 ); System.out.println( s0==s2 ); System.out.println( s1==s2 ); 结果为: false false false 例2中s0还是常量池中”kvill”的应用,s1因为无法在编译期确定,所以是运行时创建的新对象”kvill”的引用,s2因为有后半部分new String(“ill”)所以也无法在编译期确定,所以也是一个新创建对象”kvill”的应用;明白了这些也就知道为何得出此结果了。 4. String.intern(): 再补充介绍一点:存在于.class文件中的常量池,在运行期被JVM装载,并且可以扩充。String的intern()方法就是扩充常量池的一个方法;当一个String 实例str调用intern()方法时,Java查找常量池中是否有相同Unicode的字符串常量,如果有,则返回其的引用,如果没有,则在常量池中增加一个Unicode等于str的字符串并返回它的引用;看例3就清楚了 例3: String s0= “kvill”; String s1=new String(”kvill”); String s2=new String(“kvill”); System.out.println( s0==s1 ); System.out.println( “**********” ); s1.intern(); s2=s2.intern(); //把常量池中“kvill”的引用赋给s2 System.out.println( s0==s1); System.out.println( s0==s1.intern() ); System.out.println( s0==s2 ); 结果为: false ********** false //虽然执行了s1.intern(),但它的返回值没有赋给s1 true //说明s1.intern()返回的是常量池中”kvill”的引用 true 最后我再破除一个错误的理解: 有人说,“使用String.intern()方法则可以将一个String类的保存到一个全局String表中,如果具有相同值的Unicode字符串已经在这个表中,那么该方法返回表中已有字符串的地址,如果在表中没有相同值的字符串,则将自己的地址注册到表中“如果我把他说的这个全局的String表理解为常量池的话,他的最后一句话,“如果在表中没有相同值的字符串,则将自己的地址注册到表中”是错的: 看例4: String s1=new String("kvill"); String s2=s1.intern(); System.out.println( s1==s1.intern() ); System.out.println( s1+" "+s2 ); System.out.println( s2==s1.intern() ); 结果: false kvill kvill true 在这个类中我们没有声名一个”kvill”常量,所以常量池中一开始是没有”kvill”的,当我们调用s1.intern()后就在常量池中新添加了一个”kvill”常量,原来的不在常量池中的”kvill”仍然存在,也就不是“将自己的地址注册到常量池中”了。 s1==s1.intern()为false说明原来的“kvill”仍然存在; s2现在为常量池中“kvill”的地址,所以有s2==s1.intern()为true。 5. 关于equals()和==: 这个对于String简单来说就是比较两字符串的Unicode序列是否相当,如果相等返回true;而==是比较两字符串的地址是否相同,也就是是否是同一个字符串的引用。 6. 关于String是不可变的 这一说又要说很多,大家只要知道String的实例一旦生成就不会再改变了,比如说:String str=”kv”+”ill”+” “+”ans”; 就是有4个字符串常量,首先”kv”和”ill”生成了”kvill”存在内存中,然后”kvill”又和” “ 生成 ”kvill “存在内存中,最后又和生成了”kvill ans”;并把这个字符串的地址赋给了str,就是因为String的“不可变”产生了很多临时变量,这也就是为什么建议用StringBuffer的原因了,因为StringBuffer是可改变的 |
classloader:
http://www.iteye.com/topic/83978
当运行一个程序的时候,JVM启动,运行bootstrap classloader,该ClassLoader加载java核心API(ExtClassLoader和AppClassLoader也在此时被加载),然后调用ExtClassLoader加载扩展API,最后AppClassLoader加载CLASSPATH目录下定义的Class,这就是一个程序最基本的加载流程。
双亲委托模式,因为在任何一个自定义ClassLoader加载一个类之前,它都会先委托它的父亲ClassLoader进行加载,只有当父亲ClassLoader无法加载成功后,才会由自己加载,在上面这个例子里,因为java.lang.String是属于java核心API的一个类,所以当使用ClientDefClassLoader加载它的时候,该ClassLoader会先委托它的父亲ClassLoader进行加载,上面讲过,当ClassLoader的parent为null时,ClassLoader的parent就是bootstrap classloader,所以在ClassLoader的最顶层就是bootstrap classloader,因此最终委托到bootstrap classloader的时候,bootstrap classloader就会返回String的Class。
Class类中有个静态方法forName,这个方法和ClassLoader中的loadClass方法的目的一样,都是用来加载class的,但是两者在作用上却有所区别。通过loadClass加载类实际上就是加载的时候并不对该类进行解释,因此也不会初始化该类。Class类的forName方法则是相反,使用forName加载的时候就会将Class进行解释和初始化,forName也有另外一个版本的方法,可以设置是否初始化以及设置ClassLoader,在此就不多讲了。
在JVM加载类的时候,需要经过三个步骤,装载、连接、初始化。连接分三步,第一步是验证class是否符合规格,第二步是准备,就是为类变量分配内存同时设置默认初始值,第三步就是解释,而这步就是可选的,根据上面loadClass方法的第二个参数来判定是否需要解释。
Java变量:
1. 类变量
l 在类定义中使用关键字static修饰的字段
l 在接口声明中使用或者不使用static修饰的字段
说明:当准备好类或接口时就会创建一个类变量并将其初始化为一个默认值。当卸载类或接口时,就会有效的使类变量停止存在
2. 实例变量
在类声明中不使用关键字static声明的字段
3. 数组元素(未命名的变量)
无论何时创建一个作为数组的新对象,都会创建这些变量并将其初始化为默认值。当数组不在被引用时,就会有效地使数组元素停止存在。
4. 方法参数变量
对于方法声明中声明的每个参数,当调用该方法时,都会创建一个新的参数变量。新变量会用来方法调用的相应的参数值进行初始化。当方法体执行完成时,就会有效地是方法参数停止存在。
5. 构造函数参数变量
对于构造函数中声明的每个参数,每当类实例创建表达式或显示构造函数调用调用该构造函数时,就会创建一个新的参数变量。新变量会用来自创建表达式或构造函数调用的相应的参数值进行初始化。当构造函数体执行完成时,就会有效地使构造函数参数停止存在。、
6. 异常处理程序参数变量
每当catch语句或者try语句捕获一个异常时,就会创建一个异常处理程序参数。新变量会用与异常关联的实际对象进行初始化。当与catch子句关联的代码块执行完成时,就会有效地使异常处理程序参数停止存在。
7. 局部变量
通过局部变量声明语句声明的变量。局部变量使用前一定要赋值。局部变量不会自动初始化为默认值
8. Final变量
Final修饰的变量,只能被赋值一次,除非在赋值之前明确地取消对fianl变量进行赋值,否则,对final变量赋值,将会引发编译时错误。
进制表示:
八进制:0开头
十六进制:0x开头
数据类型:
数据类型 大小 范围 默认值断言只有在Debug模式下才有效,它可以有两种形式 1.assert Expression1 2.assert Expression1:Expression2 其中Expression1应该总是一个布尔值,Expression2是断言失败时输出的失败消息的字符串。如果Expression1为假,则抛出一个 AssertionError,这是一个错误,而不是一个异常,也就是说是一个不可控制异常(unchecked Exception),AssertionError由于是错误,所以可以不捕获,但不推荐这样做,因为那样会使你的系统进入不稳定状态。 断言在默认情况下是关闭的,要在编译时启用断言,需要使用source1.4标记 既javac source1.4 Test.java ,在运行时启用断言需要使用 -ea参数 。要在系统类中启用和禁用断言可以使用 -esa和 -dsa参数。 运算符: 优先级 . () ++ -- new * / % + - >> << >> > > < >= <= == != & ^ ! && || ?: = += -= *= /= %= ^= &= <<= >>= 注: & | ^ 位运算符 同时是布尔逻辑运算符 && || 是具有短路效应的逻辑运算符 a=5 (a<5)?10.9:9 输出值会根据运算符的精度类型进行自动转换,后面的9会变成9.0 当后两个表达式有一个是常量表达式,另一个是类型T,而常量表达式可以被T表示时,那么输出结果就是T类型。 多态:
byte(字节) 8 -128 - 127 0
short(短整型) 16 -32768 - 32768 0
int(整型) 32 -2147483648-2147483648 0
long(长整型) 64 -9233372036854477808-9233372036854477808 0
float(浮点型) 32 -3.40292347E+38-3.40292347E+38 0.0f
double(双精度) 64 -1.79769313486231570E+308-1.79769313486231570E+308 0.0d
char(字符型) 16 ‘ \u0000 - u\ffff ’ ‘\u0000 ’
boolean(布尔型) 1 true/false false
断言assert:
编写代码时,我们总是会做出一些假设,断言就是用于在代码中捕捉这些假设,可以将断言看作是异常处理的一种高级形式。断言表示为一些布尔表达式,程序员相信在程序中的某个特定点该表达式值为真。可以在任何时候启用和禁用断言验证,因此可以在测试时启用断言,而在部署时禁用断言。同样,程序投入运行后,最终用户在遇到问题时可以重新起用断言。
多态性是指允许不同类的对象对同一消息作出响应。多态性包括参数化多态性和包含多态性。多态性语言具有灵活、抽象、行为共享、代码共享的优势,很好的解决了应用程序函数同名问题。
多态有两种表现形式:重载和覆盖
首先说重载(overload),是发生在同一类中。与什么父类子类、继承毫无关系。
标识一个函数除了函数名外,还有函数的参数(个数和类型)。也就是说,一个类中可以有两个或更多的函数,叫同一个名字而他们的参数不同。
他们之间毫无关系,是不同的函数,只是可能他们的功能类似,所以才命名一样,增加可读性,仅此而已!
再说覆盖(override),是发生在子类中!也就是说必须有继承的情况下才有覆盖发生。
我们知道继承一个类,也就有了父类了全部方法,如果你感到哪个方法不爽,功能要变,那就把那个函数在子类中重新实现一遍。
这样再调用这个方法的时候,就是执行子类中的过程了。父类中的函数就被覆盖了。(当然,覆盖的时候函数名和参数要和父类中完全一样,不然你的方法对父类中的方法就不起任何作用,因为两者是两个函数,毫不关系)
private public protected权限:
equals == hashcode():
equals在object类中是用==实现的,比较的是引用地址; 而string 等类型中被重写为 比较内容了; hashcode()也是根据对象的地址来计算的,所以比较的也是引用。 如果在hash结构中,要用类来做key,必须重写类的hashcode和equals方法。因为new test()两次出来两个对象,地址肯定不一样,到时候key比较的时候也就不一样了。只有重写了hashcode()。仅仅实现合适的hashcode方法还是不够的,hashcode只用于实现查找hash地址。还是实现equals方法,equals方法严格判断两个对象是否相等。 HashMap就是根据key的hashcode计算其hash code
,hashcode值一样的key-value对放在同一个bucket里面,同一个bucket里面也就可能好多个key-value值。
当进行查找的时候,HashMap先计算这个key的hashcode,通过hashcode马上定位到这个key-value在哪个bucket
里面,这样查询的速度就非常快,然后在bucket里面通过equals方法在一个个查找,如果有一个key-value满足,则就找到了key,value就可以取到了,否则就没有相应的key了。
java 里对象使用后设置为NULL会减少内存占用吗?
被设置为NULL的实际是对象的引用,而不是对象本身
比如:
class Test(){
...
}
class Test2(){
public static void main(String[] args) {
Test Exam=new Test();
...
Exam=null;
...
}
}
第一个“=”将new Test()所创建的对象,引用给Exam。Exam是对象的引用而不是对象本身,Exam=null只是解除了Exam与原对象的引用关系,而并不没有释放原对象。所以,在这个时候,内存并不会减少。这时,该对象就成为了“垃圾”占用着内存空间,需要通过垃圾收集器来回收这些空间。
由于Java的垃圾回收机制,Java不需要像C或C++那样通过程序代码来显示地释放空间,而会由JVM自行回收,这部分空间何时回收是不可预知的。