前言
网上的Java面试题文章有非常多,但是我觉得有的比较繁琐,有的比较浅,不太好理解甚至随着java版本更新,很多以前的答案也不太准确了,正巧准备要跳槽了,所以我就整理了一篇java基础的面试题!
注:Java面试题系列包括:基础篇,数据库篇,框架篇,微服务篇,设计模式篇,详情请查看我的个人文章,谢谢!
目录
目录
目录
八,String str="i"与 String str=new String(“i”)一样吗?
十七,final、finally、finalize 有什么区别?
十八,try-catch-finally 中,如果 try中 return 了,finally 还会执行吗?
二十一,访问修饰符public,private,protected,以及不写时的区别?
二十四,String和StringBuilder、StringBuffer的区别?
二十五,两个对象的 hashCode() 相同,则 equals() 也一定为 true,对吗?
二十六,两个对象的 equals() 相同,则 hashCode() 也一定为 true,对吗?
二十九,当一个对象被当作参数传递到一个方法后,此方法可改变这个对象的属性,并可返回变化后的结果,那么这里到底是值传递还是引用传递?
三十三,在 Java 中,允许从静态方法中访问非静态变量吗?为什么?
三十七,Collection 和 Collections 有什么区别?
四十一,HashSet,TreeSet和LinkedHashSet的区别?
四十二,HashMap 和 Hashtable 有什么区别?
四十七,concurrentHashMap和HashTable有什么区别?
五十三, Synchronized和ReentrantLock的区别?
正文
一,面向过程与面向对象的区别?
1,区别?
面向过程(Procedure Oriented):以过程为核心,强调事件的流程,顺序;
面向对象(Object Oriented):以对象为核心,强调事件的角色,主体;
2,示例(小明上班)
从面向过程的角度看:
- 起床;
- 刷牙洗脸;
- 开车上班;
从面向对象的角度看:
- 主人公(对象):小明;
- 拥有的财产(属性):床、牙刷、车;
- 小明可以做什么(方法):起床、刷牙洗脸、开车上班;
3,总结
面向过程:每一步都是一个过程,整个过程循环渐进。
优点:根据事情的目的分解出过程,然后一步步实施;对于不复杂的事件执行效率快。
缺点:只关注眼前事件的实现。
面向对象:先定义一个类,列出属于该类的属性及方法,new出一个实例对象,调用实例对象的方法work,在调用实例对象的方法中大家可以看到其内部也是一种面向过程的实现,所以面向对象与面向过程并不是互斥的,面向对象内可以有面向过程的设计。
优点:不仅关注眼前的事件实现,也关注未来可能发生的事件,比如要实现‘小明开车送朋友’。只需要在Person类里面加入‘小明开车送朋友’方法就可以。具有高度的拓展性(体现出对象的多态性)和复用性(对于类似的人,动作,直接new一个对象传入不同的参数即可),特点是继承、封装、多态。
缺点:跟面向过程正好相反,如果只是单一的功能实现,面向对象的设计思路就显得过于繁琐。
二,面向对象的三大特征?
1,三大特征:
- 封装:把现实中的一类实例封装成一个类,包括它的属性和行为,并且类可以把自己的属性和行为只让可信的类或者对象操作,对不可信的类或者对象进行信息隐藏。(例:风扇,它也是一个对象,扇叶,扇体,线,长,宽,高等都是它的属性,吹风是它的方法)
- 继承:一个类继承另外一个类,子类继承父类,子类继承父类的一些特性,子类可以从父类那里继承到方法和实例变量,并且可以修改和增加方法来满足当前的需要,通过继承可以满足代码的重用性 。
- 多态:多态存在的3个条件:1,继承;2,重写;3,父类引用指向子类对象。 对于同一个行为,不同的子类对象有不同的表现形式。
三,JDK 和 JRE 有什么区别?
- JDK(Java Development Kit),Java开发工具包
- JRE(Java Runtime Environment),Java运行环境
JDK中包含JRE,JDK中有一个名为jre的目录,里面包含两个文件夹bin和lib,bin就是JVM,lib就是JVM工作所需要的类库。
四,== 和 equals的区别是什么?
- 对于基本类型,==比较的是值;
- 对于引用类型,==比较的是地址;
- object 类中定义的方法,equals不能用于基本类型的比较;
- 如果没有重写equals,equals比较的是值;
- 如果重写了equals方法,equals比较的是对象的内容;
五,final 在 java 中有什么作用?
- final修饰过的基本数据类型变量,则该变量为常量,该值无法修改创建时必须要初始化,不能被二次赋值;
- final修饰过的引用数据类型变量,比如对象,数组,则该对象,数组本身可以修改,指向该对象或数组的地址的引用不能修改;
- final修饰过的方法,该方法将成为最终方法,无法被子类重写。但是,该方法仍然可以被继承;
- final修饰过的类,该类成为最终类,无法被继承, 比如常用的String类就是最终类
六,String 属于基础的数据类型吗?
答:不属于,Sring属于引用数据类型。
基本数据类型:byte,short,int,long,char,float,double,boolean。
七,float和double有什么区别?
1,定义
- float:单精度浮点数
- double:双精度浮点数
2,区别
- 在内存中占有的字节数不同
单精度浮点数在机内存占4个字节
双精度浮点数在机内存占8个字节 - 有效数字位数不同
单精度浮点数有效数字8位
双精度浮点数有效数字16位 - 数值取值范围
单精度浮点数的表示范围:-3.40E+38~3.40E+38
双精度浮点数的表示范围:-1.79E+308~-1.79E+308 - 在程序中处理速度不同
一般来说,CPU处理单精度浮点数的速度比处理双精度浮点数快。
八,String str="i"与 String str=new String(“i”)一样吗?
String str="i"会将其分配到常量池中,常量池中没有重复的元素,如果常量池中存中i,就将i的地址赋给变量,如果没有就创建一个再赋给变量。
String str=new String(“i”)会将对象分配到堆中,即使内存一样,还是会重新创建一个新的对象。
九,如何实现将字符串反转?
将对象封装到StringBuilder中,调用reverse方法实现反转。
十,String 类的常用方法都有那些?
- 常见String类的获取功能
length:获取字符串长度;
charAt(int index):获取指定索引位置的字符;
indexOf(int ch):返回指定字符在此字符串中第一次出现处的索引;
substring(int start):从指定位置开始截取字符串,默认到末尾;
substring(int start,int end):从指定位置开始到指定位置结束截取字符串; - 常见String类的判断功能
equals(Object obj): 比较字符串的内容是否相同,区分大小写;
contains(String str): 判断字符串中是否包含传递进来的字符串;
startsWith(String str): 判断字符串是否以传递进来的字符串开头;
endsWith(String str): 判断字符串是否以传递进来的字符串结尾;
isEmpty(): 判断字符串的内容是否为空串""; - 常见String类的转换功能
byte[] getBytes(): 把字符串转换为字节数组;
char[] toCharArray(): 把字符串转换为字符数组;
String valueOf(char[] chs): 把字符数组转成字符串。valueOf可以将任意类型转为字符串;
toLowerCase(): 把字符串转成小写;
toUpperCase(): 把字符串转成大写;
concat(String str): 把字符串拼接; - 常见String类的其他常用功能
replace(char old,char new) 将指定字符进行互换
replace(String old,String new) 将指定字符串进行互换
trim() 去除两端空格
int compareTo(String str) 会对照ASCII 码表 从第一个字母进行减法运算 返回的就是这个减法的结果,如果前面几个字母一样会根据两个字符串的长度进行减法运算返回的就是这个减法的结果,如果连个字符串一摸一样 返回的就是0。
十一,普通类和抽象类有哪些异同?
- 抽象类的存在就是为了被继承,抽象类不能实例化,而普通类存在是为了实例化一个对象
- 抽象类的子类必须重写抽象类中的所有抽象方法,而普通类可以选择是否重写父类的方法,也可以直接调用父类的方法
- 抽象类必须用abstract来修饰,普通类则不用
- 普通类和抽象类都可以含有普通成员属性和普通方法
- 普通类和抽象类都可以继承别的类或者被别的类继承
- 普通类和抽象类的属性和方法都可以通过子类对象来调用
十二,接口和抽象类有什么区别?
- 抽象类只能单继承,但是可以实现多个接口。
- 抽象类可以有构造方法,接口没有。
- 抽象类可以有普通方法,接口只能有抽象方法。
- 抽象类可以有成员变量,接口只能有常量。
我在网上看到有个说法,挺形象的:普通类像亲爹 ,他有啥都是你的;抽象类像叔伯,有一部分会给你,还能指导你做事的方法;接口像干爹,可以给你指引方法,但是做成啥样得你自己努力实现。
十三,java中流分为几种?
1,按照流的方向:
- 输入流(inputStream)
- 输出流(outputStream)
2,按照实现功能分:
- 节点流(可以从或向一个特定的地方(节点)读写数据 如 FileReader。
- 处理流(是对一个已存在的流的连接和封装,通过所封装的流的功能调用实现数据读写。如 BufferedReader。
3,按照处理数据的单位:
- 字节流:字节流继承于 InputStream 和 OutputStream
- 字符流:字符流继承于InputStreamReader 和 OutputStreamWrite
十四、Files(文件)的常用方法都有哪些?
- Files.exists():检测文件路径是否存在。
- Files.createFile():创建文件。
- Files.createDirectory():创建文件夹。
- Files.delete():删除一个文件或目录。
- Files.copy():复制文件。
- Files.move():移动文件。
- Files.size():查看文件长度。
- Files.read():读取文件。
- Files.write():写入文件。
十五,什么是反射?
所谓反射,是java在运行时进行自我观察的能力,通过class、constructor、field、method四个方法获取一个类的各个组成部分。
在Java运行时环境中,对任意一个类,可以知道类有哪些属性和方法。这种动态获取类的信息以及动态调用对象的方法的功能来自于反射机制。
十六,throw 和 throws 的区别?
- throw:作用在方法内,表示抛出具体异常,由方法体内的语句处理,一定抛出了异常;
- throws:作用在方法的声明上,表示抛出异常,由调用者来进行异常处理,可能出现异常,不一定会发生异常;
十七,final、finally、finalize 有什么区别?
- final:final可以修饰类,变量,方法,修饰的类不能被继承,修饰的变量不能重新赋值,修饰的方法不能被重写
- finally:finally用于抛异常,finally代码块内语句无论是否发生异常,都会在执行finally,常用于一些流的关闭。
- finalize:方法用于垃圾回收。
一般情况下不需要我们实现finalize,当对象被回收的时候需要释放一些资源,比如socket链接,在对象初始化时创建,整个生命周期内有效,那么需要实现finalize方法,关闭这个链接。但是当调用finalize方法后,并不意味着gc会立即回收该对象,所以有可能真正调用的时候,对象又不需要回收了,然后到了真正要回收的时候,因为之前调用过一次,这次又不会调用了,产生问题。所以,不推荐使用finalize方法。
十八,try-catch-finally 中,如果 try中 return 了,finally 还会执行吗?
答:只要写了try-catch-finally并且进入到try,那么不管什么情况都会执行finally
十九,常见的异常类有哪些?
- NullPointerException:空指针异常;
- SQLException:数据库相关的异常;
- IndexOutOfBoundsException:数组下角标越界异常;
- FileNotFoundException:打开文件失败时抛出;
- IOException:当发生某种IO异常时抛出;
- ClassCastException:当试图将对象强制转换为不是实例的子类时,抛出此异常;
- NoSuchMethodException:无法找到某一方法时,抛出;
- ArrayStoreException:试图将错误类型的对象存储到一个对象数组时抛出的异常;
- NumberFormatException:当试图将字符串转换成数字时,失败了,抛出;
- IllegalArgumentException 抛出的异常表明向方法传递了一个不合法或不正确的参数。
- ArithmeticException当出现异常的运算条件时,抛出此异常。例如,一个整数“除以零”时,抛出此类的一个实例。
二十,用最有效率的方法计算2乘以8?
可以使用位运算符:2 << 3 (2*2*2*2) ,通常情况下,可以认为位运算是性能最高的。
二十一,访问修饰符public,private,protected,以及不写时的区别?
- public :对所有类可见。使用对象:类、接口、变量、方法。
- private :在同一类内可见。使用对象:变量、方法。 注意:不能修饰类(外部类)。
- protected : 对同一包内的类和所有子类可见。使用对象:变量、方法。 注意:不能修饰类(外部类)。
- default (默认): 在同一包内可见,不使用任何修饰符。使用对象:类、接口、变量、方法。
二十二,&和&&的区别?
都可以用作逻辑与的运算符,表示逻辑与(and),当运算符两边的表达式的结果都为true时,整个运算结果才为true,否则,只要有一方为false,则结果为false。
&(逻辑与):可以用作位运算符,当 & 操作符两边的表达式不是boolean类型时,& 表示按位与操作。
&&(短路与):具有短路的功能,即如果第一个表达式为false,则不再计算第二个表达式。
二十三,String 类可以继承吗?
答:不行。String 类使用 final 修饰,无法被继承。
二十四,String和StringBuilder、StringBuffer的区别?
- String : 声明不可变的类,每一次操作都会创建新的对象,并且把指针指向新的对象。
- StringBuffer : 声明可变的对象,线程安全,效率低。
- StringBuilder : 声明可变的对象,线程不安全,效率高。
因此我们在不考虑线程安全的情况下推荐使用StringBuilder,考虑线程安全的情况下推荐使用StringBuffer 。
二十五,两个对象的 hashCode() 相同,则 equals() 也一定为 true,对吗?
答:不对。当 a.hashCode() == b.hashCode() 时,a.equals(b) 不一定为 true。
二十六,两个对象的 equals() 相同,则 hashCode() 也一定为 true,对吗?
答:对。当有 a.equals(b) == true 时,则 a.hashCode() == b.hashCode() 必然成立。
二十七,重载与重写的区别?
- 重载(overload):在同一个类中有几个方法,它们的方法名一致,但参数列表不同,与返回值无关。
- 重写(override):子类重写父类的方法,他们方法名,参数列表一致,返回值不能大于父类类型。
例子:
重载:你想吃一碗面,我给你提供了拉面,炒面,刀削面,担担面供你选择,这是重载; 重写:你想吃一碗面,我不但给你端来了面,还给你加了青菜,加了鸡蛋,这个是重写;
二十八,构造器是否可被重写?
构造器(Constructor) 不能被重写,但是可以被重载,这就是⼀个类中可以有多个构造函数的原因。
二十九,当一个对象被当作参数传递到一个方法后,此方法可改变这个对象的属性,并可返回变化后的结果,那么这里到底是值传递还是引用传递?
答:值传递。Java 中只有值传递,对于对象参数,值的内容是对象的引用。
三十,静态变量与成员变量的区别?
- 静态变量存储在方法区中,成员变量存储在堆内存中。
- 静态变量从属于类,随着类的存在而存在,消失而消失,成员变量与对象共存亡。
- 静态方法通过类名的方式调用,成员变量通过对象调用。
三十一,Exception 和 Error 有什么区别?
Error 和 Exception 都是 Throwable 的子类,用于表示程序出现了异常的情况。区别在于:
- Exception:表示需要捕捉或者需要程序进行处理的异常,是一种设计或实现问题。
- Error:表示系统级的错误和程序不必处理的异常,是一种严重问题,比如内存溢出,不可能指望程序能处理这样的情况。
三十二,equals和hashcode的关系
- 如果equals为true,hashcode一定相等;
- 如果equals为false,hashcode不一定不相等;
- 如果hashcode值相等,equals不一定相等;
- 如果hashcode值不等,equals一定不等;
三十三,在 Java 中,允许从静态方法中访问非静态变量吗?为什么?
- 静态变量从属于类,在类加载的时候就会分配内存,可以通过类名加点直接访问;
- 非静态变量属于类的对象,只有在类的对象产生时,才会分配内存,通过类的实例去访问;
- 静态方法也属于类本身,但是此时没有类的实例,内存中没有非静态变量,所以无法调用。
三十四,构造器是否可被重写?
构造器(Constructor) 不能被重写,但是可以被重载,这就是⼀个类中可以有多个构造函数的原因。
三十五,哪些集合类是线程安全的
- Vector:就比Arraylist多了个同步化机制(线程安全)。
- Stack:栈,也是线程安全的,继承于Vector。
- Hashtable:就比Hashmap多了个线程安全。
- ConcurrentHashMap:是一种高效但是线程安全的集合。
三十六,迭代器 Iterator 是什么?
为了方便的处理集合中的元素,Java中出现了一个对象,该对象提供了一些方法专门处理集合中的元素.例如删除和获取集合中的元素.该对象就叫做迭代器(Iterator)。
三十七,Collection 和 Collections 有什么区别?
- Collection是最基本的集合接口,Collection派生了两个子接口list和set,分别定义了两种不同的存储方式。
- Collections是一个包装类,它包含各种有关集合操作的静态方法此类不能实例化,就像一个工具类,服务于Collection框架。
三十八,常用的集合有哪些?
两个顶级集合父类:Collection和Map,Collection下面有两大子类List和Set,另外还有这三大集合的子类等
三十九,list与Set区别
List,Set都是继承自Collection接口
- List特点:元素存取有顺序,元素可重复 ;
- Set特点:元素存取无顺序,元素不可重复,加入Set的元素必须定义equals()方法以确保对象的唯一性,重复元素会覆盖掉,元素在set中的位置是由该元素的HashCode决定的;
- list支持for循环,也就是通过下标来遍历,也可以用迭代器;
- set只能用迭代器,因为它存取无顺序,无法用下标来取得想要的值;
- Set:检索元素效率低下,删除和插入效率高,插入和删除不会引起元素位置改变;
- List:和数组类似,List可以动态增长,查找元素效率高,插入删除元素效率低,因为会引起其他元素位置改变。
四十,ArrayList和LinkedList区别?
1,数据结构
- ArrayList 内部使用的动态数组来存储元素
- LinkedList 内部使用的双向链表来存储元素。
2,空间灵活性
- ArrayList自由性较低,因为它需要手动的设置固定大小的容量(默认10),如果超过设置的容量那么下次查询就会自动扩容至1.5倍,但是它的使用比较方便,只需要创建,然后添加数据,通过调用下标进行使用。
- 而LinkedList自由性较高,能够动态的随数据量的变化而变化,但是它不便于使用
3,效率不同
-
对于随机访问get和set,ArrayList觉得优于LinkedList,因为LinkedList要移动指针。
-
对于新增和删除操作add和remove,LinedList比较占优势,因为ArrayList要移动数据。
四十一,HashSet,TreeSet和LinkedHashSet的区别?
1,HashSet
- 不能保证元素的排列顺序,顺序有可能发生变化
- 当向HashSet结合中存入一个元素时,HashSet会调用该对象的hashCode()方法来得到该对象的hashCode值,然后根据 hashCode值来决定该对象在HashSet中存储位置。
- HashSet底层数据结构是哈希表,哈希表就是存储唯一系列的表,而哈希值是由对象的hashCode()方法生成。
- HashSet集合判断两个元素相等的标准是两个对象通过equals方法比较相等,并且两个对象的hashCode()方法返回值相等
2,LinkedHashSet
- LinkedHashSet集合同样是根据元素的hashCode值来决定元素的存储位置,但是它同时使用链表维护元素的次序。这样使得元素看起 来像是以插入顺 序保存的,也就是说,当遍历该集合时候,LinkedHashSet将会以元素的添加顺序访问集合的元素。
- LinkedHashSet在迭代访问Set中的全部元素时,性能比HashSet好,但是插入时性能稍微逊色于HashSet。
3,TreeSet
- TreeSet是SortedSet接口的唯一实现类,TreeSet可以确保集合元素处于排序状态。TreeSet支持两种排序方式,自然排序 和定制排序,其中自然排序为默认的排序方式
- TreeSet判断两个对象不相等的方式是两个对象通过equals方法返回false,或者通过CompareTo方法比较没有返回0
- 自然排序: 自然排序使用要排序元素的CompareTo(Object obj)方法来比较元素之间大小关系,然后将元素按照升序排列。
- 定制排序:自然排序是根据集合元素的大小,以升序排列,如果要定制排序,应该使用Comparator接口,实现 int compare(To1,To2)方法
总结:三者都保证了元素的唯一性,如果无排序要求可以选用HashSet;如果想取出元素的顺序和放入元素的顺序相同,那么可以选用LinkedHashSet。如果想插入、删除立即排序或者按照一定规则排序可以选用TreeSet。
四十二,HashMap 和 Hashtable 有什么区别?
- HashMap是线程不安全的,HashTable是线程安全的;
- HashMap中允许键和值为null,HashTable不允许;
- HashMap的默认容器是16,为2倍扩容,HashTable默认是11,为2倍+1扩容;
四十三,说一下 HashMap 的实现原理?
1,概述
我们常见的有数据结构有三种结构:1、数组结构 2、链表结构 3、哈希表结构 下面我们来看看各自的数据结构的特点:
- 数组结构: 存储区间连续、内存占用严重、空间复杂度大
优点:随机读取和修改效率高,原因是数组是连续的(随机访问性强,查找速度快)
缺点:插入和删除数据效率低,因插入数据,这个位置后面的数据在内存中都要往后移动,且大小固定不易动态扩展。 - 链表结构:存储区间离散、占用内存宽松、空间复杂度小
优点:插入删除速度快,内存利用率高,没有固定大小,扩展灵活
缺点:不能随机查找,每次都是从第一个开始遍历(查询效率低) - 哈希表结构:结合数组结构和链表结构的优点,从而实现了查询和修改效率高,插入和删除效率也高的一种数据结构,常见的HashMap就是哈希表结构
2,HashMap底层原理分析
相比 jdk1.7 的 HashMap 而言,jdk1.8最重要的就是引入了红黑树的设计,当hash表的单一链表长度超过 8 个的时候先判断数组是否大于64,如果大于链表结构就会转为红黑树结构如果小于则数组会进行成倍扩容但是链表并不会转换成红黑树。
为什么jdk1.8要这样设计呢?好处就是避免在最极端的情况下链表变得很长很长,在查询的时候,效率会非常慢。
四十四,HashMap和Hashtable的区别?
- 对Null key 和Null value的支持:HashMap可以接受为null的键值(key)和值(value),Hashtable不能接受null值,会产生空指针异常。
- 线程是否安全: HashMap是非synchronized,而Hashtable是synchronized,这意味着Hashtable是线程安全的,HashMap是线程不安全的。
- 效率: 由于Hashtable是线程安全的,所以在单线程环境下比HashMap要慢。如果你不需要同步,只需要单一线程,那么使用HashMap性能要好过Hashtable。
- 初始容量大小和每次扩充容量大小的不同:HashMap的默认容量是16,为2倍扩容,HashTable默认容量是11,为2倍+1扩容
- 底层结构:HashMap会将链表长度大于阈值是转化为红黑树(会先判断当前数组的长度是否小于 64,是则扩容,而不转化),将链表转化为红黑树,以减少搜索时间。Hashtable 没有这样的机制。
四十五,说一下 HashSet 的实现原理?
- HashSet实际上是一个HashMap实例,数据存储结构都是数组+链表。
- HashSet是基于HashMap实现的,HashSet中的元素都存放在HashMap的key上面,而value都是一个统一的对象PRESENT。
- HashSet中add方法调用的是底层HashMap中的put方法,put方法要判断插入值是否存在,而HashSet的add方法,首先判断元素是否存在,如果存在则不插入,如果不存在则插入,这样就保证了HashSet中不存在重复值。
- 通过对象的hashCode和equals方法保证对象的唯一性。
四十六,数组和集合的区别?
- 数组是大小固定的,一旦创建无法扩容;集合大小不固定,支持动态扩容;
- 数组的存放的类型只能是一种,集合存放的类型可以不是一种(不加泛型时添加的类型是Object);
- 数组是java语言中内置的数据类型,是线性排列的,执行效率或者类型检查(不懂),都是最快的;
四十七,concurrentHashMap和HashTable有什么区别?
concurrentHashMap融合了hashmap和hashtable的优势,hashmap是不同步的,但是单线程情况下效率高,hashtable是同步的同步情况下保证程序执行的正确性,但hashtable每次同步执行的时候都要锁住整个结构.
concurrentHashMap锁的方式是细粒度的。concurrentHashMap将hash分为16个桶(默认值),诸如get、put、remove等常用操作只锁住当前需要用到的桶。
concurrentHashMap的读取并发,因为读取的大多数时候都没有锁定,所以读取操作几乎是完全的并发操作,只是在求size时才需要锁定整个hash。
而且在迭代时,concurrentHashMap使用了不同于传统集合的快速失败迭代器的另一种迭代方式,弱一致迭代器。在这种方式中,当iterator被创建后集合再发生改变就不会抛出ConcurrentModificationException,取而代之的是在改变时new新的数据而不是影响原来的数据,iterator完成后再讲头指针替代为新的数据,这样iterator时使用的是原来的数据
四十八,HashMap和HashSet的区别?
- HashMap实现Map接口,HashSet实现Set接口;
- HashMap存储类型键值对,HashSet存储类型对象
- HashMap调用 put() 执行添加操作,HashSet调用 add() 执行添加操作
- HashMap用key计算HashCode,HashSet用对象计算HashCode,但是对于两个对象来说HashCode可能相等所以 equals() 来判断两个对象的相等性,如果两个对象不等则返回false
- HashMap比HashSet快
四十九,如何创建一个线程?
- 继承Thread类并重写该类的run方法;
- 实现runnable接口,实现接口的run()方法;
- 实现 Runnable、Callable 接口创建多线程,实现 call() 方法
五十,线程的run()和start()有什么区别?
- run()方法称为线程体。通过调用Thread类的start()方法来启动一个线程。
- start() 方法用于启动线程,run() 方法用于执行线程的运行时代码。run() 可以重复调用,而 start() 只能调用一次。
- start()方法来启动一个线程,真正实现了多线程运行。调用start()方法无需等待run方法体代码执行完毕,可以直接继续执行其他的代码; 此时线程是处于就绪状态,并没有运行。 然后通过此Thread类调用方法run()来完成其运行状态, run()方法运行结束, 此线程终止。然后CPU再调度其它线程。
五十一,多线程的常用方法
- start():启动当前线程,调用当前线程run();
- run():线程执行操作的代码放在此方法中 ;
- currentThread() 静态方法:获取当前线程对象;
- getName()和setName():设置线程名字,获取线程名字
- yieid():释放当前线程的执行权;但是不会释放锁yield()只让有相同执行权的线程获得cup时间片,但是yield()不能控制cup交出的时间,,yeild()只是让线程恢复到就绪状态,那么可能在执行yeild()后进入就绪状态,然后马上又进入运行状态。
- join():在线程a中调用线程b.join()方法,此时线程a就进入阻塞状态,直到线程b完全执行完以后,线程a才结束阻塞状态
- stop():线程停止
- sleep():线程休眠让当前线程休眠指定时间,在指定时间内,线程处于阻塞状态,但是不会释放锁;当执行sleep() 后,当前线程会让出CPU的使用权,当时间到后,线程从睡眠状态转成就绪状态,等待CUP分配时间片,并不会直接进入运行状态。
- isAlive():判断线程是否处于激活状态
- getPriority()和setPriority():获取和设置线程的优先级
五十二,什么是悲观锁?什么是乐观锁?
悲观锁(Pessimistic Lock)
顾名思义,就是很悲观,每次去拿数据的时候都认为别人会修改,所以每次在拿数据的时候都会上锁,这样别人想拿这个数据就会block直到它拿到锁。传统的关系型数据库里边就用到了很多这种锁机制,比如行锁,表锁等,读锁,写锁等,都是在做操作之前先上锁。它指的是对数据被外界(包括本系统当前的其他事务,以及来自外部系统的事务处理)修改持保守态度,因此,在整个数据处理过程中,将数据处于锁定状态。悲观锁的实现,往往依靠数据库提供的锁机制(也只有数据库层提供的锁机制才能真正保证数据访问的排他性,否则,即使在本系统中实现了加锁机制,也无法保证外部系统不会修改数据)。
乐观锁(Optimistic Lock)
顾名思义,就是很乐观,每次去拿数据的时候都认为别人不会修改,所以不会上锁,但是在更新的时候会判断一下在此期间别人有没有去更新这个数据,可以使用版本号等机制。乐观锁适用于多读的应用类型,这样可以提高吞吐量,像数据库如果提供类似于write_condition机制的其实都是提供的乐观锁。
总结:两种锁各有优缺点,不可认为一种好于另一种,像乐观锁适用于写比较少的情况下,即冲突真的很少发生的时候,这样可以省去了锁的开销,加大了系统的整个吞吐量。但如果经常产生冲突,上层应用会不断的进行retry,这样反倒是降低了性能,所以这种情况下用悲观锁就比较合适。
五十三, Synchronized和ReentrantLock的区别?
- 都是可重入锁;
- synchronized 是和 if、else、for、while 一样的关键字,ReentrantLock 是类
- ReentrantLock内部是实现了Sync,Sync继承于AQS抽象类。Sync有两个实现,一个是公平锁,一个是非公平锁,通过构造函数定义。AQS中维护了一个state来计算重入次数,避免频繁的持有释放操作带来的线程问题。
- ReentrantLock只能定义代码块,而Synchronized可以定义方法和代码块;
- Synchronized是JVM的一个内部关键字,ReentrantLock是JDK1.5之后引入的一个API层面的互斥锁;
- Synchronized实现自动的加锁、释放锁,ReentrantLock需要手动加锁和释放锁,中间可以暂停;
- Synchronized由于引进了偏向锁和自旋锁,所以性能上和ReentrantLock差不多,但操作上方便很多,所以优先使用Synchronized。
五十四,线程池四种创建方式
- newCachedThreadPool创建一个可缓存线程池,如果线程池长度超过处理需要,可灵活回收空闲线程,若无可回收,则新建线程。
- newFixedThreadPool 创建一个定长线程池,可控制线程最大并发数,超出的线程会在队列中等待。
- newScheduledThreadPool 创建一个定长线程池,支持定时及周期性任务执行。
- newSingleThreadExecutor 创建一个单线程化的线程池,它只会用唯一的工作线程来执行任务,保证所有任务按照指定顺序(FIFO, LIFO, 优先级)执行。
五十五,说一下 jsp 的 4 种作用域?
application、session、request、page
五十六、session 和 cookie 有什么区别?
1,存储位置不同
- cookie在客户端浏览器;
- session在服务器;
2,存储容量不同
- cookie<=4K,一个站点最多保留20个cookie;
- session没有上线,出于对服务器的保护,session内不可存过多东西,并且要设置session删除机制;
3,存储方式不同
- cookie只能保存ASCII字符串,并需要通过编码方式存储为Unicode字符或者二进制数据;
- session中能存储任何类型的数据,包括并不局限于String、integer、list、map等;
4,隐私策略不同
- cookie对客户端是可见的,不安全;
- session存储在服务器上,安全;
5,有效期不同
- 开发可以通过设置cookie的属性,达到使cookie长期有效的效果;
- session依赖于名为JESSIONID的cookie,而cookie JSESSIONID的过期时间默认为-1,只需关闭窗口该session就会失效,因而session达不到长期有效的效果;
6,跨域支持上不同
- cookie支持跨域;
- session不支持跨域;
五十七,如果客户端禁止 cookie 能实现 session 还能用吗?
一般默认情况下,在会话中,服务器存储 session 的 sessionid 是通过 cookie 存到浏览器里。如果浏览器禁用了 cookie,浏览器请求服务器无法携带 sessionid,服务器无法识别请求中的用户身份,session失效。但是可以通过其他方法在禁用 cookie 的情况下,可以继续使用session。
- 通过url重写,把 sessionid 作为参数追加的原 url 中,后续的浏览器与服务器交互中携带 sessionid 参数。
- 服务器的返回数据中包含 sessionid,浏览器发送请求时,携带 sessionid 参数。
- 通过 Http 协议其他 header 字段,服务器每次返回时设置该 header 字段信息,浏览器中 js 读取该 header 字段,请求服务器时,js设置携带该 header 字段。
五十八,get 和 post 请求有哪些区别?
- get请求参数是连接在url后面的,而post请求参数是存放在requestbody内的;
- get请求因为浏览器对url长度有限制,所以参数个数有限制,而post请求参数个数没有限制;
- 因为get请求参数暴露在url上,所以安全方面post比get更加安全;
- get请求只能进行url编码,而post请求可以支持多种编码方式;
- get请求参数会保存在浏览器历史记录内,post请求并不会;
- get请求浏览器会主动cache,post并不会,除非主动设置;
- get请求产生1个tcp数据包,post请求产生2个tcp数据包;
- 在浏览器进行回退操作时,get请求是无害的,而post请求则会重新请求一次;
- 浏览器在发送get请求时会将header和data一起发送给服务器,服务器返回200状态码,而在发送post请求时,会先将header发送给服务器,服务器返回100,之后再将data发送给服务器,服务器返回200 OK;
五十九,如何实现跨域?
1,最流行的跨域方案cors
cors是目前主流的跨域解决方案,跨域资源共享(CORS) 是一种机制,它使用额外的 HTTP 头来告诉浏览器 让运行在一个 origin (domain) 上的Web应用被准许访问来自不同源服务器上的指定的资源。当一个资源从与该资源本身所在的服务器不同的域、协议或端口请求一个资源时,资源会发起一个跨域 HTTP 请求。
2,最方便的跨域方案Nginx
nginx是一款极其强大的web服务器,其优点就是轻量级、启动快、高并发。现在的新项目中nginx几乎是首选,我们用node或者java开发的服务通常都需要经过nginx的反向代理。
反向代理的原理:即所有客户端的请求都必须先经过nginx的处理,nginx作为代理服务器再讲请求转发给node或者java服务,这样就规避了同源策略。