Java-题目

可刷题的网站

复习必须掌握

Java语言8大特性

  1. 简单的
  2. 解释的(有解释器也叫java虚拟机JVM
  3. 面向对象的
  4. 与平台无关的(跨平台可以运行)
  5. 健壮的(强类型的)
  6. 多线程的
  7. 安全的(内存方面、保护重要文件)
  8. 动态的语言

数据类型分为哪两大类?

基本数据类型和引用数据类型

请说说有哪些基本数据类型,它们对应的存储空间分别是多少?

  1. 数值型
    • 整数类型
名称 占据空间大小 取值范围
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个字节
  1. 字符型
名称 占据空间大小 取值范围
char无符号整数 2个字节 0~2^16
  1. 布尔型
名称 占据空间大小 取值范围
boolean 1个字节 只有true、false两个值

引用类型有哪些种类?请举例说明

类(class)、接口(interface)、数组([]

那种数据类型不能进行类型转换?

布尔型Boolean

说说空指针异常如何解决?

空指针异常问题一般是定义变量没有赋初值造成的,在调用某个对象之前,要进行非空的验证或者赋初值,否则就容易出空指针异常。

do whilewhile之间的区别

前者先执行一次循环体再判断。后者先判断,符合条件再执行循环体。

whilefor之间的区别

两者都是循环的作用。但是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;这个时候ij分别说多少?为什么呢得到这个结果?

i=33 j=33 其中–i就是在使用i之前先减1

进制有哪些表现形式?

二进制、八进制、十进制、十六进制

分别说说breakcontinue以及return的作用

  • break跳出结束当前结构的代码块,可用于分支结构和循环结构
  • continue结束当次循环体,不再执行continue后面的循环体代码,直接进行下一次循环,可用于循环结构。
  • return(不一定有参数类表就有返回值,没有参数类表不一定没有返回值)结束方法
    (1) return 从当前的方法中退出,返回到该调用的方法的语句处,继续执行
    (2) return 返回一个值给调用该方法的语句,返回值的数据类型必须与方法的声明中的返回值的类型一致,可以使用强制类型转换来使数据类型一致
    (3) return 当方法说明中用void声明返回类型为空时,应使用这种格式,不返回任何值。

&&&的区别

&&&都可以用作逻辑的运算符,表示逻辑与(and),只有当运算符两边都为true时整个运算结果才为true,否则为false

  • &&还具有短路的功能,如果前面有false的话后面的不判断
  • &表示按位与操作,还可以用在位运算符。
  • &的两端无论如何都要执行到

|||的区别

|的两端无论如何都要执行到

分别说说5%45%-4-5%4-5%-4相对应得到的结果

1
5%4=15%-4=1,-5%4=-1,-5%-4=-1

现在有3,4,22,33,1,把他们放到数组中;说说数组如何初始化

初始化:静态、动态

  • 静态:
    1
    2
    3
    int[] arr = {3,4,22,33,1};
    //或者
    int[] arr = new int[]{3,4,22,33,1};
  • 动态:
    1
    2
    3
    4
    5
    6
    int[] 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
byteshortintchar,string(JDK1.7新加入)

如何遍历一个二维数组,并且获得二维数组中一维数组的每个元素

1
2
3
4
5
6
7
8
for(int i=0;i<=arr.length;i++){
int[] a=arr[i];
system.out.print(“第”+(i+1)+”行的是:”);
for(int j=0;j<a.length;j++){
system.out.print(a[j]+”、”);
}
system.out.println();
}

33,4,22,35,1,6,23通过编程获得它们的最大值

1
2
3
4
5
6
7
8
9
10
public static void main(String[] args) {
int[] arr = { 23, 33, 4, 22, 35, 1, 6, 23 };
int max = 0;
for (int i = 0; i < arr.length; i++) {
if (max < arr[i]) {
max = arr[i];
}
}
System.out.println(max);
}

手写一个冒泡排序:相邻两个数之间的比较

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public static void main(String[] args) {
int[] arr = { 1, 2, 4, 6, 3, 6, 32, 5, 4, 2 };
for (int i = 0; i < arr.length - 1; i++) {
for (int j = 0; j < arr.length - i - 1; j++) {
if (arr[j] < arr[j + 1]) {
int temp = arr[j];
arr[j] = arr[j + 1];
arr[j + 1] = temp;
}
}
}
for (int i = 0; i < arr.length; i++) {
System.out.println(arr[i]);
}
}

手写一个选择排序:第一个数和剩下的所有数进行比较,获取第一个最值;第二个数和….

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
int[] arr = { 33, 4, 22, 35, 1, 6, 23 };
for (int i = 0; i < arr.length-1; i++) {
for (int j = i; j < arr.length; j++) {
if (arr[i] < arr[j]) {
int temp;
temp = arr[i];
arr[i] = arr[j];
arr[j] = temp;
}

}
}
for (int i = 0; i < arr.length; i++) {
System.out.println(arr[i]);
}

数组常见异常?

数组脚标越界异常(ArrayIndexOutOfBoundsException
访问到数组中不存在的脚标时发生。
空指针异常(NullPointerException
数组引用没有指向实体,却在代码中对数组进行操作时发生。

什么是类?什么是对象?

类是对某一类事物的描述、是抽象的概念上的定义。
对象是实际的个体

如何定义一个类?

1
2
3
4
访问控制符 class 类名{
成员变量;
成员方法
}
1
2
3
public class Person{
....
}

如何定义一个方法?

1
2
3
[访问修饰符] 返回值类型 方法名(参数列表){

}
1
2
public  void sleep(){
}

如何创建对象?

1
类名  对象名=new 类名();

如何调用方法(假设对象名为p,方法名为ss且参数列表为(int age,String name))

1
对象名.方法名(参数列表)

例如:p.ss(age,name)

什么是方法重写(说出方法重写前提条件以及附加条件)?

前提条件:继承关系
附加条件:

  • 方法名必须相同
  • 参数列表必须相同
  • 返回值类型必须相同

什么是方法重载(说出方法重载前提条件以及附加条件)?

前提条件:同一个类
附加条件:

  • 方法名相同
  • 参数列表必须不同
  • 与返回值类型无关
  • 两个或两个以上

构造方法最主要的作用?

对类进行实例化,或者说是创建对象

哪个类是Java所有类的根?

Object

static可以修饰哪些?

常量、变量、方法、类、代码块

final可以修饰哪些?并且说明修饰之后它们分别有什么特点

变量、方法、类

Java中每个类如果没有构造方法,那么系统是否提供一个无参不做任务操作的构造方法?如果有,请写出来(以Person举例)

1
2
public Person(){
}

面向对象有哪些特征?

封装 继承 多态
多态:继承关系、重写父类方法、子类有特有方法

向上转型

父类型 引用变量名 = new 子类类型();//调用方法是子类重写或继承父类的方法,无法调用子类特有方法。(子类转成父类,向上转型)

向下转型

子类型 引用变量名 = (子类类型)new 父类类型();//(向下转型)要强转。注意可能会有强转异常,要用instanceof来判断

有哪些代码块?

静态、普通、构造、同步

静态代码块、普通代码块、构造代码块、构造方法的执行顺序

1
静态代码块(只执行一次)》构造方法》构造代码块》普通代码块

类的实例化顺序

问题:比如父类静态数据、构造函数、字段、子类静态数据、构造函数、字段,当new的时候它们的执行顺序?

答案:类加载器实例化进行的操作步骤(加载-->连接-->初始化)。

父类静态变量–>父类静态代码块–>子类静态变量–>子类静态代码块–>父类非静态变量(父类实例成员变量)–>父类构造函数–>子类非静态变量(子类实例化成员变量)–>子类构造函数

构造方法有哪些特点?

  • 构造方法的方法名必须与类名相同;
  • 构造方法不需要返回类型修饰符(包括void);
  • 构造方法的作用是对类对象进行初始化;
  • 构造方法在用户实例化对象时由系统自动调用;
  • 没有定义构造方法时,java默认在每个类中定义一个无任何参数不做任何操作的构造方法;

抽象类和接口之间的区别?

  • 抽象类和接口都不能直接实例化

  • 抽象类要被子类继承,接口要被类实现

  • 接口里定义的变量默认public static修饰,抽象类中的变量是普通变量

  • 抽象类里的抽象方法必须全部被子类实现,如果子类不能全部实现父类抽象方法,那么该子类只能是抽象类。

    同样,一个类实现接口的时候,如果不能全部实现接口的方法,那么该类也只能是抽象类

  • 抽象类里可以没有抽象方法

  • 如果一个类里有抽象方法,那么该类只能是抽象类

  • 抽象方法要被实现,所以不能是静态的,也不能是私有的

  • 接口可以继承接口且可多继承接口。类只能单继承

请创建一个静态内部类对象,以外部类名为P,内部类名为Q举例

1
2
3
public class P{
static class Q{}
}

errorexception有什么区别

  • error:通常出现重大问题。(如:运行的类不存在或者内存溢出等,不编写针对代码对其处理)
  • exception:在运行时函数出现的一些情况。可以通过try...catch...finally处理

errorexception的父类是什么?

Throwable

在处理异常的过程中,是否可以有多个catch?如果有,请举个例子

可以有

1
2
3
4
try{ 
}catch(IOException io){
}catch(Exception e){
}

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.

StringStringBufferStrngBuilder之间的区别

  • StringBuilderStringBuffre非常类似,均代表可变的字符序列,而且方法也一样

  • String:不可变字符序列(不能被继承)

  • StringBuffer:可变字符序列、效率低、线程安全(同步)

  • StringBuilderJDK1.5):可变字符序列、效率高、线程不安全(异步)

  • String使用陷阱:

    1
    2
    String s = "a";//创建了一个字符串
    s = s + "b";//实际上原来的"a"字符串对象已经丢弃了,现在又产生了一个字符串s+"b"(也就是"ab")。如果多次执行这些改变串内容的操作,会导致大量副本字符串对象存留在内存中,降低效率。如果这样的操作放到循环中,会极大影响程序的性能。

finalfinally以及finalize之间的区别

  • final

    修饰符(关键字),如果一个类被声明为final,意味着它不能再派生出新的子类,不能作为父类被继承。因此一个类不能既被声明为abstract的,又被声明为final的。将变量或方法声明为final,可以保证它们在使用中不被改变。

    被声明为final的变量必须在声明时给定初值,而在以后的引用中只能读取,不可修改。被声明为final的方法也同样只能使用,不能重载。

  • finally

    在异常处理时提供finally块来执行任何清除操作。如果抛出一个异常,那么相匹配的catch子句就会执行,然后控制就会进入finally块(如果有的话)

  • finalize

    继承于Object—方法名。

    (调用垃圾回收机制的时候才会用到)Java技术允许使用finalize()方法在垃圾收集器将对象从内存中清除出去之前做必要的清理工作。

说说垃圾回收机制

调用System.gc()实际上等效于调用Runtime.getRuntime().gc()

请阐述Io流处理流程

在整个IO操作中,输入和输出流是一个重要的概念。不管使用哪种流进行操作,都是采用如下步骤完成:

  1. 找到一个要操作的资源,可能是文件,可能是其他的位置。
  2. (确定操作对象)根据字节流或字符流的子类,决定输入及输出的位置。
  3. 进行读或写的操作。
  4. 关闭流。

Io流按传输方向分为?按数据单位分为?按功能分为?

  1. 按流向分:

    输入流: 程序可以从中读取数据的流。

    输出流: 程序能向其中写入数据的流。

  2. 按数据传输单位分:

    字节流: 以字节为单位传输数据的流 FileInputStream FileOutputStream BufferInputStream BufferOutputStream

    字符流: 以字符为单位传输数据的流 FileReader FileWriter BufferReader BufferWriter(后缀是父类,前面的是功能)

  3. 按功能分:

    节点流: 用于直接操作目标设备的流(低级流)

    过滤流: 是对一个已存在的流的链接和封装,通过对数据进行处理为程序提供功能强大、灵活的读写功能。(高级流,放文件或对象)

&&与|| 的优先级

  • java的 && 优先级高于 ||,而不是同级

  • “逻辑与”、“逻辑或”,都有短路作用

String类能被继承吗,为什么

不能。在Java中,只要是被定义为final的类,也可以说是被final修饰的类,就是不能被继承的。

ArrayListLinkedList的区别

简单的区别:

  1. ArrayList是实现了基于动态数组的数据结构,LinkedList基于链表的数据结构。 (LinkedList是双向链表,有next也有previous
  2. 对于随机访问getsetArrayList觉得优于LinkedList,因为LinkedList要移动指针。
  3. 对于新增和删除操作addremoveLinedList比较占优势,因为ArrayList要移动数据。

深度的区别:

  1. ArrayListLinkedList而言,在列表末尾增加一个元素所花的开销都是固定的。对ArrayList而言,主要是在内部数组中增加一项,指向所添加的元素,偶尔可能会导致对数组重新进行分配;而对LinkedList而言,这个开销是统一的,分配一个内部Entry对象。
  2. ArrayList的中间插入或删除一个元素意味着这个列表中剩余的元素都会被移动;而在LinkedList的中间插入或删除一个元素的开销是固定的。
  3. LinkedList不支持高效的随机元素访问。
  4. ArrayList的空间浪费主要体现在在list列表的结尾预留一定的容量空间,而LinkedList的空间花费则体现在它的每一个元素都需要消耗相当的空间

用过哪些Map类,都有什么区别。

问题:比如HashMap是线程安全的吗,并发下使用的Map是什么,他们 内部原理分别是什么,比如存储方式,hashcode,扩容,默认容量等。

答案: 不安全,并发下使用ConcurrentHashMap

JAVA8ConcurrentHashMap为什么放弃了分段锁?

原因:通过 JDK 的源码和官方文档看来, 他们认为的弃用分段锁的原因由以下几点:

  1. 加入多个分段锁浪费内存空间。
  2. 生产环境中, map 在放入时竞争同一个锁的概率非常小,分段锁反而会造成更新等操作的长时间等待。
  3. 为了提高 GC 的效率

既然弃用了分段锁, 那么一定有新的线程安全方案, 我们来看看源码是怎么解决线程安全的呢?CAS

首先通过 hash 找到对应链表过后, 查看是否是第一个object, 如果是, 直接用cas原则插入,无需加锁,然后如果不是链表第一个object, 则直接用链表第一个object加锁,这里加的锁是synchronized,虽然效率不如 ReentrantLock, 但节约了空间,这里会一直用第一个object为锁, 直到重新计算map大小, 比如扩容或者操作了第一个object为止。

集合赋值的坑

1
2
3
4
5
6
List<String> list1 = new ArrayList<String>();
List<String> list2 = new ArrayList<String>();

list1.add("1");
list1.add("2");
list1.add("3");

问题来了:
此时如果是赋值的话

1
list2 = list1;

那么list2的地址就指向了list1,即list2list1为同一个“引用”(指向了list1的对象,同一个对象

1
list2.addAll(list1);

就会把list1的数据添加到list2中,而list2list1指向各自的对象

Java 单例与类实例初始化相关的一个陷阱题

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
class SingleTon{
private static SingleTon singleTon = new SingleTon();
public static int count1;
public static int count2 = 0;
private SingleTon () {
count1++;
count2++;
}

public static SingleTon getInstance() {
return singleTon;
}
}

public class Demo {
public static void main(String[] args) {
SingleTon singleTon = SingleTon.getInstance();
System.out.println("count1=" + singleTon.count1);//结果:count1=1
System.out.println("count2=" + singleTon.count2);//结果:count2=0
}
}

原因为 SingleTon singleTon = SingleTon.getInstance(); 调用了类的静态方法,所以触发类的初始化,类加载的时候在准备过程中为类的静态变量分配内存并初始化默认值 singleton=nullcount1=0count2=0,类初始化时为类的静态变量赋值和执行静态代码块,singleton 赋值为 new SingleTon() 调用类的构造方法,调用类的构造方法后 count1=1count2=1,继续为 count1count2 赋值,此时 count1 没有赋值操作,所以 count1 为 1,但是 count2 执行赋值操作就变为 0。

执行顺序:类加载时为类的静态变量分配内存并初始化默认值–类初始化时为类的静态变量赋值和执行静态代码块(用构造方法赋值则也会执行中的代码)–再执行静态变量赋值表达式

Java 类加载初始化构造与继承相关的一个陷阱题

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
public class Base{
public Base(){
test();
}

public void test(){

}
}

public class Child extends Base{
private int a = 123;
public Child(){

}

public void test(){
System.out.println(a);
}
}

public static void main(String[] args){
Child c = new Child();
c.test();
}
1
2
3
//结果:
0
123

3个加号运算:i+++j(贪心规则)

1
2
3
4
5
6
7
8
public class PlusPlus{
public static void main(String[] args){
int i = 10;
int j = 20;
int k = i +++ j;
System.out.println("k= " + k);
}
}
1
2
//结果为:
k=30

i+++j 相当于 (i++) + j

编译器:贪心规则

扩展:

1
i--j //语法报错,当成了`i-- j`而不是 `i-(-j)`

例子2贪心的解释:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
package chapter2;
pub1ic class Greedy {
pub1ic static void main(String[] args) {
String s = "\17";
System out. print1n(s);
System. out. print1n("长度"+ s. 1ength0);
s =“\171”;
System out. print1n(s);
System. out. print1n("长度"+ s. 1ength0);
s =“\1717”;
System out. print1n(s);
System. out. print1n("长度"+ s. 1ength0);

s =“\43”;
System out. print1n(s);
System. out. print1n("长度"+ s. 1ength0);
s =“\431”;
System out. print1n(s);
System. out. print1n("长度"+ s. 1ength0);
}
}
1
2
3
4
5
6
7
8
9
10
11
//结果:

长度1
y
长度1
y7
长度2
#
长度1
#1
长度2

因为八进制(0~377),因此””\1717”会解析成”171”和”1”,”431”会解析成”43”和”1”

++ii++仅是“先加”与“后加”的差别吗?

++ii++都是先将变量的值加1。前置++是用增值后的变量进行运算;后置++是先将变量赋值给临时变量,用临时变量去运算。

用javap反编译命令解析程序

1
2
3
4
5
6
7
8
9
10
11
12
13
package chapter2;

public class DeepPlus{
void post(){
int i = 0;
int j = i++;
}

void pre(){
int i = 0;
int j = ++i;
}
}

保存并编译文件,打开控制台,进入class文件的目录(chapter2的上级目录)

1
javap -c chapter2.DeepPlus

javap是反编译命令,-c为显示为代码反编译后的伪指令

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
Compiled from "DeepPlus.java"
public class chapter2.DeepPlus{
public chapter2.DeepPlus();
......
void post();
Code:
//将int类型常量0压入栈,即当前栈顶值为int类型0
0: iconst_0
//从栈顶弹出一个int类型值,然后将该值存储在局部变量1中。这里局部变量1就是
//程序中的变量i,也就是将刚才压入栈的0弹出,赋值给变量i。这两条指令相当于执
//行(int i = 0;)
1: istore_1
//将局部变量1中存储的int类型值压入栈,即将i的值0压入栈。这在程序中就相当
//于将i的值赋值给一个临时变量temp,此时,temp的值为0
2: iload_1
//将局部变量1的值加1。也就是将i的值加1。在程序中,这就相当于(i += 1;)。
//此时,i的值为1
3: iinc 1, 1
//从栈顶弹出一个int类型值,然后将该值存储在局部变量2中。这里局部变量2就是
//程序中的变量j,也就是将刚才压入栈的i值0弹出(i压栈时值还没有加1),赋值
//给变量j。在程序中就相当于(j = temp;)。
6: istore_2
//返回。
7: return
......

void pre();
Code:
//将int类型常量0压入栈,即当前栈顶值为int类型0
0: iconst_0
//从栈顶弹出一个int类型值,然后将该值存储在局部变量1中。这里局部变量1就是
//程序中的变量i,也就是将刚才压入栈的0弹出,赋值给变量i。这两条指令相当于执
//行(inti=0;)。
1: istore_1
//将局部变量1的值加1。也就是将i的值加1。在程序中,这就相当于(i += 1;)。
//此时,i的值为1。注意,前置++在执行1inc指令的时候并没有将i的值压入栈,
//也就是并没有赋值给一个临时变量 。
2: iinc 11
//将局部变量1中存储的int类型值压入栈,即将i的值1压入栈。
5: iload_1
//从栈顶弹出一个int类型值,然后将该值存储在局部变量2中。这里的局部变量2就是
//程序中的变量j,也就是将刚才压入栈的i值1弹出(i压栈时值已经加1),赋值
//给变量j。在程序中就相当于(j= i;)。
6: istore_2
//返回。
7: return
......
}

从指令上说,后置 ++ 在执行增值指令(iinc)前,先将变量的值压入栈,执行增值指令后,使用的是之前压入栈的值。