Java 基础知识

一、概念

特点:

  • 书写一次,到处运行
  • 垃圾收集

解释执行: Java 源代码编译 -> 字节码 -> 运行时 JVM 解释器解释成机器码
** 编译执行:_ _**JIT 能够在运行时将热点代码编译成机器码,这种情况下部分热点代码就属于编译执行

二、基本类型

image.png
缓存池:
new Integer (123) 与 Integer.valueOf (123) 的区别在于:

  • new Integer (123) 每次都会新建一个对象
  • Integer.valueOf (123) 会使用缓存池中的对象,多次调用会取得同一个对象的引用。

Integer 缓存池的大小默认为 -128~127。

三、类型转换

image.png
float f = 3.4; 是否正确?
不正确。3.4 是双精度数,将双精度型(double)赋值给浮点型(float)属于下转型(down-casting,也称为窄化)会造成精度损失,因此需要强制类型转换 float f =(float) 3.4; 或者写成 float f =3.4F;
short s1 = 1; s1 = s1 + 1; 有错吗?short s1 = 1; s1 += 1; 有错吗?
对于 short s1 = 1; s1 = s1 + 1; 由于 1 是 int 类型,因此 s1+1 运算结果也是 int 型,需要强制转换类型才能赋值给 short 型。
而 short s1 = 1; s1 += 1; 可以正确编译,因为 s1+= 1; 相当于 s1 = (short)(s1 + 1); 其中有隐含的强制类型转换。
计算:
Java 中的 byte,short,char 进行计算时都会提升为 int 类型。
常量则不会

1
2
final byte a = 1, b = 2;
byte c = a + b; // c = 3 不需强制转换

四、字符串

Java 中 的 String,StringBuilder,StringBuffer 三者的区别:

  • 线程安全: StringBuilder 是 ** 线程不安全 ** 的,而 StringBuffer 是线程安全的
  • 运行速度:StringBuilder > StringBuffer > String
  • 常量: String 为字符串常量,而后两者的对象是变量,是可以更改的。

String 不可变好处:

  1. hash 缓存: 可以缓存 hash 值。不可变的特性可以使得 hash 值也不可变,因此只需要进行一次计算。
  2. 常量池: String Pool 的需要。如果一个 String 对象已经被创建过了,那么就会从 String Pool 中取得引用。只有 String 是不可变的,才可能使用 String Pool。
  3. 安全性
  4. 线程安全

1. 代码点与代码单元

代码点:
指可用于编码字符集的数字,即 u+0000 等。
代码点可以分成 17 个代码级别,第一个代码级别称为 基本的多语言级别(U+0000 到 U+FFFF),其余的 16 个级别码点从 U+10000 到 U+10FFFF , 其中包括一些辅助字符。
代码单元:
char 类型用于存储代码单元,其使用 uft-16 编码来表示所有代码点。
对于 ** 基本的多语言级别 ** 的代码点用一个代码单元来表示,对于辅助字符用 ** 一对连续的代码单元 ** 来表示。
image.png
String 的 length () 方法返回的是 代码单元的数量,codePointCount () 方法返回的是代码点的数量
所以尽量不要使用 char

2. 常量池

过程:

  1. 直接使用 双引号声明 出来的 String 对象会直接存储在常量池中。
  2. 如果不是用双引号声明的 String 对象,可以使用 String 提供的 intern 方法。intern 方法会从字符串常量池中查询当前字符串是否存在,若不存在就会将当前字符串放入常量池中

例 1:
String str = new String("abc");
首先查看池中是否有相同的字符串

  1. 如果有则拷贝一份放到堆中
  2. 如果池中没有则在堆中和常量池中都创建对象
  3. 然后返回堆中的地址,最后用栈中的引用 str 指向堆中的对象

例 2:

1
2
3
4
String s3 = new String("1") + new String("1");
s3.intern();
String s4 = "11";
System.out.println(s3 == s4);
  1. 生成常量池中的 “1” 和堆中的对象 “11”(由于没有声明 “11”,所以常量池中并没有”11”)
  2. intern () 将 s3 放入常量池中
    1. jdk6 中常量池在持久代(Perm)中,所以常量池中会创建一个 “11” 的对象
    2. jdk7 中常量池不在持久代中而在堆中,所以常量池中 不需要再存储一份对象了,而是直接存储堆中的引用,即指向 s3 指向的对象
  3. s4 获得常量池中的 “11”
    1. jdk6 中为新的 “11” 对象
    2. jdk7 中 s4 就是指向 s3 指向的对象
  4. 结论:jdk7 中 String#intern 方法时,如果存在堆中的对象,会直接保存对象的引用,而不会重新创建对象

例 3:
image.png
JVM 确实对形如 String str = “javablog”; 的对象放在常量池中,但是它是在编译时做的。而 String s = str1 + str2; 是在 运行时候才能知道的,也就是说 str1 + str2 是在堆里创建的,所以结果为 false 了。str1 + “b” 等类似,在运行时才能知道,所以在堆中创建
深入解析 String#intern

五、数组

int[] a1 = {1, 2, 3};
int[] a2 = new int[5];
长度:a1.length
Arrays.copyOf:int[] copiedLuckyNumbers = Arrays.copyOf(luckyNumbers , luckyNumbers .length);
Arrays.sort (a):使用了优化的归并排序算法
一个 Object 数组不能转换成自定义数组:
即创建数组时 new 表达式中使用的元素类型。将一个 Employee [] 临时地转换成 Object [] 数组, 然后再把它转换回来是可以的,但一个从开始就是 Object [ ] 的数组却永远不能转换成 Employe [ ] 数组。
自定义排序:

1
2
3
4
5
6
7
8
class LengthComparator implements Comparator<String> {
public int compare(String first, String second) {
return first.length() - second.length();
}
}

String[] friends = {"Peter", "Paul", "Mary"};
Arrays.sort(friends, new LengthComparator());

六、java 中的包

java.util 工具包
java.sql 数据库包
java.io 输入输出流包
java.net 网络包
java.lang 基础包

七、Object 通用方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
public native int hashCode()

public boolean equals(Object obj)

protected native Object clone() throws CloneNotSupportedException

public String toString()

public final native Class<?> getClass()

protected void finalize() throws Throwable {}

public final native void notify()

public final native void notifyAll()

public final native void wait(long timeout) throws InterruptedException

public final void wait(long timeout, int nanos) throws InterruptedException

public final void wait() throws InterruptedException

八、日期

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
public class DateTimeTest {
public static void main(String[] args) {
Calendar cal = Calendar.getInstance();
System.out.println(cal.get(Calendar.YEAR));
// 0-11
System.out.println(cal.get(Calendar.MONTH));
System.out.println(cal.get(Calendar.DATE));
System.out.println(cal.get(Calendar.HOUR_OF_DAY));
System.out.println(cal.get(Calendar.MINUTE));
System.out.println(cal.get(Calendar.SECOND));

// Java 8
LocalDateTime dt = LocalDateTime.now();
System.out.println(dt.getYear());
// 1-12
System.out.println(dt.getMonthValue());
System.out.println(dt.getDayOfMonth());
System.out.println(dt.getHour());
System.out.println(dt.getMinute());
System.out.println(dt.getSecond());
}
}

九、运算符

1. ==

  • 对于引用类型,判断引用是否相同
  • equals () 判断引用类型值是否相同。但是 equals () 方法的默认行为是比较引用,因此自定义的类中需覆盖 equals () 方法

2. && 与 ||

短路与 和 短路或

3. 移位运算符

  • 运算符用符号位填充高位,>>> 运算符用 0 填充高位

  • 1 << 35 == 1 << 3 == 8(int 类型只有 32 位)

十、输入输出

1. 输入

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
//1.Scanner
Scanner in = new Scanner(System.in); //构造一个 Scanner 对象,并与 System.in 关联
String name = in.nextLine(); //输入一整行
//String name = in.next(); 输入一个单词,以空格为界
//int age = in.nextInt(); 输入整数
//nextDouble......

//2.BufferedReader
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
try{
String s = br.readLine();
} catch(IOException e){
e.printStackTrance();
}

//3.Console
Console cons = System.console();
String username = cons.readLine("User name:");
char[] passwd = cons.readPassword("Password");

2. 文件输入输出

1
2
3
4
5
//读
Scanner in = new Scanner(Paths.get("C:\\mydirectory\\myfile.txt"), "UTF-8"); //反斜杠需要两个

//写
PrintWriter out = new PrintWriter("myfile.txt");