java编程技巧

来源:互联网 发布:centos 安装cassandra 编辑:程序博客网 时间:2024/05/29 15:10
1.浮点数的计算
1.10这个浮点数在计算机里面不能正确表示所以相加减乘除会出错
应该使用BigDecimal包装类来相加减乘除
2.长整除
long类型进行相加减乘除的时候会转化为int型进行操作
为了避免进行自动转化应该在long类型后面加上大写的L
小写的l和1会产生歧义(程序员会误判)
3.十六进制和八进制的趣事
十六进制和八进制本身是可以表示负数的(字面的常量的最高位被置位了,
那么他们就是负数)所以在计算的时候应该避免这种识别,在进制的后面加上
需要进行操作的类型常量(L,F, D)
4.多重转化
在数字之间的转换
System.out.println((int)(char)(byte)-1);
如果最初的数值类型是有符号的,那么就制定符号扩展;如果它是char,
那么不管他要被转换成什么类型,都执行零扩展
5.条件判断(三目运算)
最好两边都是同一类型的
char j = 'x';
int i = 0;
System.out.println(true ? j : 0);
System.out.println(false ? i : j);
会发生错误的类型转换而返回错误的结果
最好在条件表达式中使用类型星通的第二和第三操作数
6.复合赋值
复合赋值表达式自动的将所执行计算的结果转型为器左侧变量的类型
short x = 0;
int i = 123456;
x += i;
System.out.println(x);
请不要经复合赋值操作永夜byte,short , char类型的变量
7.数值的比较
数值比较操作执行的是值比较,而判等操作符执行的是引用标识比较
当两个包装类型比较时(<= 或者>=等比较)会自动解包装为基本的类型进行比较,
while(i <= j&& j<=i && i != j){
}
当i和j为包装类型就可以无限循环
8.整数的不对称性
对于每一种有符号的整数类型(int,long,byte和short)负的数值总是比整的数值
多一个,这个多出来的值总是这种类型所能表示的最小数,
对Integer.MIN_VILUE取负值不会改变它的值。Long.MIN_VILUE也是如此。
9.构造器
构造器可以抛异常
当一个类 的成员变量中有自己的实例 就会出现递归的创建 直至栈溢出
Exception in thread "main" java.lang.StackOverflowError
10.方法的重载
两个方法名称一样都有相同的传参
当调用这个方法的时候传入都能匹配上的参数是
匹配的是最精确的方法(就是最接近的参数的方法会被调用)
11.静态实例
每个静态域在声明它的类及其所有子类中共享一份单一的拷贝
12.静态方法
在父类中有一个静态的方法
子类继承父类重写了该静态方法
在要调用静态方法的时候使用类名来调用
千万别用表达式(new 出来的引用名)来调用
使用表达式调用的话会出现调用父类的静态方法
class Dog {
public static void bark(){
System.out.println("wook");
}
}
class Basenji extends Dog{
public static void bark(){
System.out.println("basenji");
}
}
public class Bark {
public static void main(String[] args) {
Dog dog = new Dog();
Dog nipper = new Basenji();
// Basenji nipper = new Basenji();
// nipper.bark();
// 输出 basenji
dog.bark();
nipper.bark();
// 输出:wook
// wook

}
}
13.类(顺序)的初始化
类中含有类的静态实例创建
类的初始化在构造器中调用了静态的成员变量
要当心类静态实例创建顺序问题
当静态实例创建(调用构造器使用下面的实例)排在前面的时候它依赖顺序下面的实例
可能会出现下面未被执行初始化进而使用默认的值进行操作


类的初始化过程是先创建成员变量赋予默认值
当执行到new 构造器的时候完才会赋值下面的值
再在执行右边的赋值操作

public class ShiYan {
public static ShiYan s = new ShiYan();//当他在date下面的时候会正常运行
public final String ss;
public static int date = (int) System.currentTimeMillis();
public ShiYan() {
ss = date + "时间";
}
public static void main(String[] args) {
System.out.println(s.ss);
}
}
切记:不要使用还初始化的参数
可能引起更改了参数的值后又被虚拟机
给赋值覆盖
解决的方法有:1>懒加载 2>积极初始化(顺序)
14.构造器对覆写方法调用
不要在构造器中调用覆写方法
因为可能调用了覆写的方法中需要往下执行进行赋值的代码
然而还没赋值覆写的方法进行了调用进而导致错误的结果
多在继承里面产生错误的代码
15.重写equals
重写equals()方法必须同时覆写hashCode()方法
注意是覆写不是重载
例如:在hash集合中是通过hashCode()方法来生成存储位置的
当要判断集合是否包含对象的时候就会调用object.hashCode()
方法来生成散列值,这个方法即使是相同的对象也会生成不同的
散列值(有大约2500w/1生成一样的),故而映射不到
16.整型
千万不要在整型字面常量的前面加上一个0;
这会使它变成一个八进制的字面常量
列如:012 会变成10
17.方法的修饰符
一个包内私有的方法不能被位于另一个包中的某个方法直接覆写
在同包中也是一样
就是说父类中方法的修饰符的范围必须能包容子类方法的修饰符
当调用父类的方法中使用了重写的方法
子类中的方法不能感知到所以会调用父类的方法
18.锁
多线程的Thread.join()方法在表示正在被连接到的那个thread实例上调用object.wait方法。这样
就在等待期间释放了该对象上的锁(object.wait会释放锁)
http://www.cnblogs.com/DreamSea/archive/2012/01/16/2263844.html
简单来说就是thread.join方法会释放锁
这样当该锁不能释放的时候释放掉别的方法获得到锁就会出现问题
解决1
添加一个object类型的私有域作为锁。并在方法中同步该对象
private final object lock = new object();
void methodName(){
synchronize(lock){
join()
}
}
在其他方法中也同样
当两个并发线程访问同一个对象object中的这个synchronized(this)同步代码块时,
一个时间内针对该对象的操作只能有一个线程得到执行(对象的内部锁决定的)。
另一个线程必须等待当前线程执行完这个代码块以后才能执行该代码块。
所以该方法只能在多线程环境中保证锁不被释放
附上源码
  1. //方法是个同步的,而且会抛出InterruptedException异常  
  2. public final synchronized void join(long millis) throws InterruptedException {  
  3.     long base = System.currentTimeMillis();  
  4.     long now = 0;  
  5.   
  6.     if (millis < 0) {  
  7.         throw new IllegalArgumentException("timeout value is negative");  
  8.     }  
  9.     //我们可以看到这里使用了while循环做判断的,然后调用wait方法的,所以说join方法的执行是完全通过wait方法实现的  
  10.     //等待时间为0的时候,就是无限等待,直到线程死亡了(即线程执行完了)  
  11.     if (millis == 0) {  
  12.         //如果当前线程还存活的话,就等待  
  13.         while (isAlive()) {  
  14.             //调用该线程的join方法的线程拿到锁之后进行等待,直到线程执行结束(这个例子就是main线程)  
  15.             wait(0);  
  16.         }  
  17.     } else {  
  18.         //如果是等待的特定时间的话  
  19.         while (isAlive()) {  
  20.             long delay = millis - now;  
  21.             if (delay <= 0) {  
  22.                 break;  
  23.             }  
  24.             wait(delay);  
  25.             now = System.currentTimeMillis() - base;  
  26.         }  
  27.     }  
  28. }
解决2
sleep()方法代替(委婉的方法)
19.反射内部类
当要反射内部类的时候
就必须通过构造方法反射,显式的传递一个直接外围实例
因为一个非静态的嵌套类的构造器,在编译的时候会将一个隐藏的参数作为它的第一个参数
,这个参数表示了它的直接外围实例。
newInstance()不具备传参的能力所以用getConstructor(Class object)
解决1
// Constructor<Inner> constructor = Inner.class.getConstructor(Shiyan8.class);
// System.out.println(constructor.newInstance(this));
解决2
把内部类改成静态的
20.线程的输出
为了确保子进程能够结束,你必须排空它的输出流;对于错误流也是一样。
对于Process类不会自动排空所以用ProcessBuilder类排空流
或者使用子线程去读取流
21.单例的序列化
条件:单例中运行
在对对象序列化的时候,保存的是从内存中克隆的对象
然后反序列化就会得到一个与原来不一样的对象
解释:
对象存储的内容不变
但是在java栈中会有两个引用指向对象堆
违反了单例的规则
单例的规则:保证一个类仅有一个实例,并提供一个访问它的全局访问点(引用百度百科)
番外:
如果用于开关机关机操作“应该”不受影响
毕竟对象的创建是反序列化来的
要避免这个就要添加一个readResolve()方法
  1. public final class MySingleton implements Serializable{  
  2.     private MySingleton() { }  
  3.     private static final MySingleton INSTANCE = new MySingleton();  
  4.     public static MySingleton getInstance() { return INSTANCE; }  
  5.     private Object readResolve() throws ObjectStreamException {  
  6.        // instead of the object we're on,  
  7.        // return the class variable INSTANCE  
  8.       return INSTANCE;  
  9.    }  
  10. }  
这样返回的就是同一个对象
22.类的惰性初始化
类的初始化过程中如果新建一个线程
会造成初始化等待。
初始化线程执行到子线程,子线程会等待Class对象(子线程会等待父线程初始化完毕)
直到初始化完成。
初始化线程在等待子线程初始化完毕
形成死锁
总之。在类的初始化期间等待某个后台线程很可能会造成死锁
写到最后
功底有限欢迎指正