1. 编译、运行
1.1 编译
javac + 源文件的路径.java
1 | javac -d . -source 1.8 /aaa/bb/xx.java |
重点可查阅官网文档。
1.2 运行
java + 主类的全名
【注意】一定要是主类的全名
【注意】一定要是主类的全名
【注意】一定要是主类的全名
1 | java com.cholen.F |
可以通过-cp选项指定类文件的搜索目录
2. 基本数据类型与数组
2.1 Java语言使用Unicode标准字符集
2.2 基本数据类型
2.2.1 逻辑类型 boolean
2.2.2 整数类型
-
byte 1字节
-
short 2字节
-
int 4字节
-
long 8字节
常量后必须加L
108L (十进制)
07123L (八进制)
0x3ABCL (十六进制)
【注意】Java没有无符号的整数类型,在整数类型前面添加unsigned都是错误的!
【注意】Java没有无符号的整数类型,在整数类型前面添加unsigned都是错误的!
【注意】Java没有无符号的整数类型,在整数类型前面添加unsigned都是错误的!
2.2.3 字符类型 char
1 | char ch = ‘A’; |
Java中的char变量,分配2个字节的内存,没有负数的char,取值范围从0~65535
【注意】在char类型前面添加unsigned是错误的!
2.2.4 浮点类型
-
float 4字节
必须有后缀f或者F
43.0f或者43.0F(小数表示法)
2e40f或者2e40F(指数表示法)
0x3ABCf 或者0x3ABCF (十六进制)
-
double 8字节
解释一下float和doble为什么是精确到有效数字前7位和15位?
1 | float f = 1234567.1934567F; |
在float f赋值时,只精确保存了前7位,从第八位开始就要加上第九位四舍五入的进位,
同理,double赋值时,只精确保存了前15位,从第十六位开始就要加上第九位四舍五入的进位。
2.3 float在计算机中存储
-
float类型数字在计算机中用4个字节存储,也就是32位。遵循IEEE-754格式标准(电气和电子工程师协会(IEEE,全称是Institute of Electrical and Electronics Engineers))
格式:SEEE EEEE EMMM MMMM MMMM MMMM MMMM MMMM
-
S表示浮点数正负
-
E指数加上127后的值得二进制数据
-
M底数
-
-
底数部分
使用二进制数来表示此浮点数的实际值。底数部分实际是占用24bit的一个值,但是最高位始终为1,所以最高位省去不存储,故底数部分在存储中占23bit
-
指数部分
指数部分占用8bit的二进制数,可表示数值范围为0-255。但是指数可正可负,所以,IEEE规定,此处算出的次方必须减去127才是真正的指数。 所以,float类型的指数可从-127到128
这种结构是一种科学计数法:用符号、指数和尾数来表示。指数可正可负,所以,IEEE规定,此处算出的次方必须减去127才是真正的指数。底数定为2,即把一个浮点数表示为尾数乘以2的指数次方再添上符号。
-
举例:浮点数-12.5 在内存中的存储
首先浮点数为负,所以 S=1。
整数部分为12 ,转换为二进制就是:1100
小数部分为0.5,转换为二进制就是 :0.1
故12.5的二进制为:1100.1 。 用科学计数法表示为:1.1001*2^3
这样,我们的底数和指数就出来了
底数:因为小数点前必为1,所以IEEE规定只记录小数点后的就好。所以,此处的底数为:1001
指数:实际为3,必须加上127(转出的时候,减去127),所以为130。也就是1000 0010
符号部分是整数,所以是1
综上所述,-12.5在内存中的存储格式是: 1100 0001 0100 1000 0000 0000 0000
2.4 数组
【注意】与C/C不同,Java不允许在声明数组的方括号中指定元素的个数! 用C/C的话来说就是不允许同时声明多个引用!
1 | int a[12]; // 错误 |
【注意】与C/C++不同,Java允许使用int类型的变量指定数组元素的个数
1 | int size = 30; |
关于length
1 | //数组的length是属性 |
3.算术混合运算的精度
精度由低到高:
byte --> short --> char --> int --> long -->float --> double
如果表达式中的最高精度抵御int类型,则按照int类型进行计算!!!,例如,表达式(byte)10+'a’和5/2的结果分别为107和2,都是int类型
请区分好如下表达式
1 | byte x = (byte)20 + 'a'; //正确 |
4.Java中的switch
从jdk1.7开始,switch支持更多的数据类型,字符串,字符类型,枚举,布尔都支持!
从jdk1.7开始,switch支持更多的数据类型,字符串,字符类型,枚举,布尔都支持!
从jdk1.7开始,switch支持更多的数据类型,字符串,字符类型,枚举,布尔都支持!
5.Java中的break、continue + 标签
标签提供了一种简单的break语句所不能实现的控制循环的方法,当在循环语句中碰到break时,不管其它控制变量,都会终止。但是,当你嵌套在几层循环中想退出循环时又怎么办呢?break只退出一重循环,但你可以用标号label标出你想退出哪一个语句。规定标号label必需放在循环之前(意味着循环前必需紧跟着标号)
1 | public void fun2() { |
6.Java中的可变参数
可变参数用…表示,必须放在方法参数列表的最后一个
1 | public int sum(int ...x){ |
7.关联与依赖
7.1 关联 (成员变量 “全局变量”)
A类的成员变量(成员变量可以理解为“全局变量”)中有B类的对象
【成员变量 “全局变量”】
【成员变量 “全局变量”】
【成员变量 “全局变量”】
7.1.1 组合、聚合
Sr.No | Key | Composition | Aggregation |
---|---|---|---|
1 | Basic | Composition(mixture) is a way to wrap simple object or data type into a single unit | Aggregation(collection) differs from ordinary composition in that it does not imply owership |
2 | Relationship | In composition,parent entity owns child entity | In agrregation,parent Has-A relationship with child entity |
3 | UML Notation | It is denoted by a filled diamond | It is denoted by an empty diamond |
4 | Life cycle | Child doesn’t have their own life time | Child can have their own life time |
5 | Association | It is a strong association | It is a weak association |
-
组合(Composition): 创建整体时部分也同时创建,整体销毁部分也销毁
例如,人包含头、躯干、四肢,它们的生命周期一致。当人出生时,头、躯干、四肢同时诞生。当人死亡时,作为人体组成部分的头、躯干、四肢同时死亡。
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//人类
public class People {
private String name;
private String age;
private Phone[] phones;
public People() {
//创建整体时部分也同时创建
phones = new Phone[3];
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getAge() {
return age;
}
public void setAge(String age) {
this.age = age;
}
public Phone[] getPhones() {
return phones;
}
public void setPhones(Phone[] phones) {
this.phones = phones;
}
public String toString() {
return "People [name=" + name + ", age=" + age + ", phones=" + Arrays.toString(phones) + "]";
}
}
//电话类
public class Phone {
private String color;
private String num;
public Phone() {
// TODO Auto-generated constructor stub
}
public String getColor() {
return color;
}
public void setColor(String color) {
this.color = color;
}
public String getNum() {
return num;
}
public void setNum(String num) {
this.num = num;
}
public String toString() {
return "Phone [color=" + color + ", num=" + num + "]";
}
}
//测试
public class Client {
public static void main(String[] args) {
People people = new People();
people = null;
System.out.println(people.getPhones());
}
}
-
聚合(Aggregation):创建整体时部分可以不创建,整体消亡时部分还存在
例如,公司部门与员工的关系,一个员工可以属于多个部门,一个部门撤消了,员工可以转到其它部门。
1 | //人类 |
7.1.2 单向、双向
-
单向:A类关联B类
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//类: 电话
public class Phone {
// 属性:型号
private String type;
// 属性:颜色
private String color;
// 属性:品牌
private String brand;
// 构造方法
public Phone() {
}
// Getters and Setters
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
public String getColor() {
return color;
}
public void setColor(String color) {
this.color = color;
}
public String getBrand() {
return brand;
}
public void setBrand(String brand) {
this.brand = brand;
}
}
//类:人
public class Person {
// 属性:电话
private Phone phone;
// 构造方法:默认的
public Person() {
}
// Getters and Setters
public Phone getPhone() {
return phone;
}
public void setPhone(Phone phone) {
this.phone = phone;
}
} -
双向:A关联B,B也关联A
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//类: 电话
public class Phone {
// 属性:型号
private String type;
// 属性:颜色
private String color;
// 属性:品牌
private String brand;
// 属性:所有者
private Person owner;
// 构造方法:默认的
public Phone() {
}
// Getters and Setters
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
public String getColor() {
return color;
}
public void setColor(String color) {
this.color = color;
}
public String getBrand() {
return brand;
}
public void setBrand(String brand) {
this.brand = brand;
}
public Person getOwner() {
return owner;
}
public void setOwner(Person owner) {
this.owner = owner;
}
}
//类:人
public class Person {
private Phone phone;
public Person() {
}
public Phone getPhone() {
return phone;
}
public void setPhone(Phone phone) {
this.phone = phone;
}
}
7.1.3 一对一、一对多
-
一对一
1 | //类: 电话 |
-
一对多
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 Phone {
// 属性:型号
private String type;
// 属性:颜色
private String color;
// 属性:品牌
private String brand;
// 构造方法
public Phone() {
}
// Getters and Setters
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
public String getColor() {
return color;
}
public void setColor(String color) {
this.color = color;
}
public String getBrand() {
return brand;
}
public void setBrand(String brand) {
this.brand = brand;
}
}
//类:人
public class Person {
//数组
private Phone[] phones0 ;
//集合
private List<Phone> phones;
private Set<Phone> phones2;
private Map<String, Phone> phone3;
public Person() {
this.phones0 = new Phone[3];
phones = new ArrayList<Phone>();
}
}
7.2 依赖 (局部变量)
A类的局部变量(方法参数、方法内、方法返回值等)中有B类的对象
【局部变量】
【局部变量】
【局部变量】
8.访问权限
Java代码 | 访问权限 | UML图表示 |
---|---|---|
public | 全部 | + |
protect | 同一包及子类 | # |
default | 同一包 | |
private | 同一类 | - |
【注意】与C++不同,Java中不能使用protected和private修饰类和接口
9.接口
-
常量必须用public static final修饰 (可省略)
-
方法必须用public abstract修饰 (可省略)
10.Java中的字符串并置
-
参与并置运算的String对象,只要有一个是变量,那么Java就会在动态区存放所得到的新String对象的实体和引用。
-
如果是两个常量的并置,那么得到的仍然是常量,如果常量池中没有这个常量,就放入常量池。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16public class Test {
public static void main(String[] args) {
String hello = "你好";
String testOne = "你" + "好";
System.out.println(hello == testOne); // 输出结果是true
System.out.println("你好" == testOne); // 输出结果是true
System.out.println("你好" == hello); // 输出结果是true
String you = "你";
String hi = "好";
String testTwo = you + hi;
System.out.println(hello == testTwo); // 输出结果是false
String testThree = you + hi; // 相当于new String("你好")
System.out.println(testTwo == testThree); // 输出结果是false
}
}
11.Java多线程
11.1 线程中断的原因
-
被迫
被JVM剥夺
-
主动
- sleep
- wait
- I/O阻塞
【说明一】
当前线程在使用CPU期间,主动执行了Thread类的类方法sleep(int millsecond),当前线程立即让出CPU 的使用权,是当前线程处于中断状态,经过参数millsecond指定的毫秒数后,该线程重新进入线程队列中排队等待CPU资源,以便从中断处继续运行。
【说明二】
当前线程在使用CPU期间,主动执行了Object类的final方法wait(),当前线程立即进入等待状态,失去CPU的使用权。进入等待状态的线程不会主动进入到线程队列中排队等待CPU资源
【说明三】
当前线程A执行了wait()后,要使得它重新进入线程队列中等待CPU资源,以便从中断处继续运行,必须选择以下四种的任何一种:
-
wait() 超时
-
在其他线程的执行体中执行了notify()
-
在其他线程的执行体中执行了notifyAll()
-
在其他线程的执行体中执行了 当前线程对象A.interrupt() (在其他线程的执行体中“吵醒”当前线程A)
【说明四】
Object类中的方法:
wait()、notify()、notifyAll()
Thread类中的方法:
sleep()、interrupt()
【说明五】
sleep()方法会抛出InterruptException异常,需要使用try-catch
11.2 目标对象与线程对象
-
目标对象是指实现了Runnable接口的类的实例
-
线程对象是指Thread类的实例
Thread(Runnable target) 参数就是目标对象
对于使用同一目标对象的线程,目标对象的成员变量自然就是这些线程共享的数据单元。
11.3 目标对象与线程对象的关系
11.3.1 完全解耦
目标对象中不组合线程对象,这种情况下,目标对象通常需要通过如下代码获得线程的名字
1 | String name = Thread.currentThread().getName(); |
1 | class House implements Runnable { |
11.3.2 弱耦合
目标对象组合线程对象,把线程对象作为自己的成员变量
1 | class House implements Runnable { |
11.4 协调同步的线程
-
当前线程要想执行wait()、notify()、notifyAll() ,必须持有当前实例的锁
-
wait()、notify()、notifyAll()这三个方法与其说时针对线程的操作,倒不如说是针对实例的等待队列的操作
-
wait()将当前线程放入到obj的等待队列中
-
notify()从obj的等待队列中唤醒一个线程
-
notifyAll从obj的等待队列中唤醒所有的线程
11.5 线程联合
在线程对象A的执行体中执行了 线程对象B.join(), 称线程对象A在运行期间联合了线程对象B。
一旦执行了上述语句,那么线程对象A将立即中断执行,一直等待到它联合的线程对象B执行完毕,A线程再重新排队等待CPU,以便恢复执行。如果A准备联合的B线程已经结束,那么B.join()不会产生任何效果。
12. Java文件读写
12.1 带进度条的输入流
如果希望读文件时看见文件的读取进度可以使用javax.swing包提供的输入流类ProgressMonitorInputStream。
1 | import javax.swing.*; |
12.2 字节—字符转换
OutputStreamWriter是Writer的子类,将输出的字符流转换成字节流。
InputStreamReader是Reader的子类, 将输入的字节流转换成字符流。
12.3 非常好用的流
DataInputStream和DataOutputStream非常好用,它们运行程序按着机器无关的风格读取Java原始数据,配合它们的方法readUTF()和writeUTF() 非常省事。
13.Java 网络编程
13.1 基本原则
在套接字通信中,有两个基本原则:
(1)服务器应该启动一个专门的线程与客户端的套接字建立连接
(2)由于套接字的输入流在读取信息时可能发生阻塞,客户端和服务器都需要在一个单独的线程中读取信息
13.2 Java远程调用RMI(Remote Method Invocation)
让本机上的JVM通过网络调用另外一台机器上的Java对象的方法。
Java的RMI严重依赖序列化和反序列化,而这种情况下可能会造成严重的安全漏洞,因为Java的序列化和反序列化不但涉及到数据,还涉及到二进制的字节码,即使使用白名单机制也很难保证100%排除恶意构造的字节码。因此,使用RMI时,双方必须是内网互相信任的机器,不要把1099端口暴露在公网上作为对外服务。
Java的RMI调用机制决定了双方必须是Java程序,其他语言很难调用Java的RMI。如果要使用不同语言进行RPC调用,可以选择更通用的协议,例如gRPC。
RMI通过自动生成stub和skeleton实现网络调用,客户端只需要查找服务并获得接口实例,服务器端只需要编写实现类并注册为服务;
RMI的序列化和反序列化可能会造成安全漏洞,因此调用双方必须是内网互相信任的机器,不要把1099端口暴露在公网上作为对外服务。
【关键点】需要服务器上的子接口字节码文件和存根字节码文件通过网络给客户端使用
-
服务器上继承java.rmi包中Remote接口,得到一个子接口A,编译成字节码
-
服务器上写一个类RemoteSubject,必须 extend java.rmi.server.UnicastRemoteObject,并implements 子接口A,然后编译成字节码X.class,然后使用命令rmic X 得到X**_Stub**.class的存根
【服务器上需要做的】
-
启动注册
1
rmiregistry
默认端口是`1099启动注册
-
启动远程对象服务 java.rmi.Naming
1
java.rmi.Naming.rebind("rmi://127.0.0.1/rect", Remote obj);
RemoteSubject.java
1 | import java.rmi.Remote; |
RemoteConcreteSubject.java
1 | import java.rmi.RemoteException; |
BindRemoteObject.java
1 | import java.rmi.Naming; |
【客户端上需要做的】
1 | java.rmi.Remote remoteObj = java.rmi.Naming.lookup("rmi://127.0.0.1/rect"); |
ClientApp.java
1 | import java.rmi.Remote; |
14.面向对象设计基本原则
14.1 UML图
类中的方法是静态方法,则在名字下面添加下划线。
-
类的UML图
-
接口的UML图
14.2 几种关系
14.3 几个原则
-
面向抽象
当设计一个类时,不应该让该类面向具体的类,应该让该类面向抽象类或接口,即设计类中的重要数据是抽象类或接口中声明的变量,而不是具体类声明的变量。
-
开闭原则
对扩展开放,对修改关闭。
对abstract类的修改关闭,对增加abstract类的子类开放。(即,不应该修改abstract类,应该去增加abstract类的子类)
-
多用组合,少用继承
继承复用 ,白盒复用
组合复用,黑盒复用
Person.java
1
2
3public abstract class Person{
public abstract String getMess();
}Car.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17public class Car {
Person person; // 组合驾驶员
public void setPerson(Person person) {
this.person = person;
}
public void show() {
if (person == null) {
System.out.println("目前没人驾驶汽车.");
} else {
System.out.print("目前驾驶汽车的是:");
System.out.println(person.getMess());
}
}
}MainClass.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21public class MainClass {
public static void main(String[] args) {
Car car = new Car();
int i = 1;
while (true) {
try {
car.show();
Thread.sleep(2000);// 每2000ms更换驾驶员
Class<?> cs = Class.forName("Driver" + i);
Person p = (Person) cs.getDeclaredConstructor().newInstance();
car.setPerson(p); // 更换驾驶员
i++;
} catch (Exception e) {
// TODO: handle exception
i++;
}
if (i > 3)
i = 1;
}
}
}Driver1.java
1
2
3
4
5public class Driver1 extends Person {
public String getMess() {
return "中国驾驶员";
}
}Driver2.java
1
2
3
4
5public class Driver2 extends Person {
public String getMess() {
return "美国驾驶员";
}
}Driver3.java
1
2
3
4
5public class Driver3 extends Person {
public String getMess() {
return "日本驾驶员";
}
} -
高类聚、低耦合