java面试题

一、java面试题

熟练掌握java是很关键的,大公司不仅仅要求你会使用几个api,更多的是要你熟悉源码实现原理,甚至要你知道有哪些不足,怎么改进,还有一些java有关的一些算法,设计模式等等。

(一) java基础面试知识点

1、java中==和equals和hashCode的区别

java中基本数据类型比较实用==,比较的是它们的值

引用类型(类,接口、数组)

当它们使用==时,比较的是它们在内存中的地址,除非是同一个new出来的对象,比较的结果是 true,否则都是false。

equals(默认情况下)主要是判断对象的内存地址(即是不是同一个对象)和==是一样的。

equals注意点:

(1)、自反性:对任意引用值X, x.equals(x)的返回值一定是true。

(2)、对称性:对于任意引用值x,y,x.equals(y)返回是true,则y.equals(x)也一定是true。

(3)、传递性:如果x.equals(y),y.equals(z)返回true,则x.equals(z)也一定是true

(4)、一致性:如果参与比较的对象没有任何改变,则对象比较的结果也不会有任何改变

(5)、非空性:任何非空的引用值X,x.equals(null)返回false。

如果两个对象equals,那么java运行时环境认为它们的hashcode是相同的.

如果两个对象不equals,它们的hashcode有可能相等。

如果两个对象的hashcode相等,它们不一定equals。

如果两个对象的hashcode不相等,它们一定不equals。

2、int、char、long各占多少字节数

byte占1个字节,

boolean至少占1个字节,

short占2个字节,

char占2个字节,

int占4个字节,

float占4个字节,

long占8个字节,

double占8个字节

英文字母:

字节数 : 1;编码:GB2312

字节数 : 1;编码:GBK

字节数 : 1;编码:ISO-8859-1

字节数 : 1;编码:UTF-8

中文汉字:

字节数 : 2;编码:GB2312

字节数 : 2;编码:GBK

字节数 : 1;编码:ISO-8859-1

字节数 : 3;编码:UTF-8

3、int与integer的区别

(1)、integer是int的包装类,int是java的基本数据类型。

(2)、Integer变量必须实例化后才能使用,int变量不需要。

(3)、Integer实际是对象的引用,当new一个Integer时,实际上是生成一个指针指向该对象,int则直接存储数据的值。

(4)、Integer的默认值是null,int的默认值是0.

4、谈谈对java多态的理解

多态分为两种:

(1)、编译时多态:方法的重载

(2)、运行时多态:java运行时,根据该方法的实例类型来决定调用哪个方法,(即父类引用指向子类对象)

多态的条件:

继承、重写、父类引用指向子类的对象。

5、String、StringBuffer、StringBuilder区别

String值不可变,每次对String操作都会生成新的String对象,不仅效率低下,而且浪费内存空间

StringBuffer是可变类,线程安全,可多线程操作字符串。

StringBuilder是可变类,非线程安全,只可单线程操作字符串,相比StringBuffer在单线程情况下更快。

6、什么是内部类?内部类的作用

定义在一个类的内部的类称为内部类。

内部类分为:静态内部类,成员内部类,匿名内部类,局部内部类

局部内部类:局部内部类是定义在一个方法或一个作用域里边的类,它和成员内部类的区别是局部内部类仅限于在方法或作用域内访问。

作用:

(1)、每个内部类都能独立的继承一个接口的实现,所以无论外部类是否已经继承某个接口的实现,对于内部类都没有影响。内部类使得多继承的解决方案变得完整。

(2)、方便将存在一定逻辑关系的类组织在一起,又可以对外界隐藏。

(3)、方便编写事件驱动程序。

(4)、方便编写线程代码。

7、抽象类和接口区别

(1)、抽象类和接口都不能直接实例化,如果要实例化,抽象类变量必须指向实现所有抽象方法的子类对象,接口变量必须指向实现所有接口方法的类对象。

(2)、抽象类要被子类继承,接口要被类实现。

(3)、接口只能做方法声明,抽象类可以做方法声明也可以做方法实现。

(4)、接口里定义的变量只能是公共的静态常量,抽象类里的变量是普通变量。

(5)、抽象类里边的抽象方法必须全部被子类所实现,如果子类不能全部实现父类的抽象方法,那么,子类也必须是抽象类。同样,一个实现接口方法时,如果不能全部实现,则该类只能是抽象类。

(6)、抽象方法只能声明,不能实现,接口是设计的结果,抽象类是重构的结果。

(7)、抽象类里边可以没有抽象方法。

(8)、如果一个类里边有抽象方法,那么这个类只能是抽象类。

(9)、抽象方法要被实现,所以抽象方法不能是静态的,也不能是私有的。

(10)、接口可继承接口,且可以多继承,类只能单根继承。

8、抽象类的意义

抽象类的意义:对代码的维护和重用

(1)、因为抽象类不能实例化对象,所以必须有子类实现它以后才能使用。这样就把一些具有相同属性和方法的组件进行抽象,这样更利于代码的维护

(2)、当又有一个相似的组件产生时,只需要实现该抽象类即可获得该抽象类的所有方法和属性。

9、抽象类与接口的应用场景

抽象类的应用场景:既需要统一的接口,又需要实例变量或缺省的的方法的情况下,使用抽象类。

其他情况下都使用接口即可。

10、抽象类是否可以没有方法和属性?

可以。

抽象类中可以没有抽象方法,但是抽象方法一定属于抽象类。所以java类中可以没有抽象方法。即使没有抽象方法和属性的抽象类,也不能直接被实例化。

11、接口的意义

(1)、通过接口可以实现不同类的相同行为。

(2)、通过接口可以指明多个类需要实现的方法。

(3)、类描述了一个实体,包括实体的状态,实体可能发生的动作。接口则定义了一个实体可能发生的动作。但只定义了这些动作的原型,没有实现,也没有任何状态信息。

(4)、接口里定义了方法的输入输出,这些都是协议,具体的实现都在每个类里。

(5)、一个类描述了一个实体,这个实体可能有很多动作,接口a定义其中一部分动作,接口b定义另一部分动作,这样结构清楚,易于维护。

(6)、解决多继承带来的问题。

12、泛型中extends和super的区别

<? extends T>限定参数类型的上限:参数类型必须是T或T的子类型。

<? super T>限定参数类型的下限:参数类型必须是T或T的超类。

总结:

<? extends T>只能用于方法返回,告诉编译器此返参的类型的最小的继承边界是T,T和T的父类都能接收,但入参类型无法确定,只能接受null的传入。

<? super T>只能用于限定方法入参,告诉编译器入参只能是T或是T的子类型,而返参只能用Object类型接收。

?既不能用于入参也不能用于返参。

13、父类的静态方法能否被子类重写

不能

所谓静态就是指:在编译时所分配的内存是一直存在的(不会被回收),直到程序退出,内存才会释放这块内存空间,在实例化之前,这个方法就已经存在于内存中了,跟类的对象没有关系。子类中如果定义相同名字的静态方法,并不会重写,而是在内存中重新开辟一块内存空间,所以父类的静态方法没有被子类重写这一说,只能说子类的静态方法与父类的静态方法名是相同的。

14、进程和线程的区别

(1)、进程是资源分配的最小单位,线程是程序执行的最小单位。

(2)、进程有自己独立的地址空间,每启动一个进程,系统会为它分配地址空间,建立数据表来维护代码段、堆栈段和数据段。而线程是共享进程中的数据的,使用相同的地址空间,因此,CPU在切换一个线程的开销比进程要小的多。

(3)、线程之间通信更方便,同一进程下的线程共享全局变量、静态变量等数据,而进程之间的通信需要以通信方式IPC进行。

(4)、多进程程序更健壮,多线程程序只要有一个线程死掉,整个进程就死掉了,而一个进程死掉,不会对其他进程造成影响,因为进程有自己独立的地址空间。

15、final,finally,finalize的区别

final修饰符(关键字)。被final修饰的类,意味着不能再派生出新的子类,不能作为父类被子类继承。因此,一个类不能既被abstract声明,又被final声明。将变量或方法声明为final,可以保证在使用过程中不会被篡改。被声明为final的变量必须在声明的时候初始化,而以后的引用中只能读取。被声明的方法也同样只能使用,不能被重写。

finally是在异常处理时提供finally块来执行任何清除工作。无论try catch是否发生异常,最后finally都会执行。

finalize是方法名。java技术允许使用finalize()方法在垃圾收集器将对象从内存中清除出去之前做必要的清理工作。这个方法由垃圾收集器在确定这个对象没有被引用时对这个对象调用的。它是在object类中定义的,因此所有的类都继承了它。finalize()方法是垃圾收集器删除对象之前对这个对象调用的。

16、序列化的方式

序列化:将对象转化为字节流。

反序列化:将字节流转化为对象。

第一种实现:实现Serializable接口(是JavaSE本身就支持的)

第二种实现:实现Parcelable接口(Android特有的功能,效率比Serializable接口高效,可用于Intent数据传递,也可以用于进程间通信(IPC))

17、Serializable 和Parcelable 的区别

(1)、在使用内存的时候,Parcelable比Serializable性能高,所以推荐使用Parcelable。

(2)、Serializable在序列化的时候会产生大量的临时变量,从而引起频繁的GC。

(3)、Parcelable不能使用在要将数据存储在磁盘上的情况,因为Parcelable不能很好的保证数据的持续性,在外界有变化的情况下。尽管Serializable效率有点低,但是还是推荐使用Serializable。

(4)、Serializable实现,只需要implements Serializable即可,这是给对象打一个标记,系统会自动将其序列化。

(5)、Parcelable实现,不仅需要implements Parcelable,还需要在类中添加一个静态成员变量CREATOR,这个变量要实现Parcelable.Creator接口。

(6)、Parcelable性能比Serializable好,在内存开销方面比较小,所以在内存间数据传输时,推荐使用Parcelable,如:Activity之间传值;而Serializable在数据持久化方面方便保存,在网络中传输时,使用Serializable,因为android不同版本的Parcelable可能不同,所以不推荐使用Parcelable做数据持久化。

18、静态属性和静态方法是否可以被继承?是否可以被重写?以及原因?

静态属性和静态方法可以被继承,不可以被重写。

原因:因为父类的静态属性,静态方法会在内存中开辟内存,在生成子类之前,所以当子类中重写该静态属性和静态方法时,子类会重新开辟内存空间,和父类的静态属性、静态方法并不指向同一内存地址,所以说,子类只是创建了与父类同样命名的属性和方法,静态属性静态方法没有被重写这一说法。

19、静态内部类的设计意图

使用静态类的情况只有一种,就是在内部类中使用静态。如果在外部类中使用static会报错。

静态内部类实例化可以直接通过外部类.来进行。

对比普通的成员内部类,必须使用外部类的对象来初始化。

静态内部类的使用场景:外部类需要使用内部类中的数据,而内部类不需要使用外部类中的数据,可以使用静态内部类。

20、成员内部类、静态内部类、局部内部类和匿名内部类的理解,以及项目中的应用

成员内部类:

作为外部类的一个成员存在,与外部类的属性、方法并列。

成员内部类持有外部类的引用。

成员内部类中不能定义static的变量和方法。

静态内部类:

静态内部类不会持有外部类的引用。

静态内部类可以访问外部类中的静态变量,如果访问外部类的成员变量必须通过外部类的实例访       问。

静态内部类的使用场景:

内部类不需要持有外部类的实例,静态内部类的存在仅仅为外部类提供服务或逻辑上属于外部类,且逻辑上可以单独存在。

局部内部类:

局部内部类,嵌套在一个方法或作用域内的内部类。

用在方法内部,作用范围仅限于该方法中。

根据情况决定持有外部类的对象的引用。

不能使用public、protected和private来修饰。

不能包含静态成员。

局部内部类的使用场景:

如果内部类对象仅仅为外部类的某个方法使用,可以使用局部内部类。

匿名内部类:

使用new创建,没有具体位置。

创建的匿名类,默认继承或实现new后面的类型。

根据使用情况决定是否持有外部类对象引用。

内嵌匿名类编译后生成的class文件的命名方式:外部类$编号.class,编号为:1、2、3...编号为x的class对应第x个内部类。

匿名内部类的使用场景:

如果一个内部类在整个操作中只是用一次的话,就可以定义为匿名内部类。

21、谈谈对kotlin的理解

(1)、代码量少,且代码末尾没有分号。

(2)、被调用的方法需要放到上边。

(3)、kotlin是空安全的:在编译时就处理了各种null的情况,避免了执行时异常。

(4)、它可扩编函数:我们也可以扩展任意类的更多的特性。

(5)、他也是函数式的:比如,使用lambda表达式来更方便的解决问题。

(6)、高度的互操作性:可以继续使用java写的代码和库,甚至可以在一个项目中使用java和kotlin混编。

22、闭包和局部内部类的区别

局部内部类使用函数的局部变量,这样就形成了闭包。

闭包:即内部作用域调用外部作用域的成员或方法。

23、string 转换成 integer的方式及原理

(1)、parserInt(String s)内部调用parserInt(s,10),默认是10进制的。

(2)、正常判断null,进制范围,length等。

(3)、判断第一个字符是否是符号位。

(4)、循环遍历确定每个字符的十进制值

(5)、通过*=和-=进行计算拼接。

(6)、判断是否为负值,返回结果。

(二) java深入源码级的面试题(有难度)

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

GC判断对象能不能被回收处理一般有两种方式:

(1)、引用计数法

这种方法是在对象的头部维护一个计数器Counter,当有一个引用指向对象的时候Counter就加一,当不再引用此对象的时候,就让Counter减一,当Counter为0的时候,虚拟机就认为此对象可以被回收,但这种方式有一个致命的缺点,就是对象循环引用。

上图即使r不再指向A,那么A、B、C,GC使用引用计数法就无法释放这三个对象。

(2)、可达性分析算法

这种方法是虚拟机会将一下对象定义为GC Roots,从GC Roots出发一直沿着引用链向下寻找,如果某个对象不能通过GC Roots寻找到,则虚拟机就认为该对象可以被回收了。

可定义为GC Roots的对象:

(1)、虚拟机栈中的引用的对象。

(2)、方法区中的静态属性引用的对象。

(3)、方法区中常量引用的对象。

(4)、本地方法栈中JNI(Native方法)引用的对象。

2、讲一下常见编码方式?

ASCII码,ISO-8859-1,GBK,GB2312,UTF-16,UTF-8。

3、utf-8编码中的中文占几个字节;int型几个字节?

utf-8编码中中文少数占3个字节,多数占4个字节,int占4个字节

4、静态代理和动态代理的区别,什么场景使用?

静态代理:由程序员创建或工具生成代理类的源码,再编译代理类。所谓静态也就是在程序运行前就已经存在了代理类的字节码文件,代理类和委托类的关系在程序运行前就已经确定了。

上图即存在一个接口,代理类和委托类都实现了该接口,委托类具体实现该接口中的方法,代理类持有委托类的一个引用,在该接口方法中调用委托类中的方法。生成一个静态代理工厂,单例模式,

客户只需调用静态代理工厂生成一个接口变量,使用该变量调用接口中的方法即可。

动态代理:动态代理类只能代理接口(不支持抽象类),代理类都需要实现InvocationHandler类,实现invoke方法。该invoke方法就是调用被代理接口的所有方法时需要调用的,该invoke方法返回的值是被代理接口的一个实现类。

我们可以通过LogHandler代理不同类型的对象,如果我们把对外的接口都通过动态代理来实现,那么所有的函数最终都会经过invoke函数的转发,因此,我们可以在这里做一些自己想做的操作,比如:日志系统、事务、拦截器、权限控制等。这也就是AOP(面向切面编程)的基本原理。

插曲:AOP:将日志记录,性能统计,安全控制,事务处理,异常处理等代码从业务逻辑里边分离出来,通过这些行为的分离,我们希望可以将他们独立到非指导业务逻辑的方法中,进而改变这些行为的时候不影响业务逻辑代码——解耦。

静态代理应用:一些第三方框架的代理,便于后期替换或定制化变更。

动态代理应用:被代理类庞大时,需要在某些方法执行前后处理一些事情,亦或接口类与实现类经常变动时(因为使用反射,所以方法的增删改并不需要修改invoke方法)。

5、Java的异常体系

Throwable类(表示可以抛出)是所有异常和错误的超类,两个直接子类Error和Exception,分别表示错误和异常。

1、Error是程序无法处理的错误,它是由JVM产生和抛出的,比如OutOfMemoryError、ThreadDeathd等。这些异常发生时,java虚拟机(JVM)一般会选择线程终止。

Exception是程序本身可以处理的异常,这种异常分两大类,运行时异常和非运行时异常。程序中应尽可能的去处理这些异常。

2、运行时异常和非运行时异常。

运行时异常都是RuntimeException类及其子类异常,如NullPointerException、IndexOutOfBoundsException等,这些异常是不检查异常,程序中可以捕获处理,也可以不捕获处理。这些异常一般是由程序逻辑错误引起的,程序应该从逻辑角度尽可能的避免这类错误。

非运行时异常是RuntimeException以外的异常,类型上都属于Exception类及其子类。从程序语法的角度来说,是必须处理的异常,如果不处理,程序就不能编译通过。如IOException、SQLException等以及用户自定义的Exception异常,一般情况下不自定义检查异常。

6、谈谈你对解析与分派的认识。

解析:在类加载的阶段,会将一部分符号引用转化为直接引用,这种解析能成立的前提是:方法在程序真正运行之前就有一个可确定的调用版本,并且这个方法的调用版本在程序运行期是不能改变的。换句话说,调用目标在程序代码写好、编译器进行编译时就确定下来,这类方法的调用称为解析。

只要能被invokestatic和invokespecial指令调用的方法,都可以在解析阶段确定唯一的调用版本,符合这个条件的方法有:静态方法、私有方法、类构造方法和父类方法4类。解析是一个静态的过程。

分派:分派调用分成两类,静态分派和动态分派。其中,重载是静态分派,重写是动态分派。

重载是在编译期间确定的,因此静态分派是一个静态的过程(因为还没有涉及到Java虚拟机)。

静态分派最直接的解释是在重载的时候是通过参数的静态类型而不是实际类型作为判断依据的,因此在编译阶段,javac编译器会根据参数的静态类型决定使用哪个重载版本。

首先确定什么是静态类型和实际类型,Human man = new Man(),在语句中Human称为变量的静态类型,Man被称为变量的实际类型。

动态分派:最直接的例子就是重写,java动态分派是java虚拟机通过稳定手段——在方法区建立一个虚方法表,通过使用方法表的索引来代替元数据查找以提高性能。虚方法表中存放着各个方法的实际入库地址,如果子类没有覆盖父类的方法,那么子类的虚方法表里面的地址入口和父类是一致的;如果重写父类的方法,那么子类方法表的地址将会替换成子类实现版本的地址。

方法表是在类加载的连接阶段(验证、准备、解析)进行初始化,准备了子类的初始化值后,虚拟机会会把该类虚方法表也进行初始化。

7、修改对象A的equals方法的签名,那么使用HashMap存放这个对象实例的时候,会调用哪个equals方法?

会调用该对象超类Object类的equals方法,

8、Java中实现多态的机制是什么?

父类引用指向子类的对象。

9、如何将一个Java对象序列化到文件里?

10、说说你对Java反射的理解

java反射提供了一种代码运行时动态改变其属性的可能,我们所写的java文件经过编译都会生成class文件,然后进入JVM虚拟机进行class加载链接运行,然而,反射就是把我们JVM中运行class文件的各种属性、变量、方法等反射成相应的对象,让我们对其修改。

获取反射对象可以用:getClassLoader(),getClass();

反射会消耗系统性能,我们不应该滥用反射。

11、说说你对Java注解的理解

java中的注解类似于生活标签的作用,就是用我们的标签(元数据),对箱标注的物品(对象、方法)进行标注,注入我们定义好的属性值,当我们需要的时候进行获取解析(可以是编译器生成的字节码,或是通过反射获取注解对象)时就可以只得到这个东西代表什么东西,有哪些属性。

12、说说你对依赖注入的理解

依赖注入:简单来说就是A类中使用了B类的实例时,B对象的构造不是在A类某个方法中初始化的,而是在A类外部初始化之后以B类的对象传进来的。这个过程就是依赖注入。

13、说一下泛型原理,并举例说明

上界通配符<T extends Integer>

下届通配符<T super String>

方法参数集合通配符List<?>

14、Java中String的了解

String是常量,所以存储在常量池中,如果String是对象,则存储在堆中。

15、String为什么要设计成不可变的?

线程安全

原子操作

对象变化得到了避免

16、Object类的equal和hashCode方法重写,为什么?

因为当把Object对象放到集合中时,通过Equals比较对象,不做处理还是会出现重复的问题,根据hash原则,对象的映射地址是根据算法生成的,因为hash碰撞的存在,即两个不同的对象的hash地址可能一样的情况,这样hash地址一样的情况还需要重写equal方法进行比较。

有两种办法可以解决这种问题,第一个重写Object类中的equals和hashCode方法;第二种就是把对象转换成String再放入到集合中,因为String类源码已经重写了这两个方法。

(三) 数据结构

1、常用数据结构简介

List子接口

LinkedList

LinkedList实现List接口,允许Null元素。此外,LinkedList提供额外的get、remove、insert方法在LinkedList的首部或尾部。这些操作使LinkedList可被用在堆栈(stack)、队列(queue)、或双向队列(deque)中

ArrayList

ArrayList实现了可变大小的数组。它允许所有元素,包括NULL。ArrayList没有同步。

size、isEmpty、get、set方法的运行时间为常数。但add方法开销为分摊的常数,添加n个元素,需要O(n)的时间。其他方法的运行时间为线性。

Vector

Vector非常类似于ArrayList,但Vector是同步的。

Stack

Stack继承自Vector,实现一个后进先出的堆栈。

Set

Set是一种不包含重复元素的Collection,即任意两个元素e1和e2都有e1.equals(e2) = false,Set最多有一个null元素。

Map

Map没有继承Collection接口,Map提供key到value的映射。一个Map中不能包含相同的key,每个key只能映射一个value。

Hashtable

Hashtable继承自Map接口,实现一个key-value的映射的哈希表。任何非空(non-null)的对象都可作为key或者value。Hashtable是同步的。

HashMap

HashMap与Hashtable类似,不同之处在于HashMap是非同步的,并允许null,即null key 和 null value。

WeakHashMap

WeakHashMap是一种改进的HashMap,它对key实行弱引用,如果一个key不再被外部所引用,那么该key就可以被GC回收掉了。

2、并发集合了解哪些?

并发集合类:

ConcurrentHashMap

ConcurrentHashMap:把整个Map划分成几个片段,只对相关的几个片段上锁,同时允许多进程访问其他未上锁的片段。

CopyOnWriteArrayList

CopyOnWriteArrayList;允许多个线程以非同步的方式读,当有线程写的时候它会将这个List复制一个副本给它。如果在读多写少这种对并发集合有利的条件下使用并发集合,这会比使用同步集合更具有可伸缩性。

CopyOnWriteHashSet

3、列举java的集合以及集合之间的继承关系

从上面的集合框架图可以看出:Java集合框架类主要包含两种类型的容器,一种是集合(Collection),存储一个元素集合,另一种是图(Map),存储键/值映射。

Collection接口又有3种类型的子类,List、Set、Queue,再下边是一些抽象类,最后是具体的实现类,常用的有ArrayList、LinkedLis、HashSet、LinkedHashSet、HashMap、LinkedHashMap等等。

4、集合类以及集合框架

同上

5、容器类介绍以及之间的区别(容器类估计很多人没听这个词,Java容器主要可以划分为4个部分:List列表、Set集合、Map映射、工具类(Iterator迭代器、Enumeration枚举类、Arrays和Collections),具体的可以看看这篇博文 Java容器类)

Array:效率高,容量固定无法动态改变。并且length只能告诉容量,而不能告诉长度。

Collection、List、Set和Map

Collection、List、Set和Map都是接口

List下最主要有链表LinkedList和数组,数组分为ArrayList和Vector,两者的区别是Vector是支持线程同步的,适合多线程使用。

6、List,Set,Map的区别

List集合中对象按照索引位置排序,可以有重复对象,允许按照对象在集合中的索引位置检索对象,例如通过list.get(i)的方式来获取集合中的元素。

Map中每一个元素包含一个键和一个值,成对出现,键对象不可以重复,值对象可以重复。

Set集合中的元素不按照特定的方式排序,并且没有重复对象,但它的实现类能对集合中的对象按照特定的方式进行排序,例如TreeSet类,可以按照默认顺序,也可以通过实现java.util.Comparator<Type>接口来自定义排序顺序。

7、List和Map的实现方式以及存储方式

List list = new ArrayList();

Map<Object,Object> map = new HashMap<Object,Object>();

List存储的元素容器是个有序的可以索引到元素的容器,并且里边的元素可以重复。

Map存储的是键值对,键不可以重复。

8、HashMap的实现原理

HashMap的存储结构是由数组和链表共同完成的。

HashMap基本原理:

1、首先判断key是否为Null,如果为Null,直接查询Entry[0],如果不是NULL,先计算key的hashcode,然后经过两次Hash。得到hash值,这里的hash特征值是一个int值。

2、根据得到的hash特征值,对Entry[]数组的长度length取于,得到的就是Entry数组的index。

3、找到了对应的数组,就找到了对应的链表,然后按照链表的操作对value进行插入、删除、查询等操作。

9、HashMap数据结构?

HashMap是由数组和链表存储数据的。

10、HashMap源码理解

HashMap里边也包含一些优化的方面,比如:Entry[]的长度一定后,随着map里边的数据越来越长,这样同一个index链会很长,会不会影响性能?HashMap里边设置了一个负载极限,随着map的size越来越大,Entry[]会以一定的规则加长长度。

HashMap默认的负载极限是0.75,表明该hash表的3/4已经被填满时,hash表会发生rehashing。

11、HashMap如何put数据(从HashMap源码角度讲解)?

首先判断key是否为null,key为null则调用putForNullKey()方法,该方法会直接访问Entry[0],如果key不为null,则将key进行两次hash算法,得到一个整型值,该值与Entry[]数组的length进行取于,得到的余数即是数组的index,Entry[index] = value

12、HashMap怎么手写实现?

https://www.jianshu.com/p/b638f19aeb64

13、ConcurrentHashMap的实现原理

JDK1.7分析

ConcurrentHashMap采用分段锁的机制,实现并发的更新操作,底层采用数组+链表的存储结构。

其中包含两个静态内部类Segment和HashEntry。

1、Segment继承ReentrantLock用来充当锁的角色,每个Segment对象守护每个散列映射表的若干个桶。

2、HashEntry用来封装映射表的键值对

3、每个桶是由若干个HashEntry对象链接起来的链表。

一个ConcurrentHashMap是由若干个Segment对象组成的数组。

JDK1.8分析

1.8的实现已经抛弃了Segment分段锁的机制,利用CAS+Synchronized来保证并发更新的安全,底层采用数组+链表+红黑树的存储结构。

14、ArrayMap和HashMap的对比

ArrayMap

ArrayMap利用两个数组,mHashes用来保存每一个key的hash值,mArray大小为mHashes的两倍,依次存储key和value。

查询的时候使用二分查找。

1、查找效率:

HashMap因为其根据hashcode的值直接计算index,所以其查找效率是随着数组长度的增加而增大。

ArrayMap使用的是二分法查找,所以当数组长度每增加一倍,就需要多进行一次判断,效率下降。

2、扩容数量:

HashMap的初始值16个长度,每次扩容的时候,直接申请双倍的数组空间。

ArrayMap每次扩容的时候,如果size长度大于8时申请size*1.5个长度,大于4,小于8时申请8个,小于4个时,申请4个。

这样比较ArrayMap其实是申请了更少的内存空间,但扩容的频率会更高。因此,如果当数据量比较大时,还是用HashMap比较合适,因为扩容次数要比ArrayMap少很多。

3、扩容效率:

HashMap每次扩容的时候重新计算每个数组成员的位置,然后放到新的位置。

ArrayMap则直接使用System.arrayCopy。

所以效率上肯定是ArrayMap更占优势。

4、内存耗费:

以ArrayMap采用了一种独特的方式,能够重复的利用因为数据扩容而遗留下来的数组空间,方便下一个ArrayMap使用,而HashMap没有这样的设计。

综上所述:数据量较小,并且频繁使用Map的时候可以使用ArrayMap;数据量较大时,使用HashMap。

15、HashTable实现原理

HashTable实际与HashMap的结构一样,都是数组和链表共同实现。

16、TreeMap具体实现

TreeMap内部实现是红黑树

红黑树是特殊的二叉查找树,又名R-B树,由于红黑树是特殊的二叉查找树,即红黑树具有二叉查找树的特性,而且红黑树还有以下特性:

1、每个节点要么是红色,要么是黑色。

2、根节点是黑色。

3、每个叶子节点是黑色,并且为空节点。

4、如果一个节点是红色,则它的子节点必须是黑色。

5、从一个节点到该节点的子孙节点的所有路径包含相同数目的黑节点。

上图:

左旋:以X为节点左旋,则Y为X的父节点,Y的左叶子节点变化为X的右叶子节点。

右旋:以Y为节点右旋,则X为Y的父节点,X的右叶子节点变化为Y的左叶子节点。

17、HashMap和HashTable的区别

1、HashMap是非同步的,没有对读写等操作进行锁保护,所以是线程不安全的,在多线程场景下会出现数据不一致的问题。而HashTable是同步的,所有操作都进行了锁(synchronize)保护,在多线程下没有安全访问的问题。但锁保护也是有代价的,会对读写的效率产生较大的影响。

2、HashMap结构中,是允许保存null的,Entry.key和Entry.value都可以为null。但HashTable不允许保存null的。

3、HashMap的迭代器(Iterator)是fail-fast迭代器,但HashTable的迭代器(enumerator)不是fail-fast迭代器。如果其他线程对HashMap进行添加或删除元素,将会抛出ConcurrentModificationException异常,但迭代器本身的remove方法移除元素则不会抛出异常。这同样也是Enumerator和Iterator的区别。

18、HashMap与HashSet的区别

HashSet是对HashMap的简单包装,对HashSet函数的调用,都会转化成合适的HashMap。

19、HashSet与HashMap怎么判断集合元素重复?

HashSet不能添加重复的元素,当调用add方法的时候,首先会调用Object的hashCode方法判断hashCode是否已存在,不存在则直接插入元素;如果一样,则需要调用Object的equals方法,判断是否返回true,如果返回true,则说明元素已存在,如果返回false则插入元素。

HashMap同样判断对象的hashCode和equals方法,不同的是HashMap遇到相同的key,会将value的值替换成新的value值。

20、集合Set实现Hash怎么防止碰撞

对象Hash的前提是实现hashCode()和equals()方法,那么hashCode()的作用是保证对象返回唯一的hash值,但当两个对象计算值一样时,这就发生了碰撞冲突。

1、开放地址法。

2、再哈希法。

3、链地址法。

4、建立一个公共溢出区。

21、ArrayList和LinkedList的区别,以及应用场景

1、ArrayList是实现了基于动态数组的数据结构,LinkedList基于链表的数据结构。

2、对于随机访问的get和set,ArrayList较优于LinkedList,因为LinkedList需要移动指针。

3、对于新增和删除操作add和remove,LinkedList比较占优势,因为ArrayList需要移动数据。

22、数组和链表的区别

内存存储:

1、数组从栈中分配空间,对程序员方便快速,自由度小。

2、链表从堆中分配内存,自由度大,但申请管理比较麻烦。

逻辑结构:

1、数组必须事先定义固定的长度(元素个数),不能适应数据的增减情况(数据插入、删除比较麻烦)。当数据增加时,可能会超出数组的最大空间(越界);当数组减少时,造成内存浪费。

2、链表动态的进行存储分配,可以使用数据动态的增减情况(数据插入删除简单)(数组插入、删除数据项时需要移动其他项),但链表查询元素时需要遍历整个链表。

23、二叉树的深度优先遍历和广度优先遍历的具体实现

二叉树的深度优先遍历的非递归的通用做法是采用栈,广度优先遍历的非递归通用做法是采用队列。

深度优先遍历:对每一个可能的分支路径深入到不能再深入为止,而且每个节点只能访问一次。深度优先遍历包含:先序遍历、中序遍历、后续遍历

先序遍历:对任一子树,先访问根,然后遍历其左子树,再遍历右子树。

中序遍历:对任一子树,先遍历其左子树,然后访问根,最后遍历其右子树。

后续遍历:对任一子树,先遍历其左子树,然后遍历其右子树,最后访问根。

广度优先遍历:又叫层次遍历,从上到下对每一层一次访问,在每一层中,从左往右(也可以从右往左)访问节点,访问完一层就进入下一层,直到没有节点可以终止访问。

24、堆的结构

堆是一个完整的二叉树,在这颗树中,所有父节点都满足大于等于其子节点的堆叫大根堆,所有父节点都满足小于等于其子节点的堆叫小根堆。堆虽然是一棵树,但通常存放在一个数组中,父节点和子节点的父子关系通过数组下标来确定

25、堆和树的区别

堆是一类特殊的树,堆的通用特点就是父节点会大于或小于所有的子节点。

26、堆和栈在内存中的区别是什么(解答提示:可以从数据结构方面以及实际实现方面两个方面去回答)?

栈内存:在函数中定义的基本类型的变量和对象的引用变量都是在函数的栈内存中分配。

堆内存:堆内存存放的是由new创建的数组或对象。

堆是用来存放对象的,栈是用来存放执行程序的。

27、什么是深拷贝和浅拷贝

浅拷贝:浅拷贝就是按位拷贝对象,它会创建一个新的对象,这个对象有着原始属性值的一份精确拷贝。如果属性是基本数据类型,拷贝的就是基本数据类型的值;如果属性是内存地址(引用类型),拷贝的就是内存地址,因此,如果其中一个对象改变了这个地址,就会影响到另一个对象。

深拷贝:深拷贝会拷贝所有的属性,并拷贝属性指向的动态分派的内存。当对象和它所引用的对象一起拷贝时即发生深拷贝。深拷贝相比于浅拷贝速度要慢,花销较大。

对象的clone方法默认是浅拷贝,若想实现深拷贝需要重写clone方法实现属性对象的拷贝。

28、手写链表逆序代码

http://blog.csdn.net/u012571415/article/details/46955535

29、讲一下对树,B+树的理解

https://www.jianshu.com/p/db226e0196b4

30、讲一下对图的理解

图由定点的有穷非空集合和定点之间边的集合组成,通常表示为:G(V,E),其中,G表示图,V是图G中定点的集合,E是图G中边的集合。

图分为有向图和无向图。

31、判断单链表成环与否?

32、链表翻转(即:翻转一个单项链表)

33、合并多个单有序链表(假设都是递增的)

大体思路:使用递归

1、判断L1、L2是否为空。

2、创建一个头指针。

3、判断当前L1、L2指向的节点值的大小,根据结果,让头指针指向节点值小的节点,并让这个节点往下走一步,作为递归函数的参数传入,返回的就是新的两个值的比较结果,则新的比较结果放入头节点的下一个节点。

4、返回头节点。

(四) 线程、多线程和线程池

1、开启线程的三种方式?

Java中创建线程主要有三种方式

继承Thread类创建线程类。

通过Runnable接口创建线程类。

通过Callable和Future创建线程。

2、线程和进程的区别?

(1)、进程是资源分配的最小单位,线程是程序执行的最小单位。

(2)、进程有自己独立的地址空间,每启动一个进程,系统会为它分配地址空间,建立数据表来维护代码段、堆栈段和数据段。而线程是共享进程中的数据的,使用相同的地址空间,因此,CPU在切换一个线程的开销比进程要小的多。

(3)、线程之间通信更方便,同一进程下的线程共享全局变量、静态变量等数据,而进程之间的通信需要以通信方式IPC进行。

(4)、多进程程序更健壮,多线程程序只要有一个线程死掉,整个进程就死掉了,而一个进程死掉,不会对其他进程造成影响,因为进程有自己独立的地址空间。

3、为什么要有线程,而不是仅仅用进程?

进程只能在一个时间干一件事,如果想同时干两件事或多件事,进程就无能为力了。

进程在执行过程中如果遇到阻塞,例如等待输入,整个进程就会挂起,即使进程中有些工作不依赖与输入的数据,也将无法执行。

4、run()和start()方法区别

start:

用start方法来启动线程,真正实现了多线程运行,这无需等待run方法体代码执行完毕而直接继续执行下面的代码。通过调用Thread类的start()方法来启动一个线程,这时线程就处于就绪(可运行)状态,并没有运行,一旦得到CPU时间片,就开始执行run()方法,这里的run()方法称为线程体,它包含了要执行这个线程的内容,Run方法运行结束,这个线程随即终止。

run:

run()方法只是类的一个普通方法而已,如果直接调用run()方法,程序依然只有一个主线程在运行,其程序执行路径仍然只有一条,还是要顺序执行,还是要等待run方法体执行完毕才能执行下面的代码,这样就没有达到写线程的目的。

总结:

调用start()方法可以启动线程,调用run()方法只是类中的一个普通方法,还是会只在主线程中运行。

start()方法启动线程,会自动调用run()方法,这是由jvm内存机制规定的。并且run()方法必须是public访问权限,返回类型必须是void。

5、如何控制某个方法允许并发访问线程的个数?

信号量(Semaphore),有时也被称为信号灯,在多线程环境下使用的一种设施,它负责协调各个线程,以保证他们能够正确合理的使用公共资源。

6、在Java中wait和seelp方法的不同;

seelp()方法属于Thread类,wait()方法属于Object类。

seelp()方法导致了程序暂停执行指定的时间,让出CPU给其他线程,但它的监控状态依然保持着,当指定的时间到了,又会恢复运行状态。

在调用seelp()方法时,线程不会释放对象锁。

而调用wait()方法时,线程会放弃对象锁,进入此对象的等待锁定池,只有针对此对象调用notify()方法后本线程才进入对象锁定池,准备获取对象锁进入运行状态。

7、谈谈wait/notify关键字的理解

wait()该方法将当前线程置入休眠状态,直到接到通知或被中断为止。

notify()方法用来通知可能等待该对象的对象锁的其他线程。如果有多个线程等待,则线程规划器任意挑出其中一个线程的wait()状态的线程来发出通知,并使它等待获取该对象的对象锁。(notify后该线程不会马上释放对象锁,wait所在的线程也不会马上获取到该对象锁,要等到程序退出synchronized代码块后,当前线程才会释放对象锁,wait所在的线程也才可以获取对象锁。)

8、什么导致线程阻塞?

(1)、线程执行了seelp()方法,当前线程放弃CPU,睡眠一段时间,然后再恢复执行。

(2)、线程执行一段同步代码,但是尚且无法获得相关的同步锁,只能进入阻塞状态,等到获得了同步锁,才能回复执行。

(3)、线程执行了一个对象的wait()方法,直接进入阻塞状态,等待其他线程执行notify()或notifyAll()方法。

(4)、线程执行某些IO操作,因等待相关资源而进入等待状态。比如,监听System.in,但是尚且没有收到键盘的输入,则进入阻塞状态。

9、线程如何关闭?

(1)、使用退出标志,使线程正常退出,也就是当run方法完成后线程终止。

(2)、使用stop方法强行终止线程(这个方法不推荐使用,因为stop和suspend、resume一样,也可能发生不可预料的结果)。

(3)、使用interrupt方法中断线程。

10、讲一下java中的同步的方法

(1)、同步方法

即有synchronized关键字修饰的方法。

(2)、同步代码块

即有synchronized关键字修饰的代码块。

被该关键字修饰的代码块会自动被加上内置锁,从而实现同步。

(3)、使用特殊域变量(volatile)实现线程同步。

a、volatile关键字为域变量提供了一种免锁机制。

b、使用volatile修饰域相当于告诉虚拟机该域可能被其他线程更新。

c、因此每次使用该域变量就需要重新计算,而不是使用寄存器中的值。

d、volatile不会提供任何原子操作,它也不能用来修饰final类型的变量。

(4)、使用重入锁实现线程同步。

注:关于Lock对象和synchronized关键字的选择:

a、最好两个都不用,使用一种java.util.concurrent包提供的机制,能够帮助用户处理所有与锁有关的代码。

b、如果synchronized关键字能够满足用户的需求,就用synchronized,因为它能简化代码。

c、如果需要更高级的功能,就用ReentrantLock类,此时需要注意及时释放锁,否则会出现死锁,通常在finally代码释放锁。

(5)、使用局部变量实现线程同步。

注:ThreadLocal与同步机制

a、ThreadLocal与同步机制都是为了解决多线程中相同变量的访问冲突问题。

b、前者采用以空间换时间的方法,后者采用以时间换空间的的方法。

11、数据一致性如何保证?

https://www.cnblogs.com/jiumao/p/7136631.html

12、如何保证线程安全?

(1)、互斥同步:同步是指多个线程并发访问共享数据时,保证共享数据在同一个时刻只能被一个线程使用。而互斥是实现同步的一种手段,临界区、互斥量、信号量都是主要的互斥实现方式。互斥是方法,同步是目的。

(2)、非阻塞同步:互斥同步最主要的问题就是进行线程阻塞和唤醒带来的性能问题,因此,这种同步也称为阻塞同步。非阻塞同步是先进行操作,如果没有其他线程争用共享数据,则操作成功;如果数据有争用,产生了冲突,则采取其他的补偿措施。

(3)、无同步方案:对于一个方法本来就不涉及共享数据,那自然不需要同步措施来保证正确性。

a、可重入代码:在代码执行的任何时候中断它,转而去执行另一端代码,控制权返回后,原来的程序不会出现任何错误。

b、线程本地存储:共享数据的代码是否在同一线程中执行,如生产者-消费者的模式。

13、如何实现线程同步?

(1)、同步方法

使用synchronized关键字修饰的方法

(2)、同步代码块

使用synchronized关键字修饰的代码块。

(3)、使用特殊域变量(volatile)。

(4)、使用重用锁实现线程同步。

(5)、使用局部变量实现线程同步。

14、两个进程同时要求写或者读,能不能实现?如何防止进程的同步?

允许多个线程读取操作,不允许读操作和写操作同时执行,不允许多个线程写操作。

控制访问数量即可防止进程同步,java并发库Semaphore可以完成信号量的控制,Semaphore可以控制某个资源可被同时访问的数量,通过acquire()获取一个许可,如果没有就等待,而release()释放一个许可。

15、线程间操作List

(1)、使用Collections.synchronizedList()构建list

(2)、操作list的方法增加同步锁。

因为Collections.synchronizedList()构建的list只针对list的add()、poll()等方法做了线程同步,在多线程中直接使用方法会同步,但是在操作list时,add()、poll()方法之前不能保证obj是否被其他线程操作过。

16、Java中对象的生命周期

java对象的生命周期包括:创建阶段、应用阶段、不可见阶段、不可达阶段、收集阶段、终结阶段、对象空间重新分配阶段。

17、Synchronized用法

Java中的每个对象都可以作为锁。

普通同步方法,锁是当前实例对象。

静态同步方法,锁是当前类的class对象。

同步代码块,锁是括号中的对象。

18、synchronize的原理

每个对象有一个监视器锁(monitor)。当monitor被占用时,就会处于锁定的状态,线程执行monitorenter指令时尝试获取monitor的所有权。

http://www.cnblogs.com/paddix/p/5367116.html

19、谈谈对Synchronized关键字,类锁,方法锁,重入锁的理解

Synchronized关键字作用:让方法或代码块的操作具有原子性,从而解决多线程共享资源的问题。

类锁:类锁是锁住整个类,当多线程声明这个类的对象时将会被阻塞,直到拥有这个类锁的对象被销毁或主动释放了类锁,这个时候在被阻塞的线程中挑选出一个占有该类锁,声明该类的对象。其他线程继续被阻塞住。

方法锁:修饰当前方法,进入同步代码块前需要先获得该实例的锁

重入锁:重进入是指任意线程在获取到锁之后,再次获取该锁而不会被该锁阻塞。

20、static synchronized 方法的多线程访问和作用

当synchronized修饰一个静态方法时,多线程下,获取的是类锁(即class本身,注意:不是实例),作用范围是整个静态方法,作用的对象是这个类所有的对象。

注:static说明了该类的一个静态资源,不管new了多少对象,只有一份,所以对该类的所有对象都加了锁。

21、同一个类里面两个synchronized方法,两个线程同时访问的问题

synchronized方法增加了对象锁,当一个线程获取到该对象锁是访问其中一个方法,另外一个线程是获取不到该对象锁的,也就是另外一个线程是没有办法访问synchronized方法的,只有一个线程一个线程的访问。

22、volatile的原理

可见性:

(1)、修改volatile变量时会强制将修改后的值刷新主内存中。

(2)、修改volatile变量后会导致其他线程工作内存中对应的变量值失效。因此,再读取该变量值的时候就需要重新从主内存中读取。

有序性:Lock前缀指令相当于一个内存屏障(也叫内存栅栏),它确保指令重排序时不会把其后边的指令排到内存屏障之前的位置,也不会把前面的指令排到内存屏障的后面。即在执行到内存屏障这句指令时,在它前面的操作已经全部完成。

内存屏障:

23、谈谈volatile关键字的用法

24、谈谈volatile关键字的作用

保持内存可见性,防止指令重排序。

25、谈谈NIO的理解

Java NIO是一种新式的IO标准,与之前普通的IO操作的工作方式不同。标准的IO基于字节流,和字符流操作,而NIO是基于通道(Channel)和缓冲区(Buffer)进行操作的,数据总是从通道读取到缓冲区中,或者从缓冲区写入通道也类似。

26、synchronized 和volatile 关键字的区别

(1)、volatile本质是在告诉jvm当前变量在寄存器(工作内存)中的值是不确定的,需要从主内存中读取;synchronized则是锁定当前变量 ,只有当前线程可以访问该变量,其他线程被阻塞住。

(2)、volatile仅能使用在变量级别;synchronized则可以使用在变量、方法和类的级别上。

(3)、volatile仅能实现变量的修改可见性,不能保证原子性;synchronized则可以保证变量的修改可见性和原子性。

(4)、volatile不会造成线程阻塞;synchronized可能会造成线程阻塞。

(5)、volatile标记的变量不会被编译器优化;synchronized标记的变量可以被编译器优化。

27、synchronized与Lock的区别

28、ReentrantLock 、synchronized和volatile比较

synchronized互斥锁,即操作互斥,并发线程过来,串行获得锁,串行执行代码。就像一个房间一把钥匙一样,一个人进去后,下个人必须等第一个人出来得到钥匙才能进入。如果代码写的不好容易出现死锁。

ReentrantLock可重入锁,和同步锁功能类似,不过需要显式的创建和销毁。

特点:

(1)、ReentrantLock有tryLock方法,如果锁被其他线程特有,返回false,可避免形成死锁。

(2)、创建时可自定义是否抢占。

(3)、ReentrantReadWriteLock,用于读多,写少,且读不需要互斥的场景,大大提高了性能。

volatile,翻译过来是易变的。只保证同一变量在多线程中的可见性。

(1)、它确保指令重排序时不会把后面的指令排在内存屏障之前,也不会把前面的指令排在内存屏障之后;即在执行到内存屏障这句指令时,前面的指令都已经执行完毕。

(2)、它会强制将对缓存的修改立即写入主内存。

(3)、如果是写操作,它会导致其他CPU中对应的缓存行无效。

29、ReentrantLock的内部实现

ReentrantLock支持两种获取锁的方式,一种是公平模型,一种非公平模型。

公平模型:

(1)、初始化时,state是0,表示无人抢占了打水权,这个时候村民A打水(线程A请求锁)

(2)、线程A获取了锁,把state的原子性+1,这个时候的state是1,A线程继续执行其他任务,然后村民B来打水(线程B请求锁),线程B无法获取锁,生成节点进行排队。

(3)、初始化的时候会生成一个空的头结点,然后才是B线程节点,这个时候,如果线程A又请求锁,是不需要排队的,否则就会直接死锁。当A再次请求打水的时候,相当于同一家人来了,是有特权的。

(4)、这就是重入锁,就是一个线程获取了锁之后,再次去获取同一个锁,这个时候仅仅把状态值进行累加。如果A释放了一次锁,就变成下图:

仅仅是把状态值减一,只有线程A把此锁全部释放了,状态值减为0了,其他线程才有机会获得此锁。当A释放掉此锁,state恢复0,然后通知队列唤醒B线程节点,使B可以再次竞争锁。当然如果B线程后边还有C线程,C线程继续休眠,除非线程B执行完,通知C线程。注意:当一个线程节点被唤醒然后取的锁,对应节点会从队列中删除。

非公平锁模式:

当线程A执行完成,要唤醒B线程是需要时间的,而且线程B醒来后还要再次竞争锁,所以如果在切换过程当中,来了一个线程C,那么线程C是有可能获取到锁的,如果线程C获取到了锁,B只能继续休眠。

30、lock原理

实现Lock接口的基本思想,必须满足两个条件。

(1)、一个表示锁状态的变量,(我们假设0表示没有线程获取锁,1表示已有线程占据锁),该变量必须声明为volatile。

(2)、另一个是队列,队列中的节点表示因未能获取锁而阻塞的线程。

31、死锁的四个必要条件?

(1)、互斥条件:一个资源每次只能被一个进程使用。

(2)、请求与保持条件:一个进程因请求资源而阻塞时,对已获得的资源保持不放。

(3)、不剥夺条件:进程已获得资源,在未使用完之前,不能强行剥夺。

(4)、循环等待条件:若干进程之间形成一种头尾相接的循环等待资源关系。

32、怎么避免死锁?

(1)、加锁顺序(线程按照一定的顺序加锁)

(2)、加锁时限(线程尝试获取锁的时候加一定的时限,超过时限则放弃对该锁的请求,并释放自己占有的锁)。

(3)、死锁检测。

33、对象锁和类锁是否会互相影响?

不会相互影响。

类锁和对象锁不是同一个东西,一个是类的class对象的锁,一个是类的实例的锁。也就是说:一个线程访问静态synchronized的时候,允许一个线程访问对象的实例synchronized方法。反过来也是成立的,因为他们需要的锁是不同的。

34、什么是线程池,如何使用?

线程池就是容纳多个线程的容器,其中的线程可以反复使用,省去频繁创建线程对象的操作,无需反复创建线程而消耗过多的资源。

Java四种线程池:

newCacheThreadPool创建一个可缓存线程池,如果线程池长度超过了处理需要,可灵活回收空闲线程,若无可回收,则新建线程。

newFixedThreadPool创建一个定长线程池,可控制线程最大并发数,超出的线程会在队列中等待。

newScheduledThreadPool创建一个定长线程池,支持定时及周期性任务执行。

newSingleThreadExecutor创建一个单线程化的线程池,它只会用唯一的工作线程来执行任务,保证所有的任务按照指定的顺序(FIFO,LIFO,优先级)执行。

35、Java的并发、多线程、线程模型

线程和锁的模型是java语言的并发模型。

36、谈谈对多线程的理解

并行:指同一时刻,有多条指令在多个处理器上同时执行。

并发:指在同一时刻,只有一条指令执行,但多个进程指令被快速轮换执行,使得在宏观上具有多个进程同时执行的效果。

从逻辑角度来看,多线程存在于一个应用程序中,让一个应用程序中可以有多个执行部分同时执行,但操作系统无需将多个线程看作多个独立的应用,对多线程实现调度和管理及资源分配。线程的调度和分配由进程本身负责完成。

37、多线程有什么要注意的问题?

并发问题,安全问题,效率问题

38、谈谈你对并发编程的理解并举例说明

编写并发程序会在代码上增加额外的开销。

正确的并发是非常复杂的即使对于很简单的问题。

并发中的缺陷因为不易重现也不容易被发现。

并发往往需要对设计策略从根本上进行修改。

生产者和消费者模型。

39、谈谈你对多线程同步机制的理解?

线程同步是为了线程安全,所谓线程安全是指多个线程访问同一资源时,有可能产生数据不一致问题,导致线程访问的资源并不是安全的。如果多线程程序运行结果和单线程程序运行结果一样,且相关变量的值和预期值一样,则是线程安全的。

Java中与线程同步相关的关键字/类包括:

volatile、synchronized、Lock、Atomiclnteger等concurrent包下的原子类等。

40、如何保证多线程读写文件的安全?

多线程文件并发安全其实就是在考察线程并发安全,最普通的方式就是使用wait/notify、Condition、

synchronized、ReentrantLock等方式,这些方式默认都是排他操作,也就是默认情况下,同一时刻只有一个线程可以对文件进行操作,所以可以保证并发文件操作的安全性,但是并发读数量远多于写数量的情况下,性能却不那么好。因此,推荐使用ReadWriteLock的实现类ReentrantReadWriteLock,它也是lock的一种实现,允许多个读线程同时访问,不允许写线程和读线程、写线程和写线程同时访问。所以相对于排他锁来说提高了并发效率。ReentrantReadWriteLock读写锁里边维护了两个继承自lock的锁,一个用于读操作(ReadLock),一个用于写操作(WriteLock)。

41、多线程断点续传原理

我们要在下载行为出现中断的时候,记录下中断的位置信息,在下次下载的行为中读取该位置信息,有了这个位置信息,直接从记录的这个位置开始下载内容,而不需要从头开始下载。

42、断点续传的实现

下载过程出现中断,抛出异常,记录当前位置,下次下载从该位置开始下载。

(五)并发编程有关知识点(这个是一般Android开发用的少的,所以建议多去看看):

平时Android开发中对并发编程可以做得比较少,Thread这个类经常会用到,但是我们想提升自己的话,一定不能停留在表面,,我们也应该去了解一下java的关于线程相关的源码级别的东西。

附;java进阶与Android内核技术大纲

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,390评论 5 459
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,821评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,632评论 0 319
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,170评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,033评论 4 355
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,098评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,511评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,204评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,479评论 1 290
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,572评论 2 309
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,341评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,213评论 3 312
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,576评论 3 298
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,893评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,171评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,486评论 2 341
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,676评论 2 335

推荐阅读更多精彩内容

  • Java SE 基础: 封装、继承、多态 封装: 概念:就是把对象的属性和操作(或服务)结合为一个独立的整体,并尽...
    Jayden_Cao阅读 2,076评论 0 8
  • 相关概念 面向对象的三个特征 封装,继承,多态.这个应该是人人皆知.有时候也会加上抽象. 多态的好处 允许不同类对...
    东经315度阅读 1,901评论 0 8
  • 接口/抽象类意义规范、扩展、回调为其子类提供一个公共的类型 封装子类中得重复内容 定义抽象方法,子类虽然有不同的实...
    MigrationUK阅读 2,152评论 1 28
  • 由于时间仓促,有些地方未写完,后面会继续补充.如有不妥之处,欢迎及时与我沟通. 如果你也是在学习java,给你们推...
    分不清java阅读 2,827评论 0 15
  • 花木兰是我国古代的女英雄,在唐朝被封为“孝烈将军”代父从军击败北方入侵民族的敌人。他是个巾帼英雄,是在父从军,...
    徐梓洋洋阅读 360评论 0 1