某东Java面试题

Java类集

1、Collection 是存放一组单值得最大接口,所谓的单值是指集合中的每个元素都是一个对象,一般会很少直接使用此接口直接操作。
2、List 是Collection接口的子接口,也是最常用的接口,此接口对Collection接口进行了大量的扩充。里面的内容是允许重复的。
3、Set 是Collection接口的子类,没有对Collection进行扩充,里面不允许存放重复的内容。
4、Map Map是存放一对值的最大接口,即,接口中每个元素都是一对,以key—>value的形式保存。
5、Iterator 集合的输出接口,用于输出集合中的内容只能进行从前到后的单向输出。
6、ListIterator 是Iterator的子接口,可以进行双向输出。
7、Enumeration 是最早的输出接口,用于输出指定集合中的内容。
8、SortedSet 单值的排序接口,凡是实现此接口的集合类,里面的内容是可以排序的,使用比较器排序。
9、SortedMap 存放一对值的排序接口,实现此接口的集合类,里面的内容按照key排序,使用比较器排序。
10、Queue 队列接口,此接口的子类可以实现队列操作。
11、Map.Entry Map.Entry的内部接口,每个Map.Entry对象都保存着一对key—>value的内容,每个Map接口中都保存着多个Map.Entry接口实例。
以上的接口必须全部掌握。并且掌握各个接口的主要特点。
接口的继承关系

Collection接口:
image

Map接口:
image

String和equals()、hashCode()

Object中的“==”,equal,hashCode()

Object中的==

  • 对于基本数据类型,“==”比较值是否相同。
  • 对于引用数据类型, “==”比较内存中的存放地址是否相同。

Object中的equals()

public boolean equals(Object o) {
    return this == o;
}

Object的equals()方法默认还是根据“==”比较,所以比较的还是内存地址。

Object的equals()方法默认还是根据“==”比较,所以比较的还是内存地址。

public int hashCode() {
    int lockWord = shadow$_monitor_;
    final int lockWordStateMask = 0xC0000000;  // Top 2 bits.
    final int lockWordStateHash = 0x80000000;  // Top 2 bits are value 2 (kStateHash).
    final int lockWordHashMask = 0x0FFFFFFF;  // Low 28 bits.
    if ((lockWord & lockWordStateMask) == lockWordStateHash) {
        return lockWord & lockWordHashMask;
    }
    return System.identityHashCode(this);
}

Object中的hashCode()

public int hashCode() {
    int lockWord = shadow$_monitor_;
    final int lockWordStateMask = 0xC0000000;  // Top 2 bits.
    final int lockWordStateHash = 0x80000000;  // Top 2 bits are value 2 (kStateHash).
    final int lockWordHashMask = 0x0FFFFFFF;  // Low 28 bits.
    if ((lockWord & lockWordStateMask) == lockWordStateHash) {
        return lockWord & lockWordHashMask;
    }
    return System.identityHashCode(this);
}

Object中的hashCode()返回值是在JVM中的32位地址。

String类的“==”,equal,hashCode()

String类中对hashCode()和equals()都进行了重写,所以调用String的equals()和hashCode()可能会和没重写这两个方法的类产生不同的结果。

String的equals()

@Override
public boolean equals(Object other) {
    if (other == this) {
      return true;
    }
    if (other instanceof String) {
        String s = (String)other;
        int count = this.count;
        if (s.count != count) {
            return false;
        }
        // TODO: we want to avoid many boundchecks in the loop below
        // for long Strings until we have array equality intrinsic.
        // Bad benchmarks just push .equals without first getting a
        // hashCode hit (unlike real world use in a Hashtable). Filter
        // out these long strings here. When we get the array equality
        // intrinsic then remove this use of hashCode.
        if (hashCode() != s.hashCode()) {
            return false;
        }
        for (int i = 0; i < count; ++i) {
            if (charAt(i) != s.charAt(i)) {
                return false;
            }
        }
        return true;
    } else {
        return false;
    }
}

同样是如果内存地址相同肯定内容也相同,返回true。
存储地址不同,要满足长度、hash码、对应的字符都相同才能返回true。

String的hashCode()

@Override public int hashCode() {
    int hash = hashCode;
    if (hash == 0) {
        if (count == 0) {
            return 0;
        }
        for (int i = 0; i < count; ++i) {
            hash = 31 * hash + charAt(i);
        }
        hashCode = hash;
    }
    return hash;
}

根据String的成员变量hashCode和字符串的内容生成hashcode。

Spring工作原理

内部最核心的就是IoC了,动态注入(DI),让一个对象的创建不用new了,可以自动的生产,这其实就是利用Java里的反射。反射其实就是在运行时动态的去创建、调用对象,Spring就是在运行时,跟xml Spring的配置
文件来动态的创建对象,和调用对象里的方法的 。
Spring还有一个核心就是AOP这个就是面向切面编程,可以为某一类对象 进行监督和控制(也就是在调用这类对象的具体方法的前后去调用你指定的模块)从而达到对一个模块扩充的功能。这些都是通过
配置类达到的。
Spring目的:就是让对象与对象(模块与模块)之间的关系没有通过代码来关联,都是通过配置类说明管理的(Spring根据这些配置 内部通过反射去动态的组装对象)
要记住:Spring是一个容器,凡是在容器里的对象才会有Spring所提供的这些服务和功能。
Spring里用的最经典的一个设计模式就是:模板方法模式。

Spring AOP与IOC
一、 IoC(Inversion of control): 控制反转
1、IoC:
概念:控制权由对象本身转向容器;由容器根据配置文件去创建实例并创建各个实例之间的依赖关系
核心:bean工厂;在Spring中,bean工厂创建的各个实例称作bean
二、AOP(Aspect-Oriented Programming): 面向方面编程
1、 代理的两种方式:
静态代理:

  • 针对每个具体类分别编写代理类;
  • 针对一个接口编写一个代理类;
    动态代理:
    针对一个方面编写一个InvocationHandler,然后借用JDK反射包中的Proxy类为各种接口动态生成相应的代理类

JDK动态代理的实现原理

首先来看一下如何使用JDK动态代理。JDK提供了java.lang.reflect.Proxy类来实现动态代理的,可通过它的newProxyInstance来获得代理实现类。同时对于代理的接口的实际处理,是一个java.lang.reflect.InvocationHandler,它提供了一个invoke方法供实现者提供相应的代理逻辑的实现。可以对实际的实现进行一些特殊的处理,像Spring AOP中的各种advice。下面来看看如何使用。
被代理的接口:

package com.mikan.proxy;  

public interface HelloWorld {  
  
    void sayHello(String name);  
  
}  

接口的实现类:

package com.mikan.proxy;  
  
public class HelloWorldImpl implements HelloWorld {  
    @Override  
    public void sayHello(String name) {  
        System.out.println("Hello " + name);  
    }  
}  

实现一个java.lang.reflect.InvocationHandler:

package com.mikan.proxy;  
  
import java.lang.reflect.InvocationHandler;  
import java.lang.reflect.Method;  

public class CustomInvocationHandler implements InvocationHandler {  
    private Object target;  
  
    public CustomInvocationHandler(Object target) {  
        this.target = target;  
    }  
  
    @Override  
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {  
        System.out.println("Before invocation");  
        Object retVal = method.invoke(target, args);  
        System.out.println("After invocation");  
        return retVal;  
    }  
}  

使用代理:

package com.mikan.proxy;  
  
import java.lang.reflect.Proxy;  
  
public class ProxyTest {  
  
    public static void main(String[] args) throws Exception {  
        System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles", "true");  
  
        CustomInvocationHandler handler = new CustomInvocationHandler(new HelloWorldImpl());  
        HelloWorld proxy = (HelloWorld) Proxy.newProxyInstance(  
                ProxyTest.class.getClassLoader(),  
                new Class[]{HelloWorld.class},  
                handler);  
        proxy.sayHello("Mikan");  
    }  
}  

可以看到,动态生成的代理类有如下特性:

  • 继承了Proxy类,实现了代理的接口,由于java不能多继承,这里已经继承了Proxy类了,不能再继承其他的类,所以JDK的动态代理不支持对实现类的代理,只支持接口的代理。
  • 提供了一个使用InvocationHandler作为参数的构造方法。
  • 生成静态代码块来初始化接口中方法的Method对象,以及Object类的equals、hashCode、toString方法。
  • 重写了Object类的equals、hashCode、toString,它们都只是简单的调用了InvocationHandler的invoke方法,即可以对其进行特殊的操作,也就是说JDK的动态代理还可以代理上述三个方法。
  • 代理类实现代理接口的sayHello方法中,只是简单的调用了InvocationHandler的invoke方法,我们可以在invoke方法中进行一些特殊操作,甚至不调用实现的方法,直接返回。

Java设计模式

image

事务隔离级别

隔离级别是指若干个并发的事务之间的隔离程度。TransactionDefinition 接口中定义了五个表示隔离级别的常量:

  • TransactionDefinition.ISOLATION_DEFAULT:这是默认值,表示使用底层数据库的默认隔离级别。对大部分数据库而言,通常这值就是TransactionDefinition.ISOLATION_READ_COMMITTED。
  • TransactionDefinition.ISOLATION_READ_UNCOMMITTED:该隔离级别表示一个事务可以读取另一个事务修改但还没有提交的数据。该级别不能防止脏读和不可重复读,因此很少使用该隔离级别。
  • TransactionDefinition.ISOLATION_READ_COMMITTED:该隔离级别表示一个事务只能读取另一个事务已经提交的数据。该级别可以防止脏读,这也是大多数情况下的推荐值。
  • TransactionDefinition.ISOLATION_REPEATABLE_READ:该隔离级别表示一个事务在整个过程中可以多次重复执行某个查询,并且每次返回的记录都相同。即使在多次查询之间有新增的数据满足该查询,这些新增的记录也会被忽略。该级别可以防止脏读和不可重复读。
  • TransactionDefinition.ISOLATION_SERIALIZABLE:所有的事务依次逐个执行,这样事务之间就完全不可能产生干扰,也就是说,该级别可以防止脏读、不可重复读以及幻读。但是这将严重影响程序的性能。通常情况下也不会用到该级别。

事务传播行为

所谓事务的传播行为是指,如果在开始当前事务之前,一个事务上下文已经存在,此时有若干选项可以指定一个事务性方法的执行行为。在TransactionDefinition定义中包括了如下几个表示传播行为的常量:

  • TransactionDefinition.PROPAGATION_REQUIRED:如果当前存在事务,则加入该事务;如果当前没有事务,则创建一个新的事务。
  • TransactionDefinition.PROPAGATION_REQUIRES_NEW:创建一个新的事务,如果当前存在事务,则把当前事务挂起。
  • TransactionDefinition.PROPAGATION_SUPPORTS:如果当前存在事务,则加入该事务;如果当前没有事务,则以非事务的方式继续运行。
  • TransactionDefinition.PROPAGATION_NOT_SUPPORTED:以非事务方式运行,如果当前存在事务,则把当前事务挂起。
  • TransactionDefinition.PROPAGATION_NEVER:以非事务方式运行,如果当前存在事务,则抛出异常。
  • TransactionDefinition.PROPAGATION_MANDATORY:如果当前存在事务,则加入该事务;如果当前没有事务,则抛出异常。
  • TransactionDefinition.PROPAGATION_NESTED:如果当前存在事务,则创建一个事务作为当前事务的嵌套事务来运行;如果当前没有事务,则该取值等价于TransactionDefinition.PROPAGATION_REQUIRED。
    这里需要指出的是,前面的六种事务传播行为是 Spring 从 EJB 中引入的,他们共享相同的概念。而 PROPAGATION_NESTED是 Spring 所特有的。以 PROPAGATION_NESTED 启动的事务内嵌于外部事务中(如果存在外部事务的话),此时,内嵌事务并不是一个独立的事务,它依赖于外部事务的存在,只有通过外部的事务提交,才能引起内部事务的提交,嵌套的子事务不能单独提交。如果熟悉 JDBC 中的保存点(SavePoint)的概念,那嵌套事务就很容易理解了,其实嵌套的子事务就是保存点的一个应用,一个事务中可以包括多个保存点,每一个嵌套子事务。另外,外部事务的回滚也会导致嵌套子事务的回滚。

JVM内存管理

堆内存、栈内存溢出

中间件

HashMap原理

是基于Map接口的实现,存储键值对时,它可以接收null的键值,是非同步的,HashMap存储着Entry(hash, key, value, next)对象。
通过hash的方法,通过put和get存储和获取对象。存储对象时,我们将K/V传给put方法时,它调用hashCode计算hash从而得到bucket位置,进一步存储,HashMap会根据当前bucket的占用情况自动调整容量(超过Load Facotr则resize为原来的2倍)。获取对象时,我们将K传给get,它调用hashCode计算hash从而得到bucket位置,并进一步调用equals()方法确定键值对。如果发生碰撞的时候,Hashmap通过链表将产生碰撞冲突的元素组织起来,在Java 8中,如果一个bucket中碰撞冲突的元素超过某个限制(默认是8),则使用红黑树来替换链表,从而提高速度。

二叉树的遍历

快速排序

堆排,如果从若干数中找最大N个数用最大堆还是最小堆?

你可能感兴趣的:(某东Java面试题)