Press "Enter" to skip to content

阿里面试题总结

  1. 自我介绍
  2. 做过的项目 

Java 基础) 

  1. Java的四个基本特性(抽象、封装、继承,多态),对多态的理解(多态的实现方式)以及在项目中那些地方用到多态 
    • Java的四个基本特性 
      • 抽象:抽象是将一类对象的共同特征总结出来构造类的过程,包括数据抽象和行为抽象两方面。抽象只关注对象有哪些属性和行为,并不关注这些行为的细节是什么。  
      • 继承:继承是从已有类得到继承信息创建新类的过程。提供继承信息的类被称为父类(超类、基类);得到继承信息的类被称为子类(派生类)。继承让变化中的软件系统有了一定的延续性,同时继承也是封装程序中可变因素的重要手段。 
      •  封装:通常认为封装是把数据和操作数据的方法绑定起来,对数据的访问只能通过已定义的接口。面向对象的本质就是将现实世界描绘成一系列完全自治、封闭的对象。我们在类中编写的方法就是对实现细节的一种封装;我们编写一个类就是对数据和数据操作的封装。可以说,封装就是隐藏一切可隐藏的东西,只向外界提供最简单的编程接口。 
      • 多态性是指允许不同子类型的对象对同一消息作出不同的响应。 
    • 多态的理解(多态的实现方式) 
      • 方法重载(overload)实现的是编译时的多态性(也称为前绑定)。 
      • 方法重写(override)实现的是运行时的多态性(也称为后绑定)。运行时的多态是面向对象最精髓的东西。 
      • 要实现多态需要做两件事:1). 方法重写(子类继承父类并重写父类中已有的或抽象的方法);2). 对象造型(用父类型引用引用子类型对象,这样同样的引用调用同样的方法就会根据子类对象的不同而表现出不同的行为)。 
    • 项目中对多态的应用 
      • 举一个简单的例子,在物流信息管理系统中,有两种用户:订购客户和卖房客户,两个客户都可以登录系统,他们有相同的方法Login,但登陆之后他们会进入到不同的页面,也就是在登录的时候会有不同的操作,两种客户都继承父类的Login方法,但对于不同的对象,拥有不同的操作。 
     
  2. 面向对象和面向过程的区别?用面向过程可以实现面向对象吗?那是不是不能面向对象? 
    • 面向对象和面向过程的区别 
      • 面向过程就像是一个细心的管家,事无具细的都要考虑到。而面向对象就像是个家用电器,你只需要知道他的功能,不需要知道它的工作原理。 
      • 面向过程”是一种是事件为中心的编程思想。就是分析出解决问题所需的步骤,然后用函数把这些步骤实现,并按顺序调用。面向对象是以“对象”为中心的编程思想。 
      • 简单的举个例子:汽车发动、汽车到站 
        • 这对于“面向过程”来说,是两个事件,汽车启动是一个事件,汽车到站是另一个事件,面向过程编程的过程中我们关心的是事件,而不是汽车本身。针对上述两个事件,形成两个函数,之 后依次调用。 
        • 然而这对于面向对象来说,我们关心的是汽车这类对象,两个事件只是这类对象所具有的行为。而且对于这两个行为的顺序没有强制要求。 
    • 用面向过程可以实现面向对象吗  
    • 那是不是不能面向对象 
  3. 重载和重写,如何确定调用哪个函数 
    • 重载:重载发生在同一个类中,同名的方法如果有不同的参数列表(参数类型不同、参数个数不同或者二者都不同)则视为重载。 
    • 重写:重写发生在子类与父类之间,重写要求子类被重写方法与父类被重写方法有相同的返回类型,比父类被重写方法更好访问,不能比父类被重写方法声明更多的异常(里氏代换原则)。根据不同的子类对象确定调用的那个方法。 
  4. 面向对象开发的六个基本原则(单一职责、开放封闭、里氏替换、依赖倒置、合成聚合复用、接口隔离),迪米特法则。在项目中用过哪些原则 
    • 六个基本原则 
      • 单一职责:一个类只做它该做的事情(高内聚)。在面向对象中,如果只让一个类完成它该做的事,而不涉及与它无关的领域就是践行了高内聚的原则,这个类就只有单一职责。 
      • 开放封闭:软件实体应当对扩展开放,对修改关闭。要做到开闭有两个要点:抽象是关键,一个系统中如果没有抽象类或接口系统就没有扩展点;封装可变性,将系统中的各种可变因素封装到一个继承结构中,如果多个可变因素混杂在一起,系统将变得复杂而换乱。 
      • 里氏替换:任何时候都可以用子类型替换掉父类型。子类一定是增加父类的能力而不是减少父类的能力,因为子类比父类的能力更多,把能力多的对象当成能力少的对象来用当然没有任何问题。 
      • 依赖倒置:面向接口编程。(该原则说得直白和具体一些就是声明方法的参数类型、方法的返回类型、变量的引用类型时,尽可能使用抽象类型而不用具体类型,因为抽象类型可以被它的任何一个子类型所替代)  
      • 合成聚和复用:优先使用聚合或合成关系复用代码。 
      • 接口隔离:接口要小而专,绝不能大而全。臃肿的接口是对接口的污染,既然接口表示能力,那么一个接口只应该描述一种能力,接口也应该是高度内聚的。 
    • 迪米特法则 
      • 迪米特法则又叫最少知识原则,一个对象应当对其他对象有尽可能少的了解。 
    • 项目中用到的原则 
      • 单一职责、开放封闭、合成聚合复用(最简单的例子就是String)、接口隔离 
  5. staticfinal的区别和用途 
    • Static 
      • 修饰变量:静态变量随着类加载时被完成初始化,内存中只有一个,且JVM也只会为它分配一次内存,所有类共享静态变量 
      • 修饰方法:在类加载的时候就存在,不依赖任何实例;static方法必须实现,不能用abstract修饰 
      • 修饰代码块:在类加载完之后就会执行代码块中的内容。 
      • 父类静态代码块->子类静态代码块->父类非静态代码块->父类构造方法->子类非静态代码块->子类构造方法 
    • Final 
      • 修饰变量: 
        • 编译期常量:类加载的过程完成初始化,编译后带入到任何计算式中。只能是基本类型。 
        • 运行时常量:基本数据类型或引用数据类型。引用不可变,但引用的对象内容可变。 
      • 修饰方法:不能被继承,不能被子类修改。 
      • 修饰类:不能被继承。 
      • 修饰形参:final形参不可变 
  6. Hash MapHash Table的区别,Hash Map中的key可以是任何对象或数据类型吗?HashTable是线程安全的么? 
    • Hash MapHash Table的区别 
      • Hashtable的方法是同步的,HashMap未经同步,所以在多线程场合要手动同步HashMap这个区别就像VectorArrayList一样。 
      • Hashtable不允许 null (key  value 都不可以)HashMap允许 null (keyvalue都可以) 
      • 两者的遍历方式大同小异,Hashtable仅仅比HashMap多一个elements方法。 
      Hashtable 和 HashMap 都能通过values()方法返回一个 Collection ,然后进行遍历处理。 两者也都可以通过 entrySet() 方法返回一个 Set  然后进行遍历处理。 
      • HashTable使用EnumerationHashMap使用Iterator 
      • 哈希值的使用不同,Hashtable直接使用对象的hashCode。而HashMap重新计算hash值,而且用于代替求模。 
      • Hashtablehash数组默认大小是11,增加的方式是 old*2+1HashMaphash数组的默认大小是16,而且一定是2的指数。 
      • HashTable基于Dictionary类,而HashMap基于AbstractMap 
    • Hash Map中的key可以是任何对象或数据类型吗 
      • 可以为null,但不能是可变对象,如果是可变对象的话,对象中的属性改变,则对象HashCode也进行相应的改变,导致下次无法查找到已存在Map中的数据。 
      • 如果可变对象在HashMap中被用作键,那就要小心在改变对象状态的时候,不要改变它的哈希值了。我们只需要保证成员变量的改变能保证该对象的哈希值不变即可。 
    • HashTable是线程安全的么 
      • HashTable是线程安全的,其实现是在对应的方法上添加了synchronized关键字进行修饰,由于在执行此方法的时候需要获得对象锁,则执行起来比较慢。所以现在如果为了保证线程安全的话,使用CurrentHashMap 
  7. HashMap和Concurrent HashMap区别, Concurrent HashMap 线程安全吗, Concurrent HashMap如何保证 线程安全? 
    • HashMapConcurrentHashMap区别? 
      • HashMap是非线程安全的,CurrentHashMap是线程安全的。 
      • ConcurrentHashMap将整个Hash桶进行了分段segment,也就是将这个大的数组分成了几个小的片段segment,而且每个小的片段segment上面都有锁存在,那么在插入元素的时候就需要先找到应该插入到哪一个片段segment,然后再在这个片段上面进行插入,而且这里还需要获取segment锁。 
      • ConcurrentHashMap让锁的粒度更精细一些,并发性能更好。 
    • ConcurrentHashMap 线程安全吗, ConcurrentHashMap如何保证 线程安全? 
      • HashTable容器在竞争激烈的并发环境下表现出效率低下的原因是所有访问HashTable的线程都必须竞争同一把锁,那假如容器里有多把锁,每一把锁用于锁容器其中一部分数据,那么当多线程访问容器里不同数据段的数据时,线程间就不会存在锁竞争,从而可以有效的提高并发访问效率,这就是ConcurrentHashMap所使用的锁分段技术,首先将数据分成一段一段的存储,然后给每一段数据配一把锁,当一个线程占用锁访问其中一个段数据的时候,其他段的数据也能被其他线程访问。 
      • get操作的高效之处在于整个get过程不需要加锁,除非读到的值是空的才会加锁重读get方法里将要使用的共享变量都定义成volatile,如用于统计当前Segement大小的count字段和用于存储值的HashEntryvalue。定义成volatile的变量,能够在线程之间保持可见性,能够被多线程同时读,并且保证不会读到过期的值,但是只能被单线程写(有一种情况可以被多线程写,就是写入的值不依赖于原值),在get操作里只需要读不需要写共享变量countvalue,所以可以不用加锁。 
      • Put方法首先定位到Segment,然后在Segment里进行插入操作。插入操作需要经历两个步骤,第一步判断是否需要对Segment里的HashEntry数组进行扩容,第二步定位添加元素的位置然后放在HashEntry数组里。 
       
  8. 因为别人知道源码怎么实现的,故意构造相同的hash的字符串进行攻击,怎么处理?jdk7怎么办? 
    • 怎么处理构造相同hash的字符串进行攻击? 
      • 当客户端提交一个请求并附带参数的时候,web应用服务器会把我们的参数转化成一个HashMap存储,这个HashMap的逻辑结构如下:key1–>value1; 
      • 但是物理存储结构是不同的,key值会被转化成Hashcode,这个hashcode有会被转成数组的下标:0–>value1 
      • 不同的string就会产生相同hashcode而导致碰撞,碰撞后的物理存储结构可能如下:0–>value1–>value2; 
      • 1、限制postget的参数个数,越少越好 
      2、限制post数据包的大小 3WAF 
    • Jdk7 如何处理hashcode字符串攻击 
      • HashMap会动态的使用一个专门的treemap实现来替换掉它 
  9. StringStringBufferStringBuilder以及对String不变性的理解 
    • StringStringBufferStringBuilder 
      • 都是 final 都不允许被继承; 
      • String 长度是不可变的, StringBufferStringBuilder 长度是可变的; 
      • StringBuffer 是线程安全的, StringBuilder 不是线程安全的,但它们两个中的所有方法都是相同的,StringBufferStringBuilder的方法之上添加了synchronized修饰,保证线程安全。 
      • StringBuilderStringBuffer拥有更好的性能。 
      • 如果一个String类型的字符串,在编译时就可以确定是一个字符串常量,则编译完成之后,字符串会自动拼接成一个常量。此时String的速度比StringBufferStringBuilder的性能好的多。 
    • String不变性的理解 
      • String 类是被final进行修饰的,不能被继承。 
      • 在用+号链接字符串的时候会创建新的字符串。 
      • String s = new String(“Hello world”); 可能创建两个对象也可能创建一个对象。如果静态区中有“Hello world”字符串常量对象的话,则仅仅在堆中创建一个对象。如果静态区中没有“Hello world”对象,则堆上和静态区中都需要创建对象。 
      •   java 通过使用 “+” 符号来串联字符串的时候实际上底层会转成通过 StringBuilder 实例的 append() 方法来实现 
  1. String有重写ObjecthashcodetoString吗?如果重写equals不重写hashcode会出现什么问题? 
    • String有重写ObjecthashcodetoString 
      • String重写了Object类的hashcodetoString方法。 
    • equals方法被重写时,通常有必要重写hashCode方法,以维护hashCode方法的常规协定,该协定声明相对等的两个对象必须有相同的hashCode 
      • object1.euqal(object2)时为true object1.hashCode() ==  object2.hashCode() true 
      • object1.hashCode() ==  object2.hashCode() false时,object1.euqal(object2)必定为false 
      • object1.hashCode() ==  object2.hashCode() true时,但object1.euqal(object2)不一定定为true 
    • 重写equals不重写hashcode会出现什么问题 
      • 在存储散列集合时(Set),如果原对象.equals(新对象),但没有对hashCode重写,即两个对象拥有不同的hashCode,则在集合中将会存储两个值相同的对象,从而导致混淆。因此在重写equals方法时,必须重写hashCode方法。 
  1. Java序列化,如何实现序列化和反序列化,常见的序列化协议有哪些 
    • Java序列化定义 
      • 将那些实现了Serializable接口的对象转换成一个字节序列,并能够在以后将这个字节序列完全恢复为原来的对象,序列化可以弥补不同操作系统之间的差异 
    • Java序列化的作用 
      • Java远程方法调用(RMI 
      • JavaBeans进行序列化 
    • 如何实现序列化和反序列化 
      • 实现序列化方法 
        • 实现Serializable接口 
          • 该接口只是一个可序列化的标志,并没有包含实际的属性和方法。 
          • 如果不在改方法中添加readObject()writeObject()方法,则采取默认的序列化机制。如果添加了这两个方法之后还想利用Java默认的序列化机制,则在这两个方法中分别调用defaultReadObject()defaultWriteObject()两个方法。 
          • 为了保证安全性,可以使用transient关键字进行修饰不必序列化的属性。因为在反序列化时,private修饰的属性也能发查看到。 
        • 实现ExternalSerializable方法 
          • 自己对要序列化的内容进行控制,控制那些属性能被序列化,那些不能被序列化。 
      • 反序列化 
        • 实现Serializable接口的对象在反序列化时不需要调用对象所在类的构造方法,完全基于字节。 
        • 实现externalSerializable接口的方法在反序列化时会调用构造方法。 
      • 注意事项 
        • static修饰的属性不会被序列化 
        • 对象的类名、属性都会被序列化,方法不会被序列化 
        • 要保证序列化对象所在类的属性也是可以被序列化的 
        • 当通过网络、文件进行序列化时,必须按照写入的顺序读取对象。 
        • 反序列化时必须有序列化对象时的class文件 
        • 最好显示的声明serializableID,因为在不同的JVM之间,默认生成serializableID 可能不同,会造成反序列化失败。 
    • 常见的序列化协议有哪些 
      • COM主要用于Windows平台,并没有真正实现跨平台,另外COM的序列化的原理利用了编译器中虚表,使得其学习成本巨大 
      • CORBA是早期比较好的实现了跨平台,跨语言的序列化协议。COBRA的主要问题是参与方过多带来的版本过多,版本之间兼容性较差,以及使用复杂晦涩 
      • XML&SOAP 
        • XML是一种常用的序列化和反序列化协议,具有跨机器,跨语言等优点。 
        • SOAPSimple Object Access protocol) 是一种被广泛应用的,基于XML为序列化和反序列化协议的结构化消息传递协议。SOAP具有安全、可扩展、跨语言、跨平台并支持多种传输层协议。 
      • JSONJavascript Object Notation 
        • 这种Associative array格式非常符合工程师对对象的理解。 
        • 它保持了XML的人眼可读(Human-readable)的优点。 
        • 相对于XML而言,序列化后的数据更加简洁。  
        • 它具备Javascript的先天性支持,所以被广泛应用于Web browser的应用常景中,是Ajax的事实标准协议。 
        • XML相比,其协议比较简单,解析速度比较快。 
        • 松散的Associative array使得其具有良好的可扩展性和兼容性。 
      • ThriftFacebook开源提供的一个高性能,轻量级RPC服务框架,其产生正是为了满足当前大数据量、分布式、跨语言、跨平台数据通讯的需求。Thrift在空间开销和解析性能上有了比较大的提升,对于对性能要求比较高的分布式系统,它是一个优秀的RPC解决方案;但是由于Thrift的序列化被嵌入到Thrift框架里面,Thrift框架本身并没有透出序列化和反序列化接口,这导致其很难和其他传输层协议共同使用 
      • Protobuf具备了优秀的序列化协议的所需的众多典型特征 
        • 标准的IDLIDL编译器,这使得其对工程师非常友好。 
        • 序列化数据非常简洁,紧凑,与XML相比,其序列化之后的数据量约为1/31/10 
        • 解析速度非常快,比对应的XML快约20-100倍。 
        • 提供了非常友好的动态库,使用非常简介,反序列化只需要一行代码。由于其解析性能高,序列化后数据量相对少,非常适合应用层对象的持久化场景 
         
      • Avro的产生解决了JSON的冗长和没有IDL的问题,Avro属于Apache Hadoop的一个子项目。 Avro提供两种序列化格式:JSON格式或者Binary格式。Binary格式在空间开销和解析性能方面可以和Protobuf媲美,JSON格式方便测试阶段的调试。适合于高性能的序列化服务。 
    • 几种协议的对比 
      • XML序列化(Xstream)无论在性能和简洁性上比较差; 
      • ThriftProtobuf相比在时空开销方面都有一定的劣势; 
      • ProtobufAvro在两方面表现都非常优越。 
       
  2. Java实现多线程的方式及三种方式的区别 
    • 实现多线程的方式 
      • 继承Thread类,重写run函数。 
      • 实现Runnable接口 
      • 实现Callable接口 
    • 三种方式的区别 
      • 实现Runnable接口可以避免Java单继承特性而带来的局限;增强程序的健壮性,代码能够被多个线程共享,代码与数据是独立的;适合多个相同程序代码的线程区处理同一资源的情况。 
      • 继承Thread类和实现Runnable方法启动线程都是使用start方法,然后JVM虚拟机将此线程放到就绪队列中,如果有处理机可用,则执行run方法。 
      • 实现Callable接口要实现call方法,并且线程执行完毕后会有返回值。其他的两种都是重写run方法,没有返回值。 
       
  3. 线程安全 
    • 定义 
      • 某个类的行为与其规范一致。 
      • 不管多个线程是怎样的执行顺序和优先级,或是wait,sleep,join等控制方式,,如果一个类在多线程访问下运转一切正常,并且访问类不需要进行额外的同步处理或者协调,那么我们就认为它是线程安全的。  
    • 如何保证线程安全?6 
      • 对变量使用volitate 
      • 对程序段进行加锁(synchronized,lock) 
    • 注意 
      • 非线程安全的集合在多线程环境下可以使用,但并不能作为多个线程共享的属性,可以作为某个线程独享的属性。 
      • 例如Vector是线程安全的,ArrayList不是线程安全的。如果每一个线程中new一个ArrayList,而这个ArrayList只是在这一个线程中使用,肯定没问题。 
  4. 多线程如何进行信息交互 
    • Object中的方法,wait() notify()notifyAll(); 
  5. 多线程共用一个数据变量需要注意什么? 
    • 当我们在线程对象(Runnable)中定义了全局变量,run方法会修改该变量时,如果有多个线程同时使用该线程对象,那么就会造成全局变量的值被同时修改,造成错误. 
    • ThreadLocalJDK引入的一种机制,它用于解决线程间共享变量,使用ThreadLocal声明的变量,即使在线程中属于全局变量,针对每个线程来讲,这个变量也是独立的。 
    • volatile变量每次被线程访问时,都强迫线程从主内存中重读该变量的最新值,而当该变量发生修改变化时,也会强迫线程将最新的值刷新回主内存中。这样一来,不同的线程都能及时的看到该变量的最新值。 
  6. 什么是线程池?如果让你设计一个动态大小的线程池,如何设计,应该有哪些方法? 
    • 什么是线程池 
      • 线程池顾名思义就是事先创建若干个可执行的线程放入一个池(容器)中,需要的时候从池中获取线程不用自行创建,使用完毕不需要销毁线程而是放回池中,从而减少创建和销毁线程对象的开销。 
    • 设计一个动态大小的线程池,如何设计,应该有哪些方法 
      • 一个线程池包括以下四个基本组成部分: 
        • 线程管理器(ThreadPool):用于创建并管理线程池,包括创建线程,销毁线程池,添加新任务; 
        • 工作线程(PoolWorker):线程池中线程,在没有任务时处于等待状态,可以循环的执行任务; 
        • 任务接口(Task):每个任务必须实现的接口,以供工作线程调度任务的执行,它主要规定了任务的入口,任务执行完后的收尾工作,任务的执行状态等; 
        • 任务队列(TaskQueue):用于存放没有处理的任务。提供一种缓冲机制; 
      • 所包含的方法 
        • private ThreadPool()  创建线程池 
        • public static ThreadPool getThreadPool()  获得一个默认线程个数的线程池  
        •  public void execute(Runnable task)  执行任务,其实只是把任务加入任务队列,什么时候执行有线程池管理器决定 
        • public void execute(Runnable[] task)  批量执行任务,其实只是把任务加入任务队列,什么时候执行有线程池管理器决定 
        • public void destroy()  销毁线程池,该方法保证在所有任务都完成的情况下才销毁所有线程,否则等待任务完成才销毁 
        • public int getWorkThreadNumber() 返回工作线程的个数  
        • public int getFinishedTasknumber() 返回已完成任务的个数,这里的已完成是只出了任务队列的任务个数,可能该任务并没有实际执行完成 
        • public void addThread() 在保证线程池中所有线程正在执行,并且要执行线程的个数大于某一值时。增加线程池中线程的个数 
        • public void reduceThread() 在保证线程池中有很大一部分线程处于空闲状态,并且空闲状态的线程在小于某一值时,减少线程池中线程的个数  
         
  7. Java是否有内存泄露和内存溢出 
    • 静态集合类,使用SetVectorHashMap等集合类的时候需要特别注意。当这些类被定义成静态的时候,由于他们的生命周期跟应用程序一样长,这时候就有可能发生内存泄漏。 
    例子 class StaticTest 
    { 
        private static Vector v = new Vector(10); 
     public void init() 
        { 
            for (int i = 1; i < 100; i++) 
            { 
                Object object = new Object(); 
                v.add(object); 
                object = null; 
            } 
        } 
    } 在上面的代码中,循环申请了Object对象,并添加到Vector中,然后设置为null,可是这些对象呗vector引用着,因此必能被GC回收,因此造成内存泄漏。因此要释放这些对象,还需要将它们从vector删除,最简单的方法就是将vector设置为null  
    • 监听器: Java编程中,我们都需要和监听器打交道,通常一个应用中会用到很多监听器,我们会调用一个控件,诸如addXXXListener()等方法来增加监听器,但往往在释放的时候却没有去删除这些监听器,从而增加了内存泄漏的机会。 
     
    • 物理连接:一些物理连接,比如数据库连接和网络连接,除非其显式的关闭了连接,否则是不会自动被GC 回收的。Java 数据库连接一般用DataSource.getConnection()来创建,当不再使用时必须用Close()方法来释放,因为这些连接是独立于JVM的。对于Resultset Statement 对象可以不进行显式回收,但Connection 一定要显式回收,因为Connection 在任何时候都无法自动回收,Connection一旦回收,Resultset Statement 对象就会立即为NULL但是如果使用连接池,情况就不一样了,除了要显式地关闭连接,还必须显式地关闭Resultset Statement 对象(关闭其中一个,另外一个也会关闭),否则就会造成大量的Statement 对象无法释放,从而引起内存泄漏。。一般情况下,在try代码块里创建连接,在finally里释放连接,就能够避免此类内存泄漏。 
     
    • 内部类和外部模块等的引用:内部类的引用是比较容易遗忘的一种,而且一旦没释放可能导致一系列的后继类对象没有释放。在调用外部模块的时候,也应该注意防止内存泄漏,如果模块A调用了外部模块B的一个方法,如: 
    public void register(Object o) 这个方法有可能就使得A模块持有传入对象的引用,这时候需要查看B模块是否提供了出去引用的方法,这种情况容易忽略,而且发生内存泄漏的话,还比较难察觉。  
    • 单例模式:因为单利对象初始化后将在JVM的整个生命周期内存在,如果它持有一个外部对象的(生命周期比较短)引用,那么这个外部对象就不能被回收,从而导致内存泄漏。如果这个外部对象还持有其他对象的引用,那么内存泄漏更严重。 
  1. concurrent包下面,都用过什么?  
    • concurrent下面的包 
      • Executor  用来创建线程池,在实现Callable接口时,添加线程。 
      • FeatureTask  FutureTask  get 方法所返回的结果类型。 
      • TimeUnit 
      • Semaphore  
      • LinkedBlockingQueue  
    • 所用过的类 
      • Executor   
       
  2. volatile 关键字的如何保证内存可见性 
    • volatile 关键字的作用 
      • 保证内存的可见性 
      • 防止指令重排 
      • 注意:volatile 并不保证原子性 
    • 内存可见性 
      • volatile保证可见性的原理是在每次访问变量时都会进行一次刷新,因此每次访问都是主内存中最新的版本。所以volatile关键字的作用之一就是保证变量修改的实时可见性。 
    • 当且仅当满足以下所有条件时,才应该使用volatile变量 
      • 对变量的写入操作不依赖变量的当前值,或者你能确保只有单个线程更新变量的值。 
      • 该变量没有包含在具有其他变量的不变式中。 
    • volatile使用建议 
      • 在两个或者更多的线程需要访问的成员变量上使用volatile。当要访问的变量已在synchronized代码块中,或者为常量时,没必要使用volatile 
      • 由于使用volatile屏蔽掉了JVM中必要的代码优化,所以在效率上比较低,因此一定在必要时才使用此关键字。 
    • volatilesynchronized区别 
      • volatile不会进行加锁操作: 
      volatile变量是一种稍弱的同步机制在访问volatile变量时不会执行加锁操作,因此也就不会使执行线程阻塞,因此volatile变量是一种比synchronized关键字更轻量级的同步机制。 
      •  volatile 变量作用类似于同步变量读写操作: 
      从内存可见性的角度看,写入volatile变量相当于退出同步代码块,而读取volatile变量相当于进入同步代码块。 
      • volatile 不如 synchronized安全: 
      在代码中如果过度依赖volatile变量来控制状态的可见性,通常会比使用锁的代码更脆弱,也更难以理解。仅当volatile变量能简化代码的实现以及对同步策略的验证时,才应该使用它。一般来说,用同步机制会更安全些。 
      • volatile 无法同时保证内存可见性和原则性: 
      加锁机制(即同步机制)既可以确保可见性又可以确保原子性,而volatile变量只能确保可见性,原因是声明为volatile的简单变量如果当前值与该变量以前的值相关,那么volatile关键字不起作用,也就是说如下的表达式都不是原子操作:“count++”、“count = count+1”  
  3. sleepwait分别是那个类的方法,有什么区别 
    • sleepwait 
      • sleepThread类的方法 
      • waitObject类的方法 
    • 有什么区别 
      • sleep()方法(休眠)是线程类(Thread)的静态方法,调用此方法会让当前线程暂停执行指定的时间,将执行机会(CPU)让给其他线程,但是对象的锁依然保持,因此休眠时间结束后会自动恢复(线程回到就绪状态)。 
      • wait()Object类的方法,调用对象的wait()方法导致当前线程放弃对象的锁(线程暂停执行),进入对象的等待池(wait pool),只有调用对象的notify()方法(或notifyAll()方法)时才能唤醒等待池中的线程进入等锁池(lock pool),如果线程重新获得对象的锁就可以进入就绪状态。 
  4. synchronizedlock的区别,使用场景。看过synchronized的源码没 
    • synchronizedlock的区别 
      • (用法)synchronized(隐式锁):在需要同步的对象中加入此控制,synchronized可以加在方法上,也可以加在特定代码块中,括号中表示需要锁的对象。 
      • (用法)lock(显示锁):需要显示指定起始位置和终止位置。一般使用ReentrantLock类做为锁,多个线程中必须要使用一个ReentrantLock类做为对 象才能保证锁的生效。且在加锁和解锁处需要通过lock()unlock()显示指出。所以一般会在finally块中写unlock()以防死锁。 
      • (性能)synchronized是托管给JVM执行的,而lockjava写的控制锁的代码。在Java1.5中,synchronize是性能低效的。因为 这是一个重量级操作,需要调用操作接口,导致有可能加锁消耗的系统时间比加锁以外的操作还多。相比之下使用Java提供的Lock对象,性能更高一些。但 是到了Java1.6,发生了变化。synchronize在语义上很清晰,可以进行很多优化,有适应自旋,锁消除,锁粗化,轻量级锁,偏向锁等等。导致 在Java1.6synchronize的性能并不比Lock差。 
      • (机制)synchronized原始采用的是CPU悲观锁机制,即线程获得的是独占锁。独占锁意味着其 他线程只能依靠阻塞来等待线程释放锁。Lock用的是乐观锁方式。所谓乐观锁就是,每次不加锁而是假设没有冲突而去完成某项操作,如果因为冲突失败就重试,直到成功为止。乐观锁实现的机制就 CAS操作(Compare and Swap)。 
       
  5. synchronized底层如何实现的?用在代码块和方法上有什么区别? 
    • synchronized底层如何实现的 
     
    • 用在代码块和方法上有什么区别? 
      • synchronized用在代码块锁的是调用该方法的对象(this),也可以选择锁住任何一个对象。 
      • synchronized用在方法上锁的是调用该方法的对象, 
      • synchronized用在代码块可以减小锁的粒度,从而提高并发性能。 
      • 无论用在代码块上还是用在方法上,都是获取对象的锁;每一个对象只有一个锁与之相关联;实现同步需要很大的系统开销作为代价,甚至可能造成死锁,所以尽量避免无谓的同步控制。 
    •  synchronizedstatic synchronized的区别 
      • synchronized是对类的当前实例进行加锁,防止其他线程同时访问该类的该实例的所有synchronized,同一个类的两个不同实例就没有这种约束了。 
      • 那么static synchronized恰好就是要控制类的所有实例的访问了,static synchronized是限制线程同时访问jvm中该类的所有实例同时访问对应的代码快。 
  1. 常见异常分为那两种(ExceptionError),常见异常的基类以及常见的异常 
    • Throwablejava语言中所有错误和异常的超类(万物即可抛)。它有两个子类:ErrorException 
    • 异常种类 
      • ErrorError为错误,是程序无法处理的,如OutOfMemoryErrorThreadDeath等,出现这种情况你唯一能做的就是听之任之,交由JVM来处理,不过JVM在大多数情况下会选择终止线程。 
      • ExceptionException是程序可以处理的异常。它又分为两种CheckedException(受捡异常),一种是UncheckedException(不受检异常)。 
        • CheckException发生在编译阶段,必须要使用try…catch(或者throws)否则编译不通过 
        • UncheckedException发生在运行期,具有不确定性,主要是由于程序的逻辑问题所引起的,难以排查,我们一般都需要纵观全局才能够发现这类的异常错误,所以在程序设计中我们需要认真考虑,好好写代码,尽量处理异常,即使产生了异常,也能尽量保证程序朝着有利方向发展。 
    • 常见异常的基类 
      • IOException 
      • RuntimeException 
    • 常见的异常 图像
  2. Java中的NIO,BIO,AIO分别是什么?  
    • BIO 
      • 同步并阻塞,服务器实现模式为一个连接一个线程,即客户端有连接请求时服务器端就需要启动一个线程进行处理,如果这个连接不做任何事情会造成不必要的线程开销,当然可以通过线程池机制改善。 
      • BIO方式适用于连接数目比较小且固定的架构,这种方式对服务器资源要求比较高,并发局限于应用中,JDK1.4以前的唯一选择,但程序直观简单易理解。 
    • NIO  
      • 同步非阻塞,服务器实现模式为一个请求一个线程,即客户端发送的连接请求都会注册到多路复用器上,多路复用器轮询到连接有I/O请求时才启动一个线程进行处理。 
      • NIO方式适用于连接数目多且连接比较短(轻操作)的架构,比如聊天服务器,并发局限于应用中,编程比较复杂,JDK1.4开始支持。 
    • AIO 
      •  异步非阻塞,服务器实现模式为一个有效请求一个线程,客户端的I/O请求都是由OS先完成了再通知服务器应用去启动线程进行处理. 
      • AIO方式使用于连接数目多且连接比较长(重操作)的架构,比如相册服务器,充分调用OS参与并发操作,编程比较复杂,JDK7开始支持。 
  3. 所了解的设计模式,单例模式的注意事项,jdk源码哪些用到了你说的设计模式 
    • 所了解的设计模式 
      • 工厂模式:定义一个用于创建对象的接口,让子类决定实例化哪一个类, Factory Method 使一个类的实例化延迟到了子类。 
      • 单例模式:保证一个类只有一个实例,并提供一个访问它的全局访问点; 
      • 适配器模式:将一类的接口转换成客户希望的另外一个接口,Adapter 模式使得原本由于接口不兼容而不能一起工作那些类可以一起工作。 
      • 装饰者模式:动态地给一个对象增加一些额外的职责,就增加的功能来说, Decorator 模式相比生成子类更加灵活。 
      • 代理:为其他对象提供一种代理以控制对这个对象的访问 
      • 迭代器模式:提供一个方法顺序访问一个聚合对象的各个元素,而又不需要暴露该对象的内部表示。 
    • 单例模式的注意事项 
      • 尽量使用懒加载 
      • 双重检索实现线程安全 
      • 构造方法为private 
      • 定义静态的Singleton instance对象和getInstance()方法 
    • jdk源码中用到的设计模式 
      • 装饰器模式:IO流中 
      • 迭代器模式:Iterator 
      • 单利模式: java.lang.Runtime 
      • 代理模式:RMI 
  4. 匿名内部类是什么?如何访问在其外面定义的变量? 
    • 匿名内部类是什么? 
      • 匿名内部类是没有访问修饰符的。 
      • 所以当所在方法的形参需要被匿名内部类使用,那么这个形参就必须为final 
      • 匿名内部类是没有构造方法的。因为它连名字都没有何来构造方法。 
    • 如何访问在其外面定义的变量? 
      • 所以当所在方法的形参需要被匿名内部类使用,那么这个形参就必须为final 
  5. 如果你定义一个类,包括学号,姓名,分数,如何把这个对象作为key要重写equalshashcode 
    • 需要重写equals方法和hashcode,必须保证对象的属性改变时,其hashcode不能改变。 
  6. 为什么要实现内存模型? 
    • 内存模型的就是为了在现代计算机平台中保证程序可以正确性的执行,但是不同的平台实现是不同的。 
    • 编译器中生成的指令顺序, 可以与源代码中的顺序不同; 
    • 编译器可能把变量保存在寄存器而不是内存中; 
    • 处理器可以采用乱序或并行等方式来执行指令; 
    • 缓存可能会改变将写入变量提交到主内存的次序; 
    • 保存在处理器本地缓存中的值,对其他处理器是不可见的; 
     

(数据库) 

  1. 常用的数据库有哪些?redis用过吗? 
    • 常用的数据库 
      • MySQL 
      • SQLServer 
    • Redis 
      • Redis是一个速度非常快的非关系型数据库,他可以存储键(key)5种不同类型的值(value)之间的映射,可以将存储在内存中的键值对数据持久化到硬盘中。 
      • Memcached相比 
        • 两者都可用于存储键值映射,彼此性能也相差无几 
        • Redis能够自动以两种不同的方式将数据写入硬盘 
        • Redis除了能存储普通的字符串键之外,还可以存储其他4种数据结构,memcached只能存储字符串键 
        • Redis既能用作主数据库,由可以作为其他存储系统的辅助数据库 
  2. 数据库索引的优缺点以及什么时候数据库索引失效 
    • 索引的特点 
      • 可以加快数据库的检索速度 
      • 降低数据库插入、修改、删除等维护的速度 
      • 只能创建在表上,不能创建到视图上 
      • 既可以直接创建又可以间接创建 
      • 可以在优化隐藏中使用索引 
      • 使用查询处理器执行SQL语句,在一个表上,一次只能使用一个索引 
    • 索引的优点 
      • 创建唯一性索引,保证数据库表中每一行数据的唯一性 
      • 大大加快数据的检索速度,这是创建索引的最主要的原因 
      • 加速数据库表之间的连接,特别是在实现数据的参考完整性方面特别有意义 
      • 在使用分组和排序子句进行数据检索时,同样可以显著减少查询中分组和排序的时间 
      • 通过使用索引,可以在查询中使用优化隐藏器,提高系统的性能 
    • 索引的缺点 
      • 创建索引和维护索引要耗费时间,这种时间随着数据量的增加而增加 
      • 索引需要占用物理空间,除了数据表占用数据空间之外,每一个索引还要占一定的物理空间,如果建立聚簇索引,那么需要的空间就会更大 
      • 当对表中的数据进行增加、删除和修改的时候,索引也需要维护,降低数据维护的速度 
    • 索引分类 
      • 直接创建索引和间接创建索引 
      • 普通索引和唯一性索引 
      • 单个索引和复合索引 
      • 聚簇索引和非聚簇索引 
    • 索引失效 
      • 如果条件中有or,即使其中有条件带索引也不会使用(这就是问什么尽量少使用or的原因) 
      • 对于多列索引,不是使用的第一部分,则不会使用索引 
      • like查询是以%开头 
      • 如果列类型是字符串,那一定要在条件中使用引号引起来,否则不会使用索引 
      • 如果mysql估计使用全表扫秒比使用索引快,则不适用索引。 
    • 各引擎支持索引 
    图像
  3. 事务隔离级别 
    • 串行化(Serializable):所有事务一个接着一个的执行,这样可以避免幻读(phantom read),对于基于锁来实现并发控制的数据库来说,串行化要求在执行范围查询的时候,需要获取范围锁,如果不是基于锁实现并发控制的数据库,则检查到有违反串行操作的事务时,需回滚该事务 
    • 可重复读(Repeated Read):所有被Select获取的数据都不能被修改,这样就可以避免一个事务前后读取不一致的情况。但是没有办法控制幻读,因为这个时候其他事务不能更改所选的数据,但是可以增加数据,因为强恶意事务没有范围锁 
    • 读已提交(Read Committed):被读取的数据可以被其他事务修改,这样可能导致不可重复读。也就是说,事务读取的时候获取读锁,但是在读完之后立即释放(不需要等事务结束),而写锁则是事务提交之后才释放,释放读锁之后,就可能被其他事务修改数据。改等级也是SQL Server默认的隔离等级 
    • 读未提交(Read Uncommitted):最低的隔离等级,允许其他事务看到没有提交的数据,会导致脏读 
    • 总结 
      • 四个级别逐渐增强,每个级别解决一个问题,每个级别解决一个问题,事务级别遇到,性能越差,大多数环境(Read committed 就可以用了) 
      • 图像
  4. 数据库中的范式有哪些? 
    • 目前关系数据库有六种范式:第一范式(1NF)、第二范式(2NF)、第三范式(3NF)、巴斯科德范式(BCNF)、第四范式(4NF)和第五范式(5NF,又称完美范式)。满足最低要求的范式是第一范式(1NF)。在第一范式的基础上进一步满足更多规范要求的称为第二范式(2NF),其余范式以次类推。一般说来,数据库只需满足第三范式(3NF)就行了。 
    • 范式的包含关系。一个数据库设计如果符合第二范式,一定也符合第一范式。如果符合第三范式,一定也符合第二范式… 
    • 范式 
      • 1NF 符合1NF的关系中的每个属性都不可再分 
      • 2NF属性完全依赖于主键 [消除部分子函数依赖] 
      • 3NF:属性不依赖于其它非主属性[消除传递依赖] 
      • BCNF:在1NF基础上,任何非主属性不能对主键子集依赖[3NF基础上消除对主码子集的依赖] 
      • 4NF:要求把同一表内的多对多关系删除。 
      • 5NF:从最终结构重新建立原始结构。 
  5. 数据库中的索引的结构?什么情况下适合建索引? 
    • 数据库中的索引结构 
      • 因为在使用二叉树的时候,由于二叉树的深度过大而造成I/O读写过于频繁,进而导致查询效率低下。因此采用多叉树结构。B树的各种操作能使B树能保持较低的高度。 
    • 树又叫平衡多路查找树,一棵 m 阶的 B 树的特性如下 
      • 树中每个结点最多含有 m 个孩子(m>=2); 
      • 除根结点和叶子结点外,其他每个结点至少有[ceil(m / 2)]个孩子(其中 ceil(x)是一个取上限的函数); 
      • 根结点至少有 2 个孩子(除非 B 树只包含一个结点:根结点); 
      • 所有叶子结点都出现在同一层, 叶子结点不包含任何关键字信息 (可以看做是外部结点或查询失败的结 
      点,指向这些结点的指针都为 null);(注:叶子节点只是没有孩子和指向孩子的指针,这些节点也存 在,也有元素。类似红黑树中,每一个 NULL 指针即当做叶子结点,只是没画出来而已)。 
      •  每个非终端结点中包含有 n 个关键字信息: (nP0K1P1K2P2……KnPn)。其中: 
      a) Ki (i=1…n)为关键字,且关键字按顺序升序排序 K(i-1)< Ki b) Pi 为指向子树根的结点,且指针 P(i-1)指向子树种所有结点的关键字均小于 Ki,但都大于 K(i-1) c) 关键字的个数 n 必须满足: [ceil(m / 2)-1]<= n <= m-1。比如有 j 个孩子的非叶结点恰好有 j-1 个关 键码。 图像 
    • B+ 图像 
    • 在什么情况下适合建立索引 
      • 为经常出现在关键字order bygroup bydistinct后面的字段,建立索引。 
      • union等集合操作的结果集字段上,建立索引。其建立索引的目的同上。 
      • 为经常用作查询选择的字段,建立索引。 
      • 在经常用作表连接的属性上,建立索引。 
      • 考虑使用索引覆盖。对数据很少被更新的表,如果用户经常只查询其中的几个字段,可以考虑在这几个字段上建立索引,从而将表的扫描改变为索引的扫描。 
  1. Redis的存储结构,或者说如何工作的,与mysql的区别?有哪些数据类型? 
    • Redis的数据结构 
      • STRING:可以是字符串、整数或者浮点数 
      • LIST:一个链表,链表上的每个节点都包含了一个字符串 
      • SET:包含字符串的无序收集器(unordered collection),并且被包含的每个字符串都是独一无二、各不相同的 
      • HAST:包含键值对的无序散列表 
      • ZSET:字符串成员(member)与浮点数分值(score)之间的有序映射,元素的排列顺序由分值的大小决定 
  1. 数据库中的分页查询语句怎么写?http://qimo601.iteye.com/blog/1634748 
    • Mysqllimit用法 
      • SELECT * FROM table LIMIT [offset,] rows | rows OFFSET offset 
      • LIMIT 接受一个或两个数字参数。参数必须是一个整数常量。如果给定两个参数,第一个参数指定第一个返回记录行的偏移量,第二个参数指定返回记录行的最大数目。初始记录行的偏移量是 0(而不是 1) 
    • 最基本的分页方式:SELECT … FROM … WHERE … ORDER BY … LIMIT …  
    • 子查询的分页方式: 
  2. 数据库ACID 
    • 原子性(Atomicity):保证事务中的所有操作全部执行或全部不执行 
    • 一致性(Consistency):保证数据库始终保持数据的一致性——事务操作之前和之后都是一致的 
    • 隔离性(Isolation):多个事务并发执行的话,结果应该与多个事务串行执行效果是一样的 
    • 持久性(Durability):事务操作完成之后,对数据库的影响是持久的,即使数据库因故障而受到破坏,数据库也能够恢复(日志) 
  3. 脏读、不可重复读和幻读 
    • 脏读:事务T1更新了一行记录的内容,但是并没有提交所做的修改。事务T2读取更新后的行,然后T1执行了回滚操作,取消了刚才所做的修改。现在T2读取的行就无效了(一个事务读取了另一个事务未提交的数据) 
    • 不可重复读:事务T1读取了一行记录,紧接着T2修改T1刚才读取的那一行记录,然后T1又再次读取这行记录,发现与刚才读取的结果不同。 
    • 幻读:事务T1读取一条指定的Where子句所返回的结果集,然后T2事务新插入一行记录,这行记录恰好可以满足T1所使用的查询条件。然后T1再次对表进行检索,但又看到了T2插入的数据。 
  4. MyISAMInnoDB引擎的区别 
    • 主要区别: 
      • MyISAM是非事务安全型的,而InnoDB是事务安全型的。 
      • MyISAM锁的粒度是表级,而InnoDB支持行级锁定。 
      • MyISAM支持全文类型索引,而InnoDB不支持全文索引。 
      • MyISAM相对简单,所以在效率上要优于InnoDB,小型应用可以考虑使用MyISAM 
      • MyISAM表是保存成文件的形式,在跨平台的数据转移中使用MyISAM存储会省去不少的麻烦。 
      • InnoDB表比MyISAM表更安全,可以在保证数据不会丢失的情况下,切换非事务表到事务表(alter table tablename type=innodb)。 
    • 应用场景: 
      • MyISAM管理非事务表。它提供高速存储和检索,以及全文搜索能力。如果应用中需要执行大量的SELECT查询,那么MyISAM是更好的选择。 
      • InnoDB用于事务处理应用程序,具有众多特性,包括ACID事务支持。如果应用中需要执行大量的INSERTUPDATE操作,则应该使用InnoDB,这样可以提高多用户并发操作的性能。 
     

Java 虚拟机) 

  1. JVM垃圾处理方法(标记清除、复制、标记整理) 
    • 标记清除算法 
      • 标记阶段:先通过根节点,标记所有从根节点开始的对象,未被标记的为垃圾对象 
      • 清除阶段:清除所有未被标记的对象 
    • 复制算法 
      • 将原有的内存空间分为两块,每次只使用其中一块,在垃圾回收时,将正在使用的内存中的存活对象复制到未使用的内存块中,然后清除正在使用的内存块中的所有对象。 
    • 标记整理 
      • 标记阶段:先通过根节点,标记所有从根节点开始的可达对象,为被标记的为垃圾对象 
      • 整理阶段:将所有的存活对象压缩到内存的一段,之后清理边界所有的空间 
    • 三种算法的比较 
      • 效率:复制算法 > 标记/整理算法 > 标记/清除算法(此处的效率只是简单的对比时间复杂度,实际情况不一定如此)。 
      • 内存整齐度:复制算法=标记/整理算法>标记/清除算法。 
      • 内存利用率:标记/整理算法=标记/清除算法>复制算法。 
  2. JVM如何GC,新生代,老年代,持久代,都存储哪些东西,以及各个区的作用? 
    • 新生代 
      • 在方法中去new一个对象,那这方法调用完毕后,对象就会被回收,这就是一个典型的新生代对象。 
    • 老年代 
      • 在新生代中经历了N次垃圾回收后仍然存活的对象就会被放到老年代中。而且大对象直接进入老年代 
      • Survivor空间不够用时,需要依赖于老年代进行分配担保,所以大对象直接进入老年代 
    • 永久代 
      • 即方法区。 
  3. GC用的引用可达性分析算法中,哪些对象可作为GC Roots对象? 
    • Java虚拟机栈中的对象 
    • 方法区中的静态成员 
    • 方法区中的常量引用对象 
    • 本地方法区中的JNINative方法)引用对象。 
  4. 什么时候进行MinGCFullGC 
    • MinGC 
      • 新生代中的垃圾收集动作,采用的是复制算法 
      • 对于较大的对象,在Minor GC的时候可以直接进入老年代 
    • FullGC 
      • Full GC是发生在老年代的垃圾收集动作,采用的是标记清除/整理算法。 
      • 由于老年代的对象几乎都是在Survivor区熬过来的,不会那么容易死掉。因此Full GC发生的次数不会有Minor GC那么频繁,并且Time(Full GC)>Time(Minor GC) 
  5. 各个垃圾收集器是怎么工作的 
    • Serial收集器 
      • 是一个单线程的收集器,不是只能使用一个CPU。在进行垃圾收集时,必须暂停其他所有的工作线程,直到收集结束。 
      • 新生代采用复制算法,Stop-The-World 
      • 老年代采用标记整理算法,Stop-The-World 
      • 简单高效,Client模式下默认的新生代收集器 
      • 图像
    • ParNew收集器 
      • ParNew收集器是Serial收集器的多线程版本 
      • 新生代采用复制算法,Stop-The-World 
      • 老年代采用标记整理算法,Stop-The-World 
      • 它是运行在Server模式下首选新生代收集器 
      • 除了Serial收集器之外,只有它能和CMS收集器配合工作 
      • 图像
    • ParNew Scanvenge收集器 
      • 类似ParNew,但更加关注吞吐量。目标是:达到一个可控制吞吐量的收集器。 
      • 停顿时间和吞吐量不可能同时调优。我们一方面希望停顿时间少,另外一方面希望吞吐量高,其实这是矛盾的。因为:在GC的时候,垃圾回收的工作总量是不变的,如果将停顿时间减少,那频率就会提高;既然频率提高了,说明就会频繁的进行GC,那吞吐量就会减少,性能就会降低。 
    • G1收集器 
      • 是当今收集器发展的最前言成果之一,对垃圾回收进行了划分优先级的操作,这种有优先级的区域回收方式保证了它的高效率 
      • 最大的优点是结合了空间整合,不会产生大量的碎片,也降低了进行gc的频率 
      • 让使用者明确指定指定停顿时间 
    • CMS收集器:(Concurrent Mark Sweep:并发标记清除老年代收集器) 
      • 一种以获得最短回收停顿时间为目标的收集器,适用于互联网站或者B/S系统的服务器上 
      • 初始标记(Stop-The-World):根可以直接关联到的对象 
      • 并发标记(和用户线程一起):主要标记过程,标记全部对象 
      • 重新标记(Stop-The-World):由于并发标记时,用户线程依然运行,因此在正式清理前,再做修正 
      • 并发清除(和用户线程一起):基于标记结果,直接清理对象 
      • 并发收集,低停顿 
      • 图像
  6. Java虚拟机内存的划分,每个区域的功能 
    • 程序计数器(线程私有) 
      • 线程创建时创建,执行本地方法时其值为undefined 
    • 虚拟机栈(线程私有) 
      • (栈内存)为虚拟机执行java方法服务:方法被调用时创建栈帧–>局部变量表->局部变量、对象引用 
      • 如果线程请求的栈深度超出了虚拟机所允许的深度,就会出现StackOverFlowError-Xss规定了栈的最大空间 
      • 虚拟机栈可以动态扩展,如果扩展到无法申请到足够的内存,会出现OOM 
    • 本地方法栈(线程私有) 
      • java虚拟机栈是为虚拟机执行java方法服务的,而本地方法栈则为虚拟机执使用到的Native方法服务。 
      • Java虚拟机没有对本地方法栈的使用和数据结构做强制规定。Sun HotSpotJava虚拟机栈和本地方法栈合二为一 
      • 会抛出StackOverFlowErrorOutOfMemoryError 
    • Java 
      • 被所有线程共享,在Java虚拟机启动时创建,几乎所有的对象实例都存放到堆中 
      • GC的管理的主要区域 
      • 物理不连续,逻辑上连续,并可以动态扩展,无法扩展时抛出OutOfMemoryError 
    • 方法区 
      • 用于存储已被虚拟机加载的类信息、常量、静态变量、即使编译器编译后的代码的数据 
      • Sun HotSpot 虚拟机把方法区叫做永久待(Permanent Generation) 
    • 运行时常量池 
      • 受到方法区的限制,抛出OutOfMemoryError 
  7. 用什么工具可以查出内存泄漏 
    • MemoryAnalyzer:一个功能丰富的 JAVA 堆转储文件分析工具,可以帮助你发现内存漏洞和减少内存消耗 
    • EclipseMAT:是一款开源的JAVA内存分析软件,查找内存泄漏,能容易找到大块内存并验证谁在一直占用它,它是基于Eclipse RCP(Rich Client Platform),可以下载RCP的独立版本或者Eclipse的插件 
    • JProbe:分析Java的内存泄漏。 
  8. JVM如何加载一个类的过程,双亲委派模型中有哪些方法有没有可能父类加载器和子类加载器,加载同一个类?如果加载同一个类,该使用哪一个类? 
    • 双亲委派机制图 
      • 图像
    • 双亲委派概念 
      • 如果一个类加载器收到了类加载的请求,它首先不会自己去尝试加载这个类,而是把这个请求委派给父类加载器去完成,每一个层次的加载器都是如此,因此所有的类加载请求都会传给顶层的启动类加载器,只有当父加载器反馈自己无法完成该加载请求(该加载器的搜索范围中没有找到对应的类)时,子加载器才会尝试自己去加载。 
    • 加载器 
      • 启动(Bootstrap)类加载器:是用本地代码实现的类装入器,它负责将 <Java_Runtime_Home>/lib下面的类库加载到内存中(比如rt.jar)。由于引导类加载器涉及到虚拟机本地实现细节,开发者无法直接获取到启动类加载器的引用,所以不允许直接通过引用进行操作。 
      • 标准扩展(Extension)类加载器:是由 Sun  ExtClassLoadersun.misc.Launcher$ExtClassLoader)实现的。它负责将< Java_Runtime_Home >/lib/ext或者由系统变量 java.ext.dir指定位置中的类库加载到内存中。开发者可以直接使用标准扩展类加载器。 
      • 系统(System)类加载器:由 Sun  AppClassLoadersun.misc.Launcher$AppClassLoader)实现的。它负责将系统类路径(CLASSPATH)中指定的类库加载到内存中。开发者可以直接使用系统类加载器。除了以上列举的三种类加载器,还有一种比较特殊的类型  线程上下文类加载器。 
    • 如果加载同一个类,该使用哪一个类? 
      • 父类的 
  1. JVM线程死锁,你该如何判断是因为什么?如果用VisualVMdump线程信息出来,会有哪些信息 
    • 常常需要在隔两分钟后再次收集一次thread dump,如果得到的输出相同,仍然是大量thread都在等待给同一个地址上锁,那么肯定是死锁了。 
  2. java是如何进行对象实例化的 
  3. Student s = new Student();在内存中做了哪些事情? 
    • 加载Student.class文件进内存 
    •  在栈内存为s开辟空间 
    • 在堆内存为学生对象开辟空间 
    • 对学生对象的成员变量进行默认初始化 
    • 对学生对象的成员变量进行显示初始化 
    • 通过构造方法对学生对象的成员变量赋值 
    • 学生对象初始化完毕,把对象地址赋值给s变量 
     
  4. 用什么工具调试程序?JConsole,用过吗?  

 JConsole 中,您将能够监视 JVM 内存的使用情况、线程堆栈跟踪、已装入的类和 VM 信息以及 CE MBean 

  1. 了解过JVM调优没,基本思路是什么 

JSP&Servlet 

  1. Servlet的生存周期 
    • Servlet接口定义了5个方法,其中前三个方法与Servlet生命周期相关:  
      • -void init(ServletConfig config) throws ServletException 
      •  void service(ServletRequest req, ServletResponse resp) throws ServletException, java.io.IOException 
      • void destory() 
      • java.lang.String getServletInfo() 
      • ServletConfig getServletConfig() 
    • Web容器加载Servlet并将其实例化后,Servlet生命周期开始,容器运行其init()方法进行Servlet的初始化;请求到达时调用Servletservice()方法,service()方法会根据需要调用与请求对应的doGetdoPost等方法;当服务器关闭或项目被卸载时服务器会将Servlet实例销毁,此时会调用Servletdestroy()方法。 
     
  2. JspServlet的区别 
    • Servlet是一个特殊的Java程序,它运行于服务器的JVM中,能够依靠服务器的支持向浏览器提供显示内容。JSP本质上是Servlet的一种简易形式,JSP会被服务器处理成一个类似于ServletJava程序,可以简化页面内容的生成。ServletJSP最主要的不同点在于,Servlet的应用逻辑是在Java文件中,并且完全从表示层中的HTML分离开来。而JSP的情况是JavaHTML可以组合成一个扩展名为.jsp的文件。有人说,Servlet就是在Java中写HTML,而JSP就是在HTML中写Java代码,当然这个说法是很片面且不够准确的。JSP侧重于视图,Servlet更侧重于控制逻辑,在MVC架构模式中,JSP适合充当视图(view)而Servlet适合充当控制器 
  3. 保存会话状态,有哪些方式、区别如何 
    • 由于HTTP协议本身是无状态的,服务器为了区分不同的用户,就需要对用户会话进行跟踪,简单的说就是为用户进行登记,为用户分配唯一的ID,下一次用户在请求中包含此ID,服务器据此判断到底是哪一个用户。