可刷题的网站
复习必须掌握
Java语言8大特性
- 简单的
- 解释的(有解释器也叫
java
虚拟机JVM
) - 面向对象的
- 与平台无关的(跨平台可以运行)
- 健壮的(强类型的)
- 多线程的
- 安全的(内存方面、保护重要文件)
- 动态的语言
数据类型分为哪两大类?
基本数据类型和引用数据类型
请说说有哪些基本数据类型,它们对应的存储空间分别是多少?
- 数值型
- 整数类型
名称 | 占据空间大小 | 取值范围 |
---|---|---|
byte |
1个字节 | -2^7~2^7-1 |
short |
2个字节 | -2^15~2^15-1 |
int |
4个字节 | -2^31~2^31-1 |
long |
8个字节 | -2^63~2^63-1 |
- 浮点类型
名称 | 占据空间大小 | 取值范围 |
---|---|---|
float |
4个字节 | |
double |
8个字节 |
- 字符型
名称 | 占据空间大小 | 取值范围 |
---|---|---|
char 无符号整数 |
2个字节 | 0~2^16 |
- 布尔型
名称 | 占据空间大小 | 取值范围 |
---|---|---|
boolean |
1个字节 | 只有true、false两个值 |
引用类型有哪些种类?请举例说明
类(class
)、接口(interface
)、数组([]
)
那种数据类型不能进行类型转换?
布尔型Boolean
说说空指针异常如何解决?
空指针异常问题一般是定义变量没有赋初值造成的,在调用某个对象之前,要进行非空的验证或者赋初值,否则就容易出空指针异常。
do while
和while
之间的区别
前者先执行一次循环体再判断。后者先判断,符合条件再执行循环体。
while
和for
之间的区别
两者都是循环的作用。但是for
要提前知道循环的次数,而while
则不需要(当然知道循环次数用while
也可以)。
随便写出30个java
关键字,并说说Java
关键字的特点
用于 | 关键字 |
---|---|
用于定义数据类型的关键字 | class interface byte short int long float double char loolean void |
用于定义数据类型值的关键字 | true false null |
用于定义流程控制的关键字 | if else switch case default while do for break continue return |
用于定义访问权限修饰符的关键字 | private protected public |
用于定义类,函数,变量修饰符的关键字 | abstract final static synchronized |
用于定义类与类之间关系的关键字 | extends implements |
用于定义建立实例及引用实例,判断实例的关键字 | new this super instanceof |
用于定义异常处理的关键字 | try catch finally throw throws |
用于包的关键字 | package inport |
其他修饰符的关键字 | native strictfp transient volatile assert |
- transient:被它修饰的成员属性变量不被序列化。
如密码等信息,不想在网络操作中被传输,用它修饰,这个字段的生命周期仅存于调用者的内存中而不会写到磁盘里持久化
- native:native method是一个java调用非java代码的接口,对应的方法不在当前文件,在其他语言(如C、C++)实现的文件中。可与java的(除abstract外的)其他标识符连用,【abstract指明这些方法无实现体】
- strictfp
- volatile
- assert
流程控制中有哪些流程结构
顺序结构、循环结构、分支结构
int i=34; int j=--i;
这个时候i
和j
分别说多少?为什么得到这个结果?
i=33 j=33 其中–i就是在使用i之前先减1
进制有哪些表现形式?
二进制、八进制、十进制、十六进制
分别说说break
、continue
以及return
的作用
break
跳出结束当前结构的代码块,可用于分支结构和循环结构continue
结束当次循环体,不再执行continue
后面的循环体代码,直接进行下一次循环,可用于循环结构。return
(不一定有参数类表就有返回值,没有参数类表不一定没有返回值)结束方法
(1)return
从当前的方法中退出,返回到该调用的方法的语句处,继续执行
(2)return
返回一个值给调用该方法的语句,返回值的数据类型必须与方法的声明中的返回值的类型一致,可以使用强制类型转换来使数据类型一致
(3)return
当方法说明中用void
声明返回类型为空时,应使用这种格式,不返回任何值。
&&
与&
的区别
&
和&&
都可以用作逻辑的运算符,表示逻辑与(and
),只有当运算符两边都为true
时整个运算结果才为true
,否则为false
&&
还具有短路的功能,如果前面有false
的话后面的不判断&
表示按位与操作,还可以用在位运算符。&
的两端无论如何都要执行到
||
和|
的区别
|
的两端无论如何都要执行到
分别说说5%4
,5%-4
,-5%4
和-5%-4
相对应得到的结果
1 | 5%4=1,5%-4=1,-5%4=-1,-5%-4=-1 |
现在有3,4,22,33,1,把他们放到数组中,说说数组如何初始化
初始化:静态、动态
- 静态:
1
2
3int[] arr = {3,4,22,33,1};
//或者
int[] arr = new int[]{3,4,22,33,1}; - 动态:
1
2
3
4
5
6int[] arr = new int[5];
arr[0] = 3;
arr[1] = 4;
arr[2] = 22;
arr[3] = 33;
arr[4] = 1;
什么是数组?什么是二维数组?
数组是同一种数据类型的集合。数组就是一个装有多个同一种类型数据的容器。
二维数组就是以一维数组为元素的一维数组。
如何获取数组的长度,数组的角标由几开始
array.length
角标由0
开始
数组的最大脚标是?
array.length-1
switch
中条件语句可以是哪些数据类型?
1 | byte,short,int,char,string(JDK1.7新加入) |
如何遍历一个二维数组,并且获得二维数组中一维数组的每个元素
1 | for(int i=0;i<=arr.length;i++){ |
33,4,22,35,1,6,23通过编程获得它们的最大值
1 | public static void main(String[] args) { |
手写一个冒泡排序:相邻两个数之间的比较
1 | public static void main(String[] args) { |
手写一个选择排序:第一个数和剩下的所有数进行比较,获取第一个最值;第二个数和….
1 | int[] arr = { 33, 4, 22, 35, 1, 6, 23 }; |
数组常见异常?
数组脚标越界异常(ArrayIndexOutOfBoundsException
)
访问到数组中不存在的脚标时发生。
空指针异常(NullPointerException
)
数组引用没有指向实体,却在代码中对数组进行操作时发生。
什么是类?什么是对象?
类是对某一类事物的描述、是抽象的概念上的定义。
对象是实际的个体
如何定义一个类?
1 | 访问控制符 class 类名{ |
1 | public class Person{ |
如何定义一个方法?
1 | [访问修饰符] 返回值类型 方法名(参数列表){ |
1 | public void sleep(){ |
如何创建对象?
1 | 类名 对象名=new 类名(); |
如何调用方法(假设对象名为p,方法名为ss且参数列表为(int age,String name))
1 | 对象名.方法名(参数列表); |
例如:p.ss(age,name)
什么是方法重写(说出方法重写前提条件以及附加条件)?
前提条件:继承关系
附加条件:
- 方法名必须相同
- 参数列表必须相同
- 返回值类型必须相同
什么是方法重载(说出方法重载前提条件以及附加条件)?
前提条件:同一个类
附加条件:
- 方法名相同
- 参数列表必须不同
- 与返回值类型无关
- 两个或两个以上
“同名不同参”
构造方法最主要的作用?
对类进行实例化,或者说是创建对象
哪个类是Java
所有类的根?
Object
static
可以修饰哪些?
常量、变量、方法、类、代码块
final
可以修饰哪些?并且说明修饰之后它们分别有什么特点
变量、方法、类
Java
中每个类如果没有构造方法,那么系统是否提供一个无参不做任务操作的构造方法?如果有,请写出来(以Person
举例)
1 | public Person(){ |
面向对象有哪些特征?
封装 继承 多态
多态:继承关系、重写父类方法、子类有特有方法
向上转型
父类型 引用变量名 = new 子类类型();
//调用方法是子类重写或继承父类的方法,无法调用子类特有方法。(子类转成父类,向上转型)
向下转型
子类型 引用变量名 = (子类类型)new 父类类型();
//(向下转型)要强转。注意可能会有强转异常,要用instanceof
来判断
有哪些代码块?
静态、普通、构造、同步
静态代码块、普通代码块、构造代码块、构造方法的执行顺序
1 | 静态代码块(只执行一次)》构造方法》构造代码块》普通代码块 |
类的实例化顺序
问题:比如父类静态数据、构造函数、字段、子类静态数据、构造函数、字段,当new的时候它们的执行顺序?
答案:类加载器实例化进行的操作步骤(
加载-->连接-->初始化
)。
父类静态变量–>父类静态代码块–>子类静态变量–>子类静态代码块–>父类非静态变量(父类实例成员变量)–>父类构造函数–>子类非静态变量(子类实例化成员变量)–>子类构造函数
构造方法有哪些特点?
- 构造方法的方法名必须与类名相同;
- 构造方法不需要返回类型修饰符(包括
void
); - 构造方法的作用是对类对象进行初始化;
- 构造方法在用户实例化对象时由系统自动调用;
- 没有定义构造方法时,java默认在每个类中定义一个无任何参数不做任何操作的构造方法;
抽象类和接口之间的区别?
- 抽象类和接口都不能直接实例化
- 抽象类要被子类继承,接口要被类实现
- 接口里定义的变量默认
public static
修饰,抽象类中的变量是普通变量 - 抽象类里的抽象方法必须全部被子类实现,如果子类不能全部实现父类抽象方法,那么该子类只能是抽象类。
同样,一个类实现接口的时候,如果不能全部实现接口的方法,那么该类也只能是抽象类 - 抽象类里可以没有抽象方法
- 如果一个类里有抽象方法,那么该类只能是抽象类
- 抽象方法要被实现,所以不能是静态的,也不能是私有的
- 接口可以继承接口且可多继承接口。类只能单继承
请创建一个静态内部类对象,以外部类名为P,内部类名为Q举例
1 | public class P{ |
error
和exception
有什么区别
error
:通常出现重大问题。(如:运行的类不存在或者内存溢出等,不编写针对代码对其处理)exception
:在运行时函数出现的一些情况。可以通过try...catch...finally
处理
error
和exception
的父类是什么?
Throwable
类
在处理异常的过程中,是否可以有多个catch
?如果有,请举个例子
可以有
1 | try{ |
RandomAcessFile
的父类是?
Object
类
RandomAcessFile
有哪些特有方法
finalize()
RandomAcessFile
是否可以读?是否可以写?为什么?
可以
说明 | |
---|---|
r | The file is opened in read-only mode. An IOException is thrown if any of the write methods is called. |
rw | The file is opened for reading and writing. If the file does not exist, it will be created. |
rws | The file is opened for reading and writing. Every change of the file’s content or metadata must be written synchronously to the target device. |
rwd | The file is opened for reading and writing. Every change of the file’s content must be written synchronously to the target device. |
String
、StringBuffer
、StrngBuilder
之间的区别
StringBuilder
和StringBuffre
非常类似,均代表可变的字符序列,而且方法也一样String
:不可变字符序列(不能被继承)StringBuffer
:可变字符序列、效率低、线程安全(同步)StringBuilder
(JDK1.5
):可变字符序列、效率高、线程不安全(异步)String
使用陷阱:1
2String s = "a";//创建了一个字符串
s = s + "b";//实际上原来的"a"字符串对象已经丢弃了,现在又产生了一个字符串s+"b"(也就是"ab")。如果多次执行这些改变串内容的操作,会导致大量副本字符串对象存留在内存中,降低效率。如果这样的操作放到循环中,会极大影响程序的性能。
final
、finally
以及finalize
之间的区别
final
:
修饰符(关键字),如果一个类被声明为final
,意味着它不能再派生出新的子类,不能作为父类被继承。因此一个类不能既被声明为abstract
的,又被声明为final
的。将变量或方法声明为final
,可以保证它们在使用中不被改变。
被声明为final
的变量必须在声明时给定初值,而在以后的引用中只能读取,不可修改。被声明为final
的方法也同样只能使用,不能重载。finally
在异常处理时提供finally
块来执行任何清除操作。如果抛出一个异常,那么相匹配的catch
子句就会执行,然后控制就会进入finally
块(如果有的话)finalize
继承于Object
—方法名。
(调用垃圾回收机制的时候才会用到)Java
技术允许使用finalize()
方法在垃圾收集器将对象从内存中清除出去之前做必要的清理工作。
说说垃圾回收机制
调用System.gc()
实际上等效于调用Runtime.getRuntime().gc()
请阐述Io
流处理流程
在整个IO操作中,输入和输出流是一个重要的概念。不管使用哪种流进行操作,都是采用如下步骤完成:
- 找到一个要操作的资源,可能是文件,可能是其他的位置。
- (确定操作对象)根据字节流或字符流的子类,决定输入及输出的位置。
- 进行读或写的操作。
- 关闭流。
Io
流按传输方向分为?按数据单位分为?按功能分为?
按流向分:
输入流: 程序可以从中读取数据的流。
输出流: 程序能向其中写入数据的流。按数据传输单位分:
字节流: 以字节为单位传输数据的流FileInputStream
FileOutputStream
BufferInputStream
BufferOutputStream
字符流: 以字符为单位传输数据的流FileReader
FileWriter
BufferReader
BufferWriter
(后缀是父类,前面的是功能)按功能分:
节点流: 用于直接操作目标设备的流(低级流)
过滤流: 是对一个已存在的流的链接和封装,通过对数据进行处理为程序提供功能强大、灵活的读写功能。(高级流,放文件或对象)
&&与|| 的优先级
java
的 && 优先级高于 ||,而不是同级- “逻辑与”、“逻辑或”,都有短路作用
String
类能被继承吗,为什么
不能。在Java
中,只要是被定义为final
的类,也可以说是被final
修饰的类,就是不能被继承的。
ArrayList
和LinkedList
的区别
简单的区别:
ArrayList
是实现了基于动态数组的数据结构,LinkedList
基于链表的数据结构。 (LinkedList
是双向链表,有next
也有previous
)- 对于随机访问
get
和set
,ArrayList
觉得优于LinkedList
,因为LinkedList
要移动指针。 - 对于新增和删除操作
add
和remove
,LinedList
比较占优势,因为ArrayList
要移动数据。
深度的区别:
- 对
ArrayList
和LinkedList
而言,在列表末尾增加一个元素所花的开销都是固定的。对ArrayList
而言,主要是在内部数组中增加一项,指向所添加的元素,偶尔可能会导致对数组重新进行分配;而对LinkedList
而言,这个开销是统一的,分配一个内部Entry
对象。 - 在
ArrayList
的中间插入或删除一个元素意味着这个列表中剩余的元素都会被移动;而在LinkedList
的中间插入或删除一个元素的开销是固定的。 LinkedList
不支持高效的随机元素访问。ArrayList
的空间浪费主要体现在在list
列表的结尾预留一定的容量空间,而LinkedList
的空间花费则体现在它的每一个元素都需要消耗相当的空间
用过哪些Map
类,都有什么区别。
问题:比如HashMap
是线程安全的吗,并发下使用的Map
是什么,他们 内部原理分别是什么,比如存储方式,hashcode
,扩容,默认容量等。
答案: 不安全,并发下使用
ConcurrentHashMap
。
JAVA8
的ConcurrentHashMap
为什么放弃了分段锁?
原因:通过 JDK
的源码和官方文档看来, 他们认为的弃用分段锁的原因由以下几点:
- 加入多个分段锁浪费内存空间。
- 生产环境中,
map
在放入时竞争同一个锁的概率非常小,分段锁反而会造成更新等操作的长时间等待。 - 为了提高
GC
的效率
既然弃用了分段锁, 那么一定有新的线程安全方案, 我们来看看源码是怎么解决线程安全的呢?CAS
首先通过 hash
找到对应链表过后, 查看是否是第一个object
, 如果是, 直接用cas
原则插入,无需加锁,然后如果不是链表第一个object
, 则直接用链表第一个object
加锁,这里加的锁是synchronized
,虽然效率不如 ReentrantLock
, 但节约了空间,这里会一直用第一个object
为锁, 直到重新计算map
大小, 比如扩容或者操作了第一个object
为止。
HashMap线程不安全;
HashTable现场安全,对整个哈希表进行加锁(效率比较低,key不能为null);而ConcurrentHashMap是针对每个桶(链表的头结点)加锁,可以大大降低锁冲突;
ConcurrentHashMap相对于HashTable做出了优化,使用链表的头结点为锁对象,降低了锁冲突概率,使用CAS策略优化了size等操作,对扩容机制也使用化整为零的优化方法。
集合赋值的坑
1 | List<String> list1 = new ArrayList<String>(); |
问题来了:
此时如果是赋值的话
1 | list2 = list1; |
那么
list2
的地址就指向了list1
,即list2
和list1
为同一个“引用”(指向了list1
的对象,同一个对象)
1 | list2.addAll(list1); |
就会把
list1
的数据添加到list2
中,而list2
和list1
指向各自的对象
Java 单例与类实例初始化相关的一个陷阱题
1 | class SingleTon{ |
原因为 SingleTon singleTon = SingleTon.getInstance();
调用了类的静态方法,所以触发类的初始化,类加载的时候在准备过程中为类的静态变量分配内存并初始化默认值 singleton=null
,count1=0
,count2=0
,类初始化时为类的静态变量赋值和执行静态代码块,singleton
赋值为 new SingleTon()
调用类的构造方法,调用类的构造方法后 count1=1
且 count2=1
,继续为 count1
与 count2
赋值,此时 count1
没有赋值操作,所以 count1
为 1,但是 count2
执行赋值操作就变为 0。
执行顺序:类加载时为类的静态变量分配内存并初始化默认值–类初始化时为类的静态变量赋值和执行静态代码块(用构造方法赋值则也会执行中的代码)–再执行静态变量赋值表达式
Java 类加载初始化构造与继承相关的一个陷阱题
1 | public class Base{ |
1 | //结果: |
3个加号运算:i+++j
(贪心规则)
1 | public class PlusPlus{ |
1 | //结果为: |
即
i+++j
相当于(i++) + j
编译器:贪心规则
扩展:
1 | i--j //语法报错,当成了`i-- j`而不是 `i-(-j)` |
例子2贪心的解释:
1 | package chapter2; |
1 | //结果: |
因为八进制(0~377),因此””\1717”会解析成”171”和”1”,”431”会解析成”43”和”1”
++i
与i++
仅是“先加”与“后加”的差别吗?
++i
和i++
都是先将变量的值加1。前置++是用增值后的变量进行运算;后置++是先将变量赋值给临时变量,用临时变量去运算。
用javap反编译命令解析程序
1 | package chapter2; |
保存并编译文件,打开控制台,进入class文件的目录(chapter2的上级目录)
1 | javap -c chapter2.DeepPlus |
javap是反编译命令,-c为显示为代码反编译后的伪指令
1 | Compiled from "DeepPlus.java" |
从指令上说,后置 ++ 在执行增值指令(iinc)前,先将变量的值压入栈,执行增值指令后,使用的是之前压入栈的值。