Java 语言基础

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 位,表示乘以 8
    num >> 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。

数据类型

数据类型分类

0083-java-language-basic-java-data-type.png

基本数据类型取值范围

0083-java-language-basic-java-basic-type-values-range.png

浮点类型特殊数值:+0, -0, NaN(Not a Number), +infinities, -infinities,其中:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
// Float.java
public static final float POSITIVE_INFINITY = 1.0F / 0.0; // 正无穷
public static final float NEGATIVE_INFINITY = -1.0F / 0.0; // 负无穷
public static final float NaN = 0.0F / 0.0; // 不定式
public static final float MAX_VALUE = 3.4028235E38F; // 最大值
public static final float MIN_NORMAL = 1.17549435E-38F; // 最小正规形式正数
public static final float MIN_VALUE = 1.4E-45F; // 最小值
public static final int MAX_EXPONENT = 127; // 最大指数
public static final int MIN_EXPONENT = -126; // 最小指数
public static final int SIZE = 32; // 位数
public static final int BYTES = 4; // 字节数

// Double.java
public static final double POSITIVE_INFINITY = 1.0D / 0.0;
public static final double NEGATIVE_INFINITY = -1.0D / 0.0;
public static final double NaN = 0.0D / 0.0;
public static final double MAX_VALUE = 1.7976931348623157E308D;
public static final double MIN_NORMAL = 2.2250738585072014E-308D;
public static final double MIN_VALUE = 4.9E-324D;
public static final int MAX_EXPONENT = 1023;
public static final int MIN_EXPONENT = -1022;
public static final int SIZE = 64;
public static final int BYTES = 8;

包装类

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
2
3
4
5
6
7
8
9
10
// 自动装箱
Integer i = 1;
// 自动拆箱
int j = i;

List<Integer> intList = new ArrayList<Integer>();
// 自动装箱
intList.add(1);
// 自动拆箱
int number = intList.get(0);

经过反编译后,可以明确看到装箱和拆箱的动作:

1
2
3
4
5
6
Integer localInteger = Integer.valueOf(1);  // 装箱
int i = localInteger.intValue(); // 拆箱

ArrayList localArrayList = new ArrayList();
localArrayList.add(Integer.valueOf(1)); // 装箱
int j = ((Integer)localArrayList.get(0)).intValue(); // 拆箱

自动装箱后的比较

因为是对象比较,所以建议直接使用 equal 而不是 ==

== 比较的是对象的首地址。但是 Java 做了部分优化,在如下范围内使用的是相同的对象(封装类型的缓存),范围外则在装箱的过程中重新生成一个对象。

  • char
    [\u0000, \u007f]:即 ASCII 码表中的 128 个字符。
  • byte/short/int/long
    [-128, 128):在 -128 <= x < 128 范围内,共用相同的对象(缓存),即 byte 能够表达的范围。
  • float/double
    没有缓存,直接重新生成一个新对象。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// char
Character ca = '\u007f', cb = '\u007f';
Character cc = '\u0080', cd = '\u0080';
System.out.println(ca == cb); //ture
System.out.println(cc == cd); //false

// byte, short, int, long: [-128, 128)
Short sa = -128, sb = -128;
Short sc = -129, sd = -129;
Integer ia = 1, ib = 1;
Integer ic = 128, id = 128;
Long la = 1L, lb = 1L;
Long lc = 128L, ld = 128L;
System.out.println(sa == sb); //true
System.out.println(sc == sd); //fasle
System.out.println(ia == ib); //true
System.out.println(ic == id); //fasle
System.out.println(la == lb); //true
System.out.println(lc == ld); //false

String 的自动拆装箱

基本概念:Java 中的 "abc" 对应的实际是常量,存储在常量池中。

1
2
3
4
5
6
7
8
9
10
11
// 对应的都是常量池中 "abc" 的地址
String str1 = "abc";
String str2 = "abc";
System.out.println(str2==str1); //true
System.out.println(str2.equals(str1)); //true

// 重新分配了一个对象,所以对象的首地址并不一样
String str3 =new String("abc");
String str4 =new String("abc");
System.out.println(str3 == str4); //false
System.out.println(str3.equals(str4)); //true

注意事项

  • 内存空间
    自动装箱涉及到重新生成对象,所以频繁大量的使用会创建很多无用的对象,增加 GC 压力,拉低程序的性能。
  • 拆箱空指针异常
    如果封装类型并没有初始化,在拆箱时会报空指针异常,因为编译过程无法检测到,只能在运行时出现,需要特别注意。

BigInteger/BigDecimal 类型

BigInteger/BigDecimal 用于大数操作和高精度计算:BigInteger 操作大整数,BigDecimal 可以指定小数保留位数。它们类似 String,但是它的初始化方式却没有 String 那么方便可以直接赋值,而是跟其他自定义的类一样,要调用它的构造器进行初始化。这个类的取值范围原则上是没有上限的,取决于你的计算机的内存。

1
2
3
4
5
6
7
8
public static void main(String[] args){
BigInteger a = new BigInteger("1234567890987654321");
BigInteger b = new BigInteger("8888888888888888888");
String c = a.add(b).toString();
System.out.println("a = " + a.toString());
System.out.println("b = " + b.toString());
System.out.println("a + b = " + c);
}

解析:ab 的值刚好可以使用 long 表示,但是相加的结果超出了 long 的范围,而使用 float/double 计算出来精度不够,会在个位数上产生误差。使用 BigInteger 正好解决这个问题。

数据类型和存储

存储区:栈和堆

  • 栈内存
    用于存放基本类型的变量和引用变量。当超过变量的作用域后, Java 会自动释放掉为该变量分配的栈内存空间。
  • 堆内存
    用于存放由 new 创建的对象和数组。堆中分配的内存由 Java 虚拟机自动垃圾回收器来管理。数组和对象在没有引用变量指向它的时候才变成垃圾,不能再被使用,但是仍然占着内存。在随后的一个不确定的时间被垃圾回收器释放掉,这个也是 Java 比较占内存的主要原因。

数据类型

  • 基本数据类型
    基本数据类型只涉及一个存储区:是存在栈内存中的,保存的是数据值本身。
  • 引用数据类型
    涉及到两块存储区:对象本身是存储在堆内存中;引用变量是存储在栈内存中,并存放指向该对象堆内存的首地址。

参数传递

形参传递分为:值传递和引用传递。

值传递

Java 的基本数据类型都属于值传递,即将栈内存中的数据传递给形参,所以值传递过程中,形参的修改不会影响到实参。String 比较特殊,根据 String 的源码可以认为 String 本身是一个常量,所有的修改都是拷贝生成一个新字符串,所以也属于值传递。

引用传递

Java 中所有的对象都是引用传递(除了 String),引用传递实际室传递了引用变量,而该变量指向了对象的堆内存,所以改变引用变量指向对象的值时,实参会跟着改变(实参指向了同一个堆内存)。但是如果重新给引用变量赋值,即重新 new 一个对象或者指向另外一个对象,这时改变引用变量指向对象的值时,实参并不会受到影响(实参指向的堆内存并没有被改变)。

示例说明:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
private MyDataParcelable updateBookOut(MyDataParcelable myDataParcelable){
if(myDataParcelable.getName() == null){
Log.d(TAG, "updateBookOut: It is AIDL type out! Parameter is null!");
// new 一个新的对象 赋值给形参:myDataParcelable
myDataParcelable = RandomData.genMyDataParcelable();
}
// 新对象对应新的堆内存,所以形参的改变,并不影响实参
return myDataParcelable;
}

private MyDataParcelable updateBookOut(MyDataParcelable myDataParcelable){
if(myDataParcelable.getName() == null){
Log.d(TAG, "updateBookOut: It is AIDL type out! Parameter is null!");
// 形参指向的堆内存被赋值修改
myDataParcelable.setName("aaa");
MyDataParcelable.Book book = new MyDataParcelable.Book("bbb", 20);
List<MyDataParcelable.Book> list =
new ArrayList<MyDataParcelable.Book>();
list.add(book);
myDataParcelable.setBookList(list);
}
// 同一块堆内存被修改,所以形参的修改会同步影响到实参
return 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
2
3
System.out.println("");         // 常规换行打印
System.err.println(""); // 标红换行打印
System.out.print(""); // 常规不换号打印

outerr 打印时不在同一个线程执行,所以混合使用它们打印时,时间上并不是顺序打印(看起来是异步在执行)。

堆栈打印

打印当前堆栈调用信息: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 的对象。

0083-java-language-basic-java7-try-catch.png

参考文档

0%