基本信息
时间:2019年7月17日14点30
地点:深圳市,罗湖区,爵士大厦23层,17-18室
公司:中科软科技股份有限公司
职位:Java开发工程师
面试官:黄利刚
办公环境:办公环境很一般,估计不到30㎡坐满了人,每个人一个格子桌连在一起,脑补下大学机房的环境,可能更紧凑。
面试流程:给HR看简历,可能是根据简历给你发试卷和基本信息调查表。特别注明试卷和调查表、答案纸是分开的,不是直接填在试卷上。写完答案以后HR就叫了面试官进行面试,这里说下这位面试官。虽然看起来他技术很厉害(谢顶),但是说话有气无力,声音非常小而且普通话也不标准。声音小到我每个问题都听不见不得不提醒他,体验非常的差。
试卷
这里夸一下这家公司,试卷比较正常,都是基础的题目,一共分为三大类:
- 选择题,考察基本语法和人脑运行Java代码
- 简答题,考察框架知识,以及基本的常用的Api
- 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
| Studnet stu = new Student(); Class cls = stu.getClass();
Class stu = Student.class;
Class<?> clz = Class.forName("fs.Student");
|
总结:
- 第一种对象都有了,还要反射干嘛?
- 需要导包,否则编译错误。
- 一般是通过forName,参数是一个类的路径,可以写在配置文件里。
资料:Java基础之—反射(非常重要)
拓展知识:
4.反射怎么获取公私有方法、字段?
获取方法以及调用思路如下:
- 获得Class对象,通过上述的三种方法即可,推荐使用:
Class.forName("xxx.student")
- 通过Class对象,生成并强转成对象实例:
(Student) aClass.newInstance();
- 通过Class对象,来获取方法数组:
aClass.getMethods()
- 通过方法数组遍历出所有的方法,一般使用
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; 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<?> aClass = Class.forName("reflect.Student");
Student stu = (Student) aClass.newInstance();
Method[] methods = aClass.getMethods();
for (Method m : methods) { System.err.println("公有方法:" + m.getName()); }
|
4.2 通过getDeclaredMethods() 遍历所有公开/私有方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| Class<?> aClass = Class.forName("reflect.Student");
Method[] declaredMethods = aClass.getDeclaredMethods();
for (Method dm : declaredMethods) { System.err.println("私有方法:" + dm.getName()); }
|
4.3调用指定共有方法
- 得到class对象,通常使用Class.forName()
- 通过class对象的newInstance() 得到一个实例,并把这个实例强转成你需要的对象
- 通过class对象的getMethod(),拿到你要调用的方法;第一个参数是需要调用的方法名,第二个是该方法的参数类型的Class。如果方法没有参数,则应该填写null,比如getName()就没有参数
- 通过指定方法的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<?> aClass = Class.forName("reflect.Student"); Student student = (Student) aClass.newInstance(); student.setName("赵云");
Method say = aClass.getMethod("say", String.class); 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(); } }
|
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<?> aClass = Class.forName("reflect.Student"); Student student = (Student) aClass.newInstance(); student.setName("赵云");
Method mySay = aClass.getDeclaredMethod("mySay",null);
mySay.setAccessible(true);
mySay.invoke(student); } catch (ClassNotFoundException | InstantiationException | IllegalAccessException | NoSuchMethodException | InvocationTargetException e) { e.printStackTrace(); } }
|
4.5 调用私有字段Field
通过aClass.getDeclaredField("mySex");
拿到私有字段
给字段设置权限,允许访问mySex.setAccessible(true);
给私有字段赋值: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<?> 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(); } }
|
4.6 调用私有构造函数Constructor
- 拿到class
- 通过
aClass.getDeclaredConstructor(String.class, int.class, Boolean.class);
拿到私有构造函数
- 使用
setAccessible()
设置权限
- 使用步骤2创建好的私有构造函数,用
newInstance("兰陵王", 30, true)
创建实例并强转类型
- 打印测试结果
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