CVTE 2017 内推笔试整理
CVTE 2017 内推笔试整理
转载:https://blog.csdn.net/leewccc/article/details/59058141
选择题
C 类网络划分子网
问题:
C 类网络,要求子网不小于 8 个,主机号不少于 14 个,哪些能作为子网掩码
回答:
从问题可得知,子网不小于 8 个,则子网号的位数至少要有 3 位;主机号不少于 14 个,则主机号的位数至少要有 4 位,因此可以作为子网掩码的是255.255.
255.224 和 255.255.255.240
知识点整理:
IP 地址分为 A、B、C、D、E 类地址,每个 IP 地址由 32 位组成,A、B、C 类 IP 地址由网络号和主机号组成
- A 类:
- B 类:
- C 类:
划分子网
由于我们使用的 IP 地址都属于两级 IP 地址(网络号+主机号),该种结构的 IP 地址存在很多缺点:
1. IP 地址空间的利用率有时很低,
如一个 A 类地址可以有 2^24-2 个主机,B 类地址可以有 2^16 -2 个主机,一般一个以太网最大的结点数会有限制,因此使用 A、B 类会造成浪费,使用 C 类又担心不够用,利用率会降低
2. 给每一个物理网络分配一个网络号会导致路由表太大
3. 两级 IP 地址不够灵活
因此为了解决这些问题,采用了划分子网的方式来,将 IP 地址设计为三级结构(网络号+子网号+主机号),使用主机号的若干位作为子网号,剩余的作为主机号。
报文传输:
报文在发送到该网络上时,会再根据子网号转发到对应的子网,来发送到对应的主机。
那么,发送到该网络上的报文,如何转发到对应的子网?
在此基础上,增加子网掩码,子网掩码也是由 32 位组成,B 类地址的子网掩码是 255.255.0.0,C 类地址的子网掩码是 255.255.255.0,划分子网后的子网掩码只需要将子网号对应的位变为 1 即可,则 C 类地址,三位子网号的子网掩码是 255.255.255.11100000,要计算报文属于哪个子网,只需要将报文的目的 IP 地址与子网掩码做 & 运算即可得到目标子网。一般子网号不能全为 0 或全为 1。
总结:
对于划分子网问题,只需要根据子网掩码来计算出子网号和主机号各有多少位即可解决
synchronized 和 lock
问题:
该题主要是考察 synchronized 和 lock 的区别,具体选项忘记了。
关于 synchronized 和 lock 的区别的知识点之后再整理
类别 | synchronized | Lock |
---|---|---|
存在层次 | Java的关键字,在jvm层面上 | 是一个类 |
锁的释放 | 1、以获取锁的线程执行完同步代码,释放锁 2、线程执行发生异常,jvm会让线程释放锁 | 在finally中必须释放锁,不然容易造成线程死锁 |
锁的获取 | 假设A线程获得锁,B线程等待。如果A线程阻塞,B线程会一直等待 | 分情况而定,Lock有多个锁获取的方式,具体下面会说道,大致就是可以尝试获得锁,线程可以不用一直等待 |
锁状态 | 无法判断 | 可以判断 |
锁类型 | 可重入 不可中断 非公平 | 可重入 可判断 可公平(两者皆可) |
性能 | 少量同步 | 大量同步 |
反射机制
在 Java 中,我们要使用一个对象时,需要编译器在编译时打开和检查 .class 文件,加载进 JVM 才能使用。反射机制允许我们不需要再编译时加载类,而是在运行时才加载类,同时通过反射,可以获取类的信息,来动态的创建和操作对象。
反射机制的功能:
- 可以获取类名和类的继承结构
- 可以创建类的实例
- 可以获取类定义的方法、字段和构造器(包括私有的)
- 可以调用类的方法
- 可以获取类加载器
Java 的 hashcode
问题:
该题主要考察 hashcode 的规则以及特点,是一道判断对错题,以下选项
1. String 没有重写 hashcode 方法
2. 空字符串的 hashcode 值相等
3. hashcode 方法一定要返回一个确切的整数
4. 当对象的 equal 方法返回 true 时,那么它们的 hashcode 也应该相同
当时我纠结了很久,1 是否正确。主要是因为对 String 的源码不熟悉以及不了解 hashcode 的设计规范。
首先,先看看 Object 类中定义的 hashcode 方法
public native int hashCode();
它是一个本地方法,从方法签名中可以知道 hashcode 方法返回一个整数,而且同一个对象返回相同的 hashcode(这是 Java 中 hashcode 的机制)。
在看看 String 类中的 hashcode 方法
public int hashCode() {
int h = hash;
if (h == 0 && value.length > 0) {
char val[] = value;
for (int i = 0; i < value.length; i++) {
h = 31 * h + val[i];
}
hash = h;
}
return h;
}
该方法计算 hash 值与你字符串的内容有关。因此 String 类有改写它的 hashcode 方法。还有另外一种判断方法,在 hashcode 的规范中,如果两个对象的 equal 方法返回 true,那么这两个对象 hashcode 方法也应该相等。String 类的 equal 方法是只要字符串内容相同,则返回 true。
因此当两个对象拥有相同的字符串时,它们的 hashcode 相等,如果没有重写 hashcode 方法,两个不同对象应该会有不同的 hashcode 值,所以 String 方法是有重写 hashcode 方法的。
ping 命令
问题:
ping 命令是采用哪种 ICMP 报文?
回答:
该题目考察的是 ping 命令的机制以及 ICMP 的报文类型,首先我们应该要了解 ping 命令的功能是什么。学过计网的或者是使用过 ping 命令的人应该都知道,ping 命令是用于测试能否连接某一主机。
如在 cmd 中输入 ping 192.168.1.111之后,然后它会尝试去连接 IP 地址为 192.168.1.111 的主机,然后返回连接结果和延迟时间。
我记得在该题上,有两个选项是一个是报文不可达,一个是 echo报文回显,当时我选的是报文不可达,因为我想测试是否连接成功,报文不可达则代表连接失败。结果是 echo 报文回显。纯属理论题,需要对 ICMP 各个报文类型的功能有足够的了解。
知识点整理:
ICMP 报文
ICMP 报文主要有两种类型,一种是 ICMP 差错报告报文,另一种是 ICMP 询问报文。
常见的 ICMP 差错报文类型
- 终点不可达:当路由器或主机不能交付数据报时就向源点发送终点不可达报文
- 源点抑制:当路由器或主机由于阻塞而丢弃数据报时,就向源点发送源点抑制报文,使源点知道应当把数据报的发送速率放慢
- 时间超过:当路由器收到生存时间为 0 的数据包时,除丢弃该数据报外,还要向源点发送时间超过报文(当终点在规定时间,数据报片未来齐,也会发送)
- 参数问题:当路由器或目的主机收到的数据包的首部中有的字段不正确时,就丢弃该数据报,并向源点发送参数问题报文
- 改变路由(重定向):路由器把改变路由报文由报文发送给主机,让主机知道下次应将数据报发送给另外的路由器
常见的 ICMP 询问报文类型
- 回送请求和回答:向一个特定的目的主机发出询问,用于测试目的主机是否可达以及了解其有关状态
- 时间戳请求和回答:请求某个主机或路由器返回当前日期和时间
Java 类库的设计模式
问题:
该题主要考察对设计模式的了解以及 Java 类库中的实现方式,具体选项忘记了,好像有提到单例模式、桥接模式、原型模式
1、 单例模式
单例模式作用,保证类在内存中的对象唯一性。
适用场景:
- 资源共享的情况下,避免由于资源操作时导致的性能或损耗等。如上述中的日志文件,应用配置
- 控制资源的情况下,方便资源之间的互相通信。如线程池等。
饿汉与懒汉的区别
前者在类装载时就实例化,后者只有在第一次被使用时才实例化。 (饿汉的优点是避免线程同步问题,缺点是即使没用到这个实例还是会加载) (懒汉的优点是实现了懒加载,但需要解决线程安全问题!)
原型模式(Prototype)
原型模式可以通过一个对象实例确定创建对象的种类,并且通过拷贝创建新的实例。总得来说,原型模式实际上就是从一个对象创建另一个新的对象,使新的对象有具有原对象的特征。
原型模式是一种应用及其广泛的设计模式,Clone也是一种十分常见的操作,以至于在Java中,终极父类Object将Clone方法作为了所有类应具有的基本功能,并且Java也提供了Cloneable接口
创建一个原型类, 实现Cloneable 接口(该接口里面是空的,只是起标识作用),并重写clone方法(Object 方法里集成了clone方法)
桥接模式一个非常典型的使用便是在Hybrid场景中,native同事会给出一个用于桥接native与H5的模块,一般为bridge.js。
native与H5本来就是互相独立又互相变化的,如何在多个维度的变化中又不引入额外复杂度,这个时候bridge模式便派上了用场,使抽象部分与实现部分分离,各自便能独立变化。
这里另举一个应用场景,便是UI与其动画类,UI一般会有show的动作,通常便直接显示了出来,但是我们实际工作中需要的UI显示是:由下向上动画显示,由上向下动画显示等效果。
这个时候我们应该怎么处理呢,简单设计一下:
编程题
解压字符串
问题:
如 aa 解压成 2[a],即把连续相同的字符串压缩成 k[abc] 的形式,现实现一个算法来解压字符串
输入 2[a]2[b],则输出 aabb
输入2[ab3[c]a],则输出 abcccaabccca
解答:
该题目主要考察递归的思想,字符串被压缩成 k[abc] 的形式,则将其解压还原成 k 个 abc 的字符序列,那么我们的主要思路就是找到字符串中哪些字符被压缩(即有多少个 k[abc] 形式的字符),然后将其解压还原出来。那么该问题就转化成找 k[abc] 序列
算法思路就是:
对于字符串每一个字符 i:
1. 如果当前字符是数字,就使用 num 记录起来(num = num*10 + s[i] - ‘0)’,记作 k,然后遍历下一个字符
2. 如果 num > 0,下一个字符不是 ‘[‘,则代表前面数字字符是直接输入的,则将 num 序列输出,num 置 0,并将当前字符也输出
3. 如果 num > 0,下一个字符是 ‘[‘则代表该处可能是被压缩的字符串,则从此处开始向后寻找字符,一直到寻找到与它对应的 ‘]’ 结束,若找得到 ‘]’,则将找到的字符序列输出 num 次,若找不到,代表该处并不是压缩的,直接输出num 和 ‘[’ 和找到的字符串
4. 如果 num <= 0,且该字符不是 ‘[‘,则直接输出
通过看以上分析,如果并不能看出整体思路,可以尝试举一些例子来手动模拟过程。下面以 2[ab3[c]a] 为例子来说明该过程:
String = 2[ab3[c]a],length = 10
初始时 num = 0
1. 遍历第一个字符 2,由于它是数字,则num 记录,此时 num = 2
2. 遍历第二个字符 [,因为当前 num = 0 > 2,则需要向后查找 [] 的序列
2.1 第三个字符 a,不是],直接输出,此时字符序列是 a
2.2 第四个字符是b,不是],直接输出,此时字符序列是 ab
2.3 第5个字符是 3,是数字,则用 num 记录,此时 num = 3
2.4 第6个字符是 [,当前 num = 3 > 0,则又需要向后查找 [] 的序列
2.4.1 第 7 个字符是 c,不是 ],直接输出,此时字符序列是 c
2.4.2 第 8 个字符是 ],则找到了,此时结束了,返回字符序列 c
2.5 找到的序列是 c,因此输出 3 * c,此时序列是 abccc
2.6 第 9 个字符是 a,直接输出,此时序列是 abccca
2.7 第 10 个字符是 ],找到结束了,返回字符序列 abccca
3. 找到的序列是 abccca,则输出 2 * abccca,则输出 abcccaabccca
4. 此时已经到达了字符串的尾端,则解压完毕
从上述过程,我们可以看到,我们在寻找 [] 内的序列时,可能内层还嵌套了 [],而且每一次 num 都可能不相同,因此我们需要使用递归的方式来解决该问题
以下是我用 Java 写的代码,不好的地方请指出,另外我使用了两个全局变量,这是我觉得不太好的地方,如果你们更好的想法,可以提出来大家交流交流。
public class UnzipString {
private int index = 0;
private int level = 0;
public String unzip(String str) {
index = 0;
level = 0;
return unzipString(str);
}
public String unzipString(String str) {
// 判断字符串是否为空
if (str == null || "".equals(str)) {
return "";
}
// 用于记录我递归进入函数时,当前第一个字符是原字符的第几个位置
int beginIndex = index;
StringBuilder buffer = new StringBuilder();
int length = str.length();
char[] s = str.toCharArray();
int num = 0;
for (int i = 0; i < length; i++, index++) {
// 如果当前字符是数字,是数字则用 num 记录
if (s[i] >= '0' && s[i] <= '9') {
num = num * 10 + (s[i] - '0');
// 如果当前 num 大于 0 ,而数字后面不是 [,则 num 和字符属于直接输出的
} else if (num > 0 && s[i] != '[') {
buffer.append(num);
buffer.append(s[i]);
num = 0;
// 如果当前 num > 0,且num 后面接的是 [,则开始递归寻找 [] 的字符
} else if (num > 0 && s[i] == '[') {
level++;
index++;
String compressStr = unzipString(str.substring(i + 1));
i = index - beginIndex;
// 判断是否因为有匹配的 ']' 而退出,若不是,则代表该字符串并不是压缩的,而是直接输出的
if (i < length && s[i] == ']') {
for (int j = 0; j < num; j++) {
buffer.append(compressStr);
}
} else {
buffer.append(num);
buffer.append('[');
buffer.append(compressStr);
}
level--;
num = 0;
// 如果当前是有递归进入寻找 [] 序列,且当前字符是 ],则代表该层的序列已找到,可返回
} else if (level > 0 && s[i] == ']') {
return buffer.toString();
} else {
buffer.append(s[i]);
}
}
return buffer.toString();
}
}
分析获取课程信息的方式
分析一下方式的优缺点
1. 抓取学校相关网页的信息
2. 使用教务管理系统接口 API
3. 直接从数据库获取
该题目是论述题,我是从这些方式的特点从开发、维护和数据隐私的方面论述的。具体就不写了,大家可以讨论一下自己的看法
密码是否满足
题目要求:
1. 密码长度要在 8-16位
2. 密码至少要包含大写、小写、特殊符号(aaa@qq.com#$%^&*) 、数字三种
3. 不能有连续相同字符串
解答:
该题目主要考察的是正则表达式的使用,如果熟悉正则表达式,则使用正则匹配则可完成以上要求的判断,若是不会,也可以使用比较基础的方法去比较。
具体正则的知识点和Java Pattern 和 Matcher 的用法自行去百度或者 google 吧。
以下是我的代码
private Pattern upcaseP = Pattern.compile("[A-Z]");
private Pattern lowcaseP = Pattern.compile("[a-z]");
private Pattern specChapP = Pattern.compile("[aaa@qq.com#$%^&*]");
private Pattern numP = Pattern.compile("\\d");
private Pattern sameStrP = Pattern.compile("([aaa@qq.com#$%^&*]+)\\1");
public boolean checkPassword(String password) {
// 判断密码长度是否在8-16位
int length = password.length();
if (length < 8 || length > 16) {
return false;
}
// 记录大写、小写、特殊字符、数字有多少匹配
int matches = 0;
Matcher upcaseM = upcaseP.matcher(password);
Matcher lowcaseM = lowcaseP.matcher(password);
Matcher specCharM = specChapP.matcher(password);
Matcher numM = numP.matcher(password);
if (upcaseM.find()) {
matches++;
System.out.println("大写匹配");
}
if (lowcaseM.find()) {
matches++;
System.out.println("小写匹配");
}
if (specCharM.find()) {
matches++;
System.out.println("特殊字符匹配");
}
if (numM.find()) {
matches++;
System.out.println("数字匹配");
}
if (matches < 3) {
return false;
}
// 判断连续相同字符串
Matcher sameStrM = sameStrP.matcher(password);
if (sameStrM.find()) {
return false;
} ··
return true;
}
}