Java leraning Note v.3
多线程
创建线程
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| public class threadDemo { public static void main(String[] args) { Thread t = new MyThread(); **t.start(); for (int i = 0; i < 5; i++) { System.out.println("主线程执行" + i); } } } class MyThread extends Thread{ @Override public void run() { for (int i = 0; i <5; i++) { System.out.println("子线程执行输出" + i); } } }
|
多线程同时执行,输出先后不确定。
注1:不能用t.run()执行,否则会把对象当成一般方法,不能创建线程,t.start()方法内有线程注册机制。
注2:不能把主线程程序放在子线程前面,否则会永远导致主线程先跑,不能同时执行。
创建线程方法二:实现Runnable接口
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| public class threadDemo { public static void main(String[] args) { Runnable target = new MyThread(); Thread t = new Thread(target); t.start(); for (int i = 0; i < 5; i++) { System.out.println("主线程执行" + i); } } } class MyThread implements Runnable{ @Override public void run() { for (int i = 0; i <5; i++) { System.out.println("子线程执行输出" + i); } }
|
此时MyThread类可继承其他父类,也可实现其他接口。但不能有返回值。
也可用匿名内部类或lambda表达式写法:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| public class threadDemo { public static void main(String[] args) { Runnable target = new Runnable() { @Override public void run() { for (int i = 0; i <5; i++) { System.out.println("子线程执行输出" + i); } } }; Thread t = new Thread(target); t.start(); for (int i = 0; i < 5; i++) { System.out.println("主线程执行" + i); } } }
|
或:
1 2 3 4 5 6 7 8 9 10 11 12
| public static void main(String[] args) { Runnable target = () -> { for (int i = 0; i <5; i++) { System.out.println("子线程执行输出" + i); } }; Thread t = new Thread(target); t.start(); for (int i = 0; i < 5; i++) { System.out.println("主线程执行" + i); } }
|
创建线程方法三:定义任务类实现Callable接口
优势:可获取返回值,即Callable为泛型
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
| import java.util.concurrent.Callable; import java.util.concurrent.FutureTask;
public class threadDemo { public static void main(String[] args) { Callable<String> call = new MyThread(100);
FutureTask<String> ft = new FutureTask<>(call); Thread t = new Thread(ft); t.start(); try { String s = ft.get(); System.out.println("第一个结果" + s); } catch (Exception e){ e.printStackTrace(); } Callable<String> call2 = new MyThread(200); FutureTask<String> ft2 = new FutureTask<>(call2); Thread t2 = new Thread(ft2); t2.start(); try { String s2 = ft2.get(); System.out.println("第二个结果" + s2); } catch (Exception e){ e.printStackTrace(); } for (int i = 0; i < 5; i++) { System.out.println("主线程执行" + i); } } } class MyThread implements Callable<String> { private int n;
public MyThread(int n) { this.n = n; } @Override public String call() throws Exception{ int sum = 0; for (int i = 0; i < n; i++) { sum += i; } return "所求的和为" + sum; } }
|
Thread常用方法
利用Thread.*currentThread*()
.getName()区分线程名:
1 2 3 4 5 6 7 8
| public class MyThread1 extends Thread{ @Override public void run() { for (int i = 0; i < 5; i++) { System.out.println(Thread.currentThread().getName() + "执行" + i); } } }
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| public class api { public static void main(String[] args) { Thread t1 = new MyThread1(); t1.setName("NO.1"); t1.start(); System.out.println(t1.getName()); Thread t2 = new MyThread1(); t2.setName("No.2"); t2.start(); System.out.println(t2.getName()); Thread m = Thread.currentThread(); System.out.println(m.getName());
for (int i = 0; i < 5; i++) { System.out.println("主线程执行" + i); } } }
|
也可用构造器,但要重写构造方法。
线程安全
1 2 3 4 5 6 7 8 9 10
| public void drawMoney(double money) { String name = Thread.currentThread().getName(); if (this.money >= money){ System.out.println(name + "取钱成功!"); this.money -= money; System.out.println(name + "剩余" + this.money); } else { System.out.println(name + "余额不足!"); } }
|
1 2 3 4 5 6 7 8
| public class ThreadSecurity { public static void main(String[] args) { Account acc = new Account("ICBC-1011",10000);
new DrawThread(acc,"小明").start(); new DrawThread(acc,"小红").start(); } }
|
若有两个线程同时调用该方法,可能再A线程完成判断,但还未对资源进行写入时,B线程也已完成判断,造成同时完成两次修改:
1 2 3 4 5
| "C:\Program Files (x86)\Java\jdk1.8.0_333\bin\java.exe" "-javaagent:E:\IntelliJ IDEA 2022.1\lib\idea_rt.jar=60352:E:\IntelliJ IDEA 2022.1\bin" -Dfile.encoding=UTF-8 -classpath "C:\Program Files (x86)\Java\jdk1.8.0_333\jre\lib\charsets.jar;C:\Program Files (x86)\Java\jdk1.8.0_333\jre\lib\deploy.jar;C:\Program Files (x86)\Java\jdk1.8.0_333\jre\lib\ext\access-bridge-32.jar;C:\Program Files (x86)\Java\jdk1.8.0_333\jre\lib\ext\cldrdata.jar;C:\Program Files (x86)\Java\jdk1.8.0_333\jre\lib\ext\dnsns.jar;C:\Program Files (x86)\Java\jdk1.8.0_333\jre\lib\ext\jaccess.jar;C:\Program Files (x86)\Java\jdk1.8.0_333\jre\lib\ext\jfxrt.jar;C:\Program Files (x86)\Java\jdk1.8.0_333\jre\lib\ext\localedata.jar;C:\Program Files (x86)\Java\jdk1.8.0_333\jre\lib\ext\nashorn.jar;C:\Program Files (x86)\Java\jdk1.8.0_333\jre\lib\ext\sunec.jar;C:\Program Files (x86)\Java\jdk1.8.0_333\jre\lib\ext\sunjce_provider.jar;C:\Program Files (x86)\Java\jdk1.8.0_333\jre\lib\ext\sunmscapi.jar;C:\Program Files (x86)\Java\jdk1.8.0_333\jre\lib\ext\sunpkcs11.jar;C:\Program Files (x86)\Java\jdk1.8.0_333\jre\lib\ext\zipfs.jar;C:\Program Files (x86)\Java\jdk1.8.0_333\jre\lib\javaws.jar;C:\Program Files (x86)\Java\jdk1.8.0_333\jre\lib\jce.jar;C:\Program Files (x86)\Java\jdk1.8.0_333\jre\lib\jfr.jar;C:\Program Files (x86)\Java\jdk1.8.0_333\jre\lib\jfxswt.jar;C:\Program Files (x86)\Java\jdk1.8.0_333\jre\lib\jsse.jar;C:\Program Files (x86)\Java\jdk1.8.0_333\jre\lib\management-agent.jar;C:\Program Files (x86)\Java\jdk1.8.0_333\jre\lib\plugin.jar;C:\Program Files (x86)\Java\jdk1.8.0_333\jre\lib\resources.jar;C:\Program Files (x86)\Java\jdk1.8.0_333\jre\lib\rt.jar;E:\Java_IDEA\Java_lerannig_v3\out\production\Java_lerannig_v3" hit.com.d1_thread.ThreadSecurity 小明取钱成功! 小红取钱成功! 小明剩余0.0 小红剩余-10000.0
|
线程同步-线程锁
利用同步对线程进行加锁,以解决安全问题。
1.同步代码块
1 2 3 4 5 6 7 8 9 10 11 12 13
| public void drawMoney(double money) { String name = Thread.currentThread().getName(); **synchronized ("heima") { if (this.money >= money){ System.out.println(name + "取钱成功!"); this.money -= money; System.out.println(name + "剩余" + this.money); } else { System.out.println(name + "余额不足!"); } } }
|
但用任意无关对象会导致锁所有线程,因此多用共享资源作为锁对象。
1 2 3 4 5 6 7 8 9
| synchronized (this) { if (this.money >= money){ System.out.println(name + "取钱成功!"); this.money -= money; System.out.println(name + "剩余" + this.money); } else { System.out.println(name + "余额不足!"); } }
|
- 对于实例方法,共享资源对象为this
- 对于静态方法,共享资源对象为类名.class
2.同步方法
1 2 3 4 5 6 7 8 9 10 11 12
| public synchronized void drawMoney(double money) { String name = Thread.currentThread().getName(); if (this.money >= money){ System.out.println(name + "取钱成功!"); this.money -= money; System.out.println(name + "剩余" + this.money); } else { System.out.println(name + "余额不足!"); } }
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| private final Lock lock = new ReentrantLock();
public void drawMoney(double money) { String name = Thread.currentThread().getName(); lock.lock(); try { if (this.money >= money){ System.out.println(name + "取钱成功!"); this.money -= money; System.out.println(name + "剩余" + this.money); } else { System.out.println(name + "余额不足!"); } } catch (Exception e) { throw new RuntimeException(e); } finally { lock.unlock(); } }
|
线程通信
生成-消费-生成-消费—————
不断有序运行如上任务,需要用线程通信。
利用Object类如下两个方法完成线程间的唤醒与等待,实现线程依次运行。
1 2
| this.notifyAll(); this.wait();
|
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
| public synchronized void saveMoney(double money){ String name = Thread.currentThread().getName(); if(this.money == 0){ this.money += money; System.out.println(name + "存钱成功" + "余" + this.money); try { this.notifyAll(); this.wait(); } catch (InterruptedException e) { throw new RuntimeException(e); } } else{ System.out.println("有钱"); } } public synchronized void drawMoney(double money){ try { String name = Thread.currentThread().getName(); if (this.money >= money){ this.money -= money; System.out.println(name + "来取钱" + money + "余额" + this.money); this.notifyAll(); this.wait(); } else{ this.notifyAll(); this.wait(); } } catch (InterruptedException e) { throw new RuntimeException(e); } }
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| public class ThreadDemo { public static void main(String[] args) { Account acc = new Account("IDC-88",0);
new DrawThread(acc,"小明").start(); new DrawThread(acc,"小红").start();
new SaveThread(acc, "亲爹").start(); new SaveThread(acc, "干爹").start(); new SaveThread(acc, "岳父").start();
} }
|
如上存钱方法与取钱方法可实现线程通信。
用到该方法的方法需要用synchronized修饰。
★线程池
线程池处理Runnable任务
线程池构造器的参数:
临时线程创建:核心线程正在忙,且任务队列也满了
拒绝任务时:核心线程和临时线程都在忙,任务队列也满了,此时开始拒绝新的任务。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| public class threadPoolDemo { public static void main(String[] args) {
ExecutorService pool = new ThreadPoolExecutor(3, 5, 6, TimeUnit.SECONDS, new ArrayBlockingQueue<>(5), Executors.defaultThreadFactory(), new ThreadPoolExecutor.AbortPolicy());
Runnable target = new MyRunnable(); pool.execute(target); pool.execute(target); pool.execute(target); pool.execute(target); pool.execute(target); } }
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| public class MyRunnable implements Runnable{ @Override public void run() { for (int i = 0; i < 5; i++) { System.out.println(Thread.currentThread().getName() + "打印了" + i);
} try { System.out.println(Thread.currentThread().getName() + "本任务与线程绑定,进入休眠了~~"); Thread.sleep(1000000); } catch (InterruptedException e) { throw new RuntimeException(e); } }
|
正常有三个线程在跑,在超过五个任务时会创建临时线程,最多有两个临时线程。
线程池处理Callable任务
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
| public class threadPoolDemo { public static void main(String[] args) throws ExecutionException, InterruptedException {
ExecutorService pool = new ThreadPoolExecutor(3, 5, 6, TimeUnit.SECONDS, new ArrayBlockingQueue<>(5), Executors.defaultThreadFactory(), new ThreadPoolExecutor.AbortPolicy());
Future<String> f = pool.submit(new MyCallable(100)); Future<String> f2 = pool.submit(new MyCallable(200)); Future<String> f3 = pool.submit(new MyCallable(300)); Future<String> f4 = pool.submit(new MyCallable(400));
System.out.println(f.get()); System.out.println(f2.get()); System.out.println(f3.get()); System.out.println(f4.get());
} }
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| public class MyCallable implements Callable<String> { private int n;
public MyCallable(int n) { this.n = n; }
public MyCallable() { }
@Override public String call() throws Exception { int sum = 0; for (int i = 0; i <= n; i++) { sum += i; } return Thread.currentThread().getName() + "结果为" + sum; } }
|
运行同样是三个线程:
1 2 3 4
| pool-1-thread-1结果为5050 pool-1-thread-2结果为20100 pool-1-thread-3结果为45150 pool-1-thread-2结果为80200
|
定时器
实际多用线程池工具类完成定时器
网络编程
网络通信三要素:IP地址,端口,协议
InetAddress
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| public class InetAddressDemo { public static void main(String[] args) throws Exception{ InetAddress ip1 = InetAddress.getLocalHost(); System.out.println(ip1.getHostName()); System.out.println(ip1.getHostAddress());
InetAddress ip2 = InetAddress.getByName("baidu.com"); System.out.println(ip2.getHostAddress()); System.out.println(ip2.getHostName());
InetAddress ip3 = InetAddress.getByName("39.156.66.10"); System.out.println(ip2.getHostAddress()); System.out.println(ip2.getHostName()); System.out.println(ip3.isReachable(3000)); } }
|
端口号
端口用于标记计算机上运行的进程。
端口号不能重复。
协议
传输层两个常见协议:UDP,TCP
TCP
双方先建立链接再通信 是一种可靠通信。可进行大数据量的通信,但效率较低。
三次握手:
四次挥手断开链接:
UDP
无连接,不可靠的传输协议
速度块,资源开销小,无法确认是否收到数据,可广播发送。
有大小限制,一次最多发送64KB信息
UDP通信
数据包对象:
发送端接收端对象(管道):
客户端:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| public class ClientDemo { public static void main(String[] args) throws Exception{ DatagramSocket socket = new DatagramSocket();
byte[] buffer = "我是中国人".getBytes(StandardCharsets.UTF_8); DatagramPacket packet = new DatagramPacket(buffer, buffer.length, InetAddress.getLocalHost(), 8888);
socket.send(packet); socket.close(); } }
|
服务端:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| public class ServerDemo { public static void main(String[] args) throws Exception { DatagramSocket socket = new DatagramSocket(8888);
byte[] buffer = new byte[1024 * 64]; DatagramPacket packet = new DatagramPacket(buffer, buffer.length);
socket.receive(packet); int len = packet.getLength(); System.out.println("收到了" + new String(buffer, 0, len));
socket.close(); } }
|
可用packet内的方法获取客户端IP和端口。
UDP多发多收
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| public class ServerDemo { public static void main(String[] args) throws Exception { DatagramSocket socket = new DatagramSocket(8888);
byte[] buffer = new byte[1024 * 64]; DatagramPacket packet = new DatagramPacket(buffer, buffer.length);
while (true) { socket.receive(packet); int len = packet.getLength(); System.out.println("收到了" + packet.getAddress() + "的" + packet.getPort() + "的" + new String(buffer, 0, len)); }
} }
|
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
| public class ClientDemo { public static void main(String[] args) throws Exception{ Scanner sc = new Scanner(System.in); DatagramSocket socket = new DatagramSocket();
while (true) { System.out.println("请您输入:");
String msg = sc.nextLine();
if("exit".equals(msg)){ System.out.println("已退出"); socket.close(); break; } byte[] buffer = msg.getBytes(StandardCharsets.UTF_8); DatagramPacket packet = new DatagramPacket(buffer, buffer.length, InetAddress.getLocalHost(), 8888);
socket.send(packet); } } }
|
UDP广播 组播
1 2
| DatagramPacket packet = new DatagramPacket(buffer, buffer.length, InetAddress.getByName("**255.255.255.255**"), 9999);
|
客户端修改为如上,同时服务端修改端口为9999,此时服务端可接受所有广播信息。
1 2 3
| MulticastSocket socket = new MulticastSocket(9999);
socket.joinGroup(InetAddress.getByName("224.0.0.1"));
|
TCP通信
Java Socket底层是TCP协议。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| public class ServerDemo { public static void main(String[] args) { try { System.out.println("======服务端启动成功========"); ServerSocket serverSocket = new ServerSocket(7777); Socket socket = serverSocket.accept(); InputStream is = socket.getInputStream(); BufferedReader br = new BufferedReader(new InputStreamReader(is)); String msg; while((msg = br.readLine())!=null){ System.out.println(socket.getRemoteSocketAddress() + "说了" + msg); }
} catch (IOException e) { throw new RuntimeException(e); } } }
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| public class ClientDemo { public static void main(String[] args) throws IOException { Socket socket = new Socket("127.0.0.1", 7777); OutputStream os = socket.getOutputStream();
PrintStream ps = new PrintStream(os); **ps.println("我是TCP客户端,已对接,发出邀请");** ps.flush();
} }
|
注意:应该先运行服务端再运行客户端!
注意:发送消息必须为一行,要有换行符,否则无法收到消息,会断开链接!
通信面向链接,一端挂了另一端也必挂
实现多收多发
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| public static void main(String[] args) throws IOException { Socket socket = new Socket("127.0.0.1", 7777); OutputStream os = socket.getOutputStream();
PrintStream ps = new PrintStream(os);
Scanner sc = new Scanner(System.in); **while(true){ String msg = sc.nextLine(); ps.println(msg); ps.flush(); }**
}
|
但无法完成多个客户端与服务端通信,因为一个线程只能用一个死循环接收socket对象。
实现同时接受多个客户端
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| public class ClientDemo { public static void main(String[] args) throws IOException { Socket socket = new Socket("127.0.0.1", 7777); OutputStream os = socket.getOutputStream();
PrintStream ps = new PrintStream(os);
Scanner sc = new Scanner(System.in); while(true){ String msg = sc.nextLine(); ps.println(msg); ps.flush(); } } }
|
服务端需要用多线程实现:
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
| import java.io.BufferedReader; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.net.Socket;
public class ServerReaderThread extends Thread{ private Socket socket; public ServerReaderThread(Socket socket){ this.socket = socket; } @Override public void run() { InputStream is = null; try { is = socket.getInputStream(); BufferedReader br = new BufferedReader(new InputStreamReader(is)); String msg; while((msg = br.readLine())!=null){ System.out.println(socket.getRemoteSocketAddress() + "说了" + msg); } } catch (IOException e) { System.out.println(socket.getRemoteSocketAddress() + "下线了"); }
} }
|
服务端调用多线程,并用accept方法判断上线:
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
| public class ServerDemo { public static void main(String[] args) { try { System.out.println("======服务端启动成功========"); ServerSocket serverSocket = new ServerSocket(7777); while(true){ Socket socket = serverSocket.accept(); System.out.println(socket.getRemoteSocketAddress() + "上线了"); **new ServerReaderThread(socket).start();** }
} catch (IOException e) { throw new RuntimeException(e); } } }
|
可实现多个客户端给服务端发信息,但问题是无法控制线程数。
利用线程池解决此问题:
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
| public class ServerDemo { private static ExecutorService pool = new ThreadPoolExecutor(3, 5, 6, TimeUnit.SECONDS, new ArrayBlockingQueue<>(2), Executors.defaultThreadFactory(), new ThreadPoolExecutor.AbortPolicy());
public static void main(String[] args) { try { System.out.println("======服务端启动成功========"); ServerSocket serverSocket = new ServerSocket(7777); while(true){ Socket socket = serverSocket.accept(); System.out.println(socket.getRemoteSocketAddress() + "上线了"); Runnable target = new ServerReaderRunnable(socket); pool.execute(target); }
} catch (IOException e) { throw new RuntimeException(e); } } }
|
线程池需要用任务队列而非线程类:
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
| import java.io.BufferedReader; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.net.Socket;
public class ServerReaderRunnable implements Runnable{ private Socket socket; public ServerReaderRunnable(Socket socket){ this.socket = socket; } @Override public void run() { try { InputStream is = socket.getInputStream(); BufferedReader br = new BufferedReader(new InputStreamReader(is)); String msg; while((msg = br.readLine())!=null){ System.out.println(socket.getRemoteSocketAddress() + "说了" + msg); } } catch (IOException e) { System.out.println(socket.getRemoteSocketAddress() + "下线了"); } } }
|
可以很优雅地控制线程,防止资源耗尽。
TCP即时通信
需要在服务端实现端口转发
在ServerDemo类定义静态集合存储所有在线的Socket:
1
| public static List<Socket> allOnlineSockets = new ArrayList<>();
|
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
| public class ServerReaderThread extends Thread{ private Socket socket; public ServerReaderThread(Socket socket){ this.socket = socket; } @Override public void run() { InputStream is = null; try { is = socket.getInputStream(); BufferedReader br = new BufferedReader(new InputStreamReader(is)); String msg; while((msg = br.readLine())!=null){ System.out.println(socket.getRemoteSocketAddress() + "说了" + msg); sendMsgToAll(msg); } } catch (Exception e) { System.out.println(socket.getRemoteSocketAddress() + "下线了"); ServerDemo.allOnlineSockets.remove(socket); } } private void sendMsgToAll(String msg) throws Exception{ for (Socket socket : ServerDemo.allOnlineSockets) { PrintStream ps = new PrintStream(socket.getOutputStream()); ps.println(msg); ps.flush(); } } }
|
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
| public class ClientDemo { public static void main(String[] args) throws IOException { System.out.println("=============客户端启动成功==========="); Socket socket = new Socket("127.0.0.1", 7777); new ClientReaderThread(socket).start(); OutputStream os = socket.getOutputStream();
PrintStream ps = new PrintStream(os);
Scanner sc = new Scanner(System.in); while(true){ String msg = sc.nextLine(); ps.println(msg); ps.flush(); } } }
class ClientReaderThread extends Thread{ private Socket socket; public ClientReaderThread(Socket socket){ this.socket = socket; } @Override public void run() { InputStream is = null; try { is = socket.getInputStream(); BufferedReader br = new BufferedReader(new InputStreamReader(is)); String msg; while((msg = br.readLine())!=null){ System.out.println(socket.getRemoteSocketAddress() + "说了" + msg); } } catch (IOException e) { System.out.println("被踢出去了!!"); }
} }
|
BS架构
修改端口用浏览器访问:
1
| ServerSocket serverSocket = new ServerSocket(8888);
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| public class ServerReaderThread extends Thread{ private Socket socket; public ServerReaderThread(Socket socket){ this.socket = socket; } @Override public void run() { try { PrintStream ps = new PrintStream(socket.getOutputStream()); ps.println("HTTP/1.1 200 OK"); ps.println("Content-Type:text/html;charset=UTF-8"); ps.println(); ps.println("<h1>hello world!!!!</h1>"); ps.flush();
} catch (Exception e) { e.printStackTrace(); } } }
|
单元测试 断言
1 2 3 4 5 6 7 8 9 10 11 12
| import org.testng.Assert; import org.testng.annotations.Test;
public class testtest { @Test public void testLoginName(){ test test = new test(); String rs = test.loginName("admin","12346"); Assert.assertEquals(rs,"登录成功","您的业务出问题了!"); } }
|
1 2 3 4 5 6 7 8 9 10 11 12 13
| public class test { public String loginName(String loginName, String passWord){ if ("admin".equals(loginName) && "13456".equals(passWord)){ return "登录成功"; }else { return "登录失败!"; } } public void selectName(){ System.out.println(10/0); System.out.println(1111); } }
|
测试可以跑整个类,也可跑单个测试类。
其他注解:
1 2 3 4 5 6 7 8 9
| @Before public void before(){ System.out.println("===执行前执行!"); }
@After public void after(){ System.out.println("===执行后执行!"); }
|
★反射
运行时动态获取类信息以及动态调用类中成分称之为反射
反射第一步:获取Class类对象
获取Class类对象的三种方法:
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
| public class reflectDemo { public static void main(String[] args) throws Exception{ Class c = Class.forName("hit.hit.D1_reflect.Student"); System.out.println(c);
Class c1 = Student.class; System.out.println(c1);
Student s = new Student(); Class c2 = s.getClass(); System.out.println(c2); } }
|
反射第二步:获取构造器对象 + 获取对象
1 2 3 4 5 6 7
| public void getConstructors(){ Class c = Student.class; Constructor[] constructors = c.getConstructors(); for (Constructor constructor : constructors) { System.out.println(constructor.getName() + "参数" + constructor.getParameterCount()); } }
|
由构造器new对象:
1 2 3 4 5 6 7 8 9 10 11 12 13
| public class studentTest2 { @Test public void getConstructors() throws Exception { Class c = Student.class; Constructor constructor = c.getDeclaredConstructor(String.class, int.class); System.out.println(constructor.getName() + constructor.getParameterCount()); constructor.setAccessible(true);
Student stu = (Student) constructor.newInstance("张三",10); System.out.println(stu); } }
|
反射获取成员变量:赋值与取值
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
| public class reflectDemo2 { @Test public void getDeclaredFields(){ Class c = Student.class; Field[] fields = c.getDeclaredFields(); for (Field field : fields) { System.out.println(field.getName() + field.getType()); } }
@Test public void getFields() throws Exception{ Class c = Student.class; Field ageF = c.getDeclaredField("age");
ageF.setAccessible(true); Student s = new Student(); ageF.set(s, 10); System.out.println(s);
int age = (int) ageF.get(s); System.out.println(age); } }
|
反射提取方法 运行
1 2 3 4 5 6 7 8 9
| @Test public void getDeclaredMethods(){ Class c = Dog.class;
Method[] methods = c.getDeclaredMethods(); for (Method method : methods) { System.out.println(method.getName() + "=>" + method.getParameterCount() + "=>" + method.getReturnType()); } }
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| @Test public void getDeclaredMethod() throws Exception{ Class c = Dog.class;
Method m = c.getDeclaredMethod("run",String.class); Method s = c.getDeclaredMethod("sleep");
Dog d = new Dog(); Object result = m.invoke(d,"呃呃"); System.out.println(result);
Object result2 = s.invoke(d); System.out.println(result2); }
|
反射的作用
- 擦除泛型,可在运行阶段强行加入其他类型的集合。
- 用作通用框架
注解
自定义注解
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| package hit.hit.d2_annotation;
@Mybook(name = "精通java", authors = {"11","22"}, price = 22.5) public class annotationDemo { @Mybook(name = "精通java", authors = {"11","22"}, price = 22.5) private annotationDemo(){
} @Mybook(name = "精通java", authors = {"11","22"}, price = 22.5) public static void main(String[] args) { @Mybook(name = "精通java", authors = {"11","22"}, price = 22.5) int age = 12; } }
|
1 2 3 4
| public @interface Mybook { String name(); String[] authors(); double p;
|
若自定义注解中有特殊属性value(),则若只有一个属性可省略属性名。
元注解
放在自定义注解上的注解。
1 2 3 4 5 6 7 8 9 10 11 12
| import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target;
@Target({ElementType.METHOD,ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
public @interface Book { String value(); }
|
注解解析
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| public class annotationDemo { @Test public void parseClass() throws Exception{ Class c1 = bookStore.class; Method m = c1.getDeclaredMethod("test"); Class c = bookStore.class; if (c.isAnnotationPresent(Mybook.class)){ Mybook book = (Mybook) c.getDeclaredAnnotation(Mybook.class); System.out.println(book.authors()); System.out.println(book.price()); System.out.println(book.value()); } } } @Mybook(value = "1111", authors = {"古龙"}, price = 200.0) class bookStore{ @Mybook(value = "1111", authors = {"5555"},price = 110.2) public void test(){
} }
|
注解的应用:标记做特殊处理
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
| public class annotationTest { @MyTest public void test1(){ System.out.println("====text1===="); } public void test2(){ System.out.println("====text2===="); } @MyTest public void test3(){ System.out.println("====text3===="); }
public static void main(String[] args) throws Exception{ Class c = annotationTest.class; Method[] methods = c.getDeclaredMethods();
for (Method method : methods) { if (method.isAnnotationPresent(MyTest.class)) { method.invoke(new annotationTest()); } } } }
|
多用于各种框架开发。
★动态代理
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy;
public class StarAgentProxy {
public static Skill getProxy(Star star){ return (Skill) Proxy.newProxyInstance(star.getClass().getClassLoader(), star.getClass().getInterfaces(), new InvocationHandler() { @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out.println("收取首款~~"); Object rs = method.invoke(star,args); System.out.println("收尾款"); return rs; } }); } }
|
1 2 3 4 5
| public interface Skill { void jump(); void sing(); }
|
1 2 3 4 5 6 7 8 9 10 11 12 13
| public class Star implements Skill{ private String name;
@Override public void jump() { System.out.println("跳舞~~~~"); }
@Override public void sing() { System.out.println("唱歌~~~"); } }
|
最后调用:
1 2 3 4 5 6 7 8 9 10
| public class proxyDemo { public static void main(String[] args) { Star s = new Star("serenchan"); Skill s2 = StarAgentProxy.getProxy(s); s2.jump(); } }
|
代理的作用:提高代码复用性,可将重复的方法封装成代理对象。
例如每个方法都加个性能分析的System.currentTimeMillis(),可加在代理内:
1 2 3 4 5 6 7
| public class Test { public static void main(String[] args) { UserService userService = ProxyUtil.getProxy(new UsreServiceDemo()); System.out.println(userService.login("admin", "123456")); userService.deleteUsers(); } }
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| public class ProxyUtil { public static UserService getProxy(UserService userService){ return (UserService) Proxy.newProxyInstance(userService.getClass().getClassLoader(), userService.getClass().getInterfaces(), new InvocationHandler() { @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { long startTime = System.currentTimeMillis(); Object rs = method.invoke(userService,args); long lastTime = System.currentTimeMillis(); System.out.println(method.getName() + "耗时为" + (lastTime - startTime) / 1000.0 + "s"); return rs; } }); } }
|
注:也可用泛型实现任意接口做代理:
XML
可扩展标记语言,常用于传输和存储数据。
特点:
- 纯文本,默认UTF-8
- 可嵌套
- 可存为XML文件
- 常用作消息进行网络传输, 或作为配置文件用作存储系统信息。
XML文档约束
XML解析
dom4j(DOM for java)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| import org.dom4j.Document; import org.dom4j.DocumentException; import org.dom4j.Element; import org.dom4j.io.SAXReader; import org.testng.annotations.Test;
public class Dom4jDemo1 { @Test public void parseXml() throws Exception {
SAXReader saxReader = new SAXReader(); Document document = saxReader.read(Dom4jDemo1.class.getResourceAsStream("/hello_world.xml")); Element root = document.getRootElement(); } }
|
实例:解析成对象
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| <?xml version="1.0" encoding="UTF-8" ?>
<contactlist> <contact id="1" vip="false"> <name>国王</name> <sex>male</sex> <email>1616161</email> </contact> <contact id="2" vip="true"> <name>王后</name> <sex>female</sex> <email>56565656556</email> </contact> </contactlist>
|
工具类
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
| package com.dom4j;
public class Contact { private String name; private String sex; private int id; private boolean vip; private String email;
@Override public String toString() { return "Contact{" + "name='" + name + '\'' + ", sex='" + sex + '\'' + ", id=" + id + ", vip=" + vip + ", email='" + email + '\'' + '}'; }
public String getName() { return name; }
public void setName(String name) { this.name = name; }
public String getSex() { return sex; }
public void setSex(String sex) { this.sex = sex; }
public int getId() { return id; }
public void setId(int id) { this.id = id; }
public boolean isVip() { return vip; }
public void setVip(boolean vip) { this.vip = vip; }
public String getEmail() { return email; }
public void setEmail(String email) { this.email = email; }
public Contact() { }
public Contact(String name, String sex, int id, boolean vip, String email) { this.name = name; this.sex = sex; this.id = id; this.vip = vip; this.email = email; } }
|
主程序
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
| package com.dom4j;
import org.dom4j.Document; import org.dom4j.DocumentException; import org.dom4j.Element; import org.dom4j.io.SAXReader; import org.testng.annotations.Test;
import java.util.ArrayList; import java.util.List;
public class Dom4JTest2 { @Test public void parseToList() throws DocumentException { SAXReader saxReader = new SAXReader(); Document document = saxReader.read(Dom4JTest2.class.getResourceAsStream("/hello_world.xml")); Element root = document.getRootElement();
List<Element> contactsEles = root.elements("contact"); List<Contact> contacts = new ArrayList<>(); for (Element contactsEle : contactsEles) { Contact contact = new Contact(); contact.setId(Integer.valueOf(contactsEle.attributeValue("id"))); contact.setVip(Boolean.valueOf(contactsEle.attributeValue("vip")));
contact.setName(contactsEle.elementTextTrim("name")); contact.setSex(contactsEle.elementTextTrim("sex")); contact.setEmail(contactsEle.elementTextTrim("email")); contacts.add(contact); }
for (Contact contact : contacts) { System.out.println(contact); } } }
|
注意:导包要注意同名包,尤其在用到第三方库时:
XML检索:XPath
1 2 3 4 5 6 7 8 9 10 11 12 13
| public class XPathDemo1 { @Test public void parse01() throws Exception{ SAXReader saxReader = new SAXReader(); Document document = saxReader.read(XPathDemo1.class.getResourceAsStream("/hello_world.xml"));
List<Node> nameNodes = document.selectNodes("/contactlist/contact/name"); for (Node nameNode : nameNodes) { Element nameEle = (Element) nameNode; System.out.println(nameEle.getTextTrim()); } } }
|