一、Java面向对象有哪些特征?
一共有三大特征:封装、继承、多态
封装:隐藏了内部实现机制,可以在不影响使用的情况下改变类的内部结构,同时保护了数据。封装主要是增强了代码的可维护性,便于使用和修改。
继承:关键字extends,是从已经有的类中派生出新的类,能够吸收已有类的内部结构、并能拓展新的能力。被继承的类叫父类,是从多种实现类中抽象出来的一个基类,使其具备多种实现类的共同特征,当实现类使用extends继承了之后,实现类就拥有了这些相同的属性,比如说猫类、狗类中可以抽象出一个动物类具有猫和狗的共同特征:吃、跑、叫等。继承的类叫子类或者超类。在父类中用private定义的变量或方法不会被继承,不能在子类中直接操作private定义的变量和方法。继承最主要就是避免了对一般类和特殊类之间的共同特征进行的重复描述,提高了代码的复用性。
多态是三个特性中比较难的一个,继承和封装最后归结于多态,多态是指,第一要有继承,第二要有重写,第三父类引用指向子类对象,例如Animal a = new Dog();
这三个特征能够更好地帮助我们写代码,因为封装继承都提高了代码的复用性,多态增加了代码的可移植性和健壮性,灵活性。
二、怎么样声明一个类不会被继承,在什么场景下使用?
如果一个类被final修饰,此类不可以有子类,不能被其他类继承,如果一个类中所有方法都没有重写的需要,当前类没有子类,就可以使用final修饰类。
三、Java中的异常处理机制是什么?
Java中的异常处理是通过五个关键词来实现的:try、catch、finally、throw、throws
我们可以把可能产生异常的代码块放到try…catch中处理,把资源关闭回收放到finally中,无论发生什么异常,finally都会执行。
throw和throws的区别:
- 位置不同:throw在方法内部,throws在方法声明处;
- 内容不同:
throw + 异常对象(检查异常,运行时异常)
throws + 异常的类型(可以多个类型,使用 ","拼接 ) - 作用不同:
throw:异常出现的源头,制造异常
throws:在方法的声明处,告诉方法的调用者,这个方法中可能会出现声明的这些异常,调用者对这些异常进行处理(要么处理,要么继续向上抛出异常)
四、自定义异常在生产中如何应用?
Java虽然提供了丰富的异常处理类,但是在项目中还会经常使用自定义异常,主要原因是Java提供的异常类在某些情况下还是不能满足实际需求,例如:
1.系统中有些错误是符合Java语法,但是不符合业务逻辑;
2.在分层软件结构中,通常是在表现层统一对系统其他层次的异常进行捕获处理。
五、Java中 == 和 equals有哪些区别?
equals和==最大的区别就是一个是方法,一个是运算符。
== :如果比较的对象是基本数据类型,则比较的数值是否相等;如果比较的是引用数据类型,则比较的是对象的地址值是否相等
equals:用来比较方法两个对象的内容是否相同。
注意:equals方法不能用于基本数据类型的变量,如果没有对equals方法进行重写,则比较的是引用类型变量所指向的对象地址。
六、Java中重写和重载的区别?
重写(override):
- 存在于子类和父类之间;
- 访问权限不能比父类中被重写的方法的访问权限低;
- 参数和返回类型都必须和被重写的方法一致;
- 构造方法不能被重写,声明为final和static的方法不能被重写,但是能被再次声明;
- 重写的的方法可以抛出任何非强制异常,无论被重写方法是否抛出异常,但是重写的方法不能抛出新的强制性异常,或者比被重写的方法声明得更广泛的强制性异常
重载(overload):
- 方法名一致,参数列表中参数顺序、类型、个数不同;
- 重载与方法返回值无关,存在于子类和父类,同一个类中;
- 可以抛出不同异常,可以有不同的修饰符
七、String、StringBuilder、StringBuffer区别以及使用场景?
它们都可以用于存储和操作字符串,区别如下:
- String是只读字符串,意味着String引用的字符串内容是不能被更改的。例如:String str = “abc”; str = “def”;表面是改变了,但是实际上是让str重新指向一个新的字符串对象,而原字符串对象没有改变,只不过是变成了一个不可及对象;
- StringBuffer / StringBuilder表示的字符串对象可以直接进行修改;
- StringBuilder是JDK5引入的,是线程不安全的,而StringBuffer是JDK1时出现的,是线程安全的,因为它的方法被synchronized修饰,而StringBuilder的方法没有被synchronized修饰,但正因这样,StringBuilder的效率是比StringBuffer高
八、ArrayList和LinkedList的区别?
ArrayList和LinkedList都实现了List接口,但是它们的不同点是:
- ArrayList是基于索引的数据接口,底层是数组实现的,可以以O(1)实现对元素的随机访问,但是删除数据开销大,因为要重排数组中的所有元素;
- LinkedList是以元素列表的形式存储它的数据的,查找某个元素的时间复杂度是O(n);
- 相对于ArrayList,LinkedList的插入、添加、删除速度更快,因为当元素被添加到集合的任意位置时,不需要像数组那样重新计算大小或者更新索引,然而LinkedList更占内存,因为每一个节点存储两个引用,一个指向前一个元素,一个指向后一个元素;
在以下场景更适用LinkedList:
(1)不会随机访问数据。因为如果要LinkedList中的第n个元素时,需要从第一个元素顺序数到第n个数据,然后读取数据。
(2)更多的操作是删除和插入元素,更少的是读取数据。因为插入和删除元素不涉及重排数据,所以要ArrayList块。
九、Java接口和抽象类有哪些区别?
相同:
- 不能实例化
- 可以将抽象类和接口类型作为引用类型
- 一个类如果继承了某个抽象类或者实现了某个接口,都需要对其抽象方法全部进行实现,否则该类仍然要被声明为抽象类
不同:
抽象类
- 抽象类中可以定义构造器
- 可以有抽象方法和具体方法
- 抽象类中可以定义成员变量
- 有抽象中可以包含静态方法
- 一个类只能继承一个抽象类
- 有抽象方法的类必须声明为抽象类
- 抽象类中的成员可以是private、默认、protected、public
接口
- 接口中不能定义构造器
- 方法全是抽象方法
- 接口中的成员都是public的
- 接口中定义的成员变量实际上是常量
- 接口中不能有静态方法
- 一个类可以实现多个接口
加分点:
什么时候使用接口?什么时候使用抽象类?
抽象类和接口实际上就是is和has的却别。
例如:人相对于男人、女人就是is,那么就应该是抽象类,因为都是人。
例如:可行动相对于人、汽车、飞机就是has,那么就是接口。
所以说描述概念一般用抽象类,描述特征一般用接口。
十、HashMap和HashTable的区别是什么?
- HashTable线程同步,HashMap非线程同步,其实就是涉及安全性,在多线程的时候使用HashTable数据是不会乱的,使用HashMap可能会引发一系列的问题。因为HashTable使用了synchronized关键字所以效率是比较低的,HashMap不带synchronized关键字,效率是较高的
- HashTable不允许<键,值>有空值,HashMap允许<键,值>有空值
- HashTable使用Enumeration,HashMap使用Iterator
- HashTable中hash数组的默认大小是11,增加方式是old*2+1,HashMap中hash数组的默认大小是16,增加方式是2的指数倍
- HashTable继承自Dictionary类,HashMap继承自AbstractMap类
十一、HashMap有哪些线程安全的方式?
HashMap是线程不安全的,需要通过一些方法来回避,JDK原生提供了两种方法让HashMap支持线程安全。
一:通过Collection.synchronizedMap()返回一个新的Map,这个新的Map是线程安全的,但是它返回的其实是一个实现,并不是HashMap
特点:(1)使用了synchronized来进行互斥同步;
(2)使用了代理模式new了一个新的类,这个类同样实现了Map接口;
(3)synchronized锁住的是对象,所以第一个申请的得到锁,其他线程进入阻塞,等待唤醒。
优点:代码简单
缺点:性能较差
二:重新改写了HashMap,具体是java.util.concurrentHashMap这一方法有很大的改进。
特点:(1)使用了新的锁机制,把HashMap进行了拆分,成多个独立的块,这样在高并发的情况下减少了锁冲突的可能
优点:需要互斥的代码较少,性能较好
十二、HashMap在扩容上有哪些优化?
在JDK1.7中,扩容的计算每次都是元素进行了rehash算法,计算原来每个元素在扩容之后的哈希表中的位置。
在JDK1.8之后借助2倍扩容机制,元素不需要进行重新计算位置。HashMap使用的是2次幂的拓展,所以元素的位置要么在原位置,要么在原位置再移动2次幂的位置。
这个设计省去了重新计算hash值的时间。