最近几天正在忙于各处奔走,参加各个邀约的公司的面试。在面试的过程中,发现了自己在之前的工作中有很多的不足,在这里做一个小总结。另外,不论这些公司最后是否要我,我要对这几天面试我的同行和 HR 们表示感谢,他们当中有的人我还不知道姓名,和他们的交流中,我也收获了许多,在此衷心表示感谢。
这一周的面试是一件很辛苦,但是又很有收获的一周。与日常工作的开发不一样,我会见到很多人,接触、了解到他们对于职业生涯,软件开发的想法和建议。对于我自己选择公司、职位来说都是很有帮助的。而且通过面试的这一周,我也反思、总结了很多。
这里也写一些对自己的提醒:
(1)一定要准时,即使对方也不在乎你是不是准时到。因为刚去的时候可能是填表,虽然差个一两分钟,甚至五六分钟都无所谓,但这样毕竟不好,也会滋长自己的堕性和懒散的意识,所以比较好的办法就是把手表的时间提前;
(2)看面试的情况要一个对方的电话,咨询面试结果。这一条虽然我写在这里,但我想其实除非面试的过程非常愉快,否则要电话可能面试官会比较反感;
1、写一个线程安全的单例;
以下是基于内部类的一个单例类的写法:
package com.liwei.danli; public class Singleton { private Singleton() { } private static class SingletonHolder { private final static Singleton INSTANCE = new Singleton(); } public static Singleton getInstance() { return SingletonHolder.INSTANCE; } public static void main(String[] args) { Singleton instance1 = Singleton.getInstance(); Singleton instance2 = Singleton.getInstance(); System.out.println(instance1.hashCode()); System.out.println(instance2.hashCode()); } }
2、说说 Object 的 hashCode() 方法和 equals() 方法:
这个问题原文引用下面这篇文章的解答:
Java面试题全集(上) - 骆昊的技术专栏 - 博客频道 - CSDN.NET
http://blog.csdn.net/jackfrued/article/details/44921941
两个对象值相同(x.equals(y) == true),但却可有不同的hash code,这句话对不对?
答:不对。事实上,如果两个对象x和y满足x.equals(y) == true,它们的哈希码(hash code)应当相同。
Java 对于 eqauls() 方法和 hashCode() 方法是这样规定的:
(1)如果两个对象相同( equals() 方法返回 true ),那么它们的 hashCode 值一定要相同;
(2)如果两个对象的 hashCode 相同,它们并不一定相同。
当然,你未必要按照要求去做,但是如果你违背了上述原则就会发现在使用容器时,相同的对象可以出现在 Set 集合中,同时增加新元素的效率会大大下降(对于使用哈希存储的系统,如果哈希码频繁的冲突将会造成存取性能急剧下降)。
笔者:我对这段话的理解,在使用集合存储对象的时候,就会出现问题。因为 hash 算法认为 ,进来一个对象,先通过 hashCode 计算它应该在哪一个位置上,如果这个位置上没有对象,就把这个对象放在这个位置上,如果这个位置上有对象,就通过 equals() 方法去比较,如果为 true ,就不放入这个对象,如果为 false 就放入这个对象,在该位置上形成一个链表结构。(个人理解,正确性有待检验)。
补充:关于 equals() 和 hashCode() 方法,很多 Java 程序员都知道,但很多人也就是仅仅知道而已,在 Joshua Bloch 的大作《Effective Java》(很多软件公司,《Effective Java》、《Java编程思想》以及《重构:改善既有代码质量》是 Java 程序员必看书籍,如果你还没看过,那就赶紧去亚马逊买一本吧)中是这样介绍 equals() 方法的:
首先 equals() 方法必须满足自反性( x.equals(x) 必须返回 true )、对称性( x.equals(y) 返回 true 时, y.equals(x) 也必须返回 true )、传递性( x.equals(y) 和 y.equals(z) 都返回 true 时, x.equals(z) 也必须返回 true )和一致性(当 x 和 y 引用的对象信息没有被修改时,多次调用 x.equals(y) 应该得到同样的返回值),而且对于任何非null值的引用x, x.equals(null) 必须返回 false 。
实现高质量的 equals() 方法的诀窍包括:
1、使用 == 操作符检查"参数是否为这个对象的引用";
2、使用instanceof操作符检查"参数是否为正确的类型";
3、对于类中的关键属性,检查参数传入对象的属性是否与之相匹配;
4、编写完equals方法后,问自己它是否满足对称性、传递性、一致性;
5、重写 equals() 方法时总是要重写 hashCode() 方法;
6、不要将equals方法参数中的Object对象替换为其他的类型,在重写时不要忘掉 @Override 注解。
3、写一段代码,对自定义的对象实现 List 的排序功能。
思路:使用 Collections 工具类的方法 sort() 方法来完成。我们可以查看 Java 的 API 文档。
Collections工具类的sort方法有两种重载的形式,第一种要求传入的待排序容器中存放的对象比较实现Comparable接口以实现元素的比较;第二种不强制性的要求容器中的元素必须可比较,但是要求传入第二个参数,参数是Comparator接口的子类型(需要重写compare方法实现元素的比较),相当于一个临时定义的排序规则,其实就是通过接口注入比较元素大小的算法,也是对回调模式的应用(Java中对函数式编程的支持)。
我暂时忘记了面试的问题是什么了,直接上代码:
先是一个实体类 Student :
package com.liwei.list; public class Student { private String name; // 姓名 private int age; // 年龄 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; } public Student(String name, int age) { super(); this.name = name; this.age = age; } @Override public String toString() { return "Student [name=" + name + ", age=" + age + "]"; } }
然后是我们的测试代码:
package com.liwei.list; import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; import java.util.List; public class Test { public static void main(String[] args) { List<Student> list = new ArrayList<>(); // Java 7 的钻石语法(构造器后面的尖括号中不需要写类型) list.add(new Student("Hao LUO", 33)); list.add(new Student("XJ WANG", 32)); list.add(new Student("Bruce LEE", 60)); list.add(new Student("Bob YANG", 22)); Collections.sort(list, new Comparator<Student>() { @Override public int compare(Student o1, Student o2) { // return o1.getAge() - o2.getAge();// 按照年龄升序排序 // 按照 name 字段升序排序 ( SUN 公司已经帮我们实现了 String 的比较方法) return o1.getName().compareTo(o2.getName()); } }); for(Student s:list){ System.out.println(s.getName()); } } }
4、写一个函数将一个字符串转换为数值。
我回来之后总结了两种方法,分别如下:
遍历字符串的每个字符
(1)使用正则表达式进行判断;(2)利用字符 0- 9 的 ASCII 码是连续的特点。
package com.liwei.mainshi; public class Test01 { public static void main(String[] args) { int num1 = transform1("687124a456ssdffs90"); System.out.println(num1); System.out.println("------愉快的分割线------"); int num2 = transform2("687124a456ssdffs90"); System.out.println(num2); } public static int transform1(String str) { String return_str = ""; for (int i = 0; i < str.length(); i++) { String temp = str.substring(i, i + 1); // char char_str = str.charAt(i); // System.out.println(char_str); if (temp.matches("\d")) { return_str += temp; }else{ break; } } return return_str == "" ? 0: Integer.valueOf(return_str); } public static int transform2(String str){ int result_num = 0; for (int i = 0; i < str.length(); i++) { char charAt = str.charAt(i); int diff = charAt - '0'; if(diff>9){ break; } result_num = result_num*10 +diff; } return result_num; } }
知识点复习:
(1)如何得到一个字符的 ASCII 码?
方法一:转化为 byte 类型,再打印:
@Test public void test04(){ char c = 'A'; byte b = (byte) c; System.out.println(b); // 65 }
方法二:直接使用 int 类型赋值给单个的字符变量:
@Test public void test04(){ char c = 'A'; byte b = (byte) c; System.out.println(b); // 65 // 愉快的分割线 int a = 'A'; System.out.println(a); // 65 }
5、请简单介绍 Java 中的定时器:
可以参考下面的文章进行准备:
java中的几种定时器 - 小崔的博客 - 博客频道 - CSDN.NET
http://blog.csdn.net/cuiran/article/details/5929833
Java Timer 定时器的使用_张一角_新浪博客
http://blog.sina.com.cn/s/blog_5e8782250100l20s.html
6、简单介绍一下 Memcached 。
这里可以参考下面的这篇文章:
memcached简介及java使用方法 - seelye的专栏 - 博客频道 - CSDN.NET
http://blog.csdn.net/seelye/article/details/8511073
关于多线程,可以参考下面的文章。
Java多线程面试题归纳 - 曹海成的专栏 - 博客频道 - CSDN.NET
http://blog.csdn.net/caohaicheng/article/details/38071097
SpringMVC的每个方法都是线程安全的吗?
什么是中间件?这些概念我听都没有听说过。
http 协议和 https 协议有什么不同。
MySQL 的索引类型
MySQL 的约束类型
对于自己的职业生涯要有一个明确的定位,不能太模糊;
学习自己工作中用到的;