第一题
题目描述
C语⾔标准库函数int atoi (const char *)和long int atol ( const char *),可以分别将给定的字符串转 换为整数(int)和⻓整数(long)。具体转换规则如下:丢弃前⾯的空⽩字符(空格、水平以及垂 直制表、换页、回车、换行,其相应的ASCII码值附后),直到第⼀个非空白字符为止;从该字符开 始,接收可选的+或-号,后跟任意的⼗进制数字字符并将其解释为数字值;字符串在构成整数的数 字字符之后可以包含额外的其它字符,⼀旦遇到这样的字符,转换⽴即停⽌;如果字符串的第⼀个
非空⽩字符并不是⼀个有效的数字字符,或者它为空或仅仅包含空⽩字符,那么不会执⾏转换,仅仅返回0。以下是对外提供这两个功能的NumUtil类,请根据该转换规则以及中的注释:(1)如果类 或有关⽅法的声明中存在错误,说明如何修改。main()方法体中,除了编号部分,不允许被修改。
(2)完成相关⽅法,要求不得使⽤标准Java⾃身的有关解析数值的类及其⽅法;(3)在main()⽅法中,根据下表中所给的测试⽤例执⾏单元测试(并⾮实际产品开发中的测试⽅式)。
java代码实现
public class NumUtil {
//判断是否是空字符
static private boolean isWhitespace(char c) {
switch(c) {
case ' ':
case '\t':
case '\f':
case '\r':
case '\n':
case '\u000b':
return true;
default:
return false;
}
}
//找到第一个非空字符的位置
static private int skipWhitespace(String source) {
//(2)
int i = 0;
while(i < source.length()) {
if(isWhitespace(source.charAt(i)))
i++;
else
break;
}
return i;
}
//字符串转化成长整数
public static long atol(String source) {
int sign = 1, i; // sign of the result long integer, i current position in source
long res = 0; // the final result long integer
// skip the whitespaces (3)
i = skipWhitespace(source);
if(i == source.length()) return res;//这里应该判断一下非空字符下标是否越界
// process the sign symbol (4)
switch(source.charAt(i)) {
case '+':
i++;
break;
case '-':
sign = -1;
i++;
break;
default:
break;
}
// process any digit characters
//(5)
for(; i < source.length(); i++) {
char c = source.charAt(i);
if(c >= '0' && c <= '9')
res = 10 * res + c - '0';
else
break;
}
// return the converted result long integer return (6)
return res*sign;
}
//字符串转整数,直接将long强值转化即可
public static int atoi(String source) {
//(7)
return (int)atol(source);
}
//根据上面注释给出的格式输出测试结果
public static void check(int test, int factual, int expected) {
//(8)
if(factual == expected) {
System.out.println("Test case: "+test+ " -> passed");
}
else {
System.out.println("Test case: "+test+ " -> failed");
}
}
// Just for test!
public static void main(String[] args) {
// test case 1
//main函数中不能直接调用非静态函数,应该创建对象或者转成静态函数
String source = "";
int res = atoi(source);
check(1, res, 0);
// test case 2
//也就是一个测试示例
source = " \t\f\r\n\u000b12345a8";//(9)
res = atoi(source);
check(2, res, 12345);
// test case 3
// test case 4
// test case 5
}
}
第二题
题目描述
在Web应⽤程序中,为了提⾼系统的响应性,可以将⽤户请求的结果缓存在内存中。当⽤户后续请求相同的资源时,不必从数据存储中提取,⽽可以直接从内存中提供;当某个⽤户对相同的资源 进⾏更新时,除了更新数据存储,还应该更新缓存,但统⼀资源标识符(URI-Uniform ResourceIdentifier)不能更改;当请求删除某个资源被时,除了从数据存储中将其删除之外,如果该资源已
经被缓存,还应将其从缓存中删除;为了降低系统内存的消耗,每隔⼀定的时间,应该清除缓存中 超过⼀定期限的项⽬。缓存的项⽬中包括请求的URI(字符串形式)、应答体(字符串形式)以及进⼊缓存的时间(UNIX纪元即⾃从1970年1⽉1⽇0时0分0秒以来的毫秒数)。为了简单起⻅,不考虑 多服务器的分布式缓存⽽仅仅考虑单台服务器,但需要考虑多线程同步问题。以下表示HTTP应答缓 存的类HttpCache在整个JVM中只能有⼀个实例,要求采⽤惰性(lazy)⽅式实例化,并且在多线程 环境下,对该类的实例化不得采⽤同步锁。所需要的有关类的⽅法在后续的表中给出。请编写以下 代码⻣架中编号为(1)、(2)等部分的代码(答题时标注相应的序号)并说明HttpCache在多线程环境 下哪些些⽅法需要同步以及在当前条件下如何同步。测试数据在后⾯的表格中给出。
java代码实现
CacheItem.java
package com.njfu.cache;
public class CacheItem {
private String uri; //统一资源标识符
private long since;//进入缓存时间
private String body;//应答体
// constructor (1)
public CacheItem(String uri, String body) {
this.uri = uri;
this.body = body;
//获取当前系统时间,单位为毫秒
this.since = System.currentTimeMillis();
}
// accessor for uri (2)
public void setUri(String uri) {
this.uri = uri;
}
public String getUri() {
return uri;
}
// accessor for since (3)
public void setSince(long since) {
this.since = since;
}
public long getSince() {
return since;
}
// accessor for body (4)
public void setBody(String body) {
this.body = body;
}
public String getBody() {
return body;
}
// When call System.out.println(aCacheItem), output “URI: uri, Since: since, Body: body” (5)
//直接字符串拼接即可
public String toString() {
return "URI: "+uri+", Since: "+since+", Body: "+body;
}
}
CacheItem.java
package com.njfu.cache;
import java.util.HashMap;
import java.util.Iterator; // for Java 8-
import java.util.Map;
import java.util.Set; // Optional
//Don’t inherit from HahMap etc.
public class HttpCache {
//Cache all the HTTP responses
private Map<String, CacheItem> cache;
//7, 8, 9就是为了惰性单例
//Make the globall instance of the HTTP response cache (7)
private static class CacheMaker {
private static final HttpCache instance = new HttpCache();
}
//constructor (8)私有的构造方法,禁止直接实例化
private HttpCache() {
this.cache = new HashMap<>();
}
//通过方法创建实例
public static HttpCache getInstance() {
//(9)
return CacheMaker.instance;
}
public synchronized boolean cache(String uri, String body) {
//(10)
//利用哈希表判断cache中是否存在uri,如果存在则进行更新,返回true
//不存在则插入当前item,返回false
CacheItem item = this.cache.get(uri);
if(item == null) {
this.cache.put(uri, new CacheItem(uri, body));
return false;
}
item.setBody(body);
item.setSince(System.currentTimeMillis());
return true;
}
public synchronized String get(String uri) {
//(11)
//直接调用hashmap的get函数,不空返回相对应的应答体,否则直接返回空
CacheItem item = this.cache.get(uri);
return item == null ? null : item.getBody();
}
public synchronized boolean delete(String uri) {
//调用hashmap中的方法remove删除统一资源标识符为uri的实例
//(12)
Object o = cache.remove(uri);
return !(o == null);
}
public synchronized void purge(long delta) {
long now = System.currentTimeMillis();//获取当前时间
//(13) //包括Java 8前后不同的实现
// Java 8+
//如果存在一个资源在缓存时间过长则删除
//entrySet方法获取的其实就是一个类型为Map.Entery<key, value>的set容器
//而set容器其实就是一个集合,可以用遍历整个集合或removeif+Lambda表达式删除里面所有符合条件对象
//java8后可以用Lambda表达式+removeif
this.cache.entrySet().removeIf(e -> e.getValue().getSince() <= now - delta);
// Java 8-
//java8之前只能迭代
}
//Just for test
@Override
public String toString() {
return this.cache.toString();
}
//Just for test, multi-threads not concerned!
public static void main(String[] args) {
//In this case, ‘the HttpCache.’ part is optional
HttpCache.getInstance().cache("185080101","{no: \"185080101\", name: \"zhangsan\", gender: \"M\"}");
//一些测试,可以不看
//HttpCache c = HttpCache.getInstance();
//System.out.println(c.delete("185080101"));
//c.cache(, "{no: \"185080101\", name: \"zhangsan\", gender: \"M\"}");
//System.out.println(c);
}
}
第三题
题目描述
以下是客户端通过数据报同服务器单向通信的代码框架。客户端从控制台上接收⽤户输⼊,按回⻋键后将所输⼊的内容发送给服务器,当⽤户输⼊EOF时,客户端退出;服务器端在本地环回接⼝ 上接收来⾃客户端的数据报并将其内容显示到终端窗⼝的,请根据其中的注释完成编号为(1)、(2)等 的代码。
java代码实现
DatagramServer.java
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.util.function.Consumer;
public class DatagramServer {
// Port number
private static final int SERVER_PORT = 8888;
// Receiving buffer size, maximum 1KB. For UDP, this is enough
private static final int BUF_SIZE = 1024;
public static void main(String args[]) throws Exception {
// Used to log the received packet to console
//Consumer一个函数式接口,代表一个接收输入参数但是无返回值的操作,相当于一个消费者
//::在java8之后可以用于获取类的静态方法,对象的成员方法等
Consumer<String> logger = System.out::println;//(1)
//DatagramPacket表示存放数据的数据报,DatagramSocket表示接受或发送数据报的套接字
// Create a datagram socket
//构造方法
//DatagramSocket(int port, InetAddress iad): 表示创建一个套接字,绑定到特定的端口号及指定地址
DatagramSocket server = new DatagramSocket(SERVER_PORT, InetAddress.getLoopbackAddress());//(2)
//数据缓冲池
// Receiving buffer for datagram packet
byte[] buf = new byte[BUF_SIZE];
// Keep running forever
for(;;) {
// Create a new packet
DatagramPacket packet = new DatagramPacket(buf, BUF_SIZE);//(3)
// Try to receive the datagram packet
//(4),接收数据
server.receive(packet);
// Use logger to write the content of the received packet to the console
//(5)
logger.accept(new String(packet.getData(), 0, packet.getLength()));
}
}
}
DatagramServer.java
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
//DatagramClient.java
//import omitted
public class DatagramClient {
//Server port number
private static final int SERVER_PORT = 8888;
//Client port
private static final int CLIENT_PORT = 8889;
//Size of the message buffer
private static final int BUF_SIZE = 1024;
public static void main(String args[]) throws Exception {
//Create a datagram socket
//(6)
DatagramSocket client = new DatagramSocket(CLIENT_PORT, InetAddress.getLoopbackAddress());
//Data buffer for datagram packet
byte[] buf = new byte[BUF_SIZE];
//Read user input from the console. When hit EOF, exist
//pos - the next position where to write the character.
for(int pos = 0, c; (c = System.in.read()) != -1;) {
switch(c) {
case '\r': // Windows break;
case '\n': // Finished a line
//Send the message contained in the buffer
//(8)
client.send(new DatagramPacket(buf, pos, InetAddress.getLoopbackAddress(), SERVER_PORT));
//Reset the buffer
//(9)
pos = 0;
break;
default:
// Cache the current character
//assume no more than BUF_SIZE
buf[pos++] = (byte)c;
}
}
System.out.println("Goodbye!");
//Close the datagram socket (10)
client.close();
}
}
第四题
题目描述
根据以下所给定的简单电⼦邮件客户端的截图,以及以下代码⻣架中的注释,完成编号为(1)、(2)等部分的代码。当点击按钮“退出”时,终⽌该客户端的运⾏,要求采⽤Lambda表达式。
java代码
MailPanel.java
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import javax.swing.JButton;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JTextArea;
import javax.swing.JTextField;
//MailPanel.java
//import omitted
public class MailPanel extends JPanel {
public MailPanel() {
//create main panel and set grid bag layout
//创建网格包布局
super(new GridBagLayout());
//create grid bag contraints
//GridBagConstraints是GridBagLayout的约束,即确定了组件的位置
GridBagConstraints c = new GridBagConstraints();
//row 0, column 0 -> 主题:
//(3)
//标题Label的位置为(0, 0),坐标为gridx->向右递增,girdy向下递增
c.gridx = 0;
c.gridy = 0;
this.add(new JLabel("主题:"), c);
//row 0, column 1 -> text field for subject, expand horizontally
//(4)
//第一个文本框的位置以及占用的大小
c.gridx = 1;
c.fill = GridBagConstraints.HORIZONTAL;
c.weightx = 1;
this.add(new JTextField(), c);
//row 0, column 2 -> 收件⼈:
//(5)
c.gridx = 2;
c.fill = GridBagConstraints.NONE;
c.weightx = 0;//窗口改变时增量的大小,为0时不增
this.add(new JLabel("收件人:"), c);
//row 0, column 3 -> text field for recipient, expand horizontally
//(6)
c.gridx = 3;
c.fill = GridBagConstraints.HORIZONTAL;
c.weightx = 1;
this.add(new JTextField(), c);
//row 1, colulmn 0 -> email content
//(7)
c.gridx = 0;
c.gridy = 1;
c.gridwidth = 4;
c.weightx = 0.0;
c.weighty = 1.0;
c.fill = GridBagConstraints.BOTH;
this.add(new JTextArea(), c);
//row 3, column 1 -> 发送
//(8)
c.gridx = 1;
c.gridy = 3;
c.weighty = 0;
c.fill = GridBagConstraints.NONE;
c.anchor = GridBagConstraints.CENTER;
c.gridwidth = 1;
c.ipady = 0;
this.add(new JButton("发送"), c);
//row 3, column 3 -> 退出; exit with success code
//(9)
c.gridx = 3;
c.gridy = 3;
c.gridwidth = 1;
JButton bttn = new JButton("退出");
bttn.addActionListener(e -> System.exit(0));
this.add(bttn, c);
}
}
MailClient.java
import java.awt.Container;
import javax.swing.JFrame;
import javax.swing.SwingUtilities;
import javax.swing.WindowConstants;
//MailClient.java
//import omitted
public class MailClient {
public MailClient() {
//create the main frame
//(10)
JFrame frame = new JFrame("NJFU Emailer");
//terminate the JVM when the close icon was clicked
//(11)
frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
//create and add the mail panel
//(12)
frame.add(new MailPanel());
//set the initial size of the frame, width: 600, height: 500
//(13)
frame.setSize(600, 500);
//display the frame
//(14)
frame.setVisible(true);
}
public static void main(String[] args) {
//create the frame on the event dispatching thread instead of the
//main thread. You should use Lambada
//(15)
//时间派发线程运行图形界面
SwingUtilities.invokeLater(MailClient::new);
//等价于SwingUtilities.invokeLater(() -> new MailClient());
}
}