Java面试3
一、请你说说Java的特点和优点,为什么要选择Java?
1、吸收了c++的优点,摒弃了c++多继承和指针的复杂使用。不需要对内存进行,具有垃圾回收机制。
2、面向对象,易于开发和理解。
3、跨平台,因为 jvm,即 java 虚拟机,同一个代码可以在不同的平台机器上运行,java 文件通过编译成 .class 字节码文件,再由 jvm 转成机器能识别的机器码。
4、内含大量的库,简化编写工作。
5、适合用于开发web,如servlet,jsp等。
二、说说你对面向对象的理解
1、面向对象三大基本特征:封装、继承、多态。
2、封装:将对象的状态信息隐藏在对象内部,不允许外部程序直接访问对象内部信息,让外部程序通过该类提供的方法来实现对内部信息的操作和访问,提高了代码的可维护性。
3、继承:实现代码复用的重要手段,通过extends实现类的继承,实现继承的类被称为子类,被继承的类称为父类。
4、多态的实现离不开继承,在设计程序时,我们可以将参数的类型定义为父类型。在调用程序时根据实际情况,传入该父类型的某个子类型的实例,这样就实现了多态。
三、请你说说Java基本数据类型和引用类型
1.提供8种基本数据类型:byte(8), short(16), int(32), long(64), float(32), double(64), char(16), boolean,这些基本数据类型有对应的封装类;这基本数据类型在声明之后就会立刻在栈上被分配内存空间。
2.其他类型都是引用类型:类,接口,数组,String等,这些变量在声明时不会被分配内存空间,只是存储了一个内存地址。
四、请介绍一下访问修饰符
Java 中的访问修饰符有四种,分别为 private,default,protected,public。
private:类中被 private 修饰的只能在被当前类的内部访问,
default:类中被 default 修饰的只能在当前类和当前类所在包的其他类访问。
protected:类中被 protected 修饰的可以被当前类和当前类所在的包的其他类以及子类访问。
public:类中被 public 修饰的能被当前项目下的所有类访问。
五、说说static修饰符的用法
static 可以修饰成员变量,内部类(接口、枚举),方法,初始化代码块,但是不可以修饰构造器, 被 static 修饰的类或者成员变量这四个在不创建实例的情况下都能被访问,但是被 static 修饰的类成员不能访问非静态修饰的实例成员。因为它们的初始化时间不相同,被static修饰的类成员在类初始化的时候就完成了初始化,但是非静态的实例成员要在对象创建的时候才能被实例化,所以它们的周期不同,静态不能调用非静态。
六、请你说一下final关键字
final 关键字可以用来标志其修饰的类,方法和变量不可变。
1、当 final 修饰类时,该类不能被继承,例如 java.lang.Math 类就是一个 final 类,它不能被继承。
2、final 修饰的方法不能被重写,如果出于某些原因你不希望子类重写父类的某个方法,就可以用 final 关键字修饰这个方法。
3、当 final 用来修饰变量时,代表该变量不可被改变,一旦获得了初始值,该 final 变量的值就不能被重新赋值。
4、 final 既可以修饰成员变量(包括类变量和实例变量),也可以修饰局部变量、形参。
七、请你说说List与Set的区别
list 和 set 都是接口 collection 的子接口,list 代表有序的可重复的集合,每个元素都有对应的顺序索引,可以通过索引来访问指定位置的集合元素。而 set 表示无序,不可重复的集合元素。但是它有支持排序的实现类 treeset,treeset 可以确保元素处于排序状态,并支持自然排序和定制排序两种方式,treeset 是非线程安全的,内部元素的值不能为null。
八、请你说说ArrayList和LinkedList的区别
相同点:ArrayList和LinkedList都都实现了List接口,它们都是有序集合,并且能存储重复的元素。它们都是线程不安全的
不同点:ArrayList底层是基于数组实现的,数组的内存地址连续有下标,所以根据下标读、写快,但是插入、删除慢,因为需要重新计算数组大小或是更新下标。
LinkedList底层的数据结构是双向链表,读取和写入慢,因为每次都需要从头遍历,插入删除快,可以原地进行插入和删除,链表的每一个节点保存了数据值,和指向前一个节点的指针和指向后一个节点的指针,占内存。
九、说说你对ArrayList的理解
ArrayList:ArrayList的底层是用 Object数组 实现的,通过默认构造器创建容器时,首先会初始化一个空数组,然后在第一次添加元素的时候创建一个大小为10的数组,超出限制后会增加50%的容量,并且数据以 System.arraycopy() 复制到新数组,当然也可以指定初始容量。如果一次添加多个元素,如 addAll,如果添加元素的个数大于默认扩容的长度,则会使用两者中最大的作为扩容后的容量。
十、请你说说HashMap和Hashtable的区别
1.Hashtable 在实现 Map 接口时保证了线程安全性,而 HashMap 则是非线程安全的。所以,Hashtable 的性能不如HashMap,因为为了保证线程安全它牺牲了一些性能。
2.Hashtable 不允许存入null,无论是以 null 作为 key 或value,都会引发异常。而 HashMap 是允许存入 null 的,无论是以 null 作为 key 或 value,都是可以的。
十一、请你说说HashMap底层原理
在1.8之前,HashMap的底层是数组+链表,在1.8之后是数组+链表+红黑树;
它的put流程是:基于哈希算法来确定元素位置,当我们向集合存入数据时,他会计算传入的key的哈希值,并利用哈希值取绝对值再根据集合长度取余来确定元素的位置,如果这个位置已经存在其他元素了,就会发生哈希碰撞,则hashmap就会通过链表将这些元素组织起来,如果链表的长度达到8时,就会转化为红黑树,从而提高查询速度。
扩容机制:HashMap中数组的默认初始容量为16,当达到默认负载因子0.75时,会以2的指数倍进行扩容。 Hashmap时非线程安全的,在多线程环境下回产生循环死链,因此在多线程环境下建议使用ConcurrentHashMap。
十二、HashMap是线程安全的吗?如果不是该如何解决?
HashMap 不是线程安全的。hashmap 的底层是利用数组+链表+红黑树的组合,在多线程的情况下,多个线程同时触发 hashmap 的时候可能会发生冲突。所以在多线程的时候不建议使用 hashmap。
如果想使用线程安全的 hashmap,可以使用 hashtable、collections 将 hashmap 包装成线程安全的 hashmap、concurrenthashmap。
hashtable 属于古老的api,一般不推荐使用,concurrenthashmap 是目前比较推荐使用的一种方式。
十三、请你说说ConcurrentHashMap
ConcurrentHashMap的底层数据结构与HashMap一样,也是采用“数组+链表+红黑树,采用锁定头节点的方式降低了锁粒度,以较低的性能代价实现了线程安全。
实现机制:1. 初始化数组或头节点时,ConcurrentHashMap并没有加锁,而是CAS的方式进行原子替换。2. 插入数据时会进行加锁处理,但锁定的不是整个数组,而是槽中的头节点。所以,ConcurrentHashMap中锁的粒度是槽,而不是整个数组,并发的性能很好。 3. 扩容时会进行加锁处理,锁定的仍然是头节点。并且,支持多个线程同时对数组扩容,提高并发能力。 4. 在扩容的过程中,依然可以支持查找操作。
十四、请你说说hashCode()和equals()的区别,为什么重写equals()就要重写hashCode()
hashCode()方法主要用于计算hash值,equals()用于比较两个对象是否相等。因为 equals() 和 hashCode() 存在联动关系。当equals()比较两个对象相等时,hash值一定相等,但是hash值相等,两个对象却不定相等,这就是hash冲突,所以为了解决hash冲突,必须一起重写。
十五、请说说你对Java集合的了解
java 中的集合类主要都有 Collection 和 Map 这两个接口派生而出,其中 Collection 又派生出 List,Set,Queue。所有的集合类都是List,Set,Queue,map 这四个接口的实现类。其中,List 代表有序的,可重复的数据集合;Set 代表无序的,不可重复的数据集合;Queue 代表先进先出的队列;map是具有映射关系的集合。最常用的实现类有:ArrayList,LinkedList,HashMap,TreeMap,HashSet,TreeSet,ArrayQueue。
十六、String、StringBuffer、Stringbuilder有什么区别
String 是一个不可变的类,也就是说一个String对象被创建后,直到销毁,其字符串序列都不会发生改变。
StringBuffer 和 StringBuilder 是字符序列可变的字符串,并提供一系列字符串操作方法。
StringBuffer 是线程安全的,但是效率较低。StringBuilder 是非线程安全的,但是效率块。所以针对单线程中操作大量数据使用 StringBuilder,多线程操作大量数据使用 StringBuffer。
十七、请你说说String类,以及new关键词
String 类是由 final 修饰,所以不能被继承,创建字符串由两种方式,一种是使用字符串直接量,另一种是使用new关键字。
1、当使用字符串直接量的方式来创建字符串时,JVM会使用常量池来管理这个字符串,
2、当使用 new 关键词来创建字符串时,JVM 会先使用常量池来管理字符串直接量,再调用 String 类的构造器来创建一个新的 String 对象,新创建的 String 对象会被保存在堆内存中。
十八、请说说你对反射的了解
反射就是在程序运行期间,通过class文件动态地获取对象的属性和方法的功能。要想使用反射必须先获取对应的字节码文件。
获取Class对象的三种方式:对象.getClass(),Object.class,Class.forName(“xxx”)。
优点:可以提高程序的扩展性。
缺点:执行速度较慢,因为要先加载字节码文件。
反射机制在实际项目中很常见:例如Spring AOP,JDBC中数据库中的连接方式。
十九、请你说说泛型、泛型擦除
1)泛型:Java 在 jdk5 引入了泛型,在没有泛型之前,每次从集合中读取的对象都必须进行类型转换,如果在插入对象时,类型出错,那么在运行时转换处理的阶段就会报错。在提出泛型之后就可以明确的指定集合接受哪些对象类型,编译器就能知晓并且自动为插入的代码进行泛化,在编译阶段告知是否插入类型错误的对象,程序会变得更加安全清晰。
2)泛型擦除:Java 泛型是伪泛型,因为Java代码在编译阶段,所有的泛型信息会被擦除,Java 的泛型基本上都是在编辑器这个层次上实现的,在生成的字节码文件中是不包含泛型信息的,使用泛型的时候加上的类型,在编译阶段会被擦除掉,这个过程称为泛型擦除。
二十、请你说说重载和重写的区别,构造方法能不能重写
重载发生在同一个类中,要求方法名必须相同,参数列表不同,重载的发生于方法的返回值和访问修饰符无关,最终是生成了两个方法。
重写是发生在父子类中的,子类重写方法的名称和参数列表必须和父类相同,子类的返回值类型或抛出的异常类型必须是父类的返回值或异常类型及其子类型。子类重写方法的访问修饰符必须大于父类。若父类方法的修饰符为private,则子类不能重写该方法。
二十一、请你说一下抽象类和接口的区别
1.抽象类中的方法可以有方法体,就是能实现方法的具体功能,但是接口中的方法不行。
2.抽象类中的成员变量可以是各种类型的,而接口中的成员变量只能是 public static final 类型的。
3.接口中不能含有静态代码块以及静态方法(用 static 修饰的方法),而抽象类是可以有静态代码块和静态方法。
4.一个类只能继承一个抽象类,而一个类却可以实现多个接口。
二十二、介绍一下包装类的自动拆装箱与自动装箱
动装箱是指把一个基本类型的数据直接赋值给对应的包装类型;自动拆箱是指把一个包装类型的对象直接赋值给对应的基本类型;通过自动装箱、自动拆箱功能,可以大大简化基本类型变量和包装类对象之间的转换过程。
比如,某个方法的参数类型为包装类型,调用时我们所持有的数据却是基本类型的值,则可以不做任何特殊的处理,直接将这个基本类型的值传入给方法。
二十三、请你说说Java的异常处理机制
1、异常处理机制让程序具有容错性和健壮性,程序运行出现状况时,系统会生成一个Exception对象来通知程序。
2、处理异常的语句由try、catch、finally三部分组成。try块用于包裹业务代码,catch块用于捕获并处理某个类型的异常,finally块则用于回收资源。
3、如果业务代码发生异常,系统创建一个异常对象,并将其提交给JVM,由JVM寻找可以处理这个异常的catch块,并将异常对象交给这个catch块处理。如果JVM没有找到,运行环境终止,Java程序退出。
4、Java也允许程序主动抛出异常。当业务代码中,判断某项错误的条件成立时,可以使用throw关键字向外抛出异常。
二十四、设计模式了解么
创建型模式包括:单例模式,工厂方法模式,抽象工厂模式,建造者模式和原型模式; 结构型模式包括:代理模式,装饰者模式,适配器模式,组合模式,桥梁模式,外观模式和享元模式; 行为模式包括:模板方法模式,命令模式,责任链模式,策略模式,迭代器模式,中介者模式,观察者模式。
spring中定义的bean默认是单例模式,spring中factoryBean用来创建对象的模式,它是工厂模式的体现,Aop面向切面编程是代理模式的体现,springmvc中有适配器模式体现。
二十五、请你讲讲单例模式、请你手写一下单例模式
单例模式:就是一个类只能创建一个实例对象,提供一个系统的全局访问点。
饿汉式单例模式:类加载时就会创建一个对象。
public class Single{
private static Single instance = new Single();
}
懒汉式单例模式:只有当类被引用时才会被创建一个实例对象
public class Single{
private static Single instance = null;
synchronized public static Single getInstance(){
if(instance==null){
instance = new Single();
}
return instance;
}
}
单例模式的优点:适用于需要频繁创建和销毁的类,避免消耗更多的资源
单例模式的缺点:无法创建子类,扩展困难