01-深圳市罗湖区-中科软面试总结

基本信息

时间:2019年7月17日14点30

地点:深圳市,罗湖区,爵士大厦23层,17-18室

公司:中科软科技股份有限公司

职位:Java开发工程师

面试官:黄利刚

办公环境:办公环境很一般,估计不到30㎡坐满了人,每个人一个格子桌连在一起,脑补下大学机房的环境,可能更紧凑。

面试流程:给HR看简历,可能是根据简历给你发试卷和基本信息调查表。特别注明试卷和调查表、答案纸是分开的,不是直接填在试卷上。写完答案以后HR就叫了面试官进行面试,这里说下这位面试官。虽然看起来他技术很厉害(谢顶),但是说话有气无力,声音非常小而且普通话也不标准。声音小到我每个问题都听不见不得不提醒他,体验非常的差。

试卷

这里夸一下这家公司,试卷比较正常,都是基础的题目,一共分为三大类:

  1. 选择题,考察基本语法和人脑运行Java代码
  2. 简答题,考察框架知识,以及基本的常用的Api
  3. SQL题,给两张表,然后根据表的内容来手写SQL

考题回忆

1.选择题记不住,印象最深的就是让你人脑运行Java,考你Java的作用域,所以不写总结

2. String和StringBuffer以及StringBuilder,哪个效率高?为什么?

我之前仅知道StringBuffer快,但是为什么快我不知道,更没有通过StringBuilder。下面贴出网上的答案:

在 Java 中字符串属于对象,Java 提供了 String 类来创建和操作字符串。需要注意的是,String的值是不可变的,这就导致每次对String的操作都会生成新的String对象,这样不仅效率低下,而且大量浪费有限的内存空间

StringBuilder 类在 Java 5 中被提出,它和 StringBuffer 之间的最大不同在于 StringBuilder 的方法不是线程安全的(不能同步访问)。

三者的区别

小结:

(1)如果要操作少量的数据用 String;

(2)多线程操作字符串缓冲区下操作大量数据 StringBuffer;

(3)单线程操作字符串缓冲区下操作大量数据 StringBuilder。

3.反射是什么?如何用反射?反射实例化类的3个常用方法?

我仅了解反射的原理,实际方法也只是学习的时候临摹了几次,所以不记得如何使用反射,下面贴出完整答案:

高端点说:动态加载一个类对象,然后得到这个类对象的公私有方法、字段。那么问题是怎么加载这个类对象?所以就要用到反射的实例方法,一共有三种:

1
2
3
4
5
6
7
// 1. 通过Object的getClass()加载一个实例对象
Studnet stu = new Student();
Class cls = stu.getClass();
// 2. 通过类名
Class stu = Student.class;
// 3. 通过类的具体路径,加载
Class<?> clz = Class.forName("fs.Student");

总结:

  1. 第一种对象都有了,还要反射干嘛?
  2. 需要导包,否则编译错误。
  3. 一般是通过forName,参数是一个类的路径,可以写在配置文件里。

资料:Java基础之—反射(非常重要)

拓展知识:

4.反射怎么获取公私有方法、字段?

获取方法以及调用思路如下:

  1. 获得Class对象,通过上述的三种方法即可,推荐使用:Class.forName("xxx.student")
  2. 通过Class对象,生成并强转成对象实例:(Student) aClass.newInstance();
  3. 通过Class对象,来获取方法数组:aClass.getMethods()
  4. 通过方法数组遍历出所有的方法,一般使用forEach遍历
被操作的对象 Student.class
点击查看完整Student代码
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
package reflect;
public class Student {
String name;
int age;
Boolean mySex; // false 女;true 男
private Student(String name, int age, Boolean mySex) {
this.name = name;
this.age = age;
this.mySex = mySex;
}
public void say(String str) {
if (str.equals(""))
System.err.println("你什么都没说.");
System.out.println(str);
}
private void mySay() {
System.out.println("这是私有方法");
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", age=" + age +
", mySex=" + mySex +
'}';
}
}
4.1 通过getMethods() 遍历所有公开方法
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
// 取得Class
Class<?> aClass = Class.forName("reflect.Student");
// 通过Class生成对象
Student stu = (Student) aClass.newInstance();
// 通过Class取到公开方法数组
Method[] methods = aClass.getMethods();
// 遍历方法数组,得到所有的方法名
for (Method m : methods) {
System.err.println("公有方法:" + m.getName());
}
// 打印结果
/*
公有方法:toString
公有方法:getName
公有方法:setName
公有方法:getAge
公有方法:say
公有方法:setAge
公有方法:wait
公有方法:wait
公有方法:wait
公有方法:equals
公有方法:hashCode
公有方法:getClass
公有方法:notify
公有方法:notifyAll
*/
4.2 通过getDeclaredMethods() 遍历所有公开/私有方法
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// 取得Class
Class<?> aClass = Class.forName("reflect.Student");
// 包含了私有和公有方法
Method[] declaredMethods = aClass.getDeclaredMethods();
// forEach遍历
for (Method dm : declaredMethods) {
System.err.println("私有方法:" + dm.getName());
}
// 打印结果
/*
私有方法:toString
私有方法:getName
私有方法:setName
私有方法:mySay
私有方法:getAge
私有方法:say
私有方法:setAge
*/
4.3调用指定共有方法
  1. 得到class对象,通常使用Class.forName()
  2. 通过class对象的newInstance() 得到一个实例,并把这个实例强转成你需要的对象
  3. 通过class对象的getMethod(),拿到你要调用的方法;第一个参数是需要调用的方法名,第二个是该方法的参数类型的Class。如果方法没有参数,则应该填写null,比如getName()就没有参数
  4. 通过指定方法的invoke(),第一个参数传入该方法的对象,第二个参数是该方法的参数,如果没有则不填写
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
@Test
public void function1() {
try {
// 得到class
Class<?> aClass = Class.forName("reflect.Student");
// 创建实例,并强转成学生对象
Student student = (Student) aClass.newInstance();
student.setName("赵云");

// 得到指定的方法
Method say = aClass.getMethod("say", String.class);
// 第一个参数:方法名;第二个参数:方法的参数类型。这里的getName()它不需要参数,所以为null
Method name = aClass.getMethod("getName", null);

// 传入方法的对象,以及要打印的字符串,并调用方法
say.invoke(student, "你好,测试invoke方法");
System.err.println("名字:" + name.invoke(student));
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | NoSuchMethodException | InvocationTargetException e) {
e.printStackTrace();
}
}
// 打印结果
/*
你好,测试invoke方法
名字:赵云
*/
4.4 调用私有方法

因为是私有方法,和调用公开方法的区别是需要使用method.setAccessible(true),否则无法调用。

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
@Test
public void function2() {
try {
// 得到class
Class<?> aClass = Class.forName("reflect.Student");
// 创建实例,并强转成学生对象
Student student = (Student) aClass.newInstance();
student.setName("赵云");

// 得到指定的方法
Method mySay = aClass.getDeclaredMethod("mySay",null);

// 修改权限,否则不能调用私有方法并报错:IllegalAccessException
mySay.setAccessible(true);

// 传入方法的对象,以及要打印的字符串,并调用方法
mySay.invoke(student);

} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | NoSuchMethodException | InvocationTargetException e) {
e.printStackTrace();
}
}
/* 打印输出

这是私有方法
* */

4.5 调用私有字段Field

  1. 通过aClass.getDeclaredField("mySex");拿到私有字段

  2. 给字段设置权限,允许访问mySex.setAccessible(true);

  3. 给私有字段赋值:mySex.setAccessible(true);

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
@Test
public void function3() {

try {
// 得到class
Class<?> aClass = Class.forName("reflect.Student");
// 创建实例,并强转成学生对象
Student student = (Student) aClass.newInstance();
// 获取私有的字段
Field mySex = aClass.getDeclaredField("mySex");
// 设置权限,允许访问私有字段
mySex.setAccessible(true);
// 私有字段赋值
mySex.set(student, false);

System.err.println(student.toString());

} catch (InstantiationException | ClassNotFoundException | NoSuchFieldException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
/* 打印结果
Student{name='null', age=0, mySex=false}
*/

4.6 调用私有构造函数Constructor

  1. 拿到class
  2. 通过aClass.getDeclaredConstructor(String.class, int.class, Boolean.class);拿到私有构造函数
  3. 使用setAccessible()设置权限
  4. 使用步骤2创建好的私有构造函数,用newInstance("兰陵王", 30, true)创建实例并强转类型
  5. 打印测试结果
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
@Test
public void function4() {
// 得到class
Class<?> aClass = null;
try {
aClass = Class.forName("reflect.Student");
// 创建私有构造函数,并指定参数的类型
Constructor<?> declaredConstructor = aClass.getDeclaredConstructor(String.class, int.class, Boolean.class);
// 设置权限,允许反射
declaredConstructor.setAccessible(true);
// 创建实例,并强转成Student对象
Student 兰陵王 = (Student) declaredConstructor.newInstance("兰陵王", 30, true);

System.err.println(兰陵王.toString());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | NoSuchMethodException | InvocationTargetException e) {
e.printStackTrace();
}
}
/*
打印结果:Student{name='兰陵王', age=30, mySex=true}
*/

参考资料:

Java高级特性——反射 - 简书

反射面试题 - 请了解下 - 简书

总结

如果比较空的话,可以去做做题,体验下面试氛围,获得一点面试的经验。事实上我也确实拿到了一点面试经验,并且在后面的面试中派上用场了。用我表弟的话说:“面向经验面试”。—–2019年7月19日20:34:51