java 基础知识总结

154 阅读6分钟

1. ArrayList和LinkedList区别,ArrayList是否会越界

ArrayList底层为动态数组,主要优势在于通过下标(随机查询)进行查询而LinkedList底层是双向链表结构的查询是需要便利链表的。但是如果是获取第一个或者最后一个元素这两个集合是没有区别的。因为LinkedList记录了第一个元素和最后一个元素并不需要去遍历链表。当然我们平时说的 ArrayList查询快 插入慢这里的插入指的是在中间插入而是需要维护下标索引(插入时进行扩容也会慢是因为要及进行数据 迁移)。而LinkedList中间插入的时候因为其是双向链表可以不连续只需要维护 befor 和after,不用维护索引所以查询没有ArrayList快。但是同样的 元素 linkedList比ArrayList占用的空间大。 另外lenkindlist还额外的实现了Deque(双端队列)接口,所以linkendList还可以当作队列来实现, ArrayList初始化大小为10 ,后续扩容为原始大小的1.5倍,linkendList 没有初始化大小它是一个链表直接存储。

2. ArrayList和HashSet 区别

  1. HashSet 是无序且不重复的是散列结构不是线性结构所以查询效率相对不错,并且线程不安全的。每次添加需要判断hashCode是否存在。
  2. ArrayList 可以存重复的值且是有序的,通过可变大小的数据来实现允许所有元素。
  3. 他们都不是线程安全的,但是HashSet没有ArrayList快因为它需要维护去重

3. 为什么redis 快

  1. 纯内存中的操作而且还是单线程(不用去来回切换上下文)
  2. IO多路复用
  3. 高效的数据结构(简单动态字符串SDS,双向链表,哈希表,跳跃表,整数数组,压缩列表)

4. bean 的生命周期(如何创建一个bean)

  1. 根据路径找到类创建bean定义
  2. 推断构造方法(默认都是使用无参构造,当然可以通过@Autowired指定具体的构造方法)
  3. 实例化类
  4. 依赖注入
  5. 回调Aware方法
  6. 初始化前(@PostConstruct一些业务场景比如查询数据库...)
  7. 初始化(调用InitializingBean他的afterPropertiesSet)
  8. 初始化后(这里如果有Aop的话会去走aop产生代理对象将代理对象放到单例池)
  9. 将创建出来的bean 放到IOC 容器中去(单例池也可以理解为一个map集合中去)
  10. 使用bean
  11. spring容器停止销毁bean

5.创建线程池7大参数

  1. corePoolSize核心线程数
  2. maxPoolSize 最大线程数
  3. keepAliveSeconds 线程空闲存活是时间
  4. queueCapacity队列长度
  5. 拒绝策略
  6. 线程工厂
  7. 时间单位(存活时间)

6. ArrayList 多属性去重

使用 stream 中的 filter+ 函数式方法进行去重

List<User> list=new ArrayList<>();
User user = new User();
user.setPassword("1242");
user.setNickName("xkk");
user.setType("sucess");
User user1 = new User();
user1.setPassword("1242");
user1.setNickName("xkk");
user1.setType("faile");
User user2 = new User();
user2.setPassword("666");
user2.setNickName("xqq");
user2.setType("dis");
list.add(user);
list.add(user1);
list.add(user2);
list.stream().filter(distinctByKey(new Function<User, Object>() {
    //这里为了方便理解没有使用lambada表达式
     @Override 
     public Object apply(User user) {
         //这里使用密码和昵称进行去重
        return user.getPassword()+":"+user.getNickName();    
}
 })).collect(Collectors.toList());
 
public static <T> Predicate<T> distinctByKey(Function<? super T, Object> keyExtractor) {
    Map<Object, Boolean> seen = new ConcurrentHashMap<>();
    return new Predicate<T>() {
        @Override
        public boolean test(T t) {
            return seen.putIfAbsent(keyExtractor.apply(t), Boolean.TRUE) ==null;
        }
    };
}

重载和重写的区别

重载:发生在同一个类中,方法名相同,参数类型不同,个数不同,顺序不同。 方法返回值和修饰符不可以不同,发生在编译时(报错)。

重写:发生在父子类中,方法名参数列表必须相同,返回值范围小于等于父类,抛出的异常范围小于等于父类,访问修饰符范围大于等于父类。如果父类的方法修饰符为 private 则子类就不能重写该方法。

ConcurrentHashMap的扩容机制

1.7 版本 :

  1. 是基于Segment分段实现的
  2. 每个Segment相对于一个小型的hashMap
  3. 每个Segment内部进行扩容,和hashMap的扩容逻辑类似
  4. 先生成新数组,然后将数据转移到新的数组
  5. 扩容的判断也是每个Segment内部但单独判断的,判断是否超过阈值

1.8 版本 :

  1. 不再基于Segment分段实现
  2. 当某个线程进行put时,如果发现正在扩容那么改下安城进行一起扩容
  3. 支持多个线程同时扩容
  4. 扩容之前也是先生成一个新的数组
  5. 在转移数组时,先将原数组进行分组,将每组分给不同的线程来讲进行元素的转移,每个线程负责一组或者多组的元素转移工作。

如何理解springboot中的starter

定义了一个starter的jar包,写一个配置类 @Configuration 将需要用到的bean 定义配置进来进来然后在starter的META-INF/spring.factories中写入该配置类,spring会按照约定来加载该配置类

redis 缓存淘汰策略

8种策略

  • volatile-lru,针对设置了过期时间的key,使用lru算法进行淘汰。
  • allkeys-lru,针对所有key使用lru算法进行淘汰。
  • volatile-lfu,针对设置了过期时间的key,使用lfu算法进行淘汰。
  • allkeys-lfu,针对所有key使用lfu算法进行淘汰。
  • volatile-random,从所有设置了过期时间的key中使用随机淘汰的方式进行淘汰。
  • allkeys-random,针对所有的key使用随机淘汰机制进行淘汰。
  • volatile-ttl,删除生存时间最近的一个键。
  • noeviction(默认),不删除键,值返回错误。

主要是4种算法,针对不同的key,形成的策略。 算法:

  • lru 最近很少的使用的key(根据时间,最不常用的淘汰)
  • lfu 最近很少的使用的key (根据计数器,用的次数最少的key淘汰)
  • random 随机淘汰
  • ttl 快要过期的先淘汰

java IO 流有哪些

  • 字节流 :字节流按 8 位传输以字节为单位输入输出数据。
  • 字符流 :字符流按 16 位传输以字符 为单位输入输出数据

IO 和 NIO

  • IO: 同步阻塞式IO 特点是简单方便处理并发能力低。
  • NIO:New IO 是同步非阻塞IO,是传统IO的升级客户端个服务端通过Channel(通道)通讯,实现多路复用。

HashMap的原理

HashMap是基于Hash算法实现的,我们通过put方法来进行存储,和get方法进行取值。当key 传入时 HashMap会根据key.hashCode计算出key的哈希code 根据哈希code将value保存到bucket 中,当计算 hash值相同时我们称之为hash 冲突,hash 的处理时是使用链表和 红黑树进行存储 value,冲突个数较少时使用链表,反之使用红黑树。

HashSet的原理

HashSet的底层使用的就是HashMap来保存元素,但是hashSet是不允许有重复的key。

float 和 double 的区别是什么?

float 最多可以存储 8 位的十进制数,并在内存中占 4 字节。 double 最可可以存储 16 位的十进制数,并在内存中占 8 字节。