Java
语言相关基础知识。
命名规范
- 驼式命名法
类或接口,首字母大写;方法或变量,首字母小写。 - 常量
大写字母和下划线组成,首字母不能是下划线。
原码,补码,反码,移码
- 正数
原码,补码,反码都是相同的。 - 负数
反码是其绝对值按位取反,补码是反码加 1
举例 1 :[-0]原 = 1 000000,[-0]反 = 1 1111111,[-0]补 = 0 000000
举例 2 :[-1]原 = 1 000001,[-1]反 = 1 1111110,[-1]补 = 1 1111111
- 移码
不管正负数,只要将其补码的符号位取反即可。 - 浮点数
N = 2 ^E * F
,其中E
为阶码,F
为尾数;阶码使用移码表示,尾数使用原码表示;浮点数运算前会先对阶。 - 补码快速计算
符号位不变其他的从低位开始,直到遇见第一个 1 之前,什么都不变。遇见第一个 1 后保留这个 1 ,以后按位取反。例:[-7]原 = 1 0000111 ,[-7]补= 1 1111001
逻辑运算
异或
任何数连续两次与另外一个数异或,结果是本身。A ^ B ^ B = A ^ (B ^ B) = A ^ 0 = A
移位运算符
Java
中负数都是按照反码来表示的,移位运算基于值的反码。
规则
如果移动的位数超过了该类型的最大位数,那么编译器会对移动的位数取模。如对 int
型移动 33 位,实际上只移动了 33%32=1
位。
<<
左移运算符
高位移出舍弃,右边低位补零。如:
[2] = 0 0000010,左移 2 位为 [8] = 0 0001000;
[-2] = 1 1111110,左移 2 位为 [-8] = 1 1111000;
[-127] = 1 0000001,左移 1 位为 [2] = 0 0000010;也就是负数左移,并不保留符号位,直接舍弃。
>>
右移运算符
符号位不变,左边高位补上符号位。如:
[4] = 0 0000100,右移 1 位为 [2] = 0 0000010;
[-4] = 1 1111100,右移 1 位为 [-2] = 1 1111110;>>>
无符号右移运算符
忽略了符号位扩展,最高位补零。无符号右移规则和右移运算是一样的,只是填充时不管左边的数字是正是负都用 0 来填充。
[-8] = 1 1111000,右移 2 位为 [-2] = 0 0111110;
意义: Java
中数字二进制比特的最高位为符号位,即 int, long
只能使用 31/63 位来表示取值范围,无符号右移可以将最高位不做符号位来考虑,即变相扩大了数字的取值范围,可以使用 32/64 来表示 int, long
,在图像处理,加解密等用的比较多。
作用
- 乘除
在结果没有溢出的前提下:num << n
:表示num
乘以2 ^ n
;如左移 3 位,表示乘以 8num >> n
:表示num
除以2 ^ n
;如右移 2 位,表示除以 4 - 截取数字高/低位
比如:1 << 3
表示,低三位都是 0 ,在按位操作时,截取掉低三位
负数的除法和余数
- 除法
表达式a/b
的商会向 0 取整。 - 余数
a % b
的余数的定义是(a/b)*b + a % b
恒等于a
。 - 示例
例如:-14/3 和 14/-3 的商都是 -4;但 -14 % 3 是 -2,而14 % -3 是 2。
数据类型
数据类型分类
基本数据类型取值范围
浮点类型特殊数值:+0, -0, NaN(Not a Number), +infinities, -infinities
,其中:
1 | // Float.java |
包装类
Java
为每种基本数据类型分别设计了对应的类,称之为包装类 Wrapper Classes
。
8 种基本数据类型及对应的包装类
基本类型 | 包装类 |
---|---|
byte | Byte |
short | Short |
int | Integer |
long | Long |
char | Character |
float | Float |
double | Double |
boolean | Boolean |
自动装箱和拆箱
- 自动装箱
Auto Boxing Conversation
基本类型自动转换为对应的封装类型(对象)即为自动装箱。 - 自动拆箱
Unboxing Conversation
封装类型自动转换为基本类型为拆箱。
转换过程
- 装箱:
*.valueOf(*)
- 拆箱:
*.intValue()
源码如下,展示了常见的自动装箱和拆箱的用法:
1 | // 自动装箱 |
经过反编译后,可以明确看到装箱和拆箱的动作:
1 | Integer localInteger = Integer.valueOf(1); // 装箱 |
自动装箱后的比较
因为是对象比较,所以建议直接使用
equal
而不是==
。
==
比较的是对象的首地址。但是 Java
做了部分优化,在如下范围内使用的是相同的对象(封装类型的缓存),范围外则在装箱的过程中重新生成一个对象。
char
[\u0000, \u007f]
:即ASCII
码表中的 128 个字符。byte/short/int/long
[-128, 128)
:在-128 <= x < 128
范围内,共用相同的对象(缓存),即byte
能够表达的范围。float/double
没有缓存,直接重新生成一个新对象。
1 | // char |
String
的自动拆装箱
基本概念:Java
中的 "abc"
对应的实际是常量,存储在常量池中。
1 | // 对应的都是常量池中 "abc" 的地址 |
注意事项
- 内存空间
自动装箱涉及到重新生成对象,所以频繁大量的使用会创建很多无用的对象,增加GC
压力,拉低程序的性能。 - 拆箱空指针异常
如果封装类型并没有初始化,在拆箱时会报空指针异常,因为编译过程无法检测到,只能在运行时出现,需要特别注意。
BigInteger/BigDecimal
类型
BigInteger/BigDecimal
用于大数操作和高精度计算:BigInteger
操作大整数,BigDecimal
可以指定小数保留位数。它们类似 String
,但是它的初始化方式却没有 String
那么方便可以直接赋值,而是跟其他自定义的类一样,要调用它的构造器进行初始化。这个类的取值范围原则上是没有上限的,取决于你的计算机的内存。
1 | public static void main(String[] args){ |
解析:a
和 b
的值刚好可以使用 long
表示,但是相加的结果超出了 long
的范围,而使用 float/double
计算出来精度不够,会在个位数上产生误差。使用 BigInteger
正好解决这个问题。
数据类型和存储
存储区:栈和堆
- 栈内存
用于存放基本类型的变量和引用变量。当超过变量的作用域后,Java
会自动释放掉为该变量分配的栈内存空间。 - 堆内存
用于存放由new
创建的对象和数组。堆中分配的内存由Java
虚拟机自动垃圾回收器来管理。数组和对象在没有引用变量指向它的时候才变成垃圾,不能再被使用,但是仍然占着内存。在随后的一个不确定的时间被垃圾回收器释放掉,这个也是Java
比较占内存的主要原因。
数据类型
- 基本数据类型
基本数据类型只涉及一个存储区:是存在栈内存中的,保存的是数据值本身。 - 引用数据类型
涉及到两块存储区:对象本身是存储在堆内存中;引用变量是存储在栈内存中,并存放指向该对象堆内存的首地址。
参数传递
形参传递分为:值传递和引用传递。
值传递
Java
的基本数据类型都属于值传递,即将栈内存中的数据传递给形参,所以值传递过程中,形参的修改不会影响到实参。String
比较特殊,根据 String
的源码可以认为 String
本身是一个常量,所有的修改都是拷贝生成一个新字符串,所以也属于值传递。
引用传递
Java
中所有的对象都是引用传递(除了 String
),引用传递实际室传递了引用变量,而该变量指向了对象的堆内存,所以改变引用变量指向对象的值时,实参会跟着改变(实参指向了同一个堆内存)。但是如果重新给引用变量赋值,即重新 new
一个对象或者指向另外一个对象,这时改变引用变量指向对象的值时,实参并不会受到影响(实参指向的堆内存并没有被改变)。
示例说明:
1 | private MyDataParcelable updateBookOut(MyDataParcelable myDataParcelable){ |
数组
Java
中二维数组有一下几个约定:
- 第一维为行数,第二维为列数
即对于int[][] a
中,a.length
为行数,a[0].length
为列数。 - 数组中默认会将数值型初始化为 0, 布尔型初始化为
false
switch
switch
能否作用在 byte
上?能否作用在 long
上,能否作用在 String
上?switch
的表达式数据类型是:整型、字符型和枚举型;可以作用在 byte
上,不可作用域 long
上,Java 7
以后可作用于 String
上。
Log
打印
System
打印
1 | System.out.println(""); // 常规换行打印 |
out
和 err
打印时不在同一个线程执行,所以混合使用它们打印时,时间上并不是顺序打印(看起来是异步在执行)。
堆栈打印
打印当前堆栈调用信息:new Exception("This is a log.").printStackTrace(System.out);
printStackTrace
默认使用的是 System.err
来打印的,如果其他 Log
都是 System.out
输出的,会导致信息看起来像是异步执行。所以要求堆栈也使用 out
打印,即 printStackTrace(System.out)
。
其他
try-catch-finally
Java 7
中的新特性,带资源的 try
可以自动 close
实现了 Closeable
的对象。