整理最近面试遇到的一些问题

最近在求职,陆陆续续会有很多面试,现在凭想象力记下来,总结自己面试中回答不太好的地方:

 

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中常见的数据结构有哪些?

共五种,并列举其中各个类型的简单的命令

String字符串
  存:set name rye
  取:get rye
List列表
  存:
    存入列表的左端:lpush list rye
    存入列表的右端:rpush list sherry
  根据index取:
    从左端取:lindex list 0      结果:rye
  范围取值,一次取多个:
    从左端取:lrange list 0 -1     (-1表示取到最后一位)  结果:rye  sherry
  弹出(删除):
    左端弹出:lpop list  结果:rye
    右端弹出:rpop list  结果:sherry    
 
Set集合(元素不重复)
  存:sadd my_set rye   //添加成功返回1 ,如果已经存在则返回0
  取元素:smembers myset 
  判断元素是否在结合中:sismember myset rye  如果在集合中,则返回1,不在则返回0
  移除元素:srem myset rye  //如果set中包含这个元素则移除并返回1, 否则返回0
    
Hash散列
  hash的value是key-value类型的值,结构如下:
  

  存储: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

 
Zset有序集合:
  存储的也是一个映射,不过不同于Hash的是,存储的是元素与其分数的映射
  
  新增元素:  zadd my-score 100 math 70 english   ==> 2 //其中math,english表示存储的key,而分数相当于value;
                         //zadd 返回成功添加新元素的个数(已经存在元素不会统计),如果元素已经存在则更改分数.

    按照排名取元素: 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在项目中的的应用范围有哪些

 

持续更...

posted @ 2018-08-13 15:02  yi杆烟枪  阅读(205)  评论(0编辑  收藏  举报