整理最近面试遇到的一些问题
最近在求职,陆陆续续会有很多面试,现在凭想象力记下来,总结自己面试中回答不太好的地方:
18/08/13 周一 上午 ,云途
1.如何将给定的字符串内容反转?即倒序
该问题,当时回答不是很好,回来后自己敲了下,发现自己不足.以下列出四种解决方案:
方案一:for循环遍历, 根据索引从后向前取字符串中的字符:
public String reverse2(String value){ if (value == null || value.length() < 2){ return value; } String newValue = ""; for (int i = (value.length() - 1); i >= 0; i--) { newValue += value.charAt(i); } return newValue; }
方案二:将字符串转为char数组,然后倒叙取出每个字符,跟方案一类似:
public String reverse3(String value){ if (value == null || value.length() < 2){ return value; } String newValue = ""; char[] charArray = value.toCharArray(); for (int i = (charArray.length- 1); i >= 0; i--) { newValue += charArray[i]; } return newValue; }
方案三:先将字符串转为StringBuilder 或者StringBuffer ,再利用二者的实例方法reverse,实现倒叙后 转为toString() ,这种方式最方便,不需要自己写方法
public String reverse3(String value){ if (value == null || value.length() < 2){ return value; } return new StringBuffer(value).reverse().toString(); }
方案四:利用递归,将传入的字符串首个字符拼到末尾,同时截取字符串,依次递归,直到字符串长度为1
public String reverse(String value){ if (value == null || value.length() < 2){ return value; } return reverse(value.substring(1)) + value.charAt(0); }
2.现有两个ArrayList,需要将两个集合元素全部添加到一个集合中, 并且去除重复的元素,如何实现?
思路:首先得到1个总的ArrayList,然后将ArrayList转为HashSet,利用HashSet的特性(元素不可重复)完成去重;
@org.junit.Test public void name() { List<Integer> list1 = new ArrayList<>(); List<Integer> list2 = new ArrayList<>(); list1.add(1); list1.add(2); list1.add(3); list2.add(2); list2.add(3); list2.add(4); list1.addAll(list2); System.out.println(list1); //将List转为Set HashSet<Integer> set = new HashSet<>(list1); System.out.println(set); //也可以通过同样的方式将Set转为List System.out.println(new ArrayList<>(set)); }
原理是构造器可以接收Collection类型的对象:
3.如何得到字符串去重后的长度? 例如:hello 去重后 是 helo ,长度也就是4;
@org.junit.Test public void myTest() { String str = "Hello"; Set<Object> hashSet = new HashSet<>(); for (int i = 0; i < str.length(); i++) { hashSet.add(str.charAt(i)); } System.out.println(hashSet.size()); }
也可以通过ArrayList完成,但是需要加一个contains的判断:
@org.junit.Test public void myTest2() { String str = "Hello"; List<Object> list = new ArrayList<>(); for (int i = 0; i < str.length(); i++) { if (!list.contains(str.charAt(i))){ list.add(str.charAt(i)); } } System.out.println(list.size()); }
4.上个问题中 ,contains(object)方法,判断list中是否已经存在某个元素,contains方法判断是否为同一个元素的原理是什么?
答: ArrayList中有一个方法: indexOf(object) 如果object存在与list中,则返回大于相应的索引,否则返回-1
如果indexOf()返回的值大于-1,则表示已存在,某则不存在;
下面是contains方法与indexOf方法的源代码:
public boolean contains(Object o) { return indexOf(o) >= 0; }
public int indexOf(Object o) { if (o == null) { for (int i = 0; i < size; i++) if (elementData[i]==null) return i; } else { for (int i = 0; i < size; i++) if (o.equals(elementData[i])) return i; } return -1; }
5.写出HashMap的迭代
@org.junit.Test public void testHashMap() { Map<Object, Object> map = new HashMap<>(); map.put("name","rye"); map.put("location","GuangZhou"); Set<Map.Entry<Object, Object>> entrySet = map.entrySet(); for (Map.Entry<Object, Object> entry : entrySet) { System.out.println("key:"+entry.getKey() + " , value:"+entry.getValue()); } }
6.Spring中@Resourse 与 @Autowired的区别:
注解的来源方面:
@Resourse 是javax.annotation下的注解:
@Autowired是org.springframework.beans.factory.annotation下的注解
注解注入Bean的规则方面:
@Service public class Demo{ } //此时会寻找name为demo的Bean ,如果找不到就会报错 @Resource(name="demo") Demo demo; //此时会寻找类型为Demo的bean,如果找不到或者找到了多于1个以上的Bean会抛错 @Resource(type=Demo.class) Demo demo; //如果不指定Resource属性,则首先按照name查找,找不到再按type查找,再找不到就会抛错 @Resource Demo demo;
//=====================================================================
//默认按照类型查找,如果找不到或找到不止1个就会报错 @Autowired Demo demo;
//Autowired本身是没有name属性的,同时如果根据type找不到是不会再进行name的匹配的,不像Resource那样帮你做一个切换 /* 那么如果我就想用Autowried,而且还必须根据name注入,那应该怎么办呢? 此时仍有解决方案,需要搭配@Qualifier注解一起组合使用, */ @Autowired @Qualifier("demo") Demo demo;
值得一提的是大多数情况下,通过注解建立的Bean,bean的name都为类名的首字母小写,但是并不是所有情况,bean的name都是类名首字母小写;
例如:
@Service
public class Demo{}
这时生成的name就为首字母小写,也就是:demo;
但是下面这种情况生成bean的name并不是首字母小写
@Service
public class DEmo{}
此时生成的name为: DEmo 而不是dEmo
为什么会这样呢?此时需要查看下源码
该方法名称也很直观表达出要构建默认的BeanName,再进一步看下 buildDefaultBeanName(definition, registry),做了什么事:
由上图代码可知,先获取到该Bean的类名称(非全限定名),最终调用了Introspector.decapitalize(shortClassName) 处理类名,下面看下这个方法的细节:
这也就解释了,为什么当类名为DEmo的时候,相应的beanName为DEmo,而不是dEmo
7.请说下Java中的动态代理
被问到这个问题,当时还是比较慌的,这块内容之前学习的时候有钻研过,但是后来工作中就很少去琢磨动态代理了
关于动态代理的主要作用,我理解的是代理类(中介)对被代理类的一个增强
假如:
现在有一个车票的接口,里面暂时只有1个购票方法,如下:
public interface ITicket { /** * 买票 */ void buyTicket(); }
旅客(class Passenger)是被代理类,实现了包含购票方法(buyTicket)的接口(interface ITicket) ;
public class Passenger implements ITicket { private String name; private long idCardNumber; public Passenger(String name, long idCardNumber) { this.name = name; this.idCardNumber = idCardNumber; } public String getName() { return name; } public long getIdCardNumber() { return idCardNumber; } @Override public void buyTicket() { System.out.println("姓名为:"+this.name+",身份证号码为:"+this.idCardNumber+"的旅客通知,请扫码付钱..."); System.out.println("...1S后拿到了票"); } }
旅客们更多的是关心买票的业务逻辑,至于买票之前的排队,旅客们都不想操作,他们宁愿找人排队,买到票后付点小费;
所以此时就有个黄牛(代理类 class Scalper),他同样也实现了ITicket接口,拥有了买票的功能;同时在买票前排了队,买票成功后收取消费:
但是你让我帮你买票,你得给我你得身份证呀,所以在黄牛类中需要拥有乘客的一个实例对象,如下:
public class Scalper implements ITicket { //代理类中包含了1个被代理类的实例 private Passenger passenger; public Scalper(Passenger passenger){ this.passenger = passenger; } @Override public void buyTicket() { System.out.println("排队很久排到了.."); passenger.buyTicket(); System.out.println("乘客:"+passenger.getName()+" 请付20元"); } }
这样一来只需要将身份证交给黄牛即可帮你排队,买票(付钱肯定还是需要乘客自己付的),收取小费,下面是test:
public class BuyTicketTest { @Test public void testByTicket() { Passenger rye = new Passenger("Rye", 10010L); Scalper scalper = new Scalper(rye); scalper.buyTicket(); } }
此时执行结果:
排队很久排到了..
姓名为:Rye,身份证号码为:10010的旅客通知,请扫码付钱...
...1S后拿到了票
乘客:Rye 请付20元小费!
通过上述举例,可以看出,代理类对被代理类中的方法进行了增强 , 代理类的主要目的也是如此
由于描述能力有限,动态代理的原理并没有组织好合适的语言(后续会补上),仅仅是通过1个生活中常见的例子,来解释了下什么是代理;
关于JDK动态代理这块更深的探讨以及原理请参阅此篇博客,个人觉得挺详细,深入浅出:https://www.cnblogs.com/gonjan-blog/p/6685611.html
8.redis中常见的数据结构有哪些?
共五种,并列举其中各个类型的简单的命令
取:get rye
存储:hset myhash name rye //如果新增的key-value中,key不在在则返回1,如果key存在,则更新value的值,返回0
获取:
获取hash中指定key的值: hget myhash name //存在则返回相应的值,不存在则返回nil
获取hash中所有key-value: hgetall myhash //如果集合不为空返回所有key-value,如果为空则返回提示empty list or set
移除:hdel myhash name //如果name存在则从myhash中移除,并返回1;否则返回0
按照排名取元素: zrange my-score 0 -1 withscores ==> english 70 math 100 //按照score升序的顺序返回集合中的所有元素,并返回元素对应的分数,命令中withscores选项是可选的
取出某个范围内的所有元素: zrangebyscore my-score 80 100 withscores ==> math 100 //取出某个分数值范围内的所有元素
获取某个元素的排名: zrank my-score math ==> 2 //排序规则是按照分数递增,因此math在有序集合中的排名是2
删除某个元素: zrem my-score english ==>1 //移除成功返回1,反之返回0
9.注解的原理是什么?
10.ArrayList的本质是什么,什么情况下会进行扩容?每次扩容的大小是多少呢?
List<String> list = new ArrayList(20);该语句执行了几次扩容?
11.请书写出工厂设计模式的实现
12.请描述Spring框架中的AOP,AOP的原理是什么,AOP在项目中的的应用范围有哪些
持续更...