java

位置:IT落伍者 >> java >> 浏览文章

Java的六大问题你都懂了吗


发布日期:2021年07月20日
 
Java的六大问题你都懂了吗

这些问题对于认真学习java的人都要必知的当然如果你只是初学者就没必要那么严格了那如果你认为自己已经超越初学者了却不很懂这些问题请将你自己重归初学者行列


到底要怎么样初始化!


本问题讨论变量的初始化所以先来看一下Java中有哪些种类的变量


类的属性或者叫值域


方法里的局部变量


方法的参数  对于第一种变量Java虚拟机会自动进行初始化如果给出了初始值则初始化为该初始值如果没有给出则把它初始化为该类型变量的默认初始值


所有对象引用类型变量默认初始值为null即不指向任何对象注意数组本身也是对象所以没有初始化的数组引用在自动初始化后其值也是null对于两种不同的类属性static属性与instance属性初始化的时机是不同的instance属性在创建实例的时候初始化static属性在类加载也就是第一次用到这个类的时候初始化对于后来的实例的创建不再次进行初始化这个问题会在以后的系列中进行详细讨论对于第二种变量必须明确地进行初始化如果再没有初始化之前就试图使用它编译器会抗议如果初始化的语句在try块中或if块中也必须要让它在第一次使用前一定能够得到赋值也就是说把初始化语句放在只有if块的条件判断语句中编译器也会抗议因为执行的时候可能不符合if后面的判断条件如此一来初始化语句就不会被执行了这就违反了局部变量使用前必须初始化的规定但如果在else块中也有初始化语句就可以通过编译因为无论如何总有至少一条初始化语句会被执行不会发生使用前未被初始化的事情对于trycatch也是一样如果只有在try块里才有初始化语句编译部通过如果在catch或  finally里也有则可以通过编译总之要保证局部变量在使用之前一定被初始化了所以一个好的做法是在声明他们的时候就初始化他们如果不知道要出事化成什么值好就用上面的默认值吧!其实第三种变量和第二种本质上是一样的都是方法中的局部变量只不过作为参数肯定是被初始化过的传入的值就是初始值所以不需要初始化


instanceof是什么东东?


instanceof是Java的一个二元操作符和==><是同一类东东由于它是由字母组成的所以也是Java的保留关键字它的作用是测试它左边的对象是否是它右边的类的实例返回boolean类型的数据然而这种做法通常被认为是没有好好利用面向对象中的多态性其实上面的功能要求用方法重载完全可以实现这是面向对象变成应有的做法避免回到结构化编程模式只要提供两个名字和返回值都相同接受参数类型不同的方法就可以了所以使用instanceof在绝大多数情况下并不是推荐的做法应当好好利用多态

“==”和equals方法究竟有什么区别?


==操作符专门用来比较变量的值是否相等比较好理解的一点是根据前一帖说过对象变量其实是一个引用它们的值是指向对象所在的内存地址而不是对象本身a和b都使用了new操作符意味着将在内存中产生两个内容为“foo”的字符串既然是“两个”它们自然位于不同的内存地址a和b的值其实是两个不同的内存地址的值所以使用“==”操作符结果会是false诚然a和b所指的对象它们的内容都是“foo”应该是“相等”但是==  操作符并不涉及到对象内容的比较对象内容的比较正是equals方法做的事看一下Object对象的equals方法是如何实现的


boolean equals(Object o){


return this==o}


Object对象默认使用了==操作符所以如果你自创的类没有覆盖equals方法那你的类使用equals和使用==会得到同样的结果同样也可以看出Object的equals方法没有达到equals方法应该达到的目标比较两个对象内容是否相等因为答案应该由类的创建者决定所以  Object把这个任务留给了类的创建者所以当你是用equals方法判断对象的内容是否相等请不要想当然因为可能你认为相等而这个类的作者不这样认为而类的equals方法的实现是由他掌握的如果你需要使用equals方法或者使用任何基于散列码的集合  (HashSetHashMapHashTable)请察看一下java doc以确认这个类的equals逻辑是如何实现的[ nextpage]


final关键字到底修饰了什么?


final使得被修饰的变量“不变”但是由于对象型变量的本质是“引用”使得“不变”也有了两种含义引用本身的不变和引用指向的对象不变


引用本身的不变


final StringBuffer a=new StringBuffer(“immutable”)


final StringBuffer b=new StringBuffer(“not immutable”)


a=b//编译期错误


引用指向的对象不变


final StringBuffer a=new StringBuffer(“immutable”)


aappend(“ broken!”)//编译通过


可见final只对引用的“值”有效它迫使引用只能指向初始指向的那个对象改变它的指向会导致编译期错误至于它所指向的对象的变化final  是不负责的这很类似==操作符==操作符只负责引用的“值”相等至于这个地址所指向的对象内容是否相等==操作符是不管的理解final问题有很重要的含义许多程序漏洞都基于此final只能保证引用永远指向固定对象不能保证那个对象的状态不变在多线程的操作中一个对象会被多个线程共享或修改一个线程对对象无意识的修改可能会导致另一个使用此对象的线程崩溃一个错误的解决方法就是在此对象新建的时候把它声明为final意图使得它“永远不变”其实那是徒劳的

我声明了什么!


许多人都做过这样的事情但是我们到底声明了什么?回答通常是一个String内容是“Hello  world!”这样模糊的回答通常是概念不清的根源如果要准确的回答一半的人大概会回答错误这个语句声明的是一个指向对象的引用名为“s”可以指向类型为String的任何对象目前指向“Hello  world!”这个String类型的对象这就是真正发生的事情我们并没有声明一个String对象我们只是声明了一个只能指向String对象的引用变量所以如果在刚才那句语句后面如果再运行一句String  string =  s我们是声明了另外一个只能指向String对象的引用名为string并没有第二个对象产生string还是指向原来那个对象也就是和s指向同一个对象


String到底变了没有?


没有因为String被设计成不可变(immutable)类所以它的所有对象都是不可变对象请看下列代码


String s = “Hello”


s = s + “ world!”


s所指向的对象是否改变了呢?从本系列第一篇的结论很容易导出这个结论我们来看看发生了什么事情在这段代码中s原先指向一个String对象内容是“Hello”然后我们对s进行了+操作那么s所指向的那个对象是否发生了改变呢?答案是没有这时s不指向原来那个对象了而指向了另一个  String对象内容为“Hello  world!”原来那个对象还存在于内存之中只是s这个引用变量不再指向它了通过上面的说明我们很容易导出另一个结论如果经常对字符串进行各种各样的修改或者说不可预见的修改那么使用String来代表字符串的话会引起很大的内存开销因为String对象建立之后不能再改变所以对于每一个不同的字符串都需要一个String对象来表示这时应该考虑使用StringBuffer类它允许修改而不是每个不同的字符串都要生成一个新的对象并且这两种类的对象转换十分容易同时我们还可以知道如果要使用内容相同的字符串不必每次都new一个String例如我们要在构造器中对一个名叫s的String引用变量进行初始化把它设置为初始值应当这样做


public class Demo {


private String s


public Demo {


s = “Initial Value”}


}


而非


s = new String(“Initial Value”)


后者每次都会调用构造器生成新对象性能低下且内存开销大并且没有意义因为String对象不可改变所以对于内容相同的字符串只要一个  String对象来表示就可以了也就说多次调用上面的构造器创建多个对象他们的String类型属性s都指向同一个对象上面的结论还基于这样一个事实对于字符串常量如果内容相同Java认为它们代表同一个String对象而用关键字new调用构造器总是会创建一个新的对象无论内容是否相同至于为什么要把String类设计成不可变类是它的用途决定的其实不只String很多Java标准类库中的类都是不可变的在开发一个系统的时候我们有时候也需要设计不可变类来传递一组相关的值这也是面向对象思想的体现不可变类有一些优点比如因为它的对象是只读的所以多线程并发访问也不会有任何问题当然也有一些缺点比如每个不同的状态都要一个对象来代表可能会造成性能上的问题所以Java标准类库还提供了一个可变版本即StringBuffer


最后呢还有些java的技术包括EJB可以选择学习与三大轻量级框架相比EJB就是当之无愧的重量级了

               

上一篇:Java的字符串函数集锦

下一篇:jsp Servlet如何处理表单数据