java现有一个泛型类 提供数组排序功能_JavaSE面试详解35题

1.抽象类和接口的区别

1.解释抽象类的诞生过程:由于继承的子类中方法很相似,只是实现的内容不同,那么抽取共性放在父类中,共性的方法只能有方法声明,没有具体的方法体(以猫狗为例)

2.一句话解释:没有具体方法体的方法就是抽象方法,有抽象方法的类就是抽象类

3.介绍抽象类的特点:

3.1抽象类里面有抽象方法

3.2抽象类和抽象方法都需要使用abstract修饰

3.3抽象类里面可以有非抽象方法

3.4一个类继承抽象类的时候,要么重写抽象类中所有的抽象方法,要么把自己变成一个抽象类(直接把抽象方法继承过来)

4.介绍抽象类的成员特点:

4.1抽象类可以存在成员变量、常量、抽象方法和非抽象方法

4.2抽象类是不能创建对象的,也就是不能被实例化

5.对于抽象类的一些问题的解释

5.1为什么抽象类不能实例化??因为如果抽象类中可能有成员变量,而如果只有抽象方法时成员变量就失效了

5.2那既然抽象类都不能实例化了,构造方法还是存在的呢??抽象类中可能有成员变量,而构造方法时给成员变量初始化用的。而构造方法是通过子类使用super关键字去调用父类的构造方法给name赋值

1.为什么要使用接口:Java中继承具有单一的局限性,所以为了解决这个问题,Java给我们提供了一种机制叫做接口

2.一句话解释接口:比抽象类还要抽象的类(只能有抽象方法,不能有非抽象方法)

3.介绍接口的特点

3.1接口和抽象类一样,也是不能实例化的(不能创建对象)

3.2接口中只能有抽象方法,不能有非抽象方法 注意:接口中的抽象方法默认是使用public abstract修饰的

3.3接口中只能有常量,不能有成员变量 注意:接口中的常量默认是使用public final static修饰

原因:原因:因为接口中只有抽象方法,木有非抽象方法,不能对变量进行二次赋值,所以不能存在变量

3.4接口中不能有构造方法

原因:因为接口里面连成员变量都木有了,不需要构造方法对成员变量进行初始化了,所以没必要存在。

4.一句话总结接口的特点:接口只能存在抽象方法和常量

类与抽象类的关系:继承的关系(重写父类中所有的抽象方法)

接口与接口的关系:继承关系 (多继承)

类与接口的关系:实现关系 implements 多实现

二、重载和重写的区别

1.什么是重载:在同一个类中,方法名相同,参数列表不同(参数的个数,参数的类型,参数的顺序),与返回值无关

2.构造函数就是重载的一个体现方面

1.什么是重写:在子父类关系中,子父类的方法名相同,调用的是子类的方法,如果父类的方法不能满足子类的需求,所以子类重写了父类的方法满足自己的需求

2.注意:方法重写时需要加注解@override(方法的覆盖,能够标明方法是否重写了父类的方法,还是自己自定义的方法)

3.权限问题,子类的权限大于或等于父类的权限

4.像toString()、equals()、run()这些就是重写的一个体现方面

三、StringBuffer和StringBuilder的区别

StringBuffer: jdk1.0 同步的,线程安全,但是效率低

StringBuilder:jdk1.5 不同步的,安全性比StringBuffer低,效率高。

四、集合的体系架构(单列集合和双列集合)

1.首先为什么会出现集合??因为数组的长度是固定了的,无法更改。一旦存储的数据超过了数组能够容纳的最大限度,那么就会出现错误,为了满足实际开发的需要,就出现了集合类,而集合的长度是可以随意更改的

2.单列集合:也可以称之为Collection家族,Collection是单列集合的最顶层,同时它也是一个根接口,根据存取方式的不同分为List集合和Set集合,它俩同样是一个接口,List集合下又分为ArrayList和LinkedList,而Set集合下又分为HashSet和TreeSet

3.双列集合:可以称之为Map家族,Map是双列集合的最顶层,同时它也是一个根接口,它的子类我了解的有三种:HashMap、HashTable和properties

五、ArrayList和LinkedList的区别

1.ArrayList集合:1.存取数据都是有序的 2.是有索引标志的 底层是数组,存取数据的模式依照数组的方式存储数据

2.LinkedList集合:1.存取数据都是有序的 2.是有索引标志的 底层是链表,存取数据的模式依照链表的方式存储数据

3.对于查询速率要求高的,使用ArrayList

4.对于增删速率要求高的,使用LinkedList

六、HashMap和HashTable的区别

1.首先表明自己认真学习过HashMap,对于HashTable只了解过

2.首先解释一下什么是HashMap:存储数据是以键值对的方式进行存储(键是不允许重复的,而值是允许重复的)

3.介绍一下HashMap的两种遍历方式:keyset(),entryset()

4.然后介绍一下两者的区别

5.Hashtable:jak1.0 ,同步的,线程安全,效率低 它的key和value都不可以是null值

6.HashMap:jdk1.5 ,不同步的,线程不安全,效率高 它的key和value是都可以为null值的*/

7.两者都是属于Hash表数据结构的内容

8.开发中一般会使用HashMap,如果对安全性有要求,那么后期会使用一些特别的技术进行安全性解决

七、java的GC机制的理解

1.对于GC机制是从堆内存泄漏、垃圾回收机制、Java常用的API System类中的一个方法gc()中了解到的

2.什么是堆内存泄漏:

2.1由实例引入,当我们给对象名赋值为null时,栈中的对象就不能够通过地址值直接找到堆中new出来的对象了,直接输出对象名输出的是一串地址值

2.2对象的引用找不到具体的对象了

3.什么是Java的垃圾回收机制:

3.1由堆内存泄漏导致的对象会不会永远存在于堆中呢??肯定不会,没有对象引用的这些对象会被垃圾回收器自动去回收,可能是1s,2s,0.5h...

3.2JVM会自动通过Object类中的finalize()方法去进行垃圾的回收

4.什么是我了解System的gc方法:

4.1如果我们想自己手动去运行垃圾回收器进行垃圾回收,那么需要在代码中添加System.gc(); 去手动进行垃圾回收

八、final,finally,finalize有什么区别

联系:三者之间没有任何联系

final 用来去修饰类 、方法、变量的,被final修饰过的类不能继承,被final修饰的方法不能重写,被final修饰的变量变成常量

finally 在处理异常的时候一定会执行的代码块,往往可以用来释放资源

finalize 这个Object类中的一个方法名称,这个方法是用来进行垃圾回收的

九、 Error和Exception的区别

1.首先它们都是程序在编译期或运行期所出现的一些错误或者问题

2.其次它们都是Throwable类的两个子类

3.Error:无法解决的错误,在程序中如果想更改,只能改代码

4.Exception:是可以通过处理方式进行解决的问题

//try{}里有一个return语句,那么紧跟在这个try后的finally{}里的代码会不会被执行,什么时候被执行,在return前还是后?

回答:不管有没有异常或者异常有没有进行处理,finally快里面的内容都会执行(除非使用System.exit(1)干掉JVM虚拟机),而且finally{}的代码在return后执行,因为return返回的结果并不会马上执行,而是要等finally全部执行完之后才会返回。

十、说说同步和异步的区别,多线程的几种实现方式,和run方法可以启动线程吗

同步和异步的区别:请求发出后,是否需要等待结果,才能继续执行其他操作。

同步是发出请求后必须需要等待对方返回结果,才能继续执行其他的操作

以打电话为例,我们在建立通话的前提下,你一句,我一句,你说的话我立马能够听到,但是我除了和你说话不能做别的事情

异步是发出请求后不需要等待对方返回结果,可以去执行其它的操作,然后有时间就过来看对方是否返回结果,如果返回就处理一下

以发短信为例,假如对方发一条短信过来,我可能去干别的事情去了,等我有空拿起手机看到了对方发来的短信,然后回对方一条短信

多线程有两种实现方式

1.继承Thread类,重写run方法

2.实现Runnable接口,重写run方法

run方法不能启动线程,它只是一个方法而已,真正要启动线程,执行的是start方法

十一、说说你对线程的几种状态的了解

cd5a5aeb49d5ca180b5199490d04208f

1 初始化状态(创建线程对象)--t1.start()-->就绪状态 -线程是可以执行的,但是没有执行权,没有分配到CPU的时间片段*/

2 就绪状态--(运行过程)-->执行状态 -线程是可以执行的,已经有了CPU的执行权*/

3 执行状态--(线程执行完毕/JVM挂了)-->死亡状态*/

4 执行状态--(CPU可能遇到了一些突发情况/wait()-等待/sleep()-休眠)-->阻塞状态*/

5 阻塞状态--(如果想要继续运行/notify()-唤醒/sleep到不需要唤醒,休息时间一到会自动进入就绪状态)-->就绪状态*/

6 执行状态--yeil()/这时线程已经获得了时间片段,将这个时间片段让出来,重现回到就绪状态,等待下一个时间片段-->就绪状态*/

注意:进程里面创建了很多的线程,这些线程没有立马去执行,而是要获取CPU的执行权之后才能去执行

十二、说说你对多线程锁机制的理解

1.首先通过三个窗口同时卖100张票引入,出现了两个问题

2.卖了重复的票----窗口一正在卖第1张票 窗口四正在卖第1张票

原因解释:1.1当票数等于2的时候,首先窗口三获得了执行权,正想卖的时候,CPU马上切换到了窗口二

1.2窗口二就卖了一张,票还剩下1张,然后窗口三继续卖票,还是面对的是原先的二张,卖了一张,也还剩一张

3.出现了负数----窗口三正在卖第-1张票,窗口二正在卖第-2张票

原因解释: 2.1 当ticket大于0的时候,t1,t2,t3都可以进这个判断语句进行卖票

2.2 ticket=1的时候,当t1过来,一看有票,就进来了,肚子开始疼了,就先离开了

2.3 这时t2过来了,一看邮票,也进来了,肚子也疼了,也离开了 这时ticket还是等于1

2.4 这时t3过来了,一看有票,也进来了,它不肚子疼,开始卖最后一张票,ticket=0

2.5 这时t1回来了,因为之前看到有票,不知道t3已经卖票了,继续卖票,ticket=-1;

2.6 这时t2回来了,因为之前看到有票,不知道t1和t3已经卖票了,继续买票,ticket=-2;

2.7 因为t1和t2已经进入了判断语句,只是休眠了,当它回来的时候,面对的还是肚子疼之前的票数

4.为了解决这个问题,所以就引入了锁机制

当某个人在享用某个东西的时候,为了避免让别人使用,我们就把它锁住,锁住之后,只有自己使用完之后,别人才能接着使用

5.使用锁有两种方式:同步代码块和同步方法

1 加了synchronized锁之后,当某个线程执行到这里的时候,其它线程给我老老实实等着这个线程执行完

2 看下一个CPU执行权分配给谁,谁就执行,其它线程还是一样的在外面等着它执行完

6.但是使用锁有好处同样有坏处

好处:程序的执行效率会降低,因为存在关锁和开锁的过程---这些过程是需要时间的

坏处:提高了程序的安全性,可能会出现死锁

十三、反射获取class对象的方式有哪些,反射创建对象的方式有哪些。

1.反射获取class对象的方式有哪些

//第一种方式:直接通过类名.class获取字节码对象

Class class1 = Person.class;

//第二种方式:通过类的对象.getClass()获取字节码对象

Class extends Person> class2 = p.getClass();

//第三种方式:通过字节码类Class.forName("类的全类名")用的最多的

Class> class3 = Class.forName("com.bianyiit.cast.Person");

2.反射创建对象的方式有哪些

利用构造方法对象调用newInstance()去创建对象

利用成员属性对象调用set和get方法去给属性赋值和取值

利用成员属性对象调用invoke方法获取自定义方法中的内容

十四、Java常用的设计模式有哪些

工厂模式,单例模式,装饰者模式等

1.这里我重点了解过一个:单例模式

2.解释什么是单例模式:做一个项目的时候,使用到某一个类,但是在整个项目中,只允许这个类创建一个对象

3.它分为饿汉式设计模式和懒汉式设计模式

4.解释饿汉式设计模式的步骤

第一步:先把把构造方法私有化

第二步:然后创建一个static修饰的对象属性用来接收对象

第三步:最后创建一个静态方法用来获取对象

5.饿汉式设计模式的弊端: 有时不需要用到对象或者想延迟使用对象,但是饿汉式是只要类一加载就马上创建了对象,会浪费内存资源

6.接着介绍懒汉式的设计模式

和饿汉式设计模式的步骤很相似,但是在第二步的时候创建一个static修饰的对象属性,这里不接收对象,通过重新创建一个方法用来封装构造方法并获取对象

7.两种设计模式的比较

7.1 单例模式中懒汉式比饿汉式的代码量更多,逻辑性要更强,

7.2 但是它的执行效率比饿汉式的要高,且不会随着类的加载就创建对象,

7.3 当类只需要调用其他静态方法时,只需要类名.方法就行了,不会创建对象

7.4 当用户需要创建对象时调用创建对象的方法进行对象的创建,所以不会浪费内存资源

十五、UDP和TCP协议的区别

1.在连接方面

TCP是面向连接的(如打电话之前要先拨号)

UDP是无连接的,即发送数据之前不需要建立连接

2.在安全方面

TCP提供可靠的服务,通过TCP连接传输的数据,无差错,不丢失,不重复且按序到达

UDP尽最大努力交互,即不保证可靠交互

3.在传输效率方面

TCP传输效率相对较低

UDP传输效率高,适用于对传输速率和实时性有较高要求的通信

4.连接对象的区别

TCP连接是点对点的,一对一的

UDP连接是一对一、一对多、多对多的交互通信

十六、说说你对面向对象的理解

1.首先它和面向过程不同,面向过程是做某件事情的每一个步骤都需要自己亲力亲为,举一个例子就是买菜,洗菜和炒菜

2.面向对象是一种编程思想,符合人的生活习惯,自己不想做的或者不会做的事情让别人去做好,自己直接使用做好的结果(懒人思想)举一个例子:饿了想吃饭,直接让爸妈做好或者让厨师做好

3.而什么是对象呢??---一句话万物皆对象

4.如何区分这些对象---通过属性和方法

5.面向对象有三大特性---封装、继承和多态

6.说明什么是封装:

封装体现在很多方面,提高了代码的复用性和安全性

1.类封装了属性和方法

2.先属性私有化,然后通过setXxx()设置属性和getXxx()获取属性

3.方法同样封装了一些功能

7.说明什么是继承:多个类拥有相同的属性和方法,把共性抽取出来放到另一个类中,然后由这多个类去继承这个类

继承的特点:

1.Java只支持单一继承(一个儿子只能有一个亲爹,一个亲爹可以有一个亲爷爷)

2.Java不支持多继承,支持多层继承

8.说明什么是多态:

实现多态的三个前提:

1.要实现子父类继承关系(通过类实现一个接口)

2.要有方法的重写

3.父类引用指向子类对象

多态的优点:

1.继承的优点也就是多态的优点(提高了代码的复用性)

2.提高程序的扩展性

十七、如何避免死锁

1.死锁避免的基本思想:系统对进程发出的每一个系统能够满足的资源申请进行动态检查,并根据检查结果决定是否分配资源,如果分配后系统可能发生死锁,则不予分配,否则予以分配,这是一种保证系统不进入死锁状态的动态策略。

2.如果操作系统能保证所有进程在有限时间内得到需要的全部资源,则系统处于安全状态否则系统是不安全的。

3.避免多次锁定。尽量避免同一个线程对多个 Lock 进行锁定。例如上面的死锁程序,主线程要对 A、B 两个对象的 Lock 进行锁定,副线程也要对 A、B 两个对象的 Lock 进行锁定,这就埋下了导致死锁的隐患。

4.具有相同的加锁顺序。如果多个线程需要对多个 Lock 进行锁定,则应该保证它们以相同的顺序请求加锁。比如上面的死锁程序,主线程先对 A 对象的 Lock 加锁,再对 B 对象的 Lock 加锁;而副线程则先对 B 对象的 Lock 加锁,再对 A 对象的 Lock 加锁。这种加锁顺序很容易形成嵌套锁定,进而导致死锁。如果让主线程、副线程按照相同的顺序加锁,就可以避免这个问题。

5.使用定时锁。程序在调用 acquire() 方法加锁时可指定 timeout 参数,该参数指定超过 timeout 秒后会自动释放对 Lock 的锁定,这样就可以解开死锁了。

6.死锁检测。死锁检测是一种依靠算法机制来实现的死锁预防机制,它主要是针对那些不可能实现按序加锁,也不能使用定时锁的场景的。

十八、说说冒泡排序的原理,选择排序的原理,二分查找的原理

1.介绍冒泡排序的原理:每两个相邻元素都要进行比较,然后用到一个交换思想

2.代码解释,内层循环是为了让最大的那个数浮到表面,而外层循环是控制循环的次数,能够保证从大到小依次冒出来

for(int i=1;i

for(int j=0;j

int c=arr[j];

arr[j]=arr[j+1];

arr[j+1]=c;

}

}

2|6|3|8|1

第一次走完:2|3|6|1|8 使用内层的循环是为了一步一步让大的数从里面冒出水面

第二次走完:2|3|1|6|8

第三次走完:2|1|3|6|8

第四次走完:1|2|3|6|8

外层循环控制走的次数

3.选择排序的原理:让每一个元素与其它所有的元素进行比较,然后用到一个交换思想

4.二分查找:通过举例说明,猜数字

十九、wait()和sleep()的区别

Wait方法调用的时候必须是在同步代码块中

Wait方法执行后会释放当前线程所持有的锁对象

Sleep方法再任何位置都能运行,他是让当前线程处于休眠状态,休眠的时间一到,线程进入就绪状态准备运行

Sleep方法执行后不会会释放当前线程所持有的锁对象

二十、为什么要使用泛型以及好处

1.从集合开始解释为什么要使用泛型

集合本身是可以存储任意类型的数据(object)给定了泛型就是规定了这个集合只能存储规定数据类型的数据

简单来说:泛型就是用来规定集合存储的数据的类型的

2.使用泛型的好处

2.1 如果不添加泛型,得到的元素是object类型的,如果想得到对应的类型,还需要强转

2.2 如果添加了泛型,集合在添加元素的时候就只能添加规定的数据类型,得到的集合中的元素也不再需要进行强转

3.泛型举例:

Collection c=new ArrayList();

二十一、什么是java的序列化,如何实现java的序列化

1.首先解释什么是java的序列化

Java的序列化就是能够将一个对象的信息(注意:这里并不是指对象的各个属性的String类型的数据,而是真正的对象)写入流中,使其能够持久化的存储在数据库或文件系统中,或者在网络中传输,然后在需要的时候通过反序列化来重构一个相同的对象

2.解释如何实现序列化接口以及可能出现的问题以及解决方法

2.1引入对象输出流,当将一个具体的对象写入txt文本中会报错,报错原因分析:NotSerializableException(类没有实现序列化)

2.2 通过实现Serializable接口对实体类进行序列化,而且这个接口是空的,意味着没有方法给我们重写

2.3 同样使用对象输入流将对象读入内存,并在控制台打印---这里需要反序列化,通过对象输入流的一个readObject方法返回一个对象

2.4 当我们在Student学生类写入txt文本文件之前不添加private char sex,但是从txt文本文件中读数据的时候添加private char sex,也就是写的时候Student只有四个属性,但是读的时候Student有五个属性了,会出现什么问题呢??

2.5 序列化和反序列化的serialVersionUID不再匹配了,原先的类四个属性会在内存中自动生成序列化代码,更改之后五个属性的类在内存中又会自动生成新的序列化代码,两个序列化代码不匹配就会报错

2.6 由于UID前后不匹配,所以要自己定义序列化编码private static final long serialVersionUID =1L;

3.注意:序列化对象是将对象编码成可永久存储的二进制文件,而不是我们一开始使用的那种存储字符串的方式,它是没有对应的编码表的,所以无法用txt、word等软件打开的。而反序列化对象是将已经序列化的二进制文本文件重新解码成可读取的对象,能够在控制台打印输出的。所以千万别想着能够编写代码能够在txt中能够看到不是乱码的对象内容。

二十二、hashSet和TreeSet的区别

1.HashSet特点:

1.1无序、无索引、不允许重复 (需要重写equals()和hashcode())

1.2由于它是无序的,所以没有通过索引获取元素的get方法

2.TreeSet特点

2.1 可以自动的给添加进集合的数据进行排序(相当于调用的Collections的sort())

3.TreeSet对元素进行排序的方式

3.1TreeSet对元素进行排序的方式一:

让元素自身具备比较功能,元素就需要实现Comparable接口。覆盖compareTo方法。

如果不要按照对象中具备的自然顺序进行排序。如果对象中不具备自然顺序。怎么办?

3.2TreeSet集合第二种排序方式二:

让集合自身具备比较功能,定义一个类实现Comparator接口,覆盖compare方法。

将该类对象作为参数传递给TreeSet集合的构造函数

我认为的最主要的区别:

首先它们都是Set集合接口的子类,拥有Set的特点:无序、无索引、不允许重复

而HashSet对对象去重需要重写equals()和hashcode(),当它们Hash值相同的前提下,比较两者的值

而TreeSet对对象去重需要实现Comparable接口。覆盖compareTo方法。或者定义一个类实现Comparator接口,覆盖compare方法。

二十三、java中IO流有哪些,各有什么特点和功能

1.首先回答java中IO流有哪些

1.1 字节流

1.2 字符流

1.3 输入流

1.4 输出流

1.5 转换流

1.6 缓冲流

1.7 标准输入输出流

1.8 打印流

1.9 对象流

2.回答这些流各有什么特点和功能

2.1 字节流的特点和功能

字节流用来操作视频、图片、音频...

字符流能够做的事情字节流也是能做的

不需要刷新

2.2 字符流的特点和功能

字符流用来操作文本文件

字符流不要去操作图片、音频、视频等一些文件的读和写,因为没有对应的编码表可以将图片、音频、视频文件的字节转换成对应的字符

字符流=编码表+字节流

字符输出流会在默认路径自动创建文件对象

使用字符输出流往文件写数据的时候,还要有一个刷新的过程

关流的close方法自带刷新的效果,如果忘记写了flush(),而写了close(),也是能够将字符写进文本文件中

2.3 使用字符输出流写数据的时候为什么一定要刷新才能把内容写入到指定的文件中

本质:

内存中存在了一个缓冲区

执行原理:

从内存中写入硬盘中,先从内存中写入缓冲区中.然后通过刷新,将字符通过编码表转换成二进制,然后通过字节输出流写入硬盘

如果是从硬盘中写入内存中,通过先加载至缓冲区,而缓冲区本身就是内存的一部分,当写入缓冲区时本质上就是写入了内容,因为缓冲区同样是内存的一部分

注意:

缓冲区的大小只有8KB,如果需要写入的内容超过了8KB,超过的部分会自动写入硬盘文件中,不需要刷新的过程

2.4 转换流:假如只给了字节流对象,但是规定要使用字符流操作文本文件,这个时候就要用到转换流了

2.5 缓冲流:

原因:经过分析我们发现,无论是字节流还是字符流,在操作数据的时候,都是一个字节一个字节(一个字符一个字符的操作),每一次操作都需要和硬件资源打交道,不仅会对硬盘产生损耗,而且效率非常低

解决:于是这是就引入了缓冲流的概念,内存中有一块8KB大小的缓冲区,我们由原来一个字节一个字节的读和写变成一个写8KB的数据进入缓冲区,而取也是一次取8KB的数据,从而减少了对硬盘的访问次数,提高了读写的效率

2.6 标准输入输出流

System.out返回的是一个输出流的对象,也就是PrintStream

通过PrintStream对象调用println()直接往控制台写数据了,并且之前字节/字符流写入时的文本文件了,且这个方法可以不带参数

通过PrintStream对象调用print()不进行换行,且这个方法必须带参数

通过PrintStream对象调用write()往控制台写入数据,参数的是byte(字节),所以这个是字节输出流

标准的字节输出流调用println()和print()底层使用的是字符流,而write()底层使用的是字节流

2.7 打印流

PrintWriter的对象调用println()使用的是字符流,可以实现自动换行的效果

字符打印流同样也需要刷新的效果

PrintWriter的对象调用write()使用的也是字符流

标准的字符输出流调用println()、print()和write()底层使用的都是是字符流

PrintWriter的对象调用write("c")不能输出字节,但是可以输出其它任意类型

pw.write("c".getBytes()); //这里如果调用getBytes()输出字节会报错

标准的字符输出流的输出不能输入,只能写不能读

2.7 对象流

结合序列化对对象进行持久化保存,并能够用于在网络中传输,通过反序列化重构一个相同的对象

序列化解释以及如何实现序列化见题二十一

二十四、hashmap底层实现原理

1.首先Map集合--存储数据是以键值对的方式进行存储(键是不允许重复的,而值是允许重复的)

2.它的底层实现是通过hash表(数组链表)来实现的 ---底层还是数组但是这个数组每一项就是一个链表。

数组 链表

|1|---->|496|**|--->|896|**|--->结束

|2|---->结束

|3|---->|1|**|--->|337|**|--->|353|**|--->结束

|4|---->|387|**|--->结束

|5|---->结束

|6|---->结束

|7|---->|184|**|--->结束

|8|---->|26|**|--->|126|**|--->结束

...

|11|---->|91|**|--->|151|**|--->|171|**|--->结束

解释: 在这个数组中,每个元素存储的其实是一个链表的头,元素的存储位置一般情况是通过hash(key)%len获得,也就是元素的key的哈希值对数组长度取模得到。

比如上述哈希表中,91%16=11,151%16=11,171%16=11 所以91、151、171都存储在数组下标为12的位置。

3.数组的特点是:寻址容易,插入和删除困难;而链表的特点是:寻址困难,插入和删除容易。那么我们能不能综合两者的特性,做出一种寻址容易,插入删除也容易的数据结构?答案是肯定的,这就是哈希表

4.哈希表是根据键值对值(Key value)而直接进行访问的数据结构。也就是说,它通过把键值对映射到表中一个位置来访问记录,以加快查找的速度

5.使用哈希表进行存储的时候,就是把key通过哈希函数转换成int数值,然后将这个数值对数组长度进行取值,取余结果就当作数组的下标,将value存储在以该数字为下标的数组空间里

6.使用哈希表进行查询的时候,就是再次使用哈希函数将key转换为对应的数组下标,并定位到该空间获取value,如此一来,就可以充分利用到数组的定位性能进行数据定位。

7.HashMap的底层通过位桶实现,位桶里面存的是链表(1.7以前)或者红黑树(有序,1.8开始) ,其实就是数组加链表(或者红黑树)的格式,通过判断hashCode定位位桶中的下标,通过equals定位目标值在链表中的位置

二十五、ArrayList底层实现原理

1.ArrayList的底层数据结构就是一个数组,数组元素的类型为Object类型,对ArrayList的所有操作底层都是基于数组的。

2.ArrayList的特点:查询快,但是增删慢

3.ArrayList支持自动扩容,长度是可以随意更改的,

4.但是集合只能存储对象,而不能存储基本数据类型,且集合没有默认值

二十六、说说你对java虚拟机的了解

1.为什么Java语言能够跨平台(一次编译,到处运行)

Java语言编写的程序想在不同版本的操作系统上运行,必须在不同版本下的操作系统上安装对应版本的虚拟机

2.Java虚拟机JVM在JRE里面,而JRE在JDK里面,所以安装Java虚拟机的本质说白了就是安装JDK,而我们在选择JDK时就是选择在哪个操作系统下的JDK,如果你想你写的Java程序在Windows下运行就选择windows的JDK,如果你想你写的Java程序在linux下运行,就选择linux的JDK

3.那什么是Java虚拟机呢??说白了就是先使用javac工具将java文件编译成class文件,然后使用JVM将class字节码文件转换成对应操作平台下能够运行的目标机器代码

4.说到JVM虚拟机肯定要了解它内存空间是如何分布的

它分为五个内存空间,分别是寄存器、本地方法区、堆、栈、方法区

寄存器和本地方法区我们不用管,主要了解堆、栈和方法区

栈:用来加载方法,变量

堆:new出来的东西以及成员变量

方法区:存储字节码文件、static修饰的变量、常量(static修饰的变量优先于对象的创建)

5.说到JVM肯定要了解堆内存泄漏以及垃圾回收机制

什么是堆内存泄漏:

由实例引入,当我们给对象名赋值为null时,栈中的对象就不能够通过地址值直接找到堆中new出来的对象了,直接输出对象名输出的是一串地址值

对象的引用找不到具体的对象了

什么是Java的垃圾回收机制:

由堆内存泄漏导致的对象会不会永远存在于堆中呢??肯定不会,没有对象引用的这些对象会被垃圾回收器自动去回收,可能是1s,2s,0.5h...

JVM会自动通过Object类中的finalize()方法去进行垃圾的回收

什么是我了解System的gc方法:

如果我们想自己手动去运行垃圾回收器进行垃圾回收,那么需要在代码中添加System.gc(); 去手动进行垃圾回收

二十七、&和&&的区别

双与和单与都是并且的含义,双或和单或都是或者的含义,但是双与和双或有短路效果

解释:什么是短路效果:假如有成百上千个条件给我们去判断,

当我们使用&&的时候,判断到一个条件是false,那么就不需要要继续判断下面的条件是否是true/false,最终的结果一定是false

而当我们使用&的时候,如果判断一个条件为false,我们还要接着判断下面所有的条件,当所有的条件判断完之后才出结果

所以:双与,双或相对于单与,单或而言,缩短了运行时间,提高了程序的运行效率,这在实际编程开发过程中是很有用的

拓展:那么&和|是不是就没用了呢? 从本质上而言是的,&&是&的一个升级版本,但是还有一个区别就是&和|可以用来作位运算,而&&和||就不行*/

二十八、==和equals的区别

1. ==是比较运算符,用于判断基本数据类型的值是否相等,用于判断引用数据类型的时候比较的是地址值,而String类型是引用数据类型。所以==在比较两个String类型的数据时比较的是两者的地址值

2. 特殊的String类的实例分析

String a="hello";

String b="hellow";

String c="hello";

System.out.println(a==b); //false

//为什么a==b时为false 因为String归根结底还是一个类(引用数据类型),所以==号比较的是a和b的地址值 常量池一个是hello,另一个是hellow

System.out.println(a==c); //true

//为什么a==b时为true 因为String归根结底还是一个类(引用数据类型),所以==号比较的是a和b的地址值 而a和b的值存在方法区里面都是hello

System.out.println(a.equals(c)); //true

//为什么a==b时为true 因为String归根结底还是一个类(引用数据类型),但是.equals()重写了object里面的.equals(),所以比较的是具体的值

}

3. Object中的equals方法和String中的equals方法有什么区别

Object的equals方法比较的是两个对象的地址值

String的equals方法已经重写了 Object的equals方法,比较的是两个对象的内容。

只有一个对象引用一个常量的时候String s1="张三"

0c9f8fa4b9daac4031e7cd20f05fd407

两个对象同时引用同一个常量的时候String s1="张三" String s2="张三" String s3="李四";

4b2488828670fa9234eb4f007611b93d

a3d0269873abd1f5a9609a4d97eb6ee7

9dc04e0f9b7636bcfd3149e8c0487bd8

二十九、String s = new String(“xyz”);创建了几个String Object? 二者之间有什么区别?

两个对象。 1个在堆里面(是new出来的对象)

1个在常量池里面(常量对象)

605c0367c3b5a4f723a961c7fcacbac1

三十、线程池的核心类是哪一个?有几种线程池?

1.实现线程池的核心类是ThreadPoolExecutor

2.Java通过Executors提供四种线程池,分别为:

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

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

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

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

三十一、说一说IO和NIO的区别以及应用场景?

1.两者的区别

IO: 面向流 阻塞IO 无选择器

NIO:面向缓冲 非阻塞IO 有选择器

2.IO的面向流和NIO的面向缓冲

JavaIO面向流意味着它每次从流中读一个字节或者多个字节,直至全部读完,它是没有被缓存到任何的地方的,此外,它不能前后移动流中的数据

JavaNIO面向缓冲意味着将先将数据读到到一个缓冲区,需要用到这些数据时可以在缓冲区前后移动,增加了读取的灵活性

3.阻塞与非阻塞IO

JavaIO的各种流是阻塞的。这意味着,当一个线程调用read() 或 write()时,该线程被阻塞,直到有一些数据被读取,或数据完全写入。该线程在此期间不能再干任何事情了

JavaNIO的非阻塞模式,使一个线程从某通道发送请求读取数据,但是它仅能得到目前可用的数据,如果目前没有数据可用时,就什么都不会获取。而不是保持线程阻塞,所以直至数据变的可以读取之前,该线程可以继续做其他的事情。

4.选择器

Java NIO的选择器允许一个单独的线程来监视多个输入通道,你可以注册多个通道使用一个选择器,然后使用一个单独的线程来“选择”通道。这种选择机制,使得一个单独的线程很容易来管理多个通道。

5.NIO诞生的原因

NIO是为弥补传统IO的不足而诞生的,但是尺有所短寸有所长,NIO也有缺点,因为NIO是面向缓冲区的操作,每一次的数据处理都是对缓冲区进行的,那么就会有一个问题,在数据处理之前必须要判断缓冲区的数据是否完整或者已经读取完毕,如果没有,假设数据只读取了一部分,那么对不完整的数据处理没有任何意义。所以每次数据处理之前都要检测缓冲区数据。

6.那么NIO和IO各适用的场景是什么呢?

如果需要管理同时打开的成千上万个连接,这些连接每次只是发送少量的数据,例如聊天服务器,这时候用NIO处理数据可能是个很好的选择。

而如果只有少量的连接,而这些连接每次要发送大量的数据,这时候传统的IO更合适。使用哪种处理数据,需要在数据的响应等待时间和检查缓冲区数据的时间上作比较来权衡选择。

三十二、1.8的新特性你知道哪些?

1. default关键字

在java里面,我们通常都是认为接口里面是只能有抽象方法,不能有任何方法的实现的,那么在jdk1.8里面打破了这个规定,引入了新的关键字default,通过使用default修饰方法,可以让我们在接口里面定义具体的方法实现

所以说这个default方法是所有的实现类都不需要去实现的就可以直接调用,那么比如说jdk的集合List里面增加了一个sort方法,那么如果定义为一个抽象方法,其所有的实现类如arrayList,LinkedList等都需要对其添加实现,那么现在用default定义一个默认的方法之后,其实现类可以直接使用这个方法了,这样不管是开发还是维护项目,都会大大简化代码量。

2. Lambda表达式:把一个函数(方法)当做一个参数传递

//不使用Lambda表达式之前

new Thread(new Runnable() {

@Override

public void run() {

System.out.println("线程1执行了");

}

});

//使用Lambda表达式之后

//将匿名内部类简化成 new Thread(()->{ System.out.println("线程2执行了");System.out.println("线程2又执行了一次");});

3. jdk1.8提供了一个@FunctionalInterface注解来定义函数式接口,如果我们定义的接口不符合函数式的规范便会报错。

//定义一个函数接口的时候必须要加上注解(里面只能放一个方法)

@FunctionalInterface

public interface Lam {

void method1();

}

work(() -> {

System.out.println("这是Lambda的演示过程3");

});

public static void work(Lam l){

l.method1();

}

4. Date Api更新

1.8之前JDK自带的日期处理类非常不方便,我们处理的时候经常是使用的第三方工具包,比如commons-lang包等。不过1.8出现之后这个改观了很多,比如日期时间的创建、比较、调整、格式化、时间间隔等。这些类都在java.time包下。比原来实用了很多。

三十三、请介绍集合的框架结构

1.集合分为两种:单列集合和双列集合

2.单列集合最顶层是Collection(它是一个接口)

3.Collection根据存储方式的不同分为List集合和Set集合,两者都是接口

List集合:1.存取数据都是有序的 2.是有索引标志的 3.允许重复

Set集合 :1.存取数据是无序的 2.没有索引标志 3.不允许重复

4.List集合最常用的有两种,ArrayList集合和LinkedList

ArrayList的底层是数组:查询快,但是增删慢

LinkedList的底层是链表:查询慢,但是增删慢

5.Set集合最常用的两种,HashSet和TreeSet

HashSet的底层是Hash表,它的元素是无序的,HashSet要求放入的对象必须要重写Object的Hashcode()和equals()方法,放入的对象,是以hashcode码作为标识的,而具有相同内容的 String对象,hashcode是一样,所以放入的内容不能重复。

TreeSet的底层是二叉树,它的Treeset中的数据是自动排好序的,如果需要存储对象,那么必须实现Comparater接口,并重写里面的比较方法

6.双列集合的最顶层是Map(它也是一种接口)

7.Map最常用的有两种,HashMap和Hashtable

HashMap的底层通过位桶实现,位桶里面存的是链表(1.7以前)或者红黑树(有序,1.8开始) ,其实就是数组加链表(或者红黑树)的格式,通过判断hashCode定位位桶中的下标,通过equals定位目标值在链表中的位置

Hashtable的底层数据结构是和Hashmap一致的

8.Hashtable和HashMap的区别

Hashtable:jak1.0 ,同步的,线程安全,效率低 它的key和value都不可以是null值

HashMap:jdk1.5 ,不同步的,线程不安全,效率高 它的key和value是都可以为null值的*/

两者都是属于Hash表数据结构的内容

开发中一般会使用HashMap,如果对安全性有要求,那么后期会使用一些特别的技术进行安全性解决

三十四、java中会存在内存泄漏吗,请简单描述

肯定会存在,JVM会通过finalize方法自动调用垃圾回收机制进行垃圾回收

什么是堆内存泄漏:

由实例引入,当我们给对象名赋值为null时,栈中的对象就不能够通过地址值直接找到堆中new出来的对象了,直接输出对象名输出的是一串地址值

对象的引用找不到具体的对象了

三十五、说一说常用的协议有哪些?

UDP:用户数据报协议

TCP/IP协议:传输控制协议

http协议:超文本传输协议

https协议:安全超文本传输协议

SMTP:简单邮件传送协议

ARP协议:地址解析协议,将ip地址转换成物理地址

FTP:文件传输协议

点赞

收藏

分享

文章举报

b4f0f25195e24879e7f354625d35bc7c

de638df49f1d5a3342eecff86bcb011e.png

飞奔的嗨少

发布了74 篇原创文章 · 获赞 11 · 访问量 3041

私信

关注

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值