Must-know-things for Java Developers.
Java环境变量配置
Windows
- 安装
安装JDK(Java Development Kit),JDK自带JRE(Java Runtime Environment) - 配置环境变量
右键计算机->属性->高级->环境变量->系统变量- (可选)添加变量JAVA_HOME=[JDK的根目录],e.g. E:\java\jdk1.8
- (可选)添加变量CLASSPATH=%JAVA_HOME%\lib\dt.jar;%JAVA_HOME%\lib\tools.jar
- 若添加了JAVA_HOME变量,在Path变量中追加:
”.;%JAVA_HOME%\bin;%JAVA_HOME%\jre\bin;” - 若未添加JAVA_HOME变量,在Path变量中追加:
”.;E:java\jdk1.8\bin;E:java\jdk1.8\jre\bin;”
- 重启
Linux
以Ubuntu安装Oracle Java 8为例
安装软件库管理工具
1
apt-get install software-properties-common
添加软件库
1
2add-apt-repository ppa:webupd8team/java
apt-get update安装Java
1
apt-get install oracle-java8-installer
验证安装
1
java -version
选择active的Java
1
sudo update-alternatives --config java
同样可以选择active的javac
设置环境变量
1
nano /etc/environment
- 添加JAVA_HOME=”[path]”
e.g. /usr/lib/jvm/java-8-oracle - PATH变量中追加:$JAVA_HOME/bin
- 添加CLASSPATH=”.:$JAVA_HOME/lib/dt.jar:$JAVA_HOME/lib/tools.jar”
- 添加JAVA_HOME=”[path]”
更新环境变量
1
source /etc/environment
验证环境变量
1
2echo $JAVA_HOME
javac
OS X
- 安装
安装JDK(Java Development Kit),JDK自带JRE(Java Runtime Environment) - 验证安装
1
2java -version
javac
JDK文件构成
一个完整的JDK主要包含以下目录。
- bin目录
- 编译器(javac)
- 解释器(java)
- 调试器(debugger)
- 文档生成器(javadoc)
- demo目录
- 一些代码的演示
- include目录
- 用于编译本地方法的文件,如JNI调用时所需的C语言头文件
- jre目录
- JVM运行环境,JDK类库,与所在平台相关
- lib目录
- 除jre目录下之外JDK额外所需的类库
- src.zip
- JDK源代码
Java平台体系
- J2SE
Java 2 Standard Edition,标准版(基础) - J2EE
Java 2 Enterprise Edition,企业版(Web应用)(基于J2SE) - J2ME
Java 2 Mobile Edition,移动版(移动设备应用)(基于J2SE)
Java特性
- OO
面向对象。以对象为基本粒度,对象的属性定义了这个对象,对象的方法可以用来操作对象。这使得代码比较human-being-like,且利于代码扩展和复用。 - Platform-independent
硬件平台的数量较多,但是要写的软件较之近乎无限。Java源代码编译后的字节码与平台无关,与底层硬件打交道的事情交给了JVM,真正做到了Write/Compile once, run everywhere. - Safe
包括语言级安全性(对象的封装),编译时安全性(语法语义检查),运行时安全性(类装载器装载及字节码校验器校验)和可执行代码安全性(网络访问类时进行了权限设置)。
当然,总有安全漏洞躺在人们意想不到的地方。 - Threading
多线程使得程序对硬件的利用率极大地提高了。
Java程序生命周期
- 编译
源文件(.java)–javac(java compiler)–>字节码(byte code)(.class)- JVM只能运行字节码文件
- 装载
字节码(byte code)(.class)–JVM类装载器(Class Loader)–>JVM中装载的类 - 校验
JVM中装载的类–>JVM字节码校验器–>进行校验 - 解释
JVM中装载的类–JVM解释器(根据OS解释)–>机器码 - 运行
机器码–>OS–>运行的程序 垃圾回收
内存垃圾–>JVM垃圾回收器–>垃圾回收垃圾
当对象失去所有的引用,变成了孤魂野鬼,也就是“垃圾”。
下面是垃圾对象的诞生过程之一:1
2
3
4String str1 = new String("abc"); //这个对象有一个引用str1指向它
String str2 = str1; //这个对象现在有两个引用:str1,str2指向它
str1 = null; //失去引用str1
str2 = null; //失去引用str2,失去全部引用,变成野对象就这样,由于new String(“abc”);产生的对象失去了所有引用,将无法再被程序访问。
在C/C++中,没有垃圾自动回收机制,因此除非程序结束,堆中申请后未被释放且无指向的内存将一直处于三无状态:无法释放,无法调用,无法更改,因为你根本没有办法找到它。当此类内存多起来时,可以利用的内存将变得越来越少,这就是内存泄露。
如下是一次内存泄露的发生过程:1
2(int *) malloc (sizeof(int)*1024);
//内存分配函数的参数没有问题,指针的转型也没有问题,But没有任何指针指向它!在Java中,由于有了垃圾回收机制,不定期地收回垃圾对象所占的内存,内存泄露的隐患将大大降低。
- 终止
JVM卸载程序
注释风格 & 命名规则
单行注释
1
// 这是一个单行注释
多行注释
1
2
3
4/*
大家好,
我是多行注释
*/类、方法注释
1
2
3
4/**
* 文档/类/方法的注释
*
*/packages命名
com.公司名.项目名.模块名
e.g. com.tencent.qq.view- 变量/方法命名
- 不与关键字冲突
- 不以数字开头
- 可含$、_,此外不能有其他符号
- 可用$、_开头
- 伪驼峰命名:userName,myAccount
- 常量命名
全大写,final修饰,常配以static修饰符,e.g.1
public static final double PI = 3.14159;
- 项目/类/接口命名
- 基本与变量/方法名相同
- 驼峰命名:MyClass,MyInterface
访问权限
- 文字版
- private
不允许同包子类、不允许同包其他类、不允许跨包子类、不允许跨包其他类访问 - 默认
允许同包子类、允许同包其他类、不允许跨包子类、不允许跨包其他类访问 - protected
允许同包子类、允许同包其他类、允许跨包子类、不允许跨包其他类访问 - public
全允许
- private
- 表格版
同一个类 | 同包其他类 | 不同包的子类 | 不同包的其他类 | |
---|---|---|---|---|
private | √ | |||
默认 | √ | √ | ||
protected | √ | √ | √ | |
public | √ | √ | √ | √ |
- 推导规则
- 继承时,子类的重写不可以缩小权限,父类方法修饰符为private时对于子类不可见
- 父类方法无修饰符时,子类重写方法可选默认、protected、public
- 父类方法修饰符为protected时,子类重写方法可选修饰符为protected或public
- 父类方法修饰符为public时,子类重写方法只能修饰为public
基本类型
类型 | byte | char | short | int | float | long | double | boolean |
---|---|---|---|---|---|---|---|---|
长度 | 1 | 2 | 2 | 4 | 4 | 8 | 8 | int/byte |
- 单个boolean变量的长度等于int的长度,boolean数组的长度等于byte数组
- 小数默认为double型,显式声明f或F才为float
整数默认为int型,显式声明为l或L才为long
1
2float f = 1.0f;
long l = 1l;零值比较: byte, char, short, int, long, boolean都可以直接对比,对于float和double,由于只能趋近于零,需要设定精度:
1
2
3final float EPISNON = 0.0001f;
f >= -EPISNON && f <= EPISNON
Math.abs(f) < EPISNON数组是一个对象,不属于基本类型
- true/false/null均不是关键字,而是字面量(literal),同数字
实例化
- 构造方法:
- 如果没有定义构造方法,JVM将为类生成一个默认的无参构造方法;如果定义了,将不会自动生成默认无参构造方法
- 如果父类未定义构造方法或定义了无参构造方法,则子类所有构造方法隐式调用该方法;若其只定义了带参构造方法,则父类没有无参构造方法,子类所有构造方法需显式调用父类任意一个带参构造方法
- 构造顺序
- 父类静态块及静态成员对象初始化(按顺序执行,仅在第一次加载时进行)
- 子类静态块及静态成员对象初始化(按顺序执行,仅在第一次加载时进行)
- 父类成员对象初始化
- 父类构造块
- 父类构造方法
- 子类成员对象初始化
- 子类构造块
- 子类构造方法
- 子类内部类
构造块 vs 静态块
- 静态块只执行一次
- 构造块在每次构造时执行
1
2
3
4
5
6{
//Construction block
}
static {
//Static block
}
拷贝一个对象
- 对象的类实现了Cloneable接口(空接口)
- 重写Object类的clone方法
- clone方法中注意深拷贝
异常
当程序的运行出现逻辑错误,或出现无效的情况时,即为异常。
Java定义的异常全部基于Throwable,继承于Throwable的两个子类Error和Exception,其中Error仅在发生JVM级的错误时才会抛出,一般程序不处理。
Unchecked Exception
- 表示错误,即程序的问题或逻辑错误,运行时无法恢复,不需要抛出异常。
- 包括Error及RuntimeException及其子类,e.g. OutOfMemoryError、IllegalArgumentException、NullPointerException、IllegalStatementException、ClassCastException、IndexOutOfBoundException、ArithmeticException。
Checked Exception
- 表示无效,即不是程序可以预测的异常,比如无效的用户输入、文件不存在等,必须显示地捕获处理或向外抛出。
- 包括Exception除RuntimeException之外的所有子类,e.g. FileNotFoundException、SocketException、SQLException、IOException。
异常处理
通常我们要处理Checked Exception,可以使用throws关键字或try-catch块进行处理。
throws
如果要监控整个方法中的某一类异常,且将其往外抛出,使用throws关键字。
这样当方法return前任何一句代码发生对应异常,或使用throw关键字手动抛出时,异常都会向外抛出,交由外层调用方法进行处理。
1
2
3
4
5
6
7
8
9
10
11
12
13
public void divide(int a, int b) throws Exception {
int c;
if(0 == b){
// 抛给method法的调用者
throw new Exception("divided by 0");
}
c = a/b;
}
public static void main(Stringp[] args) throws Exception {
int result = divide(1,0);
System.out.println(result);
//如果不作处理,异常层层外抛,最终交由JVM处理
}
try-catch-finally
如果需要对某一块代码中的代码进行监控,且有意对异常在方法内进行处理时,使用try-catch块。当然,在try-catch块中,仍然可以配合throw/throws将异常外抛。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public void divide(int a, int b) throws Exception {
int c;
if(0 == b){
// 抛给method法的调用者
throw new Exception("divided by 0");
}
c = a/b;
}
public static void main(Stringp[] args) {
try{
int result = divide(1,0);
} catch (Exception e) {
System.out.println("error, cause: " + e.getMessage());
}
System.out.println(result);
}
关于finally,多说几句,有如下几个规则:
- finally是可选子句,有try子句必有catch子句,但不一定有finally子句。
- finally子句中的内容一定会执行,除非在try或者catch中含有JVM终止的语句[System.exit(0);]。
- finally子句在try或者catch子句里的return语句执行之后(如果return紧接的是一个运算或者方法调用),return返回之前(方法出栈)。
- 如果finally子句和try或catch语句中都有return语句,真正返回的是finally中的return语句。
版本差异
Java 7
Java 7推出于2011年,是Java史上最大的一次更新,其中有许多developer应当知道的新特性。
二进制表示基本类型变量
1
2byte b = (byte)0b00000001;
int i = 0b00000001;下划线分隔数字
1
long aLongDecimal = 1234_5678_9012L;
switch字符串变量
1
2
3
4
5
6
7
8
9switch(dayName) {
case "Monday": recover();
case "Tuesday": work(hard);
case "Wednesday": work(notThatHard);
case "Thursday": work(lazily);
case "Friday": work(withoutPatience);
case "Saturday": party(hard);
case "Sunday": party(hard);
}泛型实例化自动推断类型
1
ArrayList<String> sList = new ArrayList<>();
try-with-resource
使用try-with-resource子句后,不再需要手动关闭资源1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22//java 6-
try {
FileReader fr = new FileReader(path)
BufferedReader br = new BufferedReader(fr);
return br.readLine();
} catch (Exception e) {
throw e;
} finally {
if (br != null) br.close();
if (fr != null) fr.close();
}
//java 7+
//try-with-resource can make sure the resources are closed before returning
try (
FileReader fr = new FileReader(path)
BufferedReader br = new BufferedReader(fr);
) {
return br.readLine();
} catch (Exception e) {
throw e;
}单个catch字句捕获多个异常
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18//java 6-
try{
..//block in which exceptions to be caught
} catch (NoSuchFileException e1) {
logger.log(e1);
throw new Exception(e1);
} catch (IOException e2) {
logger.log(e2);
throw new Exception(e2);
}
//java 7+
try{
..//block in which exceptions to be caught
} catch(NoSuchFileException | IOException ex){
logger.log(ex);
throw new Exception(ex);
}
Java 8
发布于2014年的Java 8有重要更新,需要developer重点关注。
- 移除位于JVM内存中的PermGen,新增位于本地内存中的Metaspace
接口扩展, 给接口方法添加默认或静态实现(default关键字)
1
2
3
4
5
6
7
8
9
10
11
12
13//默认实现
interface Example{
default int add(int a, int b){
return a+b;
}
}
//静态方法
interface Example{
static int add(int a, int b){
return a+b;
}
}函数式接口
任何接口,如果只含有一个抽象方法,就是一个函数式接口(添加@FunctionalInterface注解)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//Function<T, R>,输入T, 输出R
Function<String,String> function = (x) -> {
System.out.print(x+": ");
return "Function";
};
System.out.println(function.apply("hello world"));
//Predicate<T> -输入T, 返回一个boolean值
Predicate<String> pre = (x) ->{
System.out.print(x);
return false;
};
System.out.println(": "+pre.test("hello World"));
//Consumer<T> - 输入T, 执行动作无输出
Consumer<String> con = (x) -> {
System.out.println(x);
};
con.accept("hello world");
//Supplier<T> - 无输入, 输出T
Supplier<String> supp = () -> {
return "Supplier";
};
System.out.println(supp.get());
//BinaryOperator<T> - 输入两个T,输出T,think about reduce of map-reduce
BinaryOperator<String> bin = (x,y) ->{
System.out.print(x+" "+y);
return "BinaryOperator";
};
System.out.println(" "+bin.apply("hello ","world"));
output:
hello world: Function
hello World: false
hello world
Supplier
hello world BinaryOperator方法引用
- Reference to constructor, 构造方法引用
- Reference to static method, 类静态方法引用
- Reference to methods of an instance, 某个对象的方法引用
Reference to methods of a specific class for any instance, 特定类的任意对象的方法引用
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
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93//首先定义一个基本的entity类Person, nothing special
class Person {
private String name;
public Person() {}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int compare(Person a, Person b) {
return a.getName().compareTo(b.getName());
}
public int compareTo(Person p) {
return this.getName().compareTo(p.getName());
}
}
//再定义一个PersonFactory,注意此处有一个java.util.function.Supplier,无输入,单个输出。
```java
class PersonFactory {
private Supplier<Person> supplier;
public PersonFactory(Supplier<Person> supplier) {
this.supplier = supplier;
}
public Person getPerson() {
return supplier.get();
}
}
//最后是测试类
```java
public class MethodReference {
public static void main(String[] args) {
// 引用构造函数
PersonFactory factory = new PersonFactory(Person::new);
List<Person> personList = new ArrayList<Person>();
Person p1 = factory.getPerson();
p1.setName("Kobe");
personList.add(p1);
Person p2 = factory.getPerson();
p2.setName("James");
personList.add(p2);
Person p3 = factory.getPerson();
p3.setName("Paul");
personList.add(p3);
Person[] persons1 = personList.toArray(new Person[personList.size()]);
System.out.print("before: ");
printArray(persons1);
// 引用静态方法
Arrays.sort(persons1, MethodReference::myCompare);
System.out.print("after: ");
printArray(persons1);
System.out.println();
Person[] persons2 = personList.toArray(new Person[personList.size()]);
System.out.print("before: ");
printArray(persons2);
// 引用特定对象的实例方法
Arrays.sort(persons2, p1::compare);
System.out.print("after: ");
printArray(persons2);
System.out.println();
Person[] persons3 = personList.toArray(new Person[personList.size()]);
System.out.print("before: ");
printArray(persons3);
// 引用特定类型的任意对象的实例方法
Arrays.sort(persons3, Person::compareTo);
System.out.print("after: ");
printArray(persons3);
}
public static void printArray(Person[] persons) {
for (Person p : persons) {
System.out.print(p.getName() + " ");
}
System.out.println();
}
public static int myCompare(Person p1, Person p2) {
return p1.getName().compareTo(p2.getName());
}
}
Lambda Expression
- a.k.a clousure
- 把函数当成方法参数,可以替代匿名对象
e.g. 数组foreach
1
2
3
4
5
6
7
8
9
10
11
12List<String> names = Arrays.asList("b", "c", "a", "d");
String sep = ",";
//基本
names.forEach( e -> {
System.out.println(e + sep); //sep变为final
});
//简化, 指定迭代类型,
names.forEach( ( String e ) -> System.out.println( e ) );
//简化, 类型自动推断
names.forEach( e -> System.out.println( e ) );e.g. 字符串数组排序
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19List<String> names = Arrays.asList("b", "c", "a", "d");
//传统匿名对象
Collections.sort(names, new Comparator<String>() {
public int compare(String a, String b) {
return a.compareTo(b);
}
});
//普通lambda
Collections.sort(names, (String a, String b)->{
return a.compareTo(b);
});
//简化只有一句的方法
Collections.sort(names, (String a, String b)->a.compareTo(b));
//进一步简化,类型自动推断
Collections.sort(names, (a, b)->a.compareTo(b));