本文是学习JavaEE开发的记录笔记
大纲
Java数据结构
Java工具包提供的数据结构有
但这些类设计较落后,缺少一个核心、统一的主题
Java集合框架(java.util)
Java集合框架提供一组围绕统一标准接口设计,性能优良,使用方便的接口和类,可以直接使用这些接口的标准实现LinkedList、HashSet、TreeSet等类,也可以通过这些接口实现自己的集合
集合框架包含内容:
- 接口:代表集合的抽象数据类型
- 实现(类):集合接口的实现,具体来讲是可重复使用的数据结构,如ArrayList,LinkedList、HashSet、TreeSet等
- 算法:实现集合接口的对象里的有用的计算,如搜索和排序,是多态的,相同的方法可以在相似的接口上有不同的实现
Java泛型(Generics)
Java集合框架可以容纳任何类型的对象是通过泛型机制完成的
泛型类:泛型类的类型参数声明部分也包含一个或多个类型参数,参数间用逗号隔开,如1
ArrayList<String> list = new ArrayList<String>();
泛型方法:方法在调用时可以接收不同类型的参数,根据传递给泛型方法的参数类型,编译器适当地处理每一个方法调用,声明泛型方法用尖括号+变量名组成,参数间用逗号隔开,声明位置在方法返回类型之前,如1
2
3
4
5
6public static <E> void printArray(E[] inputArray)
{
for(E val:inputArray){
System.out.printf("%s",val);
}
}
类型通配符:当方法需要接受泛型类作为参数,可用<?>代替对象的泛型,如1
2
3public static void getData(List<?> data) {
System.out.println("data :" + data.get(0));
}
类型通配符上限/下限:通过<? extends T>示该通配符所代表的类型是T类型的子类(T类为上限),通过List<? super T>表示该通配符所代表的类型是T类型的父类(T类为下限)
Java序列化
Java提供一种对象序列化机制,可以将对象表示为字节序列,存入文件中保存,并且可以在文件中读出进行反序列化在内存中新建对象
对象序列化条件:实现java.io.Serializable接口+类所有属性是可序列化的(transient关键字修饰的属性是不参与序列化的)
序列化对象:1
2
3
4
5
6
7
8
9
10//声明并创建对象
type objectName=new type();
//序列化对象成字节序列存储文件
FileOutputStream fileOut =new FileOutputStream("/tmp/class.ser");
ObjectOutputStream out = new ObjectOutputStream(fileOut);
//序列化名为objectName的对象
out.writeObject(objectName);
//关闭序列化对象和文件流
out.close();
fileOut.close();
反序列化对象:1
2
3
4
5
6
7
8
9
10//声明存储对象的引用
type objectName=null;
//读取字节序列文件并解析成对象
FileInputStream fileIn = new FileInputStream("/tmp/employee.ser");
ObjectInputStream in = new ObjectInputStream(fileIn);
//存放在引用中
objectName=(type)in.readObject();
//关闭序列化对象和文件流
in.close();
fileIn.close();
Java网络编程
java.net包提供低层次的的通信细节,提供两种常见的网络协议支持(TCP+UDP),提供Socket编程和URL处理
Socket编程:
- Socket:套接字使用TCP提供计算机网络之间的通信机制,客户端程序创建一个套接字连接服务器的套接字,连接建立后,服务器会创建Socket对象,客户端和服务器通过对Socket对象的写入和读取来进行通信
- Socket通信过程:
- 服务器实例化一个java.net.ServerSocket对象,表示通过服务器上的端口通信
- 服务器调用ServerSocket对象的accept()方法监听客户端连接到服务器给定端口
- 客户端实例化一个java.net.Socket对象,指定服务器名称和端口号请求连接
- Socket类的构造函数试图将客户端连接到服务器给定端口号,若通信建立,则在客户端创建一个Socket对象与服务器进行通信
- accept()方法返回服务器上一个新的socket引用,该socket连接到客户端的socket
- 通过I/O流进行通信,每个socket有输入流和输出流,客户端输出流连接到服务器输入流,客户端输入流连接到服务器输出流
- ServerSocket类:服务器使用ServerSocket类获取一个端口并监听客户端请求
GreetingServer.java 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
62import java.net.*;
import java.io.*;
//继承Thread类重写run()方法进行线程开发
public class GreetingServer extends Thread
{
//声明ServerSocket类对象serverSocket
private ServerSocket serverSocket;
//构造函数 输入端口号+设置过期时间
public GreetingServer(int port) throws IOException
{
serverSocket = new ServerSocket(port);
serverSocket.setSoTimeout(10000);
}
//重写run()方法
public void run()
{
while(true){
try{
//在线程中等待连接 阻塞线程
System.out.println("等待远程连接...");
System.out.println("连接成功!端口号为:"+serverSocket.getLocalPort());
//accept()得到连接成功后的socket引用
Socket server = serverSocket.accept();
System.out.println("远程主机地址:"+server.getRemoteSocketAddress());
//通过getOutputStream和getInputStream获取相应的输入输出流,进行相应的I/O操作
DataInputStream in = new DataInputStream(server.getInputStream());
System.out.println("接受客户端消息:"+in.readUTF());
DataOutputStream out = new DataOutputStream(server.getOutputStream());
out.writeUTF("Hello! From"+server.getLocalSocketAddress());
//关闭连接 不会立即释放端口 tcp连接中的time-wait现象
server.close();
}catch(SocketTimeoutException s){
System.out.println("Socket Timed Out!");
break;
}catch(IOException e){
e.printStackTrace();
break;
}
}
}
//主函数入口
public static void main(String [] args)
{
//获取监听的端口号
int port = Integer.parseInt(args[0]);
try{
//实例化GreetingServer类,通过构造函数创建ServerSocket类对象
Thread t = new GreetingServer(port);
//run()开启线程 进行监听端口
t.run();
}catch(IOException e){
e.printStackTrace();
}
}
}
关键点:
- 使用线程:ServerSocket通过使用accept方法进行客户端请求的处理,每当请求队列里有客户端请求时,serversocket就会从队列顶端取一个socket请求进行处理,生成一个socket来负责与客户端通信。如果一个时间只能处理一个socket,当有多个客户端请求时,则必须要排队处理,等待所有前面的socket处理完,这是个不合理的过程。因此,引入了线程的概念。
- 构造函数:
2.1 ServerSocket()无参构造函数,必须使用bind方法进行端口绑定,好处在于绑定前可以设置相应属性,如so_reuseaddress等
2.2 ServerSocket(int port)需要一个端口号(1024-65535),0-1023属于系统预留端口
2.3 ServerSocket(int port, int backlog)需要端口号+backlog(监听对列大小),设置连接请求数的最大值,若超过这个值则会报ConnectException异常,若无设置则系统默认
2.4 ServerSocket(int port, int backlog, InetAddress bindAddr)需要端口号+监听队列大小+ip地址,可以进行相应服务器ip地址绑定,服务器存在多个网卡时可以使用这个参数设定客户端访问的ip
Socket类:java.net.Socket类代表客户端和服务器都用来互相沟通的套接字,客户端要获取一个Socket对象通过实例化,而服务器获得一个Socket对象则通过accept()方法的返回值
GreetingClient.java 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//客户端实例化Socket对象
import java.net.*;
import java.io.*;
public class GreetingClient
{
public static void main(String [] args)
{
//传入第一个参数作为连接服务器主机名
String serverName = args[0];
//传入第二个参数作为连接服务器端口号
int port = Integer.parseInt(args[1]);
try
{
System.out.println("连接到主机:" + serverName + " 端口号:" + port);
//实例化Socket类得到Socket对象
Socket client = new Socket(serverName, port);
//连接成功打印相关信息
System.out.println("远程主机地址:"+client.getRemoteSocketAddress());
//通过getOutputStream和getInputStream获取相应的输入输出流,进行相应的I/O操作
//发送消息
OutputStream outToServer = client.getOutputStream();
DataOutputStream out = new DataOutputStream(outToServer);
out.writeUTF("Hello from " + client.getLocalSocketAddress());
//接受消息
InputStream inFromServer = client.getInputStream();
DataInputStream in = new DataInputStream(inFromServer);
System.out.println("接受服务器消息: " + in.readUTF());
//关闭连接
client.close();
}catch(IOException e)
{
e.printStackTrace();
}
}
}编译文件:
- 执行GreetingServer监听端口号6666:
- 执行GreetingClient访问本地主机localhost的端口6666:
Java多线程
线程的生命周期:
- 新建状态:线程对象被new后,直到被调用start()启动
- 就绪状态:线程对象被调用start()启动后,线程处于就绪队列中,等待JVM线程调度器的调度
- 运行状态:就绪状态的线程获取到CPU资源,会自动执行run()进入运行状态
- 阻塞状态:运行的线程让出CPU资源进入阻塞状态,等到恢复后可以重新进入就绪状态
- 等待阻塞:运行的线程执行wait()方法
- 同步阻塞:运行的线程获取synchronized同步锁失败
- 其他阻塞:运行的线程执行sleep()、join()或者发起IO请求
- 死亡状态:运行的线程完成任务或被终止
创建线程的三种方法:
继承Thread类:创建线程类继承Thread类,重写run方法,创建线程类对象,调用start()方法启动线程
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15//继承Thread类
public class MyThread extends Thread{
//重写run方法
public void run(){
//线程执行体
}
//主函数入口
public static void main(String [] args){
//创建线程
MyThread myThread=new MyThread();
//启动线程
myThread.start();
}
}实现Runnable接口:创建线程类实现Runnable接口,重写run方法,创建线程类对象,使用线程对象作为Thread对象的构建参数创建Thread对象,调用Thread对象的start()方法启动线程
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17//实现Runnable接口
public class MyThread implements Runnable{
//重写run方法
public void run(){
//线程执行体
}
//主函数入口
public static void main(String [] args){
//创建线程对象
MyThread myThread=new MyThread();
//根据线程对象创建Thread类对象
Thread thread=new Thread(myThread);
//启动线程
thread.start();
}
}实现Callable接口和FutureTask对象:
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
33import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;
//实现Callable接口
public class MyThread implements Callable<Integer>
{
//重写call方法 有返回值
public Integer call() throws Exception
{
int i = 5;
return i;
}
//主函数入口
public static void main(String [] args){
//创建线程对象
MyThread myThread=new MyThread();
//使用FutureTask类来包装Callable对象
FutureTask<Integer> ft = new FutureTask<>(myThread);
//根据FutureTask对象创建Thread类对象
Thread thread=new Thread(ft);
//启动线程
thread.start();
//获取线程的返回值
ft.get();
}
}创建线程方式优缺点:
- 实现接口方式:实现Runnable、Callable接口的方式创建多线程时,线程类只是实现了Runnable接口或Callable接口,还可以继承其他类
- 继承Thread方式:继承Thread类的方式创建多线程时,编写简单,访问当前线程方便,则无需使用Thread.currentThread()方法,直接使用this即可获得当前线程