Java面试

  • 时间:
  • 来源:互联网

Java

基础

1. 什么是面向对象(OOP)?

面向对象编程是使用类,对象,继承性,多态性,封装性和抽象的一种程序设计方法。

首先我们要区分一下“基于对象”和“面向对象”的区别。

  1. 基于对象,通常指的是对数据的封装,以及提供一组方法对封装过的数据操作。
  2. 面向对象,则在基于对象的基础上增加了多态性。所谓多态,就是可以用统一的方法对不同的对象进行同样的操作。

什么是面向对象(OOP)?

2. 什么是多态?实现多态的机制是什么?

多态即:事物在运行过程中存在不同的状态。多态可以分为编译时多态和运行时多态,编译时多态是指方法的重载,运行时多态是指方法的重写。

对于运行时多态需要满足以下三个条件:

  1. 要有继承关系
  2. 子类重写父类方法
  3. 父类对象的引用指向子类对象(SuperClass class = new ChildClass() )

那么多态实现的机制是什么呢?

其实就是依靠静态分派和动态分派。

静态分派即在编译期间就能完全确定,在类加载的解析阶段就会把所涉及的符号引用转化为可确定的直接引用,不会延迟到运行期再去完成,典型的例子就是方法重载。

动态分派是指在运行期间才能确定变量的实际类型,典型的例子就是方法的重写。只有在运行期间,根据对实例化子类的不同,调用不同子类中重写的方法。

参考:多态性实现机制——静态分派与动态分派

3. 接口(Interface)与抽象类(Abstract Class)的区别?

抽象类是一个可同时包含具体方法和抽象方法(方法未被实现)的类。抽象方法必须被该抽象类的子类实现。抽象类是可以继承的。

接口像是描述类的一张蓝图或者说是类的一种契约,它包含了许多空方法,这代表着它的所有的子类都应该拥有共同点。它的子类应该提供这些空方法的具体实现。一 个类需要用 implements 来实现接口,接口可以用 extends 来继承其他接口。

接口(Interface)与抽象类(Abstract Class)的区别?

4. 重写(Override)与重载(Overload)的区别?

  1. 重写是子类与父类之间的关系,是一种垂直关系;重载是同一个类中方法之间的关系,是水平关系
  2. 重写只能由一个方法或者只能由一对方法产生关系;重载是多个方法之间的关系
  3. 重写要求参数列表要相同;重载要求参数列表不同
  4. 重写关系中,调用方法体是根据对象的类型(对象对应存储空间类型)决定,重载是根据调用的时候实参表和形参表来选择方法

5. 父类的静态方法能否被子类重写?

严格来说,不存在静态方法的重写,当一个子类继承父类时,写同样的方法时,只是将父类的静态方法隐藏。

6. 静态属性和静态方法是否可以被继承?是否可以被重写?为什么?

public class Test {
    protected static String TAG = "tag";
    protected static void run(){
        System.out.println("Test Run");
    }
}
public class TestChild extends Test {

}
public class User {
    public static void main(String[] args) {
        TestChild child = new TestChild();
        child.run();
        System.out.println("TAG = "+child.TAG);
    }
}
Test Run
TAG = tag

Process finished with exit code 0

父类的静态属性和方法可以被子类继承。但是静态属性和变量无法被子类重写。
static修饰函数/变量时,其实是全局的函数/变量,只是因为Java强调对象的概念所以外挂在class下,
但是它与任何类都没有关系。

7. 什么是内部类?内部类、静态内部类、局部内部类和匿名内部类的区别及作用?

内部类是定义在另一个类中的类。
内部类可以访问该类定义所在作用域中的数据,包括私有数据。
内部类可以对同一个包中的其他类隐藏起来。
想要定义一个回调函数使用匿名内部类十分便捷。

静态内部类是指在成员内部类的前面加上static修饰符的内部类。
静态内部类不需要依赖外部类,不能使用外部类的非静态属性方法。

局部内部类是定义在一个方法或者一个作用域里的内部类。与成员内部类的区别是局部内部类的访问仅在方法或者作用域内。局部内部类不能有private、public、static等修饰符,与局部变量类似。只能在定义局部内部类的方法或者作用域中实例化,局部内部类只能使用该局部内部类的final局部变量。

匿名内部类不定义类的名字,在使用的地方直接定义对象。
特点是没有构造器,类中不能存在任何静态方法或者变量。匿名内部类是局部内部类的特例。大部分匿名内部类用作接口返回。

8. == 和 equals() 和 hashCode() 的区别?

== 和 equals 都是比较,弄清楚 == 和 equals 分别比较的是什么(内存地址,还是内存地址中的内容),自然就知道了==和equals有什么区别。
java对象都存在于内存中,要清楚我们要比较的到底是内存的地址,还是内存中的值。

先看几段代码:

String s1 = "AAAAA";
String s2 = "AAAAA";
System.out.println(s1 == s2);               // true
System.out.println(s1.equals(s2));          // true
String s3 = new String("BBBBB");
String s4 = new String("BBBBB");
System.out.println(s3 == s4);               // false
System.out.println(s3.equals(s4));          // true
Integer A = new Integer(20);
Integer B = new Integer(20);
System.out.println(A == B);                 // false
System.out.println(A.equals(B));            // true

我们再分别看看String类和Integer类中equals方法的源码:

public boolean equals(Object anObject) {
	if (this == anObject) {
		return true;
	}
	if (anObject instanceof String) {
		String anotherString = (String)anObject;
		int n = value.length;
		if (n == anotherString.value.length) {
			char v1[] = value;
			char v2[] = anotherString.value;
			int i = 0;
			while (n-- != 0) {
				if (v1[i] != v2[i])
					return false;
				i++;
			}
			return true;
		}
	}
	return false;
}
public boolean equals(Object obj) {
	if (obj instanceof Integer) {
		return value == ((Integer)obj).intValue();
	}
	return false;
}

从String类和Integer类的equals方法中,可以看到,equals的比较,最后还是运用!=或者==运算符来进行比较。而且比较的,都是对象中的值。


Java中所有的类,都是从Object继承而来,也就是说,Object是所有Java类的基类。

Object类中的函数并不多,equals方法就是其中的一个,看看equals在Object类中的源码:

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

从Object源码中可以看到,equals方法最终调用的是==进行比较的,那这里的==比较的是什么呢?

答:比较的是这个东西——java.lang.Object@15db9742。

这是什么?

答:这是Object类中toString方法的返回值。

那再来看看Object类中toString方法的源码:

public String toString() {
    return getClass().getName() + "@" + Integer.toHexString(hashCode());
}

Object类toString方法返回的这一串东西java.lang.Object@15db9742是一个对象的类名和hash值,这个hash值可以理解为内存地址

所以Objcet类中equals方法,比较的是两个内存地址,而不是内存地址中的值。


结论:

equals和==都是进行比较操作,但是一个对象可比较的东西有很多,比如内存地址,内存地址中的值,但具体比较的是内存地址的值,还是内存地址中的值,就自己去看具体类的eaquals方法了。

9. Integer 和 int 之间的区别?

Integer是int的包装类,int是Java中的一种基本数据类型
Integer变量必须实例化之后才能使用,int则可以直接使用。
Integer实际上是对象的引用,new一个Integer时,实际上是生成一个指针指向对象,int则是直接存储数据值。Integer的默认值是null,int默认值是0

10. String 转换成 Integer 的方式及原理?

  1. Integer.parseInt(String str)
  2. Integer.parseInt(String s,int radix)
  3. Integer.valueOf(String s)
  4. Character.digit(char ch,int radix)

11. 自动装箱实现原理?类型转换实现原理?

自动装箱和自动拆箱由编译器自动完成。当编译器检测到需要把基本类型转换为包装类型时,编译器自动调用包装类型的valueOf()方法实现自动装箱。当编译器检测需要把包装类型转化为基本类型时,编译器自动调用包装类的xxValue()方法实现自动拆箱。

// Ingeger
public static Integer valueOf(int i) {
        if (i >= IntegerCache.low && i <= IntegerCache.high)
            return IntegerCache.cache[i + (-IntegerCache.low)];
        return new Integer(i);
    } 

当基本类型为byte,short,char,int,long类型时,首相根据赋值的数值大小判断是否在缓冲区,否则直接new一个对应类型的包装类型对象。只有double和float会直接new一个包装类。

//Todo 基本类型的转换,父类子类之间的转换

12. 对 String 的了解?

String是java中用来创建和操作字符串的类。是不可变对象,对一个已经赋值的String对象再次赋值时,其实是创建了一个新的对象,指针指向新对象。
使用字符串常量池,每当我们使用字面量(String s=”1”;)创建字符串常量时,JVM会首先检查字符串常量池,如果该字符串已经存在常量池中,那么就将此字符串对象的地址赋值给引用s(引用s在Java栈中)。如果字符串不存在常量池中,就会实例化该字符串并且将其放到常量池中,并将此字符串对象的地址赋值给引用s(引用s在Java栈中)。

使用字符串常量池,每当我们使用关键字new(String s=new String(”1”);)创建字符串常量时,JVM会首先检查字符串常量池,如果该字符串已经存在常量池中,那么不再在字符串常量池创建该字符串对象,而直接堆中复制该对象的副本,然后将堆中对象的地址赋值给引用s,如果字符串不存在常量池中,就会实例化该字符串并且将其放到常量池中,然后在堆中复制该对象的副本,然后将堆中对象的地址赋值给引用s。

13. String 为什么要设计成不可变的?

安全:首要原因是安全,不仅仅体现在你的应用中,而且在JDK中,Java的类装载机制通过传递的参数(通常是类名)加载类,这些类名在类路径下,想象一下,假设String是可变的,一些人通过自定义类装载机制分分钟黑掉应用。如果没有了安全,Java不会走到今天
性能: string不可变的设计出于性能考虑,当然背后的原理是string pool,当然string pool不可能使string类不可变,不可变的string更好的提高性能。
线程安全 :当多线程访问时,不可变对象是线程安全的,不需要什么高深的逻辑解释,如果对象不可变,线程也不能改变它。

String对象只是一个引用,不是真正的字符串对象,这个引用指向真正的字符串对象。

14. final、finally 和 finalize 的区别?

  1. final: 修饰变量,方法,类; 修饰变量时表明这对象的值不可变,你不能为这个变量赋一个新的值,或者这样说:对基本类型而言,你不能改变其数值,对于引用,你不能将其指向一个新的引用(而引用自身是可以改变的)。 修饰方法时表明我们希望把这个方法锁定,以防止任何继承类修改它的含义,这样会确保在继承中,我们的 final 方法的行为不会改变,并且不会被覆盖。使用 final 方法的另一个考虑是效率问题:在 Java 早期的时候,遇到 final 方法,编译器会将此方法调用转为内嵌调用,如此一来以减小方法调用产生的开销。 修饰类的时候表明你不打算继承该类,而且也不允许别人这样做。
  2. finally: 是异常处理中进行收场处理的代码块,比如关闭一个数据库连接,清理一些资源占用的问题。不管有没有异常被捕获,finally 子句中的代码都会被执行。
  3. finalize: finalize 出现的原因在于,我们一定需要进行清理动作。Java 没有用于释放对象的,如同 C++ 里的 delete 调用,的方法,而是使用垃圾回收器(GC)帮助我们释放空间。当垃圾回收器准备释放对象占用的存储空间的时候,将首先调用其 finalize() 方法。

15. static 关键字有什么作用?

static 是 Java 里的非访问修饰符,它可以用来创建类方法和类变量。

当修饰一个变量的时候,此变量就成了独立于对象的静态变量,无论一个类实例化了多少个对象,这个类只有一份这个静态变量的拷贝,所以 static 修饰的变量,即静态变量,也被叫做类变量。一个局部变量不能被声明为 static 变量。

当修饰一个方法的时候,此方法就成了独立于对象的静态方法,静态方法不能使用类的非静态变量,因为静态方法和静态变量先于非静态的其他成员初始化,静态方法先出来,然后才是非静态的,所以明白这个顺序很重要。静态方法从参数列表得到数据,然后计算这些数据。

16. 列举 Java 的集合以及集合之间的继承关系?

集合类:
集合类存放的都是对象的引用,而非对象本身,出于表达上的便利,我们称集合中的对象就是指集合中对象的引用(reference)。集合类型主要有3种:set(集)、list(列表)和map(映射)。
在这里插入图片描述
Collection和Map最大的区别就是Collection存储的是一组对象;Map是以“键值对”的形式对对象进行的管理。

Iterator是迭代器,Iterable是接口。很多类,像List、Set、HashMap不直接实现迭代器接口Iterator,而是去实现Iterable接口。

Collection是一个集合接口。它提供了对集合对象进行进本操作的通用接口方法。

Collections是一个工具类。内有多个对集合对象进行操作的静态方法,不能实例化。

17. List、Set、Map 的区别?

List和Set是Collection的子类

List:
1、可以允许重复的对象

2、可以插入多个null元素

3、是一个有序容器,保持了每个元素的插入顺序,输出的顺序就是插入的顺序

4、常用的实现类有ArrayList,LinkedList和Vector。ArrayList提供了使用索引的随意访问,底层结构是数组,优查询劣增删,而LinkedList经常用于添加或删除元素的场合,底层结构是链表

Set:

1、不允许重复的对象

2、无序容器,你无法保证每个元素的存储顺序,TreeSet通过Conparator或者Comparable维护了一个排序顺序

3、只允许一个null元素

4、Set常用的实现类是HashSet,LinkedHashSet以及TreeSet。最流行的是基于HashMap实现的HashSet,TreeSet还实现了SortedSet接口,因此TreeSet是一个根据其conpare()和compareTo()的进行排序的有序容器。

Map:

1、Map不是collection的子接口或者实现类,Map是一个接口

2、Map的每个Entry都持有俩个对象,一个键一个值,可能会持有相同的值对象但键对象必须是唯一的。

3、TreeMap也通过Comparator或者Comparable维护了一个排序顺序

4、Map里你可以拥有任意个null,但只能有一个null键

5、常用的实现类HashMap,LinkedHashMap,Hashtable,TreeMap

18. ArrayList、LinkedList 的区别?

  • ArrayList 实现了可变大小的数组。它允许 null。ArrayList 没有同步。增删慢,查询快。
  • LinkedList 实现了 List 接口,允许 null 元素。此外 LinkedList 提供额外的 get,remove,insert 方法在 LinkedList 的首部或尾部。LinkedList 不是同步的(不是线程安全)。LinkedList 增删快,查询慢。
    实现线程安全方式如下:
List list = Collections.synchronizedList(new LinkedList(...)); 

19. HashMap,HashTable,ConcurrentHashMap 实现原理以及区别?

一.哈希表

哈希表就是一种以 键-值(key-indexed) 存储数据的结构,我们只要输入待查找的值即key,即可查找到其对应的值。

链式哈希表从根本上说是由一组链表构成。每个链表都可以看做是一个“桶”,我们将所有的元素通过散列的方式放到具体的不同的桶中。插入元素时,首先将其键传入一个哈希函数(该过程称为哈希键),函数通过散列的方式告知元素属于哪个“桶”,然后在相应的链表头插入元素。查找或删除元素时,用同们的方式先找到元素的“桶”,然后遍历相应的链表,直到发现我们想要的元素。因为每个“桶”都是一个链表,所以链式哈希表并不限制包含元素的个数。然而,如果表变得太大,它的性能将会降低。

HashMap就是通过链式哈希表实现。

链式哈希表的其他应用场景,比如我们熟知的缓存技术(比如redis、memcached)

二.HashMap,HashTable,ConcurrentHashMap的区别

HashMap是线程不安全的,在多线程环境下,使用Hashmap进行put操作会引起死循环,因为多线程会导致HashMap的Entry链表形成环形数据结构(扩容时 ,造成next往回指),查找时会陷入死循环。,所以在并发情况下不能使用HashMap。

HashTable和HashMap的实现原理几乎一样,差别无非两点

1.HashTable不允许key和value为null

2.HashTable是线程安全的

但是HashTable线程安全的策略实现代价却太大了,简单粗暴,get/put所有相关操作都是synchronized的,这相当于给整个哈希表加了一把大锁。

多线程访问时候,只要有一个线程访问或操作该对象,那其他线程只能阻塞,相当于将所有的操作串行化,在竞争激烈的并发场景中性能就会非常差。

主要就是为了应对hashmap在并发环境下不安全而诞生的,ConcurrentHashMap避免了对全局加锁改成了局部加锁操作,极大地提高了并发环境下的操作速度,但是ConcurrentHashMap在JDK1.7和1.8中的实现非常不同。

在JDK1.7中ConcurrentHashMap采用了数组+Segment分段锁的方式实现。

ConcurrentHashMap中的分段锁称为Segment,它即类似于HashMap的结构,即内部拥有一个Entry数组,数组中的每个元素又是一个链表,ConcurrentHashMap使用分段锁技术,将数据分成一段一段的存储,然后给每一段数据配一把锁,当一个线程占用锁访问其中一个段数据的时候,其他段的数据也能被其他线程访问,能够实现真正的并发访问。

优劣:

这一种结构的带来的副作用是Hash的过程要比普通的HashMap要长,ConcurrentHashMap定位一个元素的过程需要进行两次Hash操作。第一次Hash定位到Segment,第二次Hash定位到元素所在的链表的头部。

写操作的时候可以只对元素所在的Segment进行加锁即可,不会影响到其他的Segment,这样,在最理想的情况下,ConcurrentHashMap可以最高同时支持Segment数量大小的写操作(刚好这些写操作都非常平均地分布在所有的Segment上),并发能力可以大大的提高。

JDK1.8版本的ConcurrentHashMap采用了数组+链表+红黑树的实现方式来设计,内部大量采用CAS和synchronized操作。

CAS是compare and swap的缩写,即我们所说的比较交换。cas是一种基于乐观锁的操作,CAS 操作包含三个操作数 —— 内存位置(V)、预期原值(A)和新值(B)。如果内存地址里面的值和A的值是一样的,那么就将内存里面的值更新成B。如果a线程获取地址里面的值被b线程修改了,那么a线程需要自旋,到下次循环才有可能机会执行。

CAS是compare and swap的缩写,即我们所说的比较交换。cas是一种基于锁的操作,而且是乐观锁。在java中锁分为乐观锁和悲观锁。悲观锁是将资源锁住,等一个之前获得锁的线程释放锁之后,下一个线程才可以访问。而乐观锁采取了一种宽泛的态度,通过某种方式不加锁来处理资源,比如通过给记录加version来获取数据,性能较悲观锁有很大的提高。

1.数据结构:取消了Segment分段锁的数据结构,取而代之的是数组+链表+红黑树的结构。
2.保证线程安全机制:JDK1.7采用segment的分段锁机制实现线程安全,其中segment继承自ReentrantLock。JDK1.8采用CAS+Synchronized保证线程安全。
3.锁的粒度:原来是对需要进行数据操作的Segment加锁,现调整为对每个数组元素加锁(Node)。
4.链表转化为红黑树:定位结点的hash算法简化会带来弊端,Hash冲突加剧,因此在链表节点数量大于8时,会将链表转化为红黑树进行存储。
5.查询时间复杂度:从原来的遍历链表O(n),变成遍历红黑树O(logN)。

20. HashSet 与 HashMap 怎么判断集合元素重复?

HashMap中判断元素是否相同主要有两个方法,一个是判断key是否相同,一个是判断value是否相同

HashSet不能添加重复的元素,当调用add(Object)方法时候,
首先会调用Object的hashCode方法判hashCode是否已经存在,如不存在则直接插入元素;

如果已存在则调用Object对象的equals方法判断是否返回true,如果为true则说明元素已经存在,如为false则插入元素。

发现HashSet竟然是借助HashMap来实现的,利用HashMap中Key的唯一性,来保证HashSet中不出现重复值。

从这段代码中可以看出,HashMap中的Key是根据对象的hashCode() 和 euqals()来判断是否唯一的。

结论:为了保证HashSet中的对象不会出现重复值,在被存放元素的类中必须要重写hashCode()和equals()这两个方法。

21. String、StringBuffer、StringBuilder 之间的区别?

String的值是不可变的,这就导致每次对String的操作都会生成新的String对象,不仅效率低下,而且浪费大量优先的内存空间

StringBuffer是可变类,和线程安全的字符串操作类,任何对它指向的字符串的操作都不会产生新的对象。每个StringBuffer对象都
有一定的缓冲区容量,当字符串大小没有超过容量时,不会分配新的容量,当字符串大小超过容量时,会自动增加容量

StringBuilder 可变类,速度更快,但是线程不安全。

22. 什么是序列化?怎么实现?有哪些方式?

序列化是一种将对象转换为字节流的过程,目的是为了将该对象存储到内存中,等后面再次构建该对象时可以获取到该对象先前的状态及数据信息。

Java 中,只有一种方式可以实现序列化,只需要实现 Serializable 接口。

在 Android 中,还有另外一种实现序列化的方式,实现 Parcelable,这个是 Android 独有的一种序列化方方式,相比较 Serializable,Parcelable 需要提供大量的模板代码,较为繁琐,但是效率比 Serializable 高出不少,因为 Serializable 实现的序列化利用反射,可能会进行频繁的IO操作,所以消耗比较大。而 Parcelable 则是在内存中进行序列化。

所以这里推荐的是:

  • 内存序列化优先选择 Parcelable。
  • 存储到设备优先选择 Serializable(这里推荐使用 json 方式加密保存在本地,比较简单)

23. 对反射的了解?

JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意方法和属性;这种动态获取信息以及动态调用对象方法的功能称为java语言的反射机制。
在日常的第三方应用开发过程中,经常会遇到某个类的某个成员变量、方法或是属性是私有的或是只对系统应用开放,这时候就可以利用Java的反射机制通过反射来获取所需的私有成员或是方法。当然,也不是所有的都适合反射,之前就遇到一个案例,通过反射得到的结果与预期不符。阅读源码发现,经过层层调用后在最终返回结果的地方对应用的权限进行了校验,对于没有权限的应用返回值是没有意义的缺省值,否则返回实际值起到保护用户的隐私目的。

Class类 代表类的实体,在运行的Java应用程序中表示类和接口
Field类 代表类的成员变量(成员变量也称为类的属性)
Method类 代表类的方法
Constructor类 代表类的构造方法

24. 对注解的了解?

Java 注解用于为 Java 代码提供元数据。作为元数据,注解不直接影响你的代码执行,但也有一些类型的注解实际上可以用于这一目的。Java 注解是从 Java5 开始添加到 Java 的。

注解理解的前提:知道什么是反射

我认为注解就是标签

它是在 Java SE 5.0 版本中开始引入的概念,就如同java中的 class和interface

元注解: 元注解是可以注解到注解上的注解,或者说元注解是一种基本注解,但是它能够应用到其它的注解上面。

元标签有 @Retention、@Documented、@Target、@Inherited、@Repeatable 5 种。

25. 对依赖注入的了解?

依赖注入原理—IoC框架

26. 对泛型的了解?

型是程序设计语言的一种特性。允许程序员在强类型程序设计语言中编写代码时定义一些可变部分,那些部分在使用前必须作出指明。各种程序设计语言和其编译器、运行环境对泛型的支持均不一样。将类型参数化以达到代码复用提高软件开发工作效率的一种数据类型。泛型类是引用类型,是堆对象,主要是引入了类型参数这个概念。

1.可以统一数据类型,便于操作。
2.将运行时的异常提前到了编译时,提高了效率。
3.避免了强制类型转换
4.实现代码的模板化,把数据类型当作参数传递,提高了可重用性。

27. 泛型中 extends 和 super 的区别?

泛型中extends的主要作用是设定类型通配符的上限
上界<? extends T>不能往里存,只能往外取
一个能放水果以及一切是水果派生类的盘子。再直白点就是:啥水果都能放的盘子。这和我们人类的逻辑就比较接近了。Plate<? extends Fruit>和Plate最大的区别就是:Plate<? extends Fruit>是Plate以及Plate的基类。直接的好处就是,我们可以用“苹果盘子”给“水果盘子”赋值了。

Plate<extends Fruit>

相对应的,“下界通配符(Lower Bounds Wildcards)”:

Plate<super Fruit>

表达的就是相反的概念:一个能放水果以及一切是水果基类的盘子。Plate<? super Fruit>是Plate的基类,但不是Plate的基类。

super与extends是完全相反的,其定义的是下界通配符。
下界<? super T>不影响往里存,但往外取只能放在Object对象里

28. 对 Java 的异常体系的了解?

Java异常体系简析

29. 对解析与分派的了解?

JAVA方法调用中的解析与分派

30. 静态代理和动态代理的区别?有什么场景使用?

静态代理和动态代理,什么场景使用

31. 谈谈对 Java 状态机理解?

java状态机理解

线程与并发

1. 线程和进程的区别?

一、进程与线程的区别:
1、进程是资源分配的最小单位,线程是程序执行的最小单位(资源调度的最小单位)
2、进程有自己的独立地址空间,每启动一个进程,系统就会为它分配地址空间,建立数据表来维护代码段、堆栈段和数据段,这种操作非常昂贵。
而线程是共享进程中的数据的,使用相同的地址空间,因此CPU切换一个线程的花费远比进程要小很多,同时创建一个线程的开销也比进程要小很多。
3、线程之间的通信更方便,同一进程下的线程共享全局变量、静态变量等数据,而进程之间的通信需要以通信的方式(IPC)进行。不过如何处理好同步与互斥是编写多线程程序的难点。
4、但是多进程程序更健壮,多线程程序只要有一个线程死掉,整个进程也死掉了,而一个进程死掉并不会对另外一个进程造成影响,因为进程有自己独立的地址空间。

2. 开启线程的三种方式

方式1:继承Thread类
方式2:实现Runnable接口
方式3:实现Callable接口

public class Thread1 extends Thread {
    @Override
    public void run() {
        super.run();
        System.out.println("thread1");
    }
}
public class Thread2 implements Runnable {
    @Override
    public void run() {
        System.out.println("Thread2");
    }
}
import java.util.concurrent.Callable;

public class Thread3 implements Callable<String> {
    @Override
    public String call() throws Exception {
        return "Thread3";
    }
}
public class User {
    public static void main(String[] args) {
        Thread1 thread1 = new Thread1();
        thread1.start();

        Thread thread2 = new Thread(new Thread2());
        thread2.start();

        Thread3 thread3 = new Thread3();
        FutureTask<String> futureTask = new FutureTask<>(thread3);
        Thread thread = new Thread(futureTask);
        thread.start();
        try {
            System.out.println(futureTask.get());
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (ExecutionException e) {
            e.printStackTrace();
        }
    }
}

3. 如何正确的结束一个Thread?

1.不用Stop()方法
一般来说,当一个线程的方法体执行完,这个方法就会结束了。我们一般不直接使用stop方法来终止线程,这可能会导致业务逻辑不完整或者破坏原子性的操作,一段代码可能用来输出某些重要的信息,在方法的执行中使用stop方法终止线程,会使得输出的信息破坏或者不完整;在原子操作比如重入锁的使用时,当一个线程对象多次获得锁会使得计数加一,接下来需要以相同的次数释放锁直到计数减到0,别的线程才能获得锁资源,这种操作是原子性的,因为对线程来说,他们拿到锁和最终释放锁时计数都是为0的,因此这是一种原子性操作,假设在一个对象使用锁的期间,使用stop方法强行终止线程,这会导致锁的计数不为0,即破坏了原子性操作。综合来说,我们如果要正确地终止一个线程,不应该使用stop()这种过时的方法。

2.使用标志位
使用一个flag控制while循环,如果flag为true执行线程逻辑,如果为false则释放资源。

3.使用interrupt方法
1、interrupt()方法:对某个线程对象调用这个方法就可以把它的中断标志位置为true,但它不能立刻结束一个线程。
2、currentThread()方法:静态方法,用线程类调用获得当前线程对象的引用。
4、sleep():使当前线程睡眠,释放资源但是不释放锁。
5、join():等待线程终止,停住调用这个方法的当前线程,等待指定的线程运行结束。减少程序运行的不确定性。
6、start():启动一个线程。
7、interrupted()方法:是静态方法,测试当前线程是否被中断,同时清除中断标志。也就是说,在中断标志置位后第一次调用返回true,第二次调用返回false。
8、isinterrupt()方法:这个方法由对象调用,测试线程是否已经被中断,仅仅返回标志的状态,不会对标志有任何影响。

4. Thread 与 Runnable 的区别?

Thread是类,Runnable是接口。Java类只能继承一个父类,但是能实现很多接口。Runnable接口实现的线程便于资源共享。而通过Thread类实现,各自线程的资源是独立的,不方便共享。

5. run() 与 start() 方法的区别?

run()直接执行run方法中的逻辑,并没有开启线程。
start()开启线程并执行run方法中的逻辑

6. sleep() 与 wait() 方法的区别?相同点:

都使程序进入一个暂停的状态,

不同点:

wait():当线程等待状态为真,其他程序申请线程时,该线程会释放线程锁;如果该线程调用notify()方法,本线程会进入对象锁定池准备,获取对象锁进入运行状态。

.sleep();程序暂停执行指定的时间,释放cpu资源,在调用sleep()方法的过程中,线程不会释放对象锁。当指定时间到了,就会自动恢复运行状态。

7. wait 与 notify 关键字的区别?

void notify()
Wakes up a single thread that is waiting on this object’s monitor.
译:唤醒在此对象监视器上等待的单个线程

void notifyAll()
Wakes up all threads that are waiting on this object’s monitor.
译:唤醒在此对象监视器上等待的所有线程

void wait( )
Causes the current thread to wait until another thread invokes the notify() method or the notifyAll( ) method for this object.
译:导致当前的线程等待,直到其他线程调用此对象的notify( ) 方法或 notifyAll( ) 方法

void wait(long timeout)
Causes the current thread to wait until either another thread invokes the notify( ) method or the notifyAll( ) method for this object, or a specified amount of time has elapsed.
译:导致当前的线程等待,直到其他线程调用此对象的notify() 方法或 notifyAll() 方法,或者指定的时间过完。

void wait(long timeout, int nanos)
Causes the current thread to wait until another thread invokes the notify( ) method or the notifyAll( ) method for this object, or some other thread interrupts the current thread, or a certain amount of real time has elapsed.

8. synchronized 关键字的用法、作用及实现原理?

synchronized关键字及其实现原理

9. volatile 关键字的用法、作用及实现原理?

volatile关键字的作用、原理

10. transient 关键字的用法、作用及实现原理?

transient的作用及使用方法

11. ReentrantLock、synchronized、volatile 之间的区别?

synchronized与ReentrantLock、Volatile的区别

12. 什么是线程池,如何使用?

由浅入深理解Java线程池及线程池的如何使用

13. 多线程断点续传的实现原理?

多线程断点续传原理

14. 什么是深拷贝和浅拷贝?

Java基础 - 深拷贝和浅拷贝

15. Java 中对象的生命周期?

java对象生命周期和类生命周期

16. 对并发编程的了解?

JVM

1. 简述 JVM 内存模型和内存区域?

内存模型:

Java 内存模型规定了所有的变量都是存储在主内存中。每条线程还有自己的工作内存,线程的工作内存中保存了该线程使用到的变量的主内存副本拷贝,线程对变量的所有操作都必须在工作内存中进行,而不能直接读写主内存中的变量。不同的线程之间也无法直接访问对方工作内存中的变量,线程间变量值的传递均需要通过主内存来完成。

内存区域:

内存区域可以分为以下几块:

  • 程序计数器

    程序计数器是一块较小的内存空间,它可以看作是当前线程所执行的字节码的行号指示器。如果线程正在执行的是一个 Java 方法,这个计数器记录的是正在执行的虚拟机字节码指令的地址;如果正在执行的是 Native 方法,这个计数器值为空。此内存区域是唯一一个没有规定任何 OOM 的区域。

  • 虚拟机栈

    虚拟机栈描述的是 Java 方法执行的内存模型:每个方法在执行的同时都会创建一个栈桢用于存储局部变量表、操作数栈、动态链接地址、方法出口等信息。每一个方法从调用直至执行完成的过程,就对应着一个栈桢在虚拟机中入栈到出栈的过程。

  • 本地方法栈

    本地方法栈和虚拟机栈所发挥的作用是非常相似的,它们之间的区别不过是虚拟机栈为虚拟机执行 Java 方法服务,而本地方法栈则为虚拟机执行 Native 方法服务。

  • Java 堆是虚拟机管理的内存中最大的一块,此内存的唯一目的就是存放对象实例,几乎所有的对象实例都在这里分配内存。

  • 方法区

    用于存储已经被虚拟机加载的类信息、常量、静态变量、即时编译器编译后的代码等数据。相对而言,垃圾回收在这个区域是比较少出现的。

更多请参考:

Java 内存模型

Java 内存区域

2. 简述垃圾回收器的工作原理?

java垃圾回收器的工作原理

3. 如何判断对象的生死?垃圾回收算法?新生代,老生代?

Java基础:JVM垃圾回收算法

4. 哪些情况下的对象会被垃圾回收机制处理掉?

哪些情况下的对象会被垃圾回收机制处理掉

5. 垃圾回收机制与调用 System.gc() 的区别?

垃圾回收机制与调用System.gc()区别

6. 强引用、软引用、弱引用、虚引用之间的区别?

强引用:不会被 GC 轻易清理,只要引用存在,垃圾回收器永远不会回收。

Object obj = new Object();

软引用: 非必须引用,内存溢出之前进行回收

Object obj = new Object();
SoftReference<Object> sf = new SoftReference<Object>(obj);
obj = null;
sf.get();//有时候会返回 null

弱引用: 第二次垃圾回收时回收

Object obj = new Object(); 
WeakReference wf = new WeakReference(obj); 
obj = null; wf.get();//有时候会返回 null 
wf.isEnQueued();//返回是否被垃圾回收器标记为即将回收的垃圾

虚引用: 垃圾回收时回收,无法通过引用取到对象值

Object obj = new Object();
PhantomReference<Object> pf = new PhantomReference<Object>(obj);
obj=null;
pf.get();//永远返回 null
pf.isEnQueued();//返回是否从内存中已经删除

7. 强引用设置为 null,会不会被回收?

强引用置为null,会不会被回收

8. 简述 ClassLoader 类加载机制?

面试官,不要再问我“Java虚拟机类加载机制”了

9. 对双亲委派模型的了解?

双亲委派模型

10. String a = “a”+“b”+“c” 在内存中创建几个对象?

这个问题涉及到了字符串常量池和字符串拼接

String a = "a"+"b"+"c"

通过编译器优化后,得到的效果是

String a = "abc"

此时,如果字符串常量池中存在 abc,则该语句并不会创建对象,只是讲字符串常量池中的引用返回而已。

如果字符串常量池中不存在 abc,则会创建并放入字符串常量池,并返回引用,此时会有一个对象进行创建。

两个深入阅读的链接:

  • 字符串常量池
  • 字符串拼接内部实现

11. 对 Dalvik、ART 虚拟机的了解?

JAVA虚拟机、Dalvik虚拟机和ART虚拟机简要对比

Android热修复技术初探(一):了解Dalvik与Art虚拟机

12. 对动态加载(OSGI)的了解?

OSGI

13. 常见编码方式有哪些?

Java几种常见的编码方式

14. utf-8 编码中的中文占几个字节?int 型占几个字节?

ava基本类型占用的字节数:
1字节: byte , boolean
2字节: short , char
4字节: int , float
8字节: long , double

编码与中文:
Unicode/GBK: 中文2字节
UTF-8: 中文通常3字节,在拓展B区之后的是4字节
综上,中文字符在编码中占用的字节数一般是2-4个字节。

其他

1. 谈谈你对 Java 平台的理解?

java本身是一种面向对象的语言,具有很好的跨平台的能力,能够做到“write once ,run anywhere”。另外就是GC机制,java通过垃圾回收器回收分配内存,程序员无需自己操心内寸的回收问题。 我们日常会接触到jre和jdk。jre是java运行环境,包含了jvm和java类库等。jdk则是在jre的基础上提供了更多的工具,比如编译器,和一些诊断工具等。
java是解释执行,这句话不是很准确。java代码在执行时,先通过javac编译为字节码bytecode,在运行时,通过jvm内嵌解释器转换为最终机器码。但常见jvm,比如Oracle JDK提供的Hotspot JVM,都提供了jit(just in time)动态编译器,能够在运行时将热点代码编译成机器码,这部分属于编译执行。

2. Exception 和 Error 有什么区别?

① Exception 和Error 都是继承了Throwable类,在Java中只有Throwable类型的实例才可以被抛出或者捕获,它是异常处理机制的基本类型。

#② Exception和Error体现了Java平台设计者对不同异常情况的分类。

⑴Exception是程序正常运行中,可以预料的意外情况,可能并且应该被捕获,进行相应处理。

⑵Exception又分为可检查(checked)异常和不可检查(unchecked)异常。可检查异常在源代码里必须显式的进行捕获处理,这是编译期检查的一部分。不可检查时异常是指运行时异常,像NullPointerException、ArrayIndexOutOfBoundsException之类,通常是可以编码避免的逻辑错误,具体根据需要来判断是否需要捕获,并不会在编译期强制要求。

⑶Error是指正常情况下,不大可能出现的情况,绝大部分的Error都会导致程序处于非正常的、不可恢复的状态。既然是非正常情况,不便于也不需要捕获。常见的比如OutOfMemoryError之类都是Error的子类。# 3. 谈谈 final、finally、finalize 有什么不同?

4. 强引用、软引用、弱引用、幻想引用有什么区别?

强引用、软引用、弱引用、幻想引用的区别

5. String、StringBuffer、StringBuilder 有什么区别?

String : 是java.lang包中的immutable类,String里面所有的属性几乎也是final,由于它的不可变性,类似拼接,裁剪字符串等动作都会产生大量无用的中间对象。由于字符串操作在项目中很常见,所以对String的操作对项目的性能往往有很明显的影响。
StringBuffer : 这个类是为了解决String拼接产生多余对象的问题而提供的一个类。StringBuffer保证了线程的安全,也带来了多余的开销。
StringBuilder : StringBuilder的功能与StringBuffer一样。但是区别是StringBuilder没有处理线程安全,减少了开销。

6. 动态代理基于什么原理?

java是静态的强类型语言,但是因为提供了反射等机制,也就具备了部分动态类型的语言能。

反射机制是java语言提供的一种基础功能,赋予程序在运行时自省的能力。通过反射我们可以直接操作类或者对象

动态代理是一种方便运行时动态构建代理、动态处理代理方法调用的机制,很多场景都会用到。rmi、rpc等

动态代理主要是让调用者与实现者之间解耦。

7. int 和 Integer 有什么区别?

上面有,重复了

8. 对比 Vector、ArrayList、LinkedList 有何区别?

上面有,重复了

9. 对比Hashtable、HashMap、TreeMap 有什么不同?

上面有,重复了

10. 如何保证集合是线程安全的?ConcurrentHashMap 如何实现高效地线程安全?

如何保证集合是线程安全的? ConcurrentHashMap如何实现高效地线程安全?

11. Java 提供了哪些 IO 方式?NIO 如何实现多路复用?

Java提供了哪些IO方式?NIO如何实现复用?

12. Java 有几种文件拷贝方式?哪一种最高效?

Java有几种文件拷贝方式?哪一种最高效?

13. 谈谈接口和抽象类有什么区别?

接口和抽象类有什么区别

14. 谈谈你知道的设计模式?

这个tm内容太多了,稍后再谈

15. synchronized 和 ReentrantLock 有什么区别?

java synchronized和(ReentrantLock)区别

16. synchronized 底层如何实现?什么是锁的升级、降级?

synchronized 底层如何实现?什么是锁的升级、降级?

17. 一个线程两次调用 start() 方法会出现什么情况?

Exception in thread "main" java.lang.IllegalThreadStateException
	at java.lang.Thread.start(Thread.java:708)
	at User.main(User.java:12)

Java经典面试题:一个线程两次调用start()方法会出现什么情况?

18. 什么情况下 Java 程序会产生死锁?如何定位、修复?

[核心技术36问]18.什么情况下java程序会产生死锁?如何定位、修复?

19. Java 并发包提供了哪些并发工具类?

Java并发包提供了哪些并发工具类?

20. 并发包中 ConcurrentLinkedQueue 和 LinkedBlockingQueue 有什么区别?

[核心技术36问]20.并发包中的ConcurrentLinkedQueue和LinkedBlockingQueue有什么区别?

21. Java 并发库提供的线程池有哪几种?分别有什么特点?

Java并发类库提供的线程池有哪几种?分别有什么特点?

22. AtomicInteger 底层实现原理是什么?如何在自己的产品代码中应用 CAS 操作?

AtomicInteger底层实现原理是什么?如何在自己的产品代码中应用CAS操作?

23. 请介绍类加载过程,什么是双亲委派模型?

Java面试题之类加载器有哪些?什么是双亲委派模型

24. 有哪些方法可以在运行时动态生成一个 Java 类?

有哪些方法可以在运行时动态生成一个Java类?

25. 谈谈 JVM 内存区域的划分,哪些区域可能发生 OutOfMemoryError?

谈谈JVM内存区域的划分,哪些区域可能发生 OutOfMemoryError?

26. 如何监控和诊断 JVM 堆内和堆外内存使用?

如何监控和诊断JVM堆内和堆外内存使用

27. Java 常见的垃圾收集器有哪些?

Java常见的垃圾收集器有哪些?

28. 谈谈你的 GC 调优思路?

谈谈你的GC调优思路?

29. Java 内存模型中的 happen-before 是什么?

【死磕Java并发】-----Java内存模型之happens-before

30. Java 程序运行在 Docker 等容器环境有哪些新问题?

Java程序运行在Docker等容器环境有哪些新问题

31. 你了解 Java 应用开发中的注入攻击吗?

了解Java应用中的开发攻击

32. 如何写出安全的 Java 代码?

1.只写一行
2.如何写出安全的java代码

33. 后台服务出现明显“变慢”,谈谈你的诊断思路?

后台服务出现明显变慢,谈谈你的诊断思路

34. 有人说“Lambda 能让 Java 程序慢 30 倍”,你怎么看?

1.放屁
2.有人说“Lambda能让Java程序慢30倍”,你怎么看

35. JVM 优化 Java 代码时都做了什么?

JVM优化Java代码时都做了什么?

36. 谈谈 MySQL 支持的事务隔离级别,以及悲观锁和乐观锁的原理和应用场景?

谈谈MySQL支持的事务隔离级别,以及悲观锁和乐观锁的原理和应用场景?

Psychedelic9
发布了3 篇原创文章 · 获赞 0 · 访问量 16
私信 关注

本文链接http://element-ui.cn/news/show-1010.aspx