【代码】Java小练习

来自廖雪峰JavaSE课程练习题

处理注解练习
处理注解
请根据注解:
@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”
进制转换算法:

  1. 不断对整数除以16,得到商和余数,余数压栈
  2. 用得到的商重复步骤1
  3. 当商为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 对 应 的 值 &quot; B o b ” , {name}被替换为Map对应的值&quot;Bob”, nameMap"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(十)多线程编程,并使用了TimeUnitsleep方法

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();
    }
}

wait和notify的理解与使用

谈谈你对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();
            }
        }
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值