处理注解练习
处理注解
请根据注解:
@NotNull:检查该属性为非null
@Range:检查整型介于min~max,或者检查字符串长度介于min~max
@ZipCode:检查字符串是否全部由数字构成,且长度恰好为value
实现对Java Bean的属性值检查。如果检查未通过,抛出异常。
输出
checking City@14ae5a5 ...
Error:field name is null
Error:field latitude is out of range
Error:field password's length is not 8
import java.lang.reflect.Field;
public class Main {
public static void main(String[] args) throws Exception {
City city1 = new City(null, 91, "1234567");
checkCity(city1);
}
private static void checkCity(City c) throws Exception {
System.out.println("checking " + c + " ...");
Class cls = City.class;
for (Field f : cls.getFields()) {
checkField(f, c);
}
}
private static void checkField(Field f, City c) throws Exception {
if (f.isAnnotationPresent(NotNull.class)) {
Object r = f.get(c);
if (r == null) {
System.out.println("Error:field " + f.getName() + " is null");
}
}
if (f.isAnnotationPresent(Range.class)) {
Range range = f.getAnnotation(Range.class);
int n = (Integer) f.get(c);
if (n < range.min() || n > range.max()) {
System.out.println("Error:field " + f.getName() + " is out of range");
}
}
if (f.isAnnotationPresent(ZipCode.class)) {
ZipCode z = f.getAnnotation(ZipCode.class);
String p = (String) f.get(c);
if (p.length() != z.value()) {
System.out.println("Error:field " + f.getName() + "'s length is not "+z.value());
}
if(!isInteger(p)){
System.out.println("Error:field " + f.getName() + "is not Integer");
}
}
}
private static boolean isInteger(String s){
try {
Integer.parseInt(s);
return true;
}catch (NumberFormatException e){
return false;
}
}
}
public class City{
@NotNull
public String name;
@Range(max = 90)
public int latitude;
@ZipCode(value = 8)
public String password;
public City(String name,int latitude,String password){
this.name = name;
this.latitude = latitude;
this.password = password;
}
}
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface NotNull{
}
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface Range {
int min() default 0;
int max() default 100;
}
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface ZipCode {
int value() default 8;
}
Set练习
请将List的元素去重,但保留元素在List中的原始顺序,即:
[“abc”, “xyz”, “abc”, “www”, “edu”, “www”, “abc”]
去重时应该删除:
[“abc”, “xyz”, “abc”, “www”, “edu”, “www”, “abc”]
去重后的结果应该为:
[“abc”, “xyz”, “www”, “edu”]
提示:LinkedHashSet
import java.util.*;
public class Main{
public static void main(String[] args){
List<String> s = Arrays.asList("abc", "xyz", "abc", "www", "edu", "www", "abc");
Set<String> set = new LinkedHashSet<>(s);
List<String> res = new ArrayList<>(set);
for (String i:res){
System.out.println(i);
}
}
}
Stack练习
请用Stack将整数转换为十六进制字符串表示,即:
toHex(12500) => “30d4”
进制转换算法:
- 不断对整数除以16,得到商和余数,余数压栈
- 用得到的商重复步骤1
- 当商为0时,计算结束。将栈中的数依次弹出并组成String
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
public class Main{
public static void main(String[] args){
int n = 12500;
String s = toHex(n);
System.out.println(s);
}
private static String toHex(int n){
// StringBuffer sb = new StringBuffer();
// while (n!=0){
// sb.insert(0,Integer.toHexString(n%16)); //插入StringBuffer头部,再直接转String
// n /= 16;
// }
// return sb.toString();
List<String> list = new ArrayList<>();
while (n!=0){
list.add(Integer.toHexString(n%16));
n /= 16;
}
Collections.reverse(list); //先反转List,再用String连接
return String.join("",list);
}
}
File练习
请编写一个Java程序,能列出当前目录下的所有子目录和文件,并按层次打印。
例如:
输出:
IOFilePractice
.classpath
.project
bin
com
feiyangedu
sample
Main.class
src
com
feiyangedu
sample
Main.java
如果不指定参数,则使用当前目录,如果指定参数,则使用指定目录。
例如:
在Eclipse中设置命令行参数 C:\Users
输出:
Users
public
Michael
Desktop
download.txt
Documents
Music
import java.io.File;
public class Main {
public static void main(String[] args) {
String PATH = "D:\\廖雪峰的java教程";
File fp = new File(PATH);
walk(fp); //遍历当前目录,打印所有文件和子目录
}
private static void walk(File fp) {
if (fp.isDirectory()) {
System.out.println(fp.getAbsolutePath()); //打印完整目录
for (File subFp : fp.listFiles()) {
walk(subFp);
}
} else {
String path = fp.getAbsolutePath();
String name = path.substring(path.lastIndexOf("\\") + 1); //打印文件名
for (int i = 0; i < path.length() - name.length(); ++i) {
System.out.print(" ");
}
System.out.println(name);
}
}
}
Input/Output练习
FileInputStream可以从文件读入数据,FileOutputStream可以把数据写入文件
如果我们一边从一个文件读取数据,一边把数据写入到另一个文件,就完成了文件的拷贝。
请编写一个程序,接收两个命令行参数,分别表示源文件和目标文件, 然后用InputStream/OutputStream把源文件复制到目标文件。
复制后,请检查源文件和目标文件是否相同(文件长度相同,内容相同),分别用文本文件、图片文件和zip文件测试。
import java.io.*;
public class Main {
public static void main(String[] args) throws Exception {
String in_path = "test.tar.gz";
String out_path = "test_bak.tar.gz";
cpFile(in_path, out_path);
}
private static void cpFile(String in_path, String out_path) throws IOException {
try (InputStream in = new BufferedInputStream(new FileInputStream(in_path));
OutputStream out = new BufferedOutputStream(new FileOutputStream(out_path))) {
byte[] buffer = new byte[1024];
int n;
while ((n = in.read(buffer)) != -1) {
out.write(buffer, 0, n);
}
}
}
}
Reader/Writer练习
请编写一个程序,接收两个命令行参数,分别表示源文件和目标文件,然后用Reader/Writer把GBK编码的源文件转换为UTF-8编码的目标文件。
import java.io.*;
public class Main {
public static void main(String[] args) throws Exception {
String in_path = "test.txt";
String out_path = "test_bak.txt";
transFormat(in_path, out_path);
}
private static void transFormat(String in_path, String out_path) throws IOException {
try (Reader reader = new InputStreamReader(new FileInputStream(in_path), "GBK");
Writer writer = new OutputStreamWriter(new FileOutputStream(out_path), "UTF-8")) {
int n;
while ((n = reader.read()) != -1) {
writer.write(n);
}
}
}
}
DateTime练习
某航线从北京飞到纽约需要12小时15分钟,请根据起飞日期和时间计算到达纽约的当地日期和时间。
例如,用户输入一个标准的日期和一个标准的时间:
Departure date: 2016-12-01
Departure time: 07:50
输出到达的当地日期和时间:
Arrival date: xxxx-xx-xx
Arrival time: xx:xx
import java.time.*;
public class Main {
public static void main(String[] args) {
LocalDate bjDate = LocalDate.parse("2016-12-01"); //解析出发北京本地日期和时间
LocalTime bjTime = LocalTime.parse("07:50");
System.out.println("Departure date: " + bjDate);
System.out.println("Departure time: " + bjTime);
LocalDateTime bjDateTime = LocalDateTime.of(bjDate, bjTime); //出发时北京时间
LocalDateTime bjArrivalDateTime = bjDateTime.plusHours(12).plusMinutes(15); //加上路程时间,到达时北京时间
ZonedDateTime bj = bjArrivalDateTime.atZone(ZoneId.systemDefault()); //本地时间转时区时间
ZonedDateTime ny = bj.withZoneSameInstant(ZoneId.of("America/New_York")); //到达时纽约时间
LocalDate nyDate = ny.toLocalDate(); //到达纽约的时区时间,转成纽约的本地日期和时间
LocalTime nyTime = ny.toLocalTime();
System.out.println("Arrival date: " + nyDate);
System.out.println("Arrival time: " + nyTime);
}
}
WorldClock练习
通过System.currentMillis()可以获得当前的Epoch Time,以long类型表示,
请编写一个方法,根据用户传入的时区,以yyyy-MM-dd HH:mm:ss的格式打印出本地日期和时间。
public class WorldClock {
public String getCurrentDateTime(String zoneId) {
return “”;
}
}
import java.time.Instant;
import java.time.ZoneId;
import java.time.ZonedDateTime;
import java.time.format.DateTimeFormatter;
import java.time.format.FormatStyle;
public class Main {
public static void main(String[] args) {
String zoneid = "Asia/Shanghai";//"America/New_York";
System.out.println(getCurrentDateTime(zoneid)); //传入时区
}
private static String getCurrentDateTime(String zoneId) {
long epoch = System.currentTimeMillis(); // 获得当前时间戳long类型,通过Instant构造
// 日期和时间格式化
DateTimeFormatter f = DateTimeFormatter.ofLocalizedDateTime(FormatStyle.LONG, FormatStyle.SHORT);
return f.format(ZonedDateTime.ofInstant(Instant.ofEpochMilli(epoch), ZoneId.of(zoneId)));
}
}
匹配规则练习
请编写一个正则表达式判断用户输入的QQ号是否合法。合法的QQ号是5~10位数字。
目标:通过所有JUnit测试。
public class Main {
public static void main(String[] args) {
assert isValidQQ("10000");
assert isValidQQ("99999");
assert isValidQQ("1234567890");
assert !isValidQQ("00001");
assert !isValidQQ("099999");
}
private static boolean isValidQQ(String qq) {
return qq.matches("[1-9]\\d{4,9}");
}
}
Java正则表达式
复杂匹配练习
请编写一个正则表达式,可以验证第二代身份证号是否合法,规则满足:
18位数字
最后一位可能是X
首位不能是0
目标:通过所有单元测试。
public class Main {
public static void main(String[] args) {
assert isValidIdCard("320926195511175276");
assert isValidIdCard("32092619551117527x");
assert !isValidIdCard("020926195511175276");
assert !isValidIdCard("32092619551117527");
}
private static boolean isValidIdCard(String idCard) {
return idCard.matches("^[1-9]\\d{16}[0-9x]$");
}
}
分组匹配练习
请编写一个正则表达式,可以提取合法时间字符串的时,分,秒,或者当字符串不合法时返回null
目标:通过所有单元测试。
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class Main {
public static void main(String[] args) {
System.out.println(getTime("00:12:03"));
System.out.println(getTime("23:00:59"));
System.out.println(getTime("70:12:03"));
System.out.println(getTime("00:60:03"));
}
private static String getTime(String time) {
Pattern p = Pattern.compile("^([0-1][0-9]|2[0-3])\\:([0-5][0-9])\\:([0-5][0-9]$)");
Matcher m = p.matcher(time);
if (m.matches()) {
return m.group(0);
}
return null;
}
}
搜索和替换练习
模板引擎是指,定义一个字符串作为模板:
Hello, ${name}! You are learning
l
a
n
g
!
其
中
,
以
{lang}! 其中,以
lang!其中,以{key}表示的是变量,也就是将要被替换的内容
当传入一个Map<String, Object>给模板后,需要把对应的key替换为Map的value。
例如,传入Map为:
{
“name”: “Bob”,
“lang”: “Java”
}
然后,
n
a
m
e
被
替
换
为
M
a
p
对
应
的
值
"
B
o
b
”
,
{name}被替换为Map对应的值"Bob”,
name被替换为Map对应的值"Bob”,{lang}被替换为Map对应的值"Java",最终输出的结果为:
Hello, Bob! You are learning Java!
请编写一个简单的模板引擎,利用正则表达式实现这个功能。
参考:
Matcher对象的appendReplacement()方法和appendTail()方法。
replaceAll()方法
import java.util.HashMap;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class Main {
public static void main(String[] args) {
Map<String, Object> map = new HashMap<>();
map.put("name", "Bob");
map.put("lang", "Java");
String template = "Hello, ${name}! You are learning ${lang}!";
System.out.println(templateEngine(template, map));
}
private static String templateEngine(String template, Map<String, Object> map) {
for (Map.Entry<String, Object> entry : map.entrySet()) {
Pattern p = Pattern.compile("\\$\\{" + entry.getKey() + "\\}");
Matcher m = p.matcher(template);
if (m.find()) {
template = m.replaceAll(entry.getValue().toString());
}
}
return template;
}
}
appendReplacement()和appendTail()方法
import java.util.HashMap;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class Main {
public static void main(String[] args) {
Map<String, Object> map = new HashMap<>();
map.put("name", "Bob");
map.put("lang", "Java");
String template = "Hello, ${name}! You are learning ${lang}!";
System.out.println(templateEngine(template, map));
}
private static String templateEngine(String template, Map<String, Object> map) {
for (Map.Entry<String, Object> entry : map.entrySet()) {
Pattern p = Pattern.compile("\\$\\{" + entry.getKey() + "\\}");
Matcher m = p.matcher(template);
StringBuffer sb = new StringBuffer();
while (m.find()) {
m.appendReplacement(sb, entry.getValue().toString());
}
m.appendTail(sb);
template = sb.toString();
}
return template;
}
}
Java加密与安全
MD5练习
练习
从MySQL官方网站下载MySQL的JDBC驱动程序文件,保存到本地,然后编写一个MD5.java,读取文件内容,计算MD5,并与网页给出的MD5比较是否相同。
目标:
通过所有JUnit测试
运行main(),计算下载文件MD5,与官方网站一致
import java.io.BufferedInputStream;
import java.io.ByteArrayOutputStream;
import java.io.FileInputStream;
import java.io.IOException;
import java.math.BigInteger;
import java.security.MessageDigest;
public class Main {
public static void main(String[] args) throws Exception {
String path = "C:\\Users\\XPS-15\\Desktop\\mysql-installer-web-community-8.0.15.0.msi";
ByteArrayOutputStream bos = new ByteArrayOutputStream(); // 指向内存的流可以不用关闭,指向存储卡/硬盘的流一定要关闭
try (BufferedInputStream bis = new BufferedInputStream(new FileInputStream(path))) {
final int bufferSize = 1024;
byte[] buffer = new byte[bufferSize];
int n;
while ((n = bis.read(buffer, 0, bufferSize)) != -1) {
bos.write(buffer, 0, n);
}
} catch (IOException e) {
e.printStackTrace();
throw e;
}
byte[] r = toMD5(bos.toByteArray());
System.out.println(String.format("%032x", new BigInteger(1, r))); // 输出是一致的:66363d23d2474113decf683cf84f5820
}
private static byte[] toMD5(byte[] input) {
MessageDigest md;
try {
md = MessageDigest.getInstance("MD5");
} catch (Exception e) {
throw new RuntimeException(e);
}
md.update(input);
return md.digest();
}
}
对称加密算法练习
请用PBEwithSHA1and128bitAES-CBC-BC(需要使用BouncyCastle)算法设计一个加密软件:
给用户分配一个USB Key(暂以文件如usb-key.txt存储); 用户在加密/解密文件时需要输入正确的口令; 加密/解密时程序会读取同一个USB Key,当USB Key不
存在时(通过修改usb-key.txt的文件名)无法进行加密/解密。
目标:
通过所有JUnit测试
运行Main正常
import javax.crypto.Cipher;
import javax.crypto.SecretKey;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.PBEKeySpec;
import javax.crypto.spec.PBEParameterSpec;
import java.io.BufferedInputStream;
import java.io.FileInputStream;
import java.io.InputStream;
import java.math.BigInteger;
import java.nio.charset.StandardCharsets;
import java.security.GeneralSecurityException;
import java.util.Base64;
public class Main {
public static void main(String[] args) throws Exception {
// Security.addProvider(new BouncyCastleProvider());
// 此处不需要引入jar包并在CLASSPATH下实例化,因为在两个jre的\lib\security\java.security中已添加:
// security.provider.11=org.bouncycastle.jce.provider.BouncyCastleProvider
// 且在两个jre的lib\ext下已添加 bcprov-ext-jdk15on-161.jar
// 来自https://www.bouncycastle.org/latest_releases.html
String message = "hello world";
String password = "123456";
System.out.println(message);
// 将salt存到文件
// byte[] salt = SecureRandom.getInstanceStrong().generateSeed(16);
// try (OutputStream out = new BufferedOutputStream(new FileOutputStream("usb-key.txt"))) {
// out.write(salt);
// }
//从文件读取salt
byte[] salt = new byte[16];
try (InputStream in = new BufferedInputStream(new FileInputStream("usb-key.txt"))) {
in.read(salt);
}
System.out.printf("salt: %032x\n", new BigInteger(1, salt));
byte[] data = message.getBytes(StandardCharsets.UTF_8);
byte[] encrypted = encrypt(password, salt, data);
System.out.println("encrypted: " + Base64.getEncoder().encodeToString(encrypted));
byte[] decrypted = decrypt(password, salt, encrypted);
String s = new String(decrypted, "UTF-8");
System.out.println(s);
}
private static final String CIPHER_NAME = "PBEWITHSHA1AND128BITAES-CBC-BC";
private static byte[] encrypt(String password, byte[] salt, byte[] input) throws GeneralSecurityException {
PBEKeySpec keySpec = new PBEKeySpec(password.toCharArray());
SecretKeyFactory secretKeyFactory = SecretKeyFactory.getInstance(CIPHER_NAME);
SecretKey secretKey = secretKeyFactory.generateSecret(keySpec);
PBEParameterSpec pbeParameterSpec = new PBEParameterSpec(salt, 1000);
Cipher cipher = Cipher.getInstance(CIPHER_NAME);
cipher.init(Cipher.ENCRYPT_MODE, secretKey, pbeParameterSpec);
return cipher.doFinal(input);
}
private static byte[] decrypt(String password, byte[] salt, byte[] input) throws GeneralSecurityException {
PBEKeySpec keySpec = new PBEKeySpec(password.toCharArray());
SecretKeyFactory secretKeyFactory = SecretKeyFactory.getInstance(CIPHER_NAME);
SecretKey secretKey = secretKeyFactory.generateSecret(keySpec);
PBEParameterSpec pbeParameterSpec = new PBEParameterSpec(salt, 1000);
Cipher cipher = Cipher.getInstance(CIPHER_NAME);
cipher.init(Cipher.DECRYPT_MODE, secretKey, pbeParameterSpec);
return cipher.doFinal(input);
}
}
Java多线程编程
join练习
join练习
小明设计了一个多线程程序,希望创建3个线程,每个线程先打印
Hello, xxx!
然后等待1秒,再打印:
Goodbye, xxx!
小明期望的输出是:
START
Hello, Bob!
Hello, Alice!
Hello, Tom!
(等待约1秒)
Goodbye, Bob!
Goodbye, Alice!
Goodbye, Tom!
END
但实际输出却是:
START
Hello, Bob!
(等待约1秒)
Goodbye, Bob!
Hello, Alice!
(等待约1秒)
Goodbye, Alice!
Hello, Tom!
(等待约1秒)
Goodbye, Tom!
END
请帮他找找问题原因,并修复。
class HelloThread extends Thread {
String name;
public HelloThread(String name) {
this.name = name;
}
@Override
public void run() {
System.out.println("Hello, " + name + "!");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
}
System.out.println("Goodbye, " + name + "!");
}
}
修改小明代码,参考了JavaSE(十)多线程编程,并使用了TimeUnit
的sleep
方法
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.TimeUnit;
public class Main {
public static void main(String[] args) throws Exception {
List<Thread> threads = new ArrayList<>();
for (String name : Arrays.asList("Bob", "Alice", "Tom")) {
threads.add(new HelloThread(name));
}
System.out.println("START");
for (Thread t : threads) {
t.start();
TimeUnit.MILLISECONDS.sleep(10); //睡10毫秒,以保证列表的打印顺序,TimeUnit.sleep()内部会调用Thread.sleep()
}
for (Thread t : threads) {
t.join(); // 将join()和start()分开,线程才会同时运行
}
System.out.println("END");
}
}
Java函数式编程
Lambda练习
Lambda表达式
请将Runnable接口实现类改为lambda表达式:
Thread t = new Thread(new Runnable() {
@Override
public void run() {
System.out.println("start new thread...");
}
});
t.start();
最简单的写法:
public class Main {
public static void main(String[] args) throws Exception {
new Thread(() -> System.out.println("start new thread...")).start();
}
}
或者一般的写法:
public class Main {
public static void main(String[] args) throws Exception {
Thread t = new Thread(() -> {
System.out.println("start new thread...");
});
t.start();
}
}
Method Reference练习
方法引用
请尽可能地使用方法引用来简化代码:
public static void main(String[] args) throws Exception {
String[] array = new String[] { "Orange", "apple", "blackberry", "Pear" };
ExecutorService executor = Executors.newCachedThreadPool();
// 忽略大小写异步排序:
Future<String[]> f = executor.submit(new Callable<String[]>() {
public String[] call() {
String[] copy = Arrays.copyOf(array, array.length);
Arrays.sort(copy, new Comparator<String>() {
public int compare(String s1, String s2) {
return s1.toLowerCase().compareTo(s2.toLowerCase());
}
});
return copy;
}
});
System.out.println("Original: " + Arrays.toString(array));
System.out.println(" Sorted: " + Arrays.toString(f.get()));
executor.shutdown();
}
使用lambda,Comparator.comparing:
import java.util.Arrays;
import java.util.Comparator;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
public class Main {
public static void main(String[] args) throws Exception {
String[] array = new String[]{"Orange", "apple", "blackberry", "Pear"};
ExecutorService executor = Executors.newCachedThreadPool();
// 忽略大小写异步排序:
Future<String[]> f = executor.submit(() -> { //使用lambda
String[] copy = Arrays.copyOf(array, array.length);
Arrays.sort(copy, Comparator.comparing(s -> s.toLowerCase()) // Comparator.comparing,使用lambda或方法引用String::toLowerCase
);
return copy;
});
System.out.println("Original: " + Arrays.toString(array));
System.out.println(" Sorted: " + Arrays.toString(f.get()));
executor.shutdown();
}
}
谈谈你对Java平台的理解?
Java是解析运行吗?
不正确!
1,Java源代码经过Javac编译成.class文件
2,.class文件经JVM解析或编译运行。
(1)解析:.class文件经过JVM内嵌的解析器解析执行。
(2)编译:存在JIT编译器(Just In Time Compile 即时编译器)把经常运行的代码作为"热点代码"编译与本地平台相关的机器码,并进行各种层次的优化。
(3)AOT编译器: Java 9提供的直接将所有代码编译成机器码执行。
volatile关键字
保证变量在线程之间的可见性。可见性的保证是基于CPU的内存屏障指令。
阻止编译时和运行时的指令重排。编译时JVM编译器遵循内存屏障的约束,运行时依靠CPU屏障指令来阻止重排。
Java网络编程
TCP多线程编程练习
请把多线程模型中每次创建一个新线程改为使用线程池实现。
import java.io.*;
import java.net.InetAddress;
import java.net.Socket;
import java.nio.charset.StandardCharsets;
public class TCPClient {
public static void main(String[] args) throws Exception {
InetAddress addr = InetAddress.getLoopbackAddress();
try (Socket socket = new Socket(addr, 5001)) {
try (BufferedReader reader = new BufferedReader(new InputStreamReader(socket.getInputStream(), StandardCharsets.UTF_8))) {
try (BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream(), StandardCharsets.UTF_8))) {
writer.write("time\n");
writer.flush();
String response = reader.readLine();
System.out.println("response:" + response);
Thread.sleep(5000);
writer.write("q\n");
writer.flush();
response = reader.readLine();
System.out.println("response:" + response);
}
}
}
}
}
import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;
import java.nio.charset.StandardCharsets;
import java.time.LocalDateTime;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class TCPServer {
public static void main(String[] args) throws Exception {
@SuppressWarnings("resource")
ServerSocket ss = new ServerSocket(5001);
System.out.println("sever ready...");
ExecutorService executor = Executors.newFixedThreadPool(10);
while (true) {
Socket socket = ss.accept();
System.out.println("connect from " + socket.getRemoteSocketAddress());
executor.submit(new TimeHandler(socket));
}
// executor.shutdown();
// ss.close();
}
}
class TimeHandler implements Runnable {
private Socket socket;
public TimeHandler(Socket socket) {
this.socket = socket;
}
public void run() {
try (BufferedReader reader = new BufferedReader(
new InputStreamReader(socket.getInputStream(), StandardCharsets.UTF_8)
)) {
try (BufferedWriter writer = new BufferedWriter(
new OutputStreamWriter(socket.getOutputStream(), StandardCharsets.UTF_8)
)) {
for (; ; ) {
String cmd = reader.readLine();
if ("q".equals(cmd)) {
writer.write("bye!\n");
writer.flush();
break;
} else if ("time".equals(cmd)) {
writer.write(LocalDateTime.now().toString() + "\n");
writer.flush();
} else {
writer.write("excuse me?\n");
writer.flush();
}
}
}
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
this.socket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}