代码之旅

I love Coding !

  1. Java 的基本原理就是“形式错误的代码不会运行”。(编译错误=“形式错误的代码”)
  2. 捕获错误最理想的是在编译期间,最好在试图运行程序以前。然而,并非所有错误都能在编译期间侦测到。有些问题必须在运行期间解决,让错误的缔结者通过一些手续向接收者传递一些适当的信息,使其知道该如何正确地处理遇到的问题。

摘自 thinking in java 4th

阅读全文 »

如果匿名类的实现非常简单,比如只包含一个方法的接口,那么匿名类的语法可能看起来很笨重且不明确。使用Lambda表达式可以更简洁地表达单方法类的实例。

阅读全文 »

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
public class Demo {
public static void main(String[] args){
method_1();
method_2();
}
private static void method_1(){
int j = 0;
for(int i=0;i<100;i++){
j = j++;
}
System.out.println("method_1---"+j);
}
private static void method_2(){
int j = 0;
for(int i=0;i<100;i++){
j = ++j;
}
System.out.println("method_2---"+j);
}
}

上面程序的输出结果是:

1
2
method_1---0  
method_2---100

为什么会是这样呢?

因为在计算过程中,使用了Java中间变量缓存机制。在java中,执行自增运算时,会为每一个自增操作分配一个临时变量,如果是前缀加(++i),就会“先自加1后赋值(给临时变量)”;如果是后缀加(i++),就会“先赋值(给临时变量)后自加1”。运算最终使用的,并不是变量本身,而是被赋了值的临时变量。

  • j = j++; ==> {int temp = j; j = j + 1; j = temp; }
  • j = ++j; ==> {j = j + 1; int temp = j; j = temp; }

可变类和不可变类(Mutable and Immutable Objects)的初步定义:

  1. 可变类:当你获得这个类的一个实例引用时,你可以改变这个实例的内容。
  2. 不可变类:当你获得这个类的一个实例引用时,你不可以改变这个实例的内容。不可变类的实例一但创建,其内在成员变量的值就不能被修改。
  • jdk的可变类和不可变类
    • 不可变类:jdk的java.lang包中 Boolean, Byte, Character, Double, Float, Integer, Long, Short,String
    • 可变类:StringBuffer,java.util.Date
阅读全文 »

  • 修饰类,代表不可以继承扩展
  • 修饰变量,代表变量不可以修改
  • 修饰方法,代表方法不可以重写
阅读全文 »

本文介绍一些位运算技巧:

异或消除

a=0a=a00=aaa=0 \oplus a= a \oplus 0 \newline 0=a \oplus a

由上面两个推导出:

a=abba=a \oplus b \oplus b

交换两个数

a=abb=aba=aba=a \oplus b \newline b=a \oplus b \newline a=a \oplus b \newline

移除二进制最后一个1

a=n(n1)a=n \land (n-1)

获取二进制最后一个1

diff=(n(n1))ndiff=(n \land (n-1)) \oplus n

复合选项

在实际开发中常会用到选项,选项开启和关闭可以有效的控制代码执行逻辑。

对于选项,我们可以简单的用类的一个属性去判断。也可以使用一些编程技巧,通过一个数字来表示组合选项。

一个简单的例子如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
public class ByteOption {

public enum Option {
/*
option
*/
OPTION_1(op1),
OPTION_2(op2),
OPTION_3(op3),
OPTION_4(op4),
OPTION_5(op5),
OPTION_6(op6),
OPTION_7(op7)
;

byte value;

Option(byte i) {
value = i;
}
}
// 将多个属性设置为不冲突的二进制数
public static byte op1 =0b00000001;
public static byte op2 =0b00000010;
public static byte op3 =0b00000100;
public static byte op4 =0b00001000;
public static byte op5 =0b00010000;
public static byte op6 =0b00100000;
public static byte op7 =0b01000000;
private static int ALL = 0b11111111;


private byte op = 0b00000000;

public void enableOption(Option option){
op = (byte) (op | option.value);
}

public void disableOption(Option option){
op = (byte) (op & (option.value^ALL));
}

public boolean isEnable(Option option){
return option.value == (op & option.value);
}
}

运算符<<(左移),>>(右移)和 >>>(无符号右移)称为移位运算符。移位运算符的左侧操作数是要移位的值; 右侧操作数指定移位距离。下面是语法描述:

1
2
3
4
5
ShiftExpression:
AdditiveExpression
ShiftExpression << AdditiveExpression
ShiftExpression >> AdditiveExpression
ShiftExpression >>> AdditiveExpression
  • n << s的意思是n左移s个bit位,这相当于 n2sn*2^s,注意溢出。
  • n >> s的意思是n带符号位右移s个bit位,若左操作数是正数,则高位补“0”,若左操作数是负数,则高位补“1”,这相当于 n/2sn/2^s
  • n >>>s的意思是不带符号右移,无论左操作数是正数还是负数,在高位都补“0”。
    • 如果n是正数,那么结果等同于n >> s
    • 如果n是负数,且类型是int,那么结果等同于(n >> s) + (2 << ~s)
    • 如果n是负数,且类型是long,那么结果等同(n >> s) + (2L << ~s)

按位补码运算符: ~

shift表达式的类型是左侧操作数的提升类型。

  • 如果左手操作数的提升类型是int,则只使用右手操作数的五个最低位作为移位距离.
  • 如果左侧操作数的提升类型是long,则只使用右侧操作数的六个最低位作为移位距离.
1
2
3
5 >> 32 == 5
// 5 >> 0
// 32 100000 后5位是 00000

参考

Java源码在运行之前都要编译成为字节码格式(如.class文件),然后由ClassLoader将字节码载入运行。在字节码文件中,指令代码只是其中的一部分,里面还记录了字节码文件的编译版本、常量池、访问权限、所有成员变量和成员方法等信息(详见Java字节码规范)。本文主要简单介绍不同Java指令的功能。

阅读全文 »

JVM支持使用PrintAssembly选项来检查JIT编译器生成的代码。对了解代码是如何执行的,以及JIT编译器如何工作和优化代码有很大帮助。

阅读全文 »

Java 动态代理

本篇文章引用源码默认为Java 8

阅读全文 »