阿里《JAVA实习生入职测试题—2019最新》之答案详解(连载二)

力争清晰完整准确(逐步完善,持续更新)


3、反射中,Class.forName和ClassLoader.loadClass的区别

更准确的说,是Class.forName("SomeClass")和ClassLoader.getSystemClassLoader().loadClass("SomeClass")的区别

Class.forName(“SomeClass”)= Class.forName(className,true,classloader)
ClassLoader.loadClass(className) = ClassLoader.loadClass(className,false)
  • 第一个会用调用这段代码的class loader 来load class, 并且完成初始化(true——表示要初始化,即静态初始化会被执行,包括静态代码块,静态变量)
  • 第二个会调用“system" class loader (这个会被覆盖掉),不会被初始化(如果用它来load  JDBC driver,那么它不会被注册,因此你无法使用JDBC)

类和接口的装载及初始化,请参看 https://docs.oracle.com/javase/specs/jls/se7/html/jls-12.html#jls-12.2 之12.2和12.4。

详情参看 https://stackoverflow.com/questions/8100376/class-forname-vs-classloader-loadclass-which-to-use-for-dynamic-loading

 

4、session和cookie的区别与联系,session的生命周期,多个服务部署时session管理

这个得从HTTP协议是无状态说起。http协议就是客服端向服务器发起一次请求,链接,然后传数据,断开链接。

  • 无状态(stateless)

就是第一次客户端A来了,在服务器要了些资源,干了些事,然后断开链接,走了。没有谁记录这次链接的一些状态,下一次客户端A再来一次,服务器不认识它,不知道它来过,也不知道它上次干过啥,完全当作新的一次链接访问来处理。就如同生活中有个陌生人来你家做客,吃饭打牌玩游戏,然后他回家了。下次这家伙又来,结果你有遗忘症,完全认不得这哥们。

  • 有状态 (stateful)

就是第一次客户端A来了,在服务器要了些资源,干了些事,然后断开链接,走了。有东西记录这次访问的一些信息,比如操作记录、用户名密码等。这次就是有个陌生人来你家做客,吃饭打牌玩游戏,然后他回家了。下次这家伙又来,主人是有正常记忆的人,对这哥们有印象。

 

session和cookie的作用就是记录访问用户和服务器的交互状态。它们让http请求变成了能记录状态的会话。cookie存在客户端,B/S里客户端就是浏览器;session存在服务器。

cookie截图如下:

 

  •  cookie

存放key/value键值对数据。当用户通过HTTP访问服务器,服务器会在reponse里将这些“键值对”信息给到客户端浏览器,并对这些数据做一定的限制。在条件符合的时候,用户再次访问这个服务器,数据又被完整带回服务器(在HTTP header里)。 Cookie设计的初衷是记录一段时间内用户访问Web应用的行为路径。

缺点:Cookie可能被盗和伪造等

  • session(会话)

session基于cookie工作的。同一客户端和服务器交互时,不需要每次都回传所有Cookie信息,只需要传个唯一ID(客户端第一次访问服务器时服务器生成的唯一标识),服务端每次回传这个唯一标识(sessionId),服务器就知道这个客户端是谁了,来服务器端干过啥。减少了客服端和服务端每次交互传输的数据量,节省带宽。在PV过亿的情况,节省的带宽就相当可观了

缺点:Session不易在多台服务器间共享等

  • session生命周期
  • 多服务器部署的session管理

5、Java中的队列都有哪些,有什么区别


6、详谈一下Java的内存模型以及GC算法

Java内存模型(Java Memory Model, JMM)

  • 为什么Java要抽象和实现这么一套东西?

简单地先从硬件角度说,CPU和存储设备速度相差几个数量级,而CPU高速运算的数据源及运算的结构,不可避免要和存储设备有读写交互,单靠寄存器搞不定。所以中间搞个高速缓存,解决CPU与内存的速度矛盾。这样一来,解决了问题,又增加了新的问题,复杂性增加了(自不待言),缓存一致性的问题又来了(Cache Coherence)。

Java虚拟机的“内存模型”可以说是对特定的内存和高速缓存读写过程的抽象,屏蔽掉各种硬件和操作系统的内存访问差异,确保Java程序在各种平台上运行效果一样,不需要再改代码(跨平台, Write once, run everywhere)。而诸如C++等语言,直接使用物理硬件和操作系统的内存模型,换个平台可能就会出错,所以在某些场景必须针对不同的平台编写不同的分支处理程序。

不说了,上图

  • 运行时数据区域

这个和前面的划分不是同一层次角度,面试时有面试官,随手写段代码,让我区分里面的变量等分别是放在那个区域的。

1)程序计数器(Program Counter Register)

  • 据说可以理解为JAVA底层执行的字节码的行号指示器,通过改变此的数值,来选取执行不同的字节码指令,完成分支、循环、跳转、异常处理、线程恢复等编程语言基本都具备的基础功能
  • 不会抛出OutOfMemoryError

线程私有(有游戏主程问我哪些是线程私有的,这个问题在思考什么呢)

2)  Java虚拟机栈(Java Virtual Machine Stacks)

  • 生命周期 = 线程的生命周期,数据结构:stack
  • 栈帧(Stack Frame) ,存储局部变量表、操作数栈、动态链接、方法出口等。和方法息息相关,方法的调用一直到执行完毕,对应着一个栈桢的入栈到出栈之全过程。
  • 局部变量表,存放8种基本数据类型(boolean、byte、char、short、integer、long、float、double)、对象引用、returnAddress(指向一条字节码指令的地址)
  • 会抛出StackOverflowError及OutOfMemoryError

线程私有

3)  本地方法栈 (Native Method Stack)

  • 为Native方法服务(类似虚拟机栈)
  • 实现方式、数据结构、语言可灵活处理,如Sun HotSpot 虚拟机把本地方法栈和虚拟机栈合二为一
  • 会抛出StackOverflowError及OutOfMemoryError

4)堆(heap)

  • 存放几乎所有对象
  • 细分为新生代和老年代(内存空间占比为1:2),新生代根据死亡概率,会有不同的回收算法(有游戏主程专门问到,老年代的回收算法有哪些?)

  • 会抛出OutOfMemoryError

线程共享

5)   方法区(Method Area)

  • 存储被虚拟机加载的类信息、常量、静态变量、即时编译器编译后的代码等
  • 会抛出OutOfMemoryError

线程共享

6)运行时常量池(Runtime Constant Pool)

  •  存放编译期生成的各种字面量和符号引用及直接引用(?)
  • 动态可变,即在运行期也可将新的常量放入池中,如通过String类的intern()方法
  • 会抛出OutOfMemoryError

 

  • GC算法

 

 推荐 https://blog.csdn.net/javazejian/article/details/72772461

7、JAVA10、JAVA11的新特性

JAVA10 新特性官方发布页: https://www.oracle.com/technetwork/java/javase/10-relnote-issues-4108729.html

  • 新增 Optional.orElseThrow() 方法
  • 新增几个Unmodifiable Collections API,如Collectors 类新增 toUnmodifiableListtoUnmodifiableSet, 和toUnmodifiableMap 方法
  • ...

JAVA11 新特性官方发布页:  https://www.oracle.com/technetwork/java/javase/11-relnote-issues-5012449.html

卧槽,最近jdk更新有点快,都13了

8、Java内存泄漏的问题调查定位:jmap, jstack的使用
9、Spring的体系结构和jar用途
10、Spring MVC的运行原理 
11、Spring Boot的执行过程

spring boot 类加载过程和tomcat类加载过程的区别,也是面试常问

12、Spring IOC和AOP的底层实现
14、Spring boot 的优势和劣势,以及适用场景等
15、讲一下Sping Cloud和Dubbo的优缺点
16、什么是Hystrix?它如何实现容错?

hystrx 工作原理,详见 https://segmentfault.com/a/1190000012439580


17、什么Netflix Feign?它的优点是什么?


18、谈一谈分布式一致性到CAP理论,BASE理论

  • CAP — C:Consistency(一致性),A:Avaliaility(可用性)和P:Partition tolerance(分区容错性)

三者至多同时满足其中的两个,具体参见CAP猜想: https://people.eecs.berkeley.edu/~brewer/cs262b-2004/PODC-keynote.pdf  —— 由加州大学伯克利分校的Eric Brewer教授于2000年在ACM PODC(Principles of Distributed Computing)会议上提出

  • BASE — BA: Basic Available(基本可用),S: Soft state(软状态)和 E: Eventually consistent(最终一致性)

基于大规模互联网分布式实践经验和CAP理论,权衡考虑CAP中的一致性和可用性,大家发现即使难以满足强一致性(Strong consistency),但应用应该结合自身业务特点,实现在有限时间内达到最终一致性(Eventual consistency)


19、常用的线程池模式以及不同线程池的使用场景—面试出现频率非常高

  • FixedThreadPool

  定长线程池

源码如下:

    public static ExecutorService newFixedThreadPool(int nThreads) {
        return new ThreadPoolExecutor(nThreads, nThreads,
                                      0L, TimeUnit.MILLISECONDS,
                                      new LinkedBlockingQueue<Runnable>());
    }

 

  • CachedThreadPool

  可缓存的线程池

 

源码如下:

    public static ExecutorService newCachedThreadPool() {
        return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
                                      60L, TimeUnit.SECONDS,
                                      new SynchronousQueue<Runnable>());
    }

 

  • SingleThreadExecutor

  单一线程池

  1)只会创建一个工作线程,即corePoolSize和maxiumPoolSize都被设置1,其它的同FixedThreadPool

    public static ExecutorService newSingleThreadExecutor() {
        return new FinalizableDelegatedExecutorService
            (new ThreadPoolExecutor(1, 1,
                                    0L, TimeUnit.MILLISECONDS,
                                    new LinkedBlockingQueue<Runnable>()));
    }

  2)采用的阻塞队列为LinkedBlockingQueue

  • ScheduledThreadPool

  可调度线程池  (处理延时或定时任务)

源码如下:

    public ScheduledThreadPoolExecutor(int corePoolSize) {
        super(corePoolSize, Integer.MAX_VALUE, 0, NANOSECONDS,
              new DelayedWorkQueue());
    }
  •  线程池如果满了,会抛什么异常,拒绝策略包含哪些?(游戏主程的问题)

抛出 "RejectedExecutionException"异常,拒绝策略包括4种(implements RejectedExecutionHandler), 

  1. CallerRunsPolicy —— 让新来的task在调用它的线程里自己跑
  2. AbortPolicy —— 不执行新来的task,直接抛出异常——默认
  3. DiscardPolicy —— 静悄悄地丢弃掉被拒绝地task,不抛出异常通知一下哈
  4. DiscardOldestPolicy —— 丢弃掉最早地未处理地请求;如果线程池关闭,则丢弃掉新来的task

推荐: https://blog.csdn.net/z_s_z2016/article/details/81674893

20、ReentrantLock 和 synchronized的区别
21、AtomicInteger和volatile等线程安全操作的关键字的理解和使用

关于线程安全,我的理解就是一段代码在单线程下跑完的结果在多线程下跑完一样。

volatie作用于fields,语义表示内存可见性。也就是说,一旦最新对此值作了修改,所有的线程都知道;又可防止指令重排序,保证多线程下的线程安全。

AtomicInteger的存储值的value被volatile修饰符限定,即保证了其线程安全。主要用来计数

源码片段如下:

    private static final Unsafe unsafe = Unsafe.getUnsafe();
    private static final long valueOffset;

    static {
        try {
            valueOffset = unsafe.objectFieldOffset
                (AtomicInteger.class.getDeclaredField("value"));
        } catch (Exception ex) { throw new Error(ex); }
    }

    private volatile int value;

 AtomicInteger源码比较简单,主要的几个接口,都是调用Unsafe类的 compareAndSwapInt()、getAndAddInt()、getAndSetInt()实现。

valueOffset,也就是内存偏移量。AtomicInteger的原子操作就是依靠内存偏移量来实现的。

  • 关于CAS(compareAndSwap)后面单独写篇博客吧,乐观锁的机制就是CAS
    public final boolean compareAndSet(int expect, int update) {
        return unsafe.compareAndSwapInt(this, valueOffset, expect, update);
    }

 

简单说,就是更新之前,先读取,看读取的actual vulue 和原来的值(expect)是否是一致的。如果一致,就直接更新;否则更新expect,再次读取。保证了在不加锁的情况下,不会出问题,效率还高

  • Unsafe类读写速度很快,类似C的指针,可以操作系统内存资源、自主管理内存。没有JVM的内存管理,可能会有内存泄漏等安全问题,所以JAVA官方不推荐使用

关于volatile详情,请参见:

关于Unsafe类,请移步 https://tech.meituan.com/2019/02/14/talk-about-java-magic-class-unsafe.html

关于
22、分布式锁三种实现方式
23、socket框架netty的使用,以及NIO的实现原理,为什么是异步非阻塞
24、简述NIO的最佳实践
25、Zookeeper的用途,选举的原理是什么
26、手写一个哈夫曼树

哈夫曼树(Huffman Tree)又称最优二叉树

*****************************************************************************************************

精力有限,想法太多,专注做好一件事就行

  • 我只是一个程序猿。5年内把代码写好,技术博客字字推敲,坚持零拷贝和原创
  • 写博客的意义在于锻炼逻辑条理性,加深对知识的系统性理解,锻炼文笔,如果恰好又对别人有点帮助,那真是一件令人开心的事

*****************************************************************************************************

posted @ 2019-08-29 09:58  CoderBaby  阅读(810)  评论(0编辑  收藏  举报