美文网首页Java高开发JavaJava 杂谈
想面试稳过?想吊打面试官?20个阿里面试题等你来拿!!

想面试稳过?想吊打面试官?20个阿里面试题等你来拿!!

作者: 48730ba83b2d | 来源:发表于2019-05-11 14:04 被阅读8次

    这是高级Java面试系列题中的第一部分。这一部分论述了可变参数,断言,垃圾回收,初始化器,令牌化,日期,日历等等Java核心问题。大家可以先自己试试,然后再参考答案。

    1. 什么是可变参数?

    可变参数允许调用参数数量不同的方法。请看下面例子中的求和方法。此方法可以调用1个int参数,或2个int参数,或多个int参数。

    //int(type) followed ... (three dot's) is syntax of a variable argument. 
     public int sum(int... numbers) { //inside the method a variable argument is similar to an array.
     //number can be treated as if it is declared as int[] numbers;
     int sum = 0; for (int number: numbers) {
     sum += number;
     } return sum;
     } public static void main(String[] args) {
     VariableArgumentExamples example = new VariableArgumentExamples(); //3 Arguments
     System.out.println(example.sum(1, 4, 5));//10
     //4 Arguments
     System.out.println(example.sum(1, 4, 5, 20));//30
     //0 Arguments
     System.out.println(example.sum());//0
     }
    

    2. 断言的用途?

    断言是在Java 1.4中引入的。它能让你验证假设。如果断言失败(即返回false),就会抛出AssertionError(如果启用断言)。基本断言如下所示。

    private int computerSimpleInterest(int principal,float interest,int years){ assert(principal>0); return 100;
    }
    

    3. 什么时候使用断言?

    断言不应该用于验证输入数据到一个public方法或命令行参数。IllegalArgumentException会是一个更好的选择。在public方法中,只用断言来检查它们根本不应该发生的情况。

    4. 什么是垃圾回收?

    垃圾回收是Java中自动内存管理的另一种叫法。垃圾回收的目的是为程序保持尽可能多的可用堆(heap)。 JVM会删除堆上不再需要从堆引用的对象。

    5. 用一个例子解释垃圾回收?

    比方说,下面这个方法就会从函数调用。

    void method(){
     Calendar calendar = new GregorianCalendar(2000,10,30);
     System.out.println(calendar);
    }
    

    通过函数第一行代码中参考变量calendar,在堆上创建了GregorianCalendar类的一个对象。

    函数结束执行后,引用变量calendar不再有效。因此,在方法中没有创建引用到对象。

    JVM认识到这一点,会从堆中删除对象。这就是所谓的垃圾回收。

    6. 什么时候运行垃圾回收?

    垃圾回收在JVM突发奇想和心血来潮时运行(没有那么糟糕)。运行垃圾收集的可能情况是:

    堆可用内存不足

    CPU空闲

    7. 垃圾回收的最佳做法?

    用编程的方式,我们可以要求(记住这只是一个请求——不是一个命令)JVM通过调用System.gc()方法来运行垃圾回收。

    当内存已满,且堆上没有对象可用于垃圾回收时,JVM可能会抛出OutOfMemoryException。

    对象在被垃圾回收从堆上删除之前,会运行finalize()方法。我们建议不要用finalize()方法写任何代码。

    8. 什么是初始化数据块?

    初始化数据块——当创建对象或加载类时运行的代码。

    有两种类型的初始化数据块:

    静态初始化器:加载类时运行的的代码

    实例初始化器:创建新对象时运行的代码

    9. 什么是静态初始化器?

    请看下面的例子:static{ 和 }之间的代码被称为静态初始化器。它只有在第一次加载类时运行。只有静态变量才可以在静态初始化器中进行访问。虽然创建了三个实例,但静态初始化器只运行一次。

    /**
     * 
     */public class InitializerExamples {
     static int count; int i; static{ //This is a static initializers. Run only when Class is first loaded.
     //Only static variables can be accessed
     System.out.println("Static Initializer"); //i = 6;//COMPILER ERROR
     System.out.println("Count when Static Initializer is run is " + count);
     } public static void main(String[] args) {
     InitializerExamples example = new InitializerExamples();
     InitializerExamples example2 = new InitializerExamples();
     InitializerExamples example3 = new InitializerExamples();
     }
    }
    示例输出
    Static Initializer
    Count when Static Initializer is run is 0.
    

    10. 什么是实例初始化块?

    让我们来看一个例子:每次创建类的实例时,实例初始化器中的代码都会运行。

    /**
     * !
     */public class InitializerExamples {
     static int count; int i;
     { //This is an instance initializers. Run every time an object is created.
     //static and instance variables can be accessed
     System.out.println("Instance Initializer");
     i = 6;
     count = count + 1;
     System.out.println("Count when Instance Initializer is run is " + count);
     } public static void main(String[] args) {
     InitializerExamples example = new InitializerExamples();
     InitializerExamples example1 = new InitializerExamples();
     InitializerExamples example2 = new InitializerExamples();
     }
    }
    示例输出
    Instance Initializer
     Count when Instance Initializer is run is 1
     Instance Initializer
     Count when Instance Initializer is run is 2
     Instance Initializer
     Count when Instance Initializer is run is 3
    

    11. 什么是正则表达式?

    正则表达式能让解析、扫描和分割字符串变得非常容易。Java中常用的正则表达式——Patter,Matcher和Scanner类。

    12. 什么是令牌化?

    令牌化是指在分隔符的基础上将一个字符串分割为若干个子字符串。例如,分隔符;分割字符串ac;bd;def;e为四个子字符串ac,bd,def和e。

    分隔符自身也可以是一个常见正则表达式。

    String.split(regex)函数将regex作为参数。

    13. 给出令牌化的例子?

    private static void tokenize(String string,String regex) {
     String[] tokens = string.split(regex);
     System.out.println(Arrays.toString(tokens));
    }
    tokenize("ac;bd;def;e",";");//[ac, bd, def, e]
    

    14.notify和notifyAll区别

    他们的作用都是通知处于等待该对象的线程。

    1、notifyAll使所有原来在该对象上等待被notify的线程统统退出wait的状态,变成等待该对象上的锁,一旦该对象被解锁,他们就会去竞争。

    2、notify是通知其中一个线程,不会通知所有的线程。

    15.谈一谈对MySQL InnoDB的认识

    介绍:

    InnoDB引擎是MySQL数据库的一个重要的存储引擎,和其他存储引擎相比,InnoDB引擎的优点是支持兼容ACID的事务(类似于PostgreSQL),以及参数完整性(有外键)等。现在Innobase实行双认证授权.MySQL5.5.5以后默认的存储引擎都是InnoDB引擎。

    特点是:

    1、具有较好的事务支持:支持4个事务隔离级别,支持多版本读

    2、行级锁定:通过索引实现,全表扫描仍然会是表锁,注意间隙锁的影响

    3、读写阻塞与事务隔离级别相关

    4、具有非常高效的缓存特性:能缓存索引,也能缓存数据

    5、整个表和主键以Cluster方式存储,组成一颗平衡树

    6、所有Secondary Index都会保存主键信息

    适用场景:

    1、需要事务支持(具有较好的事务特性)

    2、行级锁定对高并发有很好的适应能力,但需要确保查询是通过索引完成

    3、数据更新较为频繁的场景

    4、数据一致性要求较高

    5、硬件设备内存较大,可以利用InnoDB较好的缓存能力来提高内存利用率,尽可能减少磁盘IO

    16.谈一谈数据库事务的隔离级别?

    1、Read uncommitted(读未提交)就是一个事务可以读取另一个未提交事务的数据。

    2、Read committed(读提交)就是一个事务要等另一个事务提交后才能读取数据。

    3、Repeatable read(重复读)就是在开始读取数据(事务开启)时,不再允许修改操作。

    4、Serializable(序列化)在该级别下,事务串行化顺序执行,可以避免脏读、不可重复读与幻读。是最高的事务隔离级别,但是这种事务隔离级别效率低下,比较耗数据库性能,一般不使用。

    事务的作用就是保证数据的一致性、完整性。事务隔离级别越高,在并发下会产生的问题就越少,但同时付出的性能消耗也将越大,因此很多时候必须在并发性和性能之间做一个权衡。所以设立了几种事务隔离级别,以便让不同的项目可以根据自己项目的并发情况选择合适的事务隔离级别,对于在事务隔离级别之外会产生的并发问题,在代码中做补偿。

    17.HashMap和ConcurrentHashMap的区别

    1、HashMap 不是线程安全的,而 ConcurrentHashMap 是线程安全的。

    2、ConcurrentHashMap 采用锁分段技术,将整个 Hash 桶进行了分段 segment,也就是将这个大的数组分成了几个小的片段 segment,而且每个小的片段 segment 上面都有锁存在,那么在插入元素的时候就需要先找到应该插入到哪一个片段 segment,然后再在这个片段上面进行插入,而且这里还需要获取 segment 锁。

    3、ConcurrentHashMap 让锁的粒度更精细一些,并发性能更好。

    18.String,StringBuffer和StringBuilder的区别

    1、运行速度,或者说是执行速度,在这方面运行速度快慢为:StringBuilder > StringBuffer > String。

    2、线程安全上,StringBuilder是线程不安全的,而StringBuffer是线程安全的。

    适用场景分析:

    String:适用于少量的字符串操作的情况

    StringBuilder:适用于单线程下在字符缓冲区进行大量操作的情况

    StringBuffer:适用多线程下在字符缓冲区进行大量操作的情况

    19.JVM的内存结构

    根据 JVM 规范,JVM 内存共分为虚拟机栈、堆、方法区、程序计数器、本地方法栈五个部分。

    1、Java虚拟机栈:

    线程私有;每个方法在执行的时候会创建一个栈帧,存储了局部变量表,操作数栈,动态连接,方法返回地址等;每个方法从调用到执行完毕,对应一个栈帧在虚拟机栈中的入栈和出栈。

    2、堆:

    线程共享;被所有线程共享的一块内存区域,在虚拟机启动时创建,用于存放对象实例。

    3、方法区:

    线程共享;被所有线程共享的一块内存区域;用于存储已被虚拟机加载的类信息,常量,静态变量等。

    4、程序计数器:

    线程私有;是当前线程所执行的字节码的行号指示器,每条线程都要有一个独立的程序计数器,这类内存也称为“线程私有”的内存。

    5、本地方法栈:

    线程私有;主要为虚拟机使用到的Native方法服务。

    20.springmvc的核心是什么,请求的流程是怎么处理的,控制反转怎么实现的

    核心:

    控制反转和面向切面

    请求处理流程:

    1、首先用户发送请求到前端控制器,前端控制器根据请求信息(如URL)来决定选择哪一个页面控制器进行处理并把请求委托给它,即以前的控制器的控制逻辑部分;

    2、页面控制器接收到请求后,进行功能处理,首先需要收集和绑定请求参数到一个对象,并进行验证,然后将命令对象委托给业务对象进行处理;处理完毕后返回一个ModelAndView(模型数据和逻辑视图名);

    3、前端控制器收回控制权,然后根据返回的逻辑视图名,选择相应的视图进行渲染,并把模型数据传入以便视图渲染;

    4、前端控制器再次收回控制权,将响应返回给用户。

    控制反转如何实现:

    我们每次使用 spring 框架都要配置xml文件,这个 xml 配置了 bean 的 id 和 class。

    spring 中默认的 bean 为单实例模式,通过 bean 的 class 引用反射机制可以创建这个实例。

    因此,spring 框架通过反射替我们创建好了实例并且替我们维护他们。

    A需要引用B类,spring 框架就会通过 xml 把 B 实例的引用传给了 A 的成员变量。

    觉得文章不错的朋友点个转发吧,让更多的人看到吧

    我个大家整理的一份Java面试资料题集,领取方式:加群:714526711即可免费领取

    先到先得,快来领取吧,(__) 嘻嘻**

    想面试稳过?想吊打面试官?20个阿里面试题等你来拿!! 想面试稳过?想吊打面试官?20个阿里面试题等你来拿!!

    相关文章

      网友评论

        本文标题:想面试稳过?想吊打面试官?20个阿里面试题等你来拿!!

        本文链接:https://www.haomeiwen.com/subject/wzsxaqtx.html