2013年第2周三Java的苦恼
今天遇到java基本类型Integer的比较,被findbugs报错才发现java的麻烦,java基本数据类型的封装类型真是麻烦,java中8种基本数据类型都会有默认值,但其它引用类型变量默认值为null,所有在用equals方法判断前一定要先确保对象不能为null,就是是Integer a在使用a.intValue前也要先判断a!=null,真麻烦。
Integer比较
例如:两个对象里面都有一个方法为getInteger()的方法,返回的类型也是Integer的,这时候想比较两个Integer是否相等不能用"=="号,要用equals方法.
原因很简单equals方法来自Object基类。在Object里,equals的实现是直接用 == 操作符比较两个对象的内存地址。
举两个例子:
代码1(equals方法)代码
public class MainClass {
public static void main(String[] args) {
Integer n1 = new Integer(47);
Integer n2 = new Integer(47);
System.out.println(n1.equals(n2));
}
}
public class MainClass {
public static void main(String[] args) {
Integer n1 = new Integer(47);
Integer n2 = new Integer(47);
System.out.println(n1.equals(n2));
}
}
输出结果为true,看来Integer中没有按照Object里的默认实现来比较内存地址。在Integer中,equals重载了Object的equals方法(重载指的是子类的方法覆盖掉父类的方法,而改为自己的实现),它比较的是Integer的实际值。
代码2(相等比较)代码
public class MainClass {
public static void main(String[] args) {
Integer n1 = new Integer(47);
Integer n2 = new Integer(47);
System.out.println(n1 == n2);
System.out.println(n1 != n2);
}
}
public class MainClass {
public static void main(String[] args) {
Integer n1 = new Integer(47);
Integer n2 = new Integer(47);
System.out.println(n1 == n2);
System.out.println(n1 != n2);
}
}
这里输出的第一个结果为false,第二个为true。结合上面的代码1可以看出,==和equals的区别。
Long的equals和相同数值的Integer比较为何为false
The java code:
代码后运行!">
普通浏览复制代码打印代码
Integer i = new Integer(42);
Long b = new Long(42);
System.out.println(b.equals(i));
Integer i = new Integer(42);
Long b = new Long(42);
System.out.println(b.equals(i));
运行结果为
false
我们查看一下Long.equals的源代码
public boolean equals(Object obj) {
if (obj instanceof Long) { // 这里是关键
return value == ((Long) obj).longValue();
}
return false;
}
public boolean equals(Object obj) {
if (obj instanceof Long) { // 这里是关键
return value == ((Long) obj).longValue();
}
return false;
}
如果传入的类型不是Long,那么全部返回false,这就是原因了
Java对象比较
一、简单类型比较
Java中,比较简单类型变量用"==",只要两个简单类型值相等即返回ture,否则返回false;
二、引用类型比较
引用类型比较比较变态,可以用"==",也可以用"equals()"来比较,equals()方法来自于Object类,每个自定义的类都可以重写这个方法。Object类中的equals()方法仅仅通过"=="来比较两个对象是否相等。
在用"=="比较引用类型时,仅当两个应用变量的对象指向同一个对象时,才返回ture。言外之意就是要求两个变量所指内存地址相等的时候,才能返回true,每个对象都有自己的一块内存,因此必须指向同一个对象才返回ture。
在用"equals()"比较引用类型时,情况就比较复杂,甚至有些变态,容易掉进陷阱。
在Java API中,有些类重写了equals()方法,它们的比较规则是:当且仅当该equals方法参数不是 null,两个变量的类型、内容都相同,则比较结果为true。这些类包括:String、Double、Float、Long、Integer、Short、Byte、、Boolean、BigDecimal、BigInteger等等,太多太多了,但是常见的就这些了,具体可以查看API中类的equals()方法,就知道了。
解析变态的Boolean类:在这些类中,最最变态的是要数Boolean类了,我感觉是开发Boolean类的人员头脑进水了。我也不想细说了,你有好四中方式来创建一个Boolean对象(两构造方法,两个静态方法valueOf(),推荐用静态方法)。
Boolean类的变态之处不在于其equals()方法。而是在于Boolean对象创建方法。其实Boolean类的对象最多有两个,其toString()值分别是true和false。当且仅当用true或者"true"创建的Boolean类为同一个对象且toString()值为true。其他的字符串或者false创建的Boolean对象的值一律相等且toString()值为false。
三、重写equals()方法
在定义一个类的时候,如果涉及到对象的比较,应该重写equals()方法。重写的一般规则是:
1、先用"=="判断是否相等。
2、判断equals()方法的参数是否为null,如果为null,则返回false;因为当前对象不可能为null,如果为null,则不能调用其equals()方法,否则抛java.lang.NullPointerException异常。
3、当参数不为null,则如果两个对象的运行时类(通过getClass()获取)不相等,返回false,否则继续判断。
4、判断类的成员是否对应相等。往下就随意发挥了。呵呵!
四、总结
"=="比较对象是否引用了同一个对象,或者比较简单类型变量值是否相等。
Object类的equals()方法用来比较是否一个对象(内存地址比较),可以重写。
JDK中有些类重写了equals()方法,只要类型、内容都相同,就认为相等。
很变态的Boolean类,仅存在两个实例。具体可查看API。
一般来说,一个类如果涉及到比较,应该重写equals()方法,因为内存地址比较没有意义。
深入Java关键字null
一、null是代表不确定的对象
Java中,null是一个关键字,用来标识一个不确定的对象。因此可以将null赋给引用类型变量,但不可以将null赋给基本类型变量。
比如:int a = null;是错误的。Ojbect o = null是正确的。
Java中,变量的适用都遵循一个原则,先定义,并且初始化后,才可以使用。我们不能int a后,不给a指定值,就去打印a的值。这条对对于引用类型变量也是适用的。
有时候,我们定义一个引用类型变量,在刚开始的时候,无法给出一个确定的值,但是不指定值,程序可能会在try语句块中初始化值。这时候,我们下面使用变量的时候就会报错。这时候,可以先给变量指定一个null值,问题就解决了。例如:
Connection conn = null;
try {
conn = DriverManager.getConnection("url", "user", "password");
} catch (SQLException e) {
e.printStackTrace();
}
String catalog = conn.getCatalog();
如果刚开始的时候不指定conn = null,则最后一句就会报错。
二、null本身不是对象,也不是Objcet的实例
null本身虽然能代表一个不确定的对象,但就null本身来说,它不是对象,也不知道什么类型,也不是java.lang.Object的实例。
可以做一个简单的例子:
//null是对象吗? 属于Object类型吗?
if (null instanceof java.lang.Object) {
System.out.println("null属于java.lang.Object类型");
} else {
System.out.println("null不属于java.lang.Object类型");
}
结果会输出:null不属于java.lang.Object类型
三、Java默认给变量赋值
在定义变量的时候,如果定义后没有给变量赋值,则Java在运行时会自动给变量赋值。赋值原则是整数类型int、byte、short、long的自动赋值为0,带小数点的float、double自动赋值为0.0,boolean的自动赋值为false,其他各供引用类型变量自动赋值为null。
这个具体可以通过调试来看。
四、容器类型与null
List:允许重复元素,可以加入任意多个null。
Set:不允许重复元素,最多可以加入一个null。
Map:Map的key最多可以加入一个null,value字段没有限制。
数组:基本类型数组,定义后,如果不给定初始值,则java运行时会自动给定值。引用类型数组,不给定初始值,则所有的元素值为null。
五、null的其他作用
1、判断一个引用类型数据是否null。 用==来判断。
2、释放内存,让一个非null的引用类型变量指向null。这样这个对象就不再被任何对象应用了。等待JVM垃圾回收机制去回收。
简单点说null表示还没new出对象,就是还没开辟空间 ""表示new除了对象,但是这个对象装的是空字符串。
比如声明一个 String str ;
如果说str是null,那么内存根本没创建字符串对像.
如果说str是空串,那么确实存在一个由str引用的字符串对像,只不过这个字符串的值是""
null用来表示一个引用没有实例存在,而""本身是一个实例,有自己的对象空间,和"zzyyxx"这样的String没什么区别。两个都是有值 !!!
String s1 = "";
String s2 = null;
调用s2.length() 抛出nullpointerexception.
调用s1.length() 返回0.
Java中Integer与int类型的装箱和拆箱 .
其实Integer与int类型的赋值与比较最关键的一点就是:这两个变量的类型不同。Integer是引用类型,int是原生数据类型。
我们分四种情况来讨论:
1) Integer与int类型的赋值
a.把Integer类型赋值给int类型。此时,int类型变量的值会自动装箱成Integer类型,然后赋给Integer类型的引用,这里底层就是通过调用valueOf()这个方法来实现所谓的装箱的。
b.把int类型赋值给Integer类型。此时,Integer类型变量的值会自动拆箱成int类型,然后赋给int类型的变量,这里底层则是通过调用intValue()方法来实现所谓的拆箱的。
2) Integer与int类型的比较
这里就无所谓是谁与谁比较了,Integer == int与int == Integer的效果是一样的,都会把Integer类型变量拆箱成int类型,然后进行比较,相等则返回true,否则返回false。同样,拆箱调用的还是intValue()方法。
3) Integer之间的比较
这个就相对简单了,直接把两个引用的值(即是存储目标数据的那个地址)进行比较就行了,不用再拆箱、装箱什么的。
4) int之间的比较
这个也一样,直接把两个变量的值进行比较。
值得注意的是:对Integer对象,JVM会自动缓存-128~127范围内的值,所以所有在这个范围内的值相等的Integer对象都会共用一块内存,而不会开辟多个;超出这个范围内的值对应的Integer对象有多少个就开辟多少个内存。
今天过来时间Tomcat总是无法启动项目(启动时间600多ms,很快),无法加载Spring框架,删除掉发布的工程再重新发布后就好了,或许跟我之前Myeclipse突然自动关闭后我强制把tomcat的javaw.exe进程强制结束掉有关。