黑马程序员_Java插播篇(一)——Java解惑

来源:互联网 发布:普希金怎么死的 知乎 编辑:程序博客网 时间:2024/06/18 09:10

------- android培训java培训、期待与您交流! ---------- 

这是我整理的小问题和小技巧,放在这里,以后肯定会用的着。


【1】char数组不是字符串;利用String.valueOf(char[])将其转换为字符串。

【2】从byte到String,都在使用一个字符集,不管是否显式指定了它。

【3】代码中出现一行 http://www.baidu.com; 是没错的。

【4】在switch的case中缺少break语句是非常常见的错误。

【5】当心代码中的通过拓宽原生类型转换把字符数值'M' 转换为 int数值77。如:new StringBuffer('M');
!(char不是String,而是更像int)

【6】j=0;for(100次){ j = j++; }结果还是0;(int temp=j; j=j+1; j=temp;)
!不要在单个的表达式中对相同的变量赋值超过一次。

【7】for(int i=0; i<=Integer.MAX_VALUE; i++) 实际是无限循环!(注意是小于等于)
!无论何时使用一个整数类型,都要意识到其边界条件。

【8】while(-1 << i != 0){i++;}也是个无限循环。(-1 << 32)等于-1而不是0。
!如果试图对一个int数值移位32位,或是对long数值移位64位,都只能返回这个数值本身。
!(移位长度是对32或64取余,确保在1~31之中)

【9】可以用一个double或float数值来表示无穷大。double i = 1.0e40;或1.0/0.0;
!将一个很小的浮点数加到一个很大的浮点数上时,不会改变大浮点数的值;
!二进制浮点算术只是对实际算术的一种近似。

【10】NaN不等于任何浮点数值,包括它自身在内。(0.0/0.0 => NaN)
!NaN参与的运算的结果还是NaN。

【11】声明一个i,使 while(i != 0){ i >>>= 1;}转变为一个无限循环。
!利用"窄化原生类型转换",short i = -1;(先提升为int--"拓宽原生类型转换",然后截掉高16位)

【12】不要在short、byte或char类型的变量上使用复合赋值操作符(当心自动窄化转型)

【13】如何使其成为无限循环?while(i <= j && j <= i && i != j){}
!用包装类:new Integer(0);
!对于包装类型,数值比较操作符执行的是值比较,而判等操作符执行的是引用标识的比较。

【14】(i != 0 && i == -i)为TRUE?
!int i = Integer.MIN_VALUE;即 -2^31 <=> 0x80000000
!Integer.MIN_VALUE是它自己的负值,Long.MIN_VALUE也是一样。(因为取负值产生了溢出)

【15】(float)2000000000 == 2000000050 的结果为TRUE,因为从int提升到float会舍弃低位
!不要使用浮点数作为循环索引

【16】ms % 60*1000;(注意:%和*有相同的优先级)

【17】【Exception】
①一个方法可以抛出的受检查异常集合是它所适用的所有类型声明要抛出的受检查异常集合的交集!而不是合集。
!(接口多个继承而来的throws声明子句的交集,将减少方法被允许抛出的异常数量。)
②对于捕获受检查异常的catch子句,只有在相应的try子句可以抛出这些异常时才被允许。(Exception/Throwable合法)
③对于任何finally语句块中可能抛出的受检查异常都要进行处理,而不是任其传播。
------------------------------------------------------------------------------------------------
【】探测类的丢失,使用反射来引用类。

【】构造器Confusing(Object o){}与Confusing(int[] a){},new Confusing(null)会使用后者。
!找最精确的那个,int[]是Object,但Object未必是int[]。

【】静态域由声明它的类及其所有子类所共享。

【】对静态方法的调用不存在任何动态分派机制。

【】在final类型的静态域被初始化之前,存在读取其值的可能,而此时该静态域包含的是缺省值。
!final类型的域只有在其初始化表达式是常量表达式时才是常量。

【】instanceof的左操作数为null时返回FALSE;
!如果两个操作数的类型都是类,那么其中一个必须是另一个的子类型,否则导致编译期错误。

【】千万不要在构造器中调用可覆写的方法,否则会在构造前读取域缺省值。

【】模式:私有构造器捕获(Private Constructor Capture)

【】Java语言规范不允许一个本地变量声明语句作为-一条语句-在for、while或do循环中重复执行。

【】当心equals方法的重载导致的错误。Object的equals(Object o)方法!
-----------------------------------------------------------------------------------------------------
【】绝对值函数Math.abs不能保证一定返回非负的结果。如果它的参数是Integer.MIN_VALUE,将返回它的参数。

【】不要使用基于减法的比较器,除非你能确保比较的数值之间的差永远不会大于Integer.MAX_VALUE。
!正排序比较器方法:public int compare(Integer a,Integer b){return (a>b ? 1 : (a==b ? 0 : -1));}

【】一旦一个方法在子类中被覆盖,你就不能在子类的实例上调用它了(除了在子类内部通过super关键字)。
!可以通过将子类实例转型为超类类型来访问被隐藏的域。

【】当一个变量和一个类型具有相同的名字,并且它们位于相同的作用域时,[变量名]具有优先权。
!变量名遮掩类型名,变量名和类型名可以遮掩包名。

【】一个包内私有的方法(缺省修饰符)不能被位于另一个包中的某个方法直接覆盖。
!在包外重用它们的名字不会对包内产生任何影响。

【】本身就属于某个范围的成员在该范围内与静态导入相比更具优先权。
!如:import static java.util.Arrays.toString;被从Object继承来的toString方法遮蔽了。

【】如果重载了一个方法,一定要确保所有的重载版本行为一致。

【】访问位于其他包中的非公共类型的成员是不合法的。
!在使用反射访问某个类型时,请使用表示某种可访问类型的Class对象。

【】只有在实例化时才使用反射,而方法调用都通过接口进行。

【】一个非静态的嵌套类的构造器,在编译的时候会将一个隐藏的参数作为它的第一个参数,这个参数表示而来它的直接外围实例。
!参数被隐式地传递进去,此过程只适用于普通的构造器调用,也就是不使用反射的情况。
!使用反射时的解决方式:使用java.lang.reflect.Constructor。

【】除非确实是需要一个外围实例,否则应该优先使用静态成员类。
---------------------------------------------------------------------------------------------------------------------
【】Thread.join方法在表示正在被连接的那个Thread实例上调用Object.wait方法。这样就在等待期间释放了该对象上的锁。
!Thread.join方法会释放锁,从而破坏了同步。

【】每次创建新的Thread实例,系统都会去获取Thread类上的锁。
!阻止任何新线程的创建:

synchronized(Thread.class){Thread.sleep(Long.MAX_VALUE);}

【】一个实现了Serializable的单件类,一定要有Object readResolve方法,用以返回它的唯一的实例。
!通过解序列会破坏单例。

【】调用Thread.Interrupted方法总是会清除当前线程的中断状态。

【】一个不同的死锁程序:
public class Lazy {private static boolean initialized = false;static{Thread t = new Thread(new Runnable(){public void run() {initialized = true;}});t.start();try {t.join();} catch (InterruptedException e) {e.printStackTrace();}}public static void main(String[] args) {System.out.println(initialized);}}
!当主线程调用Lazy.main方法时,它会检查Lazy类是否已经被初始化。
<此时Lazy类并未被初始化>,so主线程会记录下当前正在进行初始化,并开始对这个类进行初始化。
主线程会将initialized设为false,创建并启动一个后台线程t,然后主线程会等待t执行完毕。
--后台线程t调用它的run方法,在将Lazy.initialized设为TRUE之前,它也会去检查Lazy类是否已被初始化。
--这个时候,Lazy类正在被另一个线程<main>进行初始化。这种情况下,当前线程<后台线程t>会等待Class对象直到初始化完成。
---然而,因为t.join(),那个正在进行初始化的主线程<main>将会等待后台线程运行结束。
---死锁了!
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

【】当比较两个原生类型数值时,操作符 == 首先进行“二进制数据类型提升”,这会导致这两个数值中有一个会进行“拓宽原生类型转换”。
!大部分拓宽原生类型转换不会有问题;将int或long值转换成float值,或将long值转换成double值时,均会导致精度丢失。
!->long x = Long.MAX_VALUE; double y = (double)Long.MAX_VALUE; <x==y --> true>

【】一个原生类型很像其对应的参数化类型,但是它的所有实例成员都要被替换掉,而替换物就是这些实例成员被擦掉对应部分之后剩下的东西。
!-具体地说,在一个实例方法声明中出现的每个参数化的类型都要被其对应的原生部分所取代。
!另外,原生类型和以Object为类型参数的参数化类型也不相同。(List不同于List<Object>)

【】一个泛型类的内部类可以访问到它的外围类的类型参数。
!泛型类嵌套泛型类时,最好为它们的类型参数设置不同的名字。

【】

0 0
原创粉丝点击