牛客网面试题


第一部分 JAVA

一、JVM

  1. 讲一下JVM的1.8之前和1.8之后内存模型?

    为什么需要程序计数器?

    1.JVM内存模型是指JVM的内存区域划分
    2.JVM1.8的修改:
        -修改内容:将方法区移除,添加了元数据区,而且元数据区是在本地内存中,不再受限制于JVM内存的大小,而是和机器内存有关。
        -修改原因:随着现今框架和程序都包含很多依赖,而这些依赖有很多类对象,都存于永久代中,因此这部分内容往往会内存溢出,于是干脆把这部分内容放到堆内存或本地内存中存储。
        -元空间有以下特点:
            a.每个加载器有专门的存储空间。
            b.不会单独回收某个类。
            c.元空间里的对象的位置是固定的。
            d.如果发现某个加载器不再存活了,会把相关的空间整个回收。
        
    3.程序计数器记录当前线程正在执行的字节码的地址或行号。JVM中存在线程切换,主要作用是为了确保多线程情况下JVM程序的正常执行。
    
  2. JVM调优

    怎么调整堆的大小?要修改哪个参数?

    1.堆大小设置:
        -Xms 初始堆大小,
    
  3. 说一下 Java 方法执行的过程,说详细一点。编译、加载(类加载)、执行解释

    1. 加载:在内存中生成一个代表这个类的 java.lang.Class 对象,作为方法区这个类的各种数据的入口
    2. 验证:确保 Class 文件的字节流中包含的信息是否符合当前虚拟机的要求
    3. 准备:正式为类变量(静态变量)分配内存并设置类变量的初始值阶段,即在方法区中分配这些变量所使用的内存空间
    4. 解析:虚拟机将常量池中的符号引用替换为直接引用的过程
       - 符号引用:引用的目标并不一定要已经加载到内存中
       - 直接引用:引用的目标必定已经在内存中存在
    5. 初始化:执行类中定义的 Java 程序代码
       - 执行类构造器<client>方法
       - <client>方法是由编译器自动收集类中的类变量的赋值操作和静态语句块中的语句合并而成的
    
  4. Java 8 新特性

    1. Lambda 表达式:Lambda 允许把函数作为一个方法的参数(函数作为参数传递到方法中)
    2. 函数式接口:指的是一个有且仅有一个抽象方法,但是可以有多个非抽象方法的接口,这样的接口可以隐式转换为 Lambda 表达式
    3. 方法引用:方法引用提供了非常有用的语法,可以直接引用已有Java类或对象(实例)的方法或构造器。与lambda联合使用,方法引用可以使语言的构造更紧凑简洁,减少冗余代码
    4. 默认方法:默认方法就是一个在接口里面有了一个实现的方法
    5. **Stream API**:新添加的Stream API(java.util.stream) 把真正的函数式编程风格引入到Java中。
    6. Optional 类:Optional 类已经成为 Java 8 类库的一部分,用来解决空指针异常。
    7. Date Time API:加强对日期与时间的处理。
    8. Nashorn, JavaScript 引擎:Java 8提供了一个新的Nashorn javascript引擎,它允许我们在JVM上运行特定的javascript应用
    
  5. String s = new String("abc"); 代码运行完在内存中存什么东西?s这个变量放在哪里呢?

    String str = new String("abc");
    String str = "abc";
    /*
    第一种是用new()来创建对象的,它会存放在堆中,每调用一次就会创建一个新的对象;
    第二种是先在栈中创建一个对String类的对象引用变量str ,然后查找栈中有没有存放"abc",如果没有,则将"abc"存放进栈,并令str 指向"abc",如果已经有"abc",则直接令str 指向"abc"。
    比较类里面的数值是否相等时,用equals()方法;当测试两个包装类的引用是否指向同一个对象时,用 ==
    */
    
  6. 发生内存溢出可能的原因有哪些?

    栈什么情况下会发生内存溢出?

    Java虚拟机栈在什么情况下线程请求栈的深度超过当前Java虚拟机栈的最⼤深度?有什么典型的场景会发生这种情况?

    1.内存溢出 out of memory:是指程序在申请内存时,没有足够的内存空间供其使用,出现out of memory;比如申请了一个integer,但给它存了long才能存下的数,那就是内存溢出。
    2.内存泄露 memory leak:是指程序在申请内存后,无法释放已申请的内存空间,一次内存泄露危害可以忽略,但内存泄露堆积后果很严重,无论多少内存,迟早会被占光。
        
    3.内存泄露类型:常发性内存泄漏、偶发性内存泄漏、一次性内存泄漏、隐式内存泄漏
        -常发性内存泄漏。发生内存泄漏的代码会被多次执行到,每次被执行的时候都会导致一块内存泄漏。 
        -偶发性内存泄漏。发生内存泄漏的代码只有在某些特定环境或操作过程下才会发生。常发性和偶发性是相对的。对于特定的环境,偶发性的也许就变成了常发性的。所以测试环境和测试方法对检测内存泄漏至关重要。 
        -一次性内存泄漏。发生内存泄漏的代码只会被执行一次,或者由于算法上的缺陷,导致总会有一块仅且一块内存发生泄漏。比如,在类的构造函数中分配内存,在析构函数中却没有释放该内存,所以内存泄漏只会发生一次。 
        -隐式内存泄漏。程序在运行过程中不停的分配内存,但是直到结束的时候才释放内存。严格的说这里并没有发生内存泄漏,因为最终程序释放了所有申请的内存。但是对于一个服务器程序,需要运行几天,几周甚至几个月,不及时释放内存也可能导致最终耗尽系统的所有内存。所以,我们称这类内存泄漏为隐式内存泄漏。 
        
    4.内存溢出原因:
        -内存中加载的数据量过于庞大,如一次从数据库取出过多数据;
        -集合类中有对对象的引用,使用完后未清空,使得JVM不能回收;
        -代码中存在死循环或循环产生过多重复的对象实体;
        -使用的第三方软件中的BUG;
        -启动参数内存值设定的过小
            
    5.内存溢出解决办法
        -第一步,就是修改JVM启动参数,直接增加内存。
        -第二步,检查错误日志,查看"OutOfMemory"错误前是否有其它异常或错误。
        -第三步,安排有经验的编程人员对代码进行走查和分析,找出可能发生内存溢出的位置。
        -第四步,使用内存查看工具动态查看内存使用情况。
            
    6.内存泄露原因:
        -一种情况如在C/C++ 语言中的,在堆中的分配的内存,在没有将其释放掉的时候,就将所有能访问这块内存的方式都删掉(如指针重新赋值)
        -另一种情况则是在内存对象明明已经不需要的时候,还仍然保留着这块内存和它的访问方式(引用)
        -第一种情况,在 Java 中已经由于垃圾回收机制的引入,得到了很好的解决。所以, Java 中的内存泄漏,主要指的是第二种情况。
    
  7. 什么样的对象不能回收?用什么方法决策对象是否能回收?有哪些对象可以作为GC roots?

    有哪些垃圾回收算法 ?“复制” 算法 要浪费一半空间,为什么还要使用这种 算法 ?标记-清除和标记-整理有哪些优劣势呢?

    垃圾回收器有哪些?CMS和G1的垃圾回收过程?CMS中那个阶段会stop the world?CSM用的哪种垃圾回收 算法 ?G1用的哪种垃圾回收 算法 ?为什么CMS要用标记-清除,不用标记-整理 算法 ?

    垃圾回收怎么解决跨代引用问题?

    1. 确定垃圾的算法
        -引用计数法:引用和对象是有关联的。如果要操作对象则必须用引用进行,对象如果没有任何与之关联的引用则可以回收
        -可达性分析:解决引用计数法的循环引用问题;如果在“GC roots”和一个对象之间没有可达路径,则称该对象是不可达的;不可达对象变为可回收对象至少要经过两次标记过程
        
    2. GC roots
        -虚拟机栈(栈桢中的本地变量表)中的引用的对象
        -方法区中的类静态属性引用的对象
        -方法区中的常量引用的对象
        -本地方法栈中JNI(Native方法)的引用的对象
       
    3.垃圾回收算法:标记清除算法(Mark-Sweep)、复制算法(copying)、标记整理算法(Mark-Compact)、分代收集算法
    4.“复制” 算法 要浪费一半空间但是可以解决内存碎片化问题
        
    5.垃圾回收器:Serial、ParNew、Parallel Scavenge、Serial Old、Parallel Old、CMS、G1
    
  8. 双亲委派模型?有什么机制可以打破双亲委派模型?类加载器种类?

    1.概念:当一个类收到了类加载请求,他首先不会尝试自己去加载这个类,而是把这个请求委派给父类去完成,只有当父类加载器反馈自己无法完成这个请求的时候,子类加载器才会尝试自己去加载
    2.打破:
    - 重写loadClass():双亲委派机制是通过这个方法实现的,改写加载规则相当于打破机制,可以实现自定义加载委派机制
    - 使用线程上下文类加载器:基础类又要调用回用户代码
    - 代码热替换(追求动态性)
    
    3.类加载器:启动类加载器、扩展类加载器、应用程序类加载器(Application ClassLoader)、通过继承 java.lang.ClassLoader实现自定义的类加载器。
    

二、JAVA集合

  1. 讲一下ArrayList与LinkList的区别:数组和链表的区别---查找和修改插入

  2. Hashmap 扩容机制,为什么 hashmap 定义时一般初始化一个大小?HashMap的底层原理?HashMap是线程安全的吗?Java中HashMap的扩容因子为什么是0.75?HashMap的底层数据结构?

    1.扩容机制:使用一个容量更大的数组来代替已有的容量小的数组,transfer()方法将原有Entry数组的元素拷贝到新的Entry数组。扩容到下一个2的指数幂
    2.为什么建立hashmap时需要初始化大小:如果我们没有设置初始容量大小,随着元素的不断增加,HashMap会发生多次扩容,而HashMap中的扩容机制决定了每次扩容都需要重建hash表,是非常影响性能的。
    3.线程不安全
    4.提高空间利用率和减少查询成本的折中,主要是泊松分布,0.75的话碰撞最小,时间和空间成本上寻求的一种折衷选择
        -加载因子过高,例如为1,虽然减少了空间开销,提高了空间利用率,但同时也增加了查询时间成本;
        -加载因子过低,例如0.5,虽然可以减少查询时间成本,但是空间利用率很低,同时提高了rehash操作的次数。
    5.底层数据结构:数组+链表+红黑树(JDK1.8增加了红黑树部分)
    
  3. 说一下ConcurrentHashMap?HashMap和ConcurrentHashMap有什么区别呢?ConcurrentHashMap底层是怎么实现安全性的?ConcurrentHashMap中get操作在1.7和1.8都不需要加锁?

    1.区别:线程是否安全
    2.安全性实现:1.7(ReentrantLock+Segment)、1.8(synchronized+CAS)
    3.get操作全程不需要加锁是因为Node的成员val是用volatile修饰的。和数组用volatile修饰没有关系,数组用volatile修饰主要是保证在数组扩容的时候保证可见性。
    
  4. LinkedHashmap 如何实现的

    1.通过哈希表和链表实现
    2.LinkedHashMap实现了Map接口,继承于HashMap,与HashMap不同的是它维持有一个双链表,从而可以保证迭代时候的顺序。
    
  5. Java 的集合常用什么?一般在什么场景下用到?

        主要根据集合的特点来选用,比如我们需要根据键值获取到元素值时就选用Map接口下的集合,需要排序时选择TreeMap,不需要排序时就选择HashMap,需要保证线程安全就选用ConcurrentHashMap.当我们只需要存放元素值时,就选择实现Collection接口的集合,需要保证元素唯一时选择实现Set接口的集合比如TreeSet或HashSet,不需要就选择实现List接口的比如ArrayList或LinkedList,然后再根据实现这些接口的集合的特点来选用。
    
  6. Java的集合Collection下面还有哪些接口?Map集合有哪些?

    1.List、Set
    2.很多
    
  7. list和set的区别是什么?set是怎么保证元素不可重复?

1.说说List,Set,Map三者的区别?
    -List(对付顺序的好帮手): List接口存储一组不唯一(可以有多个元素引用相同的对象),有序的对象
    -Set(注重独一无二的性质): 不允许重复的集合。不会有多个元素引用相同的对象。
    -Map(用Key来搜索的专家): 使用键值对存储。Map会维护与Key有关联的值。两个Key可以引用相同的对象,但Key不能重复,典型的Key是String类型,但也可以是任何对象。
2.保证不可重复-(hashCode和equals)
    -在往Set集合中添加对象的时候,首先会通过该对象的hashCode方法计算该对象的hash值。
    -将计算出来的hash值去hash表中查询,如果hash表中不存在该值,则对象添加成功。
    -如果hash表中有该hash值,那么还会将要添加的对象和集合中的对象进行进一步的比较,使用对象的equals方法比较对象的field。
    -如果field也相同,则是重复的对象,该对象添加失败。
    -如果equals返回的是false,则说明两个对象不是同一个对象。则该对象就会添加到已有对象链表的末尾。该种情况称为hash碰撞。

三、JAVA线程和多线程

  1. 讲一下Java中线程的六种状态及其转换,创建线程的方式?

    1. **初始(NEW)**:新创建了一个线程对象,但还没有调用start()方法。
    2. **运行(RUNNABLE)**:Java线程中将就绪(ready)和运行中(running)两种状态笼统的称为“运行”。
       线程对象创建后,其他线程(比如main线程)调用了该对象的start()方法。该状态的线程位于可运行线程池中,等待被线程调度选中,获取CPU的使用权,此时处于就绪状态(ready)。就绪状态的线程在获得CPU时间片后变为运行中状态(running)。
    3. **阻塞(BLOCKED)**:表示线程阻塞于锁。
    4. **等待(WAITING)**:进入该状态的线程需要等待其他线程做出一些特定动作(通知或中断)。
    5. **超时等待(TIMED_WAITING)**:该状态不同于WAITING,它可以在指定的时间后自行返回。
    6. **终止(TERMINATED)**:表示该线程已经执行完毕。
    
  2. 讲一下Java里面的线程安全、概念、如何实现写一个两个线程死锁的案例,或者表述一下

    怎么保证线程安全?这几种保证线程安全的方式有什么区别?

    1.如果代码所在的进程中有多个线程在同时运行,而这些线程可能会同时运行这段代码。如果每次运行结果和单线程运行的结果是一样的,而且其他的变量的值也和预期的是一样的,就是线程安全的。 
    2.互斥同步、非阻塞同步、无同步方案
    
  3. volatile关键字的作用?

    什么场景下会使用volatile?举个实际的场景?

    volatile 的底层实现,如何防止指令重排线程,有哪些状态,说一下,并且说一下这些状态之间如何转移。

    为什么线程访问volatile的值是主内存中的值?

    1.解决线程并发的问题
    2.1)对变量的写操作不依赖于当前值 2)该变量没有包含在具有其他变量的不变式中
    
  4. 线程池用过吗?说一下四种常用线程池。

    如果让你实现具有缓存功能的线程池的类怎么实现?

    线程池的拒绝策略有哪些?

    1.四种常用线程池
    - CachedThreadPool:创建一个可根据需要创建新线程的线程池
    - FixedThreadPool:创建一个可重用固定线程数的线程池,以共享的无界队列方式来运行这些线程
    - ScheduledThreadPool:创建一个线程池,它可安排在给定延迟后运行命令或者定期地执行
    - SingleThreadExecutor:返回一个线程池(这个线程池只有一个线程),这个线程池可以在线程死后(或发生异常时)重新启动一个线程来替代原来的线程继续执行下去
    2.线程池的拒绝策略:CallerRunsPolicy\AbortPolicy\DiscardPolicy\DiscardOldestPolicy
    
  5. sleep和wait有什么区别?

    run()和start()方法的区别?

    1. 对sleep()方法属于 Thread 类中的。而 wait()方法属于Object 类中的
    2. sleep()方法导致了程序暂停执行指定的时间,让出 cpu 该其他线程,但是他的监控状态依然保持者,当指定的时间到了又会自动恢复运行状态。
    3. 在调用 sleep()方法的过程中,线程不会释放对象锁。
    4. 而当调用 wait()方法的时候,线程会放弃对象锁,进入等待此对象的等待锁定池,只有针对此对象调用 notify()方法后本线程才进入对象锁定池准备获取对象锁进入运行状态。
    
    1. start()方法来启动线程,真正实现了多线程运行。这时无需等待 run 方法体代码执行完毕,可以直接继续执行下面的代码。
    2. 通过调用 Thread 类的 start()方法来启动一个线程, 这时此线程是处于就绪状态, 并没有运行。
    3. 方法 run()称为线程体,它包含了要执行的这个线程的内容,线程就进入了运行状态,开始运行 run 函数当中的代码。 Run 方法运行结束, 此线程终止。然后 CPU 再调度其它线程。
    
  6. Callable()和Future()用过吗?

    使用Callable()创建线程比另外两种方式有什么优势吗?

    1.创建有返回值的线程
    2.也可以继承其他的类,多线程可以共享同一个target对象,适合多线程访问同一资源的情况,将cpu和数据分开,形成清晰的模型,较好的体现了面向对象的思想,还有返回值
    
  7. volatile int a=1,写一个方法对a进行累加,这个方法是有多个线程去访问的,这样实现能保证线程安全吗?

    详细讲下为什么两个线程同时访问不能保证线程安全?(如果线程安全a应该为3,但是线程不安全就不会是3)

    
    
  8. 有一个文件,有很多人在读。并发读,没有影响,但是当一个人在写入文件时,不允许其他人写入,如果已经在写这个文件时,是不允许读,因为在修改过程中读的不是最新的,如果有人读的情况下,不希望有人能写,如果要实现这个功能,代码应该怎么去实现,去做多线程的控制?

    
    
  9. new一个线程放在哪里?

    首先java里的对象一般是在堆内存中(如果JIT编译优化过可能会经过逃逸分析在栈上分配对象),所以java里Thread对象本身必然也是在堆内存的,只有在调用start()方法时会调用native方法start0(),该方法用来创建对应操作系统的os thread(与java thread 1对1关系),其开辟的线程栈空间不属于jvm管理的堆内存空间.
    
  10. Java中的valatile和syncharonized关键字的区别?使用场景?

    synchronized和ReentrantLock 的区别

    1.区别:
        -volatile关键字是线程同步的轻量级实现,所以volatile性能肯定比synchronized关键字要好。但是volatile关键字只能用于变量而synchronized关键字可以修饰方法以及代码块。synchronized关键字在JavaSE1.6之后进行了主要包括为了减少获得锁和释放锁带来的性能消耗而引入的偏向锁和轻量级锁以及其它各种优化之后执行效率有了显著提升,实际开发中使用 synchronized 关键字的场景还是更多一些。
        -多线程访问volatile关键字不会发生阻塞,而synchronized关键字可能会发生阻塞
        -volatile关键字能保证数据的可见性,但不能保证数据的原子性。synchronized关键字两者都能保证。
        -volatile关键字主要用于解决变量在多个线程之间的可见性,而 synchronized关键字解决的是多个线程之间访问资源的同步性。
    2.相同和区别:
        -两者都是可重入锁:“可重入锁”概念是:自己可以再次获取自己的内部锁
        -synchronized 依赖于 JVM 而 ReentrantLock 依赖于 API
        -ReentrantLock 比 synchronized 增加了一些高级功能:①等待可中断;②可实现公平锁;③可实现选择性通知(锁可以绑定多个条件)
    
  11. 进程和线程的区别?

    1.区别:
        -调度:线程作为调度和分配的基本单位,进程作为拥有资源的基本单位
        -并发性:不仅进程之间可以并发执行,同一个进程的多个线程之间也可并发执行
        -拥有资源:进程是拥有资源的一个独立单位,线程不拥有系统资源,但可以访问隶属于进程的资源.
        -系统开销:在创建或撤消进程时,由于系统都要为之分配和回收资源,导致系统的开销明显大于创建或撤消线程时的开销。
    
  12. Java的线程和操作系统的线程是什么关系?

    参考:https://www.cnblogs.com/cswiki/p/14676264.html
    1.操作系统线程
        //在用户空间中实现线程:
            a.在早期的操作系统中,所有的线程都是在用户空间下实现的,操作系统只能看到线程所属的进程,而不能看到线程
            b.在这种模型下,我们需要自己定义线程的数据结构、创建、销毁、调度和维护等,这些线程运行在操作系统的某个进程内,然后操作系统直接对进程进行调度。
            c.好处:第一点,就是即使操作系统原生不支持线程,我们也可以通过库函数来支持线程;第二点,线程的调度只发生在用户态,避免了操作系统从内核态到用户态的转换开销。
            d.缺点:由于操作系统看不见线程,不知道线程的存在,而 CPU 的时间片切换是以进程为维度的,所以如果进程中某个线程进行了耗时比较长的操作,那么由于用户空间中没有时钟中断机制,就会导致此进程中的其它线程因为得不到 CPU 资源而长时间的持续等待;另外,如果某个线程进行系统调用时比如缺页中断而导致了线程阻塞,此时操作系统也会阻塞住整个进程,即使这个进程中其它线程还在工作。
        //在内核空间中实现线程:
            a.内核级线程就是运行在内核空间的线程, 直接由内核负责,只能由内核来完成线程的调度。
            b.我们可以直接使用操作系统中已经内置好的线程,线程的创建、销毁、调度和维护等,都是直接由操作系统的内核来实现,我们只需要使用系统调用就好了,不需要像用户级线程那样自己设计线程调度等。
            c.类别:多对一线程模型、一对一线程模型、多对多线程模型
    2.Java线程
        -在 JDK 1.2 之前,Java 线程是基于称为 "绿色线程"(Green Threads)的用户级线程实现的,也就是说程序员大佬们为 JVM 开发了自己的一套线程库或者说线程管理机制。
        -在 JDK 1.2 及以后,JVM 选择了更加稳定且方便使用的操作系统原生的内核级线程,通过系统调用,将线程的调度交给了操作系统内核
        -也就是说,在 JDK 1.2 及之后的版本中,Java 的线程很大程度上依赖于操作系统采用什么样的线程模型,这点在不同的平台上没有办法达成一致,JVM 规范中也并未限定 Java 线程需要使用哪种线程模型来实现,可能是一对一,也可能是多对多或多对一。
        -现今 Java 中线程的本质,其实就是操作系统中的线程,其线程库和线程模型很大程度上依赖于操作系统(宿主系统)的具体实现,比如在 Windows 中 Java 就是基于 Wind32 线程库来管理线程,且 Windows 采用的是一对一的线程模型。
    
  13. 核心线程数、最大线程数、队列三者之间的关系?

    1.随着任务数量的增加,会增加活跃的线程数。
    2.当活跃的线程数 = 核心线程数,此时不再增加活跃线程数,而是往任务队列里堆积。
    3.当任务队列堆满了,随着任务数量的增加,会在核心线程数的基础上加开线程。
    4.直到活跃线程数 = 最大线程数,就不能增加线程了。
    5.如果此时任务还在增加,则: 任务数11 > 最大线程数8 + 队列长度2 ,抛出异常RejectedExecutionException,拒绝任务。
    
    a.线程池初始化的活跃线程数为0
    b.当活跃线程数<核心线程数,且活跃线程在执行任务,线程池会新生成线程用以执行提交的任务
    c.当活跃线程数=核心线程数,且活跃线程在执行任务,新任务会优先被放置到阻塞队列
    d.当活跃线程数=核心线程数,且活跃线程在执行任务,且阻塞队列已满,且没有活跃线程数<最大线程数,则线程池会新生成一个线程来执行任务
    e.当线程空闲,会被逐步回收,如果持续没有新任务提交,线程池活跃线程数会降低为0
    
    新任务提交在线程池内部处理的优先级:核心线程 > 阻塞队列 > 扩容的线程
    
  14. 单例模式了解吗?来给我手写一下!给我解释一下双重检验锁方式实现单例模式的原理

    public class Singleton { 
        private volatile static Singleton uniqueInstance; // volatile 可以禁止 JVM 的指令重排
        
        private Singleton() { } public static Singleton getUniqueInstance() { //先判断对象是否已经实例过,没有实例化过才进入加锁代码 
            if (uniqueInstance == null) { //类对象加锁 
                synchronized (Singleton.class) { 
                    if (uniqueInstance == null) { 
                        uniqueInstance = new Singleton(); 
                    } 
                } 
            } 
            return uniqueInstance; 
        } 
    }
    
  15. continue。。。。。。

四、JAVA基础知识

  1. 说下hashCode( )?什么场景下会重写hashCode()?

    ==和equals?

    1.hashCode()的作用:获取哈希码,也称为散列码;它实际上是返回一个int整数。这个哈希码的作用是确定该对象在哈希表中的索引位置。
    2.HashCode()方法和equals()方法的作用其实一样,都是比较两个对象是否相等
        a.重写的equals()一般比较全面比较复杂,这样效率就比较低,而利用HashCode()进行双向对比,则只要生成一个hash值就可以进行比较,效率较高。
        b.HashCode()不完全可靠:equals()相等的对象肯定相等,HashCode相等的对象不一定相等,不相等的对象一定不相等。
    3.不重写:不重写的HashCode比较的是地址值,对于引用数据类型不能比较。所以必须重写。
    

4.重写:a.HashTable、HashMap、HashSet b.重写了equals就一定要重写HashCode
5.如果两个对象相等,则hashcode一定也是相同的;两个对象相等,对两个对象分别调用equals方法都返回true;两个对象有相同的hashcode值,它们也不一定是相等的;因此,equals 方法被覆盖过,则 hashCode 方法也必须被覆盖
6.hashCode() 的默认行为是对堆上的对象产生独特值。如果没有重写 hashCode(),则该 class 的两个对象无论如何都不会相等(即使这两个对象指向相同的数据)

1.()
-基本数据类型 :byte,short,char,int,long,float,double,boolean。他们之间的比较,应用双等号(
),比较的是他们的值。
-引用数据类型:当他们用()进行比较的时候,比较的是他们在内存中的存放地址
2.equals()
-equals方法不能作用于基本数据类型的变量
-如果没有对equals方法进行重写,则比较的是引用类型的变量所指向的对象的地址(若某个类没有覆盖equals()方法,当它的通过equals()比较两个对象时,实际上是比较两个对象是不是同一个对象。这时,等价于通过“
”去比较这两个对象。)
-诸如String、Date等类对equals方法进行了重写的话,比较的是所指向的对象的内容




2. 你对Java反射的理解?在Java命名规范中一般采用驼峰命名的方式,假设想基于反射做一件事,有一个包,包里面有很多类,找出命名不符合规范的类名、方法 名、属性名,怎么实现?




3. 写一个Java类,属性中可能是一个类对象,怎么解决一层一层往下扫的问题?(属性是一个类,类中又有属性)




4. 什么情况下会使用序列化?

```java
- 内存中的对象状态保存到一个文件中或者数据库中
- 套接字在网络上传送对象
- 通过RMI传输对象
  1. 多态的应用场景?

    
    
  2. 接口和抽象类有什么相同点和不同点?接口里面一定有抽象的方法吗?抽象类中一定有抽象的方法吗?

    1.不同点:
        a.接口的方法默认是 public,所有方法在接口中不能有实现(Java 8 开始接口方法可以有默认实现),而抽象类可以有非抽象的方法。
        b.接口中除了static、final变量,不能有其他变量,而抽象类中则不一定。
        c.一个类可以实现多个接口,但只能实现一个抽象类。接口自己本身可以通过extends关键字扩展多个接口。
        d.接口方法默认修饰符是public,抽象方法可以有public、protected和default这些修饰符(抽象方法就是为了被重写所以不能使用private关键字修饰!)。
        e.从设计层面来说,抽象是对类的抽象,是一种模板设计,而接口是对行为的抽象,是一种行为的规范。
                                    
    2.抽象类中除了有抽象方法外,也可以有数据成员和非抽象方法,抽象类不一定有抽象方法;但是包含一个抽象方法的类一定是抽象类
    3.而接口中所有的方法必须都是抽象的(java 8之后可以定义default方法,包含方法体。),接口中也可以定义数据成员,但必须是常量。
    
  3. 什么是不可变类?String不可变有什么好处?

  4. Java常见的数据类型?

  5. JDK动态代理和CGLIB动态代理有什么区别?

第二部分 计算机网络

  1. Https和Http的区别?HTTP的状态码有哪些?HTTP常用的方法?HTTPS通过SSL加密,加密的过程是怎么样的呢?HTTPS检查公钥是通过什么方式检查的?CA证书起什么样的作用,证书是什么时候出现的?

    费用:https协议需要到ca申请证书,一般免费证书很少,需要交费。
    加密:http是超文本传输协议,信息是明文传输,https 则是具有安全性的ssl加密传输协议。
    连接:http和https使用的是完全不同的连接方式用的端口也不一样,前者是80,后者是443。
    状态:http的连接很简单,是无状态的 。
    安全性:HTTPS协议是由SSL+HTTP协议构建的可进行加密传输、身份认证的网络协议, 要比http协议安全。
    
  2. ssl证书如果验证失败会有哪些原因呢?

    申请的SSL证书与网站域名不匹配
    SSL证书无效已经过期
    颁发SSL证书的数字证书颁发机构不受信任
    页面包含HTTP不安全内容
    
  3. 访问一个网址时域名解析成ip的过程?DNS解析发生在什么阶段?通过什么方式解决DNS劫持问题?

    - 第一步:客户机提出域名解析请求,并将该请求发送给本地的域名服务器。
    - 第二步:当本地的域名服务器收到请求后,就先查询本地的缓存,如果有该纪录项,则本地的域名服务器就直接把查询的结果返回。
    - 第三步:如果本地的缓存中没有该纪录,则本地域名服务器就直接把请求发给根域名服务器,然后根域名服务器再返回给本地域名服务器一个所查询域(根的子域)的主域名服务器的地址。
    - 第四步:本地服务器再向上一步返回的域名服务器发送请求,然后接受请求的服务器查询自己的缓存,如果没有该纪录,则返回相关的下级的域名服务器的地址
    - 第五步:重复第四步,直到找到正确的纪录。
    - 第六步:本地域名服务器把返回的结果保存到缓存,以备下一次使用,同时还将结果返回给客户机。
    
    //浏览器地址栏输入url,有哪些协议?
    - 浏览器会根据你输入的 URL 地址,去查找域名是否被本地 DNS 缓存,不同浏览器对 DNS 的设置不同,如果浏览器缓存了你想访问的 URL 地址,那就直接返回 ip。
    - 如果没有缓存你的 URL 地址,浏览器就会发起系统调用来查询本机 hosts 文件是否有配置 ip 地址,如果找到,直接返回。
    - 如果找不到,就向网络中发起一个 DNS 查询。在由根域名服务器 -> 顶级域名服务器 -> 权威 DNS 服务器后,由权威服务器告诉本地服务器目标 IP 地址,再有本地 DNS 服务器告诉用户需要访问的 IP 地址//对应ip地址解析
    - 浏览器需要和目标服务器建立 TCP 连接
    - 向服务器发送HTTP请求
    - 服务器处理请求,返回网页内容
    - 浏览器解析并渲染页面
    - TCP四次挥手,连接结束
    
      DNS是域名系统(DomainNameSystem)的缩写,该系统用于命名组织到域层次结构中的计算机和网络服务,可以简单地理解为将URL转换为IP地址。
    
  4. 怎么处理跨域问题?

    跨域,指的是浏览器不能执行其他网站的脚本。它是由浏览器的同源策略造成的,是浏览器对javascript施加的安全限制。
    1.JSONP:
    2.代理
    3.PHP端修改header
    
  5. TCP3次握手,4次挥手?为什么要3次握手?不是4次,5次?为什么要4次挥手?

    1.3次握手的原因:防止丢包问题,第二次服务端的确认丢失,则c和s都会等待,若使用第三次握手,若丢失,则s收不到确认,会重发。
    2.对于三次没必要,少于三次会有意外
    

3.4次挥手的原因:不能某一方想断开就断开

  • 客户端和服务端都没有数据要发送的时候才能断开TCP。
  • 客户端发出FIN报文时只能保证客户端没有数据发了。
  • 服务端收到客户端的FIN报文后只能先回复客户端一个确认报文来告诉客户端我服务端已经收到你的FIN报文了,但我服务端还有一些数据没发完,等这些数据发完了服务端才能给客户端发FIN报文。



6. TCP有什么特性?流量控制是怎么实现的?TCP是怎么保证安全性的?TCP的超时重传说一下?怎么保证TCP传输的可靠性?拥塞控制是怎么实现的?

7. token?为什么要用token,不用session?token和session之间有什么优劣势?token和session的区别?什么是token?

8. TCP和UDP的区别?TCP和UDP的使用场景?UDP有什么特点?

9. 有哪些网络协议?

10. get和post的区别?get和post哪个能够缓存?

11. 有了解中间人攻击吗?假设去攻击一个HTTPS的请求该怎么去攻击?

12. HTTP1.1、HTTP2.0、HTTP3.0有什么区别?

13. TCP/IP四层模型?和OSI七层模型?

# 第三部分 操作系统

1. 什么场景下会出现死锁?怎么避免死锁?死锁是怎么产生的?死锁的必要条件?解决方法?
2. 进程调度算法 ?

# 第四部分 项目

1. 简单的介绍一下项目
2. 在项目中遇到哪些挑战?
3. 你写的项目中,数据库中设计了几张表?
4. 表中有没有用索引?索引是怎么设计的?
5. 项目中哪个模块比较复杂?技术难点?
6. 使用Redis做缓存,缓存中的数据是怎么来的?Redis有做容灾的考虑吗?
7. 项目优化前QPS多少,优化后QPS多少?
8. 用什么工具做的测试?在什么环境下部署的,做的压测?Jmeter是在哪里执行的?压测时本地的配置是怎么的?做性能压测在环境部署上要考虑哪些点?
9. 当并发量比较大时,先查 redis 中的缓存, redis 中没有就去查mysql,并发量大时会不会出现穿透?
10. 当并发量比较大时,面对缓存穿透问题,对于缓存空置这种解决方法,并发量大时,可能请求都直接穿透到DB中,有没有时序问题?
11. 大致说下加锁的解决办法?如果部署到多机,怎么解决加锁问题?分布式锁有没有了解过?
12. MySQL中执行SQL查询语句,mysql的server层的执行流程?
13. 新增一个记录,怎么去更新缓存和数据库?流程是什么?
14. 用 redis 缓存登录凭证和验证码?
15. 讲一下登录的流程?在登录之前是要先判断是否有登录凭证的id吗?打开网页后访问哪些业务需要登录?登录前校验登录凭证,要去查 redis 吗?登录凭证存入 redis 中有设置过期时间吗?登录凭证里面包含什么?为什么要用Redis存储登录凭证?使用Redis存储登录凭证有哪些性能提升?登录凭证怎么使用?利用userId,是不是要先从数据库中去查询用户?当登录成功后,访问另一个需要登录的页面时,需要通过Redis获取登录凭证,key是什么,从哪里来?
16. 密码是怎么存储的?为什么要加slat值?
17. 你项目中的 redis ,一主几从,几个哨兵?

# 第五部分 数据库

## 一、Redis

1. **redis 的基本数据类型和底层实现**,Reids的String类型的底层实现?

```java
//基本数据类型
1. String字符串:可以是字符串、整数或者浮点数或者序列化的对象(二进制安全);一个key对应一个value
2. List列表:链表,每个节点包含一个字符串;双端链表实现
3. Set集合:String 类型的无序集合。集合成员是唯一的;哈希表实现
4. Hash散列: string 类型的 field(字段) 和 value(值) 的映射表
5. Zset有序集合:string 类型元素的集合,且不允许重复的成员;每个元素都会关联一个 double 类型的分数。redis 正是通过分数来为集合中的成员进行从小到大的排序;有序集合的成员是唯一的,但分数(score)却可以重复;哈希表实现
    
//底层实现
  1. Redis有哪些应用场景?你用Redis有哪些场景?使用的是哪些数据结构?

  2. 如何保证缓存与数据库双写的一致性?这种方法有没有存在什么问题?

    1、缓存延时双删:先删除缓存、再更新数据库、休眠一会(比如1秒)再次删除缓存。(删除缓存重试机制)
    3、读取biglog异步删除缓存
    
  3. Redis怎么实现分布式锁?

  4. 缓存穿透、缓存击穿、缓存雪崩是什么?解决方案?

  5. Redis集群?

  6. redis 一主一从,三个哨兵,还是对外提供一个单点服务,这个时候引入3个master节点,每个master都有一个从节点,3个哨兵,这样组成一个 redis 集群?整体对外提供一个缓存服务,怎么设计缓存的key应该命中到哪个主节点上面?

  7. 解释一个IO多路复用机制? redis 采用哪种方式实现的?

  8. Redis持久化方式RDB和AOF各有哪些优缺点?

  9. Redis一致性hash 算法 ?

  10. Redis的过期删除策略?

二、MySQL

  1. 乐观锁和悲观锁知道吗?乐观锁和悲观锁的优缺点和应用场景是什么?

    1.悲观锁:每次请求都会先对数据进行加锁,然后进行数据操作,最后再解锁
        -可以完全保证数据的独占性和正确性
        -加锁释放锁的过程会造成消耗,所以性能不高
    2.乐观锁:操作数据时不会对操作的数据进行加锁,到数据提交的时候才验证数据是否存在冲突
        -多个任务可以并行的对数据进行操作
        -在并发非常高的情况下,会导致大量的请求冲突,降低性能
    
  2. 数据库是怎么使用索引的?底层实现?

  3. Innodb 的索引数据结构,回表机制,和 mysiam 的区别

  4. 事务的 ACID 特性,事务隔离级别,解决什么问题

  5. 对数据库事务是怎么理解的?数据库事务的隔离级别?分别介绍下这几种事务的隔离级别的使用场景或存在的问题?

  6. 脏读和幻读的区别?什么情况下会出现幻读?

  7. MySQL的隔离级别?串行化级别是怎么实现的?

  8. 聚集索引和非聚集索引的区别?

  9. MySQL用的是哪个版本?用的哪个引擎?InnoDB和MyISAM的区别?

  10. 数据库中的多表连接有哪几种?有什么区别?数据库中in 和exists 的用法的区别?

  11. MySQL中怎么分页查找?

  12. 如果数据量很大的情况下上亿条,还能用limit这种方式进行分页查找吗?

  13. 如果要实现在大数据量的情况下的分页,该怎么设计?

  14. MVCC的原理?

三、题目

  1. SQL:表A和表B,如果同时插入一条数据,如果要保证事务的一致性,要么同时成功,要么同时失败,代码在JDBC中应该怎么写?
  2. SQL:有一张学生课程表,里面记录学生各门课程的分数,用一条SQL将每门课程的分数都大于80分,将这些学生都找出来?
  3. 如果一个MySQL表中有 a,b,c,d,e 5个字段,在a,b,c,d四个字段上建立联合索引。如果在查询时where a=1 and b=2 and c>3 and d=4;会走哪些索引?

第六部分 框架

Spring

  1. Spring中AOP是怎么实现的?
  2. Spring中Bean的初始化过程?
  3. Spring中IOC? 平时自己写代码时有什么应用场景吗?
  4. 说一下工厂模式及使用场景?
  5. Spring里面用了什么设计模式?
  6. SpringBoot的初始化过程?自动装配原理?
  7. Spring是怎么解耦的?依赖是怎么注入的?
  8. SpringMVC说一下?
  9. SpringBoot和Spring的区别?Spring、SpingMVC、SpringBoot之间的关系?

第七部分 设计模式

  1. Java中常用的设计模式?了解哪些设计模式?
  2. 单一职责原则是什么?开闭原则是什么?双重检验单例模式?
  3. 说一说观察者模式?及应用场景?
  4. 单例模式的应用场景?单例模式有几种?
  5. 静态类和单例模式的区别?
  6. 工厂模式的使用场景?在哪里使用过?
  7. 写一个单例模式?如果自己写会选用那种方式创建单例模式?这几个单例模式之间的区别 ?

第八部分 数据结构和算法

一、数据结构

  1. 树结构的好处是什么,平时有用到哪些,bst 和 avl 树有什么区别
  2. 尝试实现下字典树的数据结构并且尝试建立一个字典树。
  3. 数组和 链表 的区别?数组的查询为什么快? 链表 的插入删除为什么快?数组的查询、插入/删除的时间复杂度?
  4. 栈和队列的区别?及应用场景?怎么用栈实现队列?说思路
  5. 常见的数据结构有哪些?
  6. 红黑树 是怎么树?红黑树 的其他的应用场景?

二、算法

  1. 求无向图中的连通分量
  2. 判断二叉树是否是 bst
  3. 海量数据 ,查找重复单词
  4. 三个线程交替打印1、2、3,直到100。
  5. 除了B+树查找 算法 ,还了解其他的查找 算法 吗?
  6. 编码:写一个死锁?
  7. 编码:写一个双重校验单例模式?解释一下细节?为什么要进行二次判单是否为null?volatile?
  8. 算法 : 链表中的节点每k个一组翻转 ?
  9. 排序 算法 ?简单的描述下快排过程?
  10. 快排和冒泡 排序 的区别?
  11. 快排时间复杂度为什么是个O(nlogn)?
  12. 怎么去看一个 算法 的时间复杂度?
  13. 有1亿一个数字,给出只能装下100个数字的内存,求top5?说下思路?
  14. 假设有一个20G的文件,文件中都是英语单词, 单词可能重复,内存只有1个G,找出英语单词出现次数最多的10个单词?
  15. 算法 :有一个正整数的数组,求组合得到的最大值?

第九部分 Linux

  1. 用Linux命令统计一个文件中某个单词出现的次数?
  2. 用Linux命令查看当前有哪些进程在活跃呢?
  3. 用Linux查看文件有哪些命令?
  4. less和more知道吗?
  5. Linux用的多吗?用过哪些Linux命令?
  6. 信号驱动IO和多路复用IO知道吗?
posted @ 2021-12-30 15:41  汤十五  阅读(96)  评论(0)    收藏  举报