1、谈谈面向对象的理解?面向对象与面向过程的区别? 面向对象的三个特性:
- 封装
封装是指将类内部的实现细节隐藏,只提供调用接口,封装不仅可以有效保护数据,还可以提高代码可维护性 - 继承
继承是指从现有类中继承基类的数据和方法,并拓展新的方法,继承可以提高代码的复用性和灵活性 - 多态
多态是指类与类之间的关系。两个类有继承关系,存在方法的重写(override),子类的方法覆盖父类的方法。多态必备的三个要素:继承,重写,父类引用指向子类对象
2、 HashMap原理是什么,在jdk1.7和1.8中有什么区别
HashMap利用hash值存储数据,具有很快的访问速度,但是遍历顺序不确定。
HashMap 非线程安全,即任一时刻可以有多个线程同时写 HashMap,可能会导致数据的不一致。如果需要满足线程安全,可以用 Collections 的 synchronizedMap 方法使 HashMap 具有线程安全的能力,或者Hashtable,但是这两种方法效率不高(同步方法无竞争),也可以使用 ConcurrentHashMap。
JAVA1.7实现
总体上就是数组+单链表,这样当冲突发生次数较多时可能会造成查找性能下降,因为链表的查找时间复杂度是O(N)
JAVA1.8实现
最大的不同是HASHMAP变成了数组+链表+红黑树实现
在 Java8 中,当链表中的元素超过了 8 个以后,会将链表转换为红黑树,在这些位置进行查找的时候可以降低时间复杂度为 O(logN)
3、ArrayList和LinkedList有什么区别
区别:ArrayList底层由数组实现,LinkedList由链表实现
- ArrayList查找的时间复杂度是O(1), 但对数据进行插入或删除操作复杂度是O(N)
- LinkedList查找的时间复杂度是O(N),但是插入数据可以很快
- 类似于插入数据,删除数据时, LinkedList 也优于 ArrayList
- LinkedList 需要更多的内存,因为需要多存储结点信息
适用场景:
ArrayList 适用与查找多的场景
LinkedList适用于增删多的场景
4、高并发中的集合有哪些问题
第一代线程安全集合类
Vector、Hashtable
是怎么保证线程安排的: 使用synchronized修饰方法*
缺点:效率低下
第二代线程非安全集合类
ArrayList、HashMap
线程不安全,但是性能好,用来替代Vector、Hashtable
使用ArrayList、HashMap,需要线程安全怎么办呢?
使用 Collections.synchronizedList(list); Collections.synchronizedMap(m);
底层使用synchronized代码块锁 虽然也是锁住了所有的代码,但是锁在方法里边,并所在方法外边性能可以理解为稍有提高吧。毕竟进方法本身就要分配资源的
第三代线程安全集合类
在大量并发情况下如何提高集合的效率和安全呢?
java.util.concurrent.*
ConcurrentHashMap:
CopyOnWriteArrayList :
CopyOnWriteArraySet: 注意 不是CopyOnWriteHashSet*
底层大都采用Lock锁(1.8的ConcurrentHashMap不使用Lock锁),保证安全的同时,性能也很高。
jdk1.8的新特性有哪些
1、默认方法
interface My{
//默认方法
default void test(){
}
}
默认方法是被default关键字修饰的方法,该方法是有方法体,并且该方法是可以不被接口的实现类重写的
2、Lambda 表达式
函数式编程
lambda表达式就和方法一样,它提供了一个正常的参数列表和一个使用这些参数的主体(body,可以是一个表达式或一个代码块)。
String[] atp = {"Rafael Nadal", "Novak Djokovic",
"Stanislas Wawrinka",
"David Ferrer","Roger Federer",
"Andy Murray","Tomas Berdych",
"Juan Martin Del Potro"};
List<String> players = Arrays.asList(atp);
// 以前的循环方式
for (String player : players) {
System.out.print(player + "; ");
}
// 使用 lambda 表达式以及函数操作(functional operation)
players.forEach((player) -> System.out.print(player + "; "));
// 在 Java 8 中使用双冒号操作符(double colon operator)
players.forEach(System.out::println);
3、函数式接口
// 1.1使用匿名内部类
new Thread(new Runnable() {
@Override
public void run() {
System.out.println("Hello world !");
}
}).start();
// 1.2使用 lambda expression
new Thread(() -> System.out.println("Hello world !")).start();
// 2.1使用匿名内部类
Runnable race1 = new Runnable() {
@Override
public void run() {
System.out.println("Hello world !");
}
};
// 2.2使用 lambda expression
Runnable race2 = () -> System.out.println("Hello world !");
// 直接调用 run 方法(没开新线程哦!)
race1.run();
race2.run();
4、方法与构造方法的引用
java8可以让你通过关键字::
来传递方法和构造函数的引用。
class Person {
String firstName;
String lastName;
Person() {
}
Person(String firstName, String lastName) {
this.firstName = firstName;
this.lastName = lastName;
}
}
接下来我们指定一个用来创建Person对象的对象工厂接口:
interface PersonFactory<P extends Person>
{
P create(String firstName, String lastName);
}
PersonFactory<Person> personFactory = Person::new;
Person person = personFactory.create("Peter", "Parker");
我们只需要使用 Person::new 来获取Person类构造函数的引用,Java编译器会自动根据PersonFactory.create方法的签名来选择合适的构造函数。
Java中重写和重载有哪些区别
方法的重载和重写都是实现多态的方式,区别在于前者实现的是编译时的多态性,而后者实现的是运行时的多态
- 重载发生在一个类中,同名的方法如果有不同的参数列表(参数类型不同、参数个数不同或者二者都不同)则视为重载
- 重写发生在子类与父类之间,重写要求子类被重写方法与父类被重写方法有相同的返回类型,比父类被重写方法更好访问
接口和抽象类有哪些区别
不同
-
抽象类具有构造方法和具体方法,而接口没有
-
抽象类的成员可以不是public的,而接口必须是public的
-
抽象类可以有静态方法,而接口不可以
-
一个类只能继承一个抽象类,而可以继承多个接口
同
- 都不能实例化
- 继承了抽象类或接口,必须实现其中的抽象方法或接口方法
怎样声明一个类不会被继承,什么场景下会用
如果一个类被final修饰,此类不可以有子类,不能被其它类继承,如果一个中的所有方法都没有重写的需要,当前类没有子类也罢,就可以使用final修饰类。
Java中==和equals有哪些区别
equals 和== 最大的区别是一个是方法一个是运算符。
==:如果比较的对象是基本数据类型,则比较的是数值是否相等;如果比较的是引用数据类型,则比较的是对象的地址值是否相等。
equals():用来比较方法两个对象的内容是否相等。
注意:equals 方法不能用于基本数据类型的变量,如果没有对 equals 方法进行重写,则比较的是引用类型的变量所指向的对象的地址。
String、StringBuffer、StringBuilder区别及使用场景
1)String 是只读字符串,也就意味着 String 引用的字符串内容是不能被改变的。初学者可能会有这样的误解:
2)StringBuffer/StringBuilder 表示的字符串对象可以直接进行修改。
3)StringBuilder 是 Java5 中引入的,它和 StringBuffer 的方法完全相同,区别在于它是在单线程环境下使用的,因为它的所有方法都没有被 synchronized 修饰,因此它的效率理论上也比 StringBuffer 要高