2021年09月23日 java1

考点1:this调用语句

This调用语句必须是构造函数中的第一个可执行语句。

  • A 正确
  • B 错误
显示答案/隐藏答案正确答案: B

this()才必须是构造函数中的第一个可执行语句,用this调用语句并不需要。

考点2:成员内部类

内部类(也叫成员内部类)可以有4种访问权限。( )

  • A 正确
  • B 错误
显示答案/隐藏答案正确答案: A

你就把内部类理解成类的成员,成员有4种访问权限吧,内部类也是!分别为private、protected、public以及默认的访问权限

考点3:CallableStatement PreparedStatement Statement

以下描述正确的是

  • A CallableStatement是PreparedStatement的父接口
  • B PreparedStatement是CallableStatement的父接口
  • C CallableStatement是Statement的父接口
  • D PreparedStatement是Statement的父接口
显示答案/隐藏答案正确答案: B

Statement 每次执行sql语句,数据库都要执行sql语句的编译 ,最好用于仅执行一次查询并返回结果的情形,效率高于PreparedStatement.

PreparedStatement是预编译的,使用PreparedStatement有几个好处
a. 在执行可变参数的一条SQL时,PreparedStatement比Statement的效率高,因为DBMS预编译一条SQL当然会比多次编译一条SQL的效率要高。
b. 安全性好,有效防止Sql注入等问题。
c. 对于多次重复执行的语句,使用PreparedStament效率会更高一点,并且在这种情况下也比较适合使用batch;
d. 代码的可读性和可维护性。

CallableStatement接口扩展PreparedStatement,用来调用存储过程,它提供了对输出和输入/输出参数的支持。CallableStatement
接口还具有对 PreparedStatement 接口提供的输入参数的支持。

1
2
public interface CallableStatement
extends PreparedStatement
1
2
public interface PreparedStatement
extends Statement
1
2
public interface Statement
extends Wrapper, AutoCloseable

考点4:多态方法调用

Test.main()函数执行后的输出是( )

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
class Test {
public static void main(String[] args) {
System.out.println(new B().getValue());
}
static class A {
protected int value;
public A (int v) {
setValue(v);
}
public void setValue(int value) {
this.value= value;
}
public int getValue() {
try {
value ++;
return value;
} finally {
this.setValue(value);
System.out.println(value);
}
}
}
static class B extends A {
public B () {
super(5);
setValue(getValue()- 3);
}
public void setValue(int value) {
super.setValue(2 * value);
}
}
}
  • A 6 7 7
  • B 22 34 17
  • C 22 74 74
  • D 11 17 34
显示答案/隐藏答案正确答案: B

始终优先调用子类重写的方法

本题中,始终需要遵循一个原则,即: 调用的方法都是实例化的子类中的重写方法,只有明确调用了super.xxx关键词或者是子类中没有该方法时,才会去调用父类相同的同名方法

这道题的知识点:子类中的方法覆盖父类的方法以后,由于向上转型,父类调用方法的时候是调用子类的,除非用super。
还有一个点就是在Try catch finally 体系当中,在return之前始终会执行finally里面的代码,如果finally里面有return,则数据跟随finally改变。如果没有return,则原数据不跟随finally里改变的数据改变!

main方法中只有一条System.out.println(new B().getValue());语句

先执行new B()

new B()

B类
1
2
3
4
public B () {
super(5);//<--执行这句
setValue(getValue()- 3);
}

super(5);

A类
1
2
3
public A (int v) { //v=5
setValue(v);//v=5
}

因为new的对象是B,执行的是子类重写的setValue():

B类
1
2
3
public void setValue(int value) {//v=5
super.setValue(2 * value);//2*value=2*5=10
}

这里明确使用了super关键字,所以调用父类的setValue方法:

A类
1
2
3
public void setValue(int value) {//value=10
this.value= value;//value=10,this.value=10
}

到这里super(5);算是执行完毕了,继续执行下一句:

B类
1
2
3
4
public B () {
super(5);//<--执行完毕
setValue(getValue()- 3);//<--执行
}

执行 setValue(getValue()- 3);

先执行getValue()方法,B类中并没有有定义getValue()方法,所以调用的是父类的getValue方法:

先执行getValue的try语句块

public int getValue() {
    try {
        value ++;//1. value=10,value++之后value=11
        return value;//2 return 11; 
    } finally {
        this.setValue(value); //3. 当前对象是B类的对象,调用B类的setValue方法,value=11
        System.out.println(value);
    }
}

try块中有return语句,getValue的返回值为11

然后执行finally语句块。

先执行this.setValue(value);

public void setValue(int value) {// value=11
    super.setValue(2 * value);//2 * value=2 *11=22,接着调用父类的setValue方法
}

调用父类的setValue方法

public void setValue(int value) {// value=22
    this.value= value;//this.value=22
}

然后执行System.out.println(value);输出22

getValue()执行完毕,返回值11

1
2
3
4
public B () {
super(5);
setValue(getValue()- 3);//getValue=11,getValue()- 3=11-3=8
}

执行setValue(8),当前对象有setValue方法,调用B类定义的setValue方法:

1
2
3
public void setValue(int value) {//value=8
super.setValue(2 * value);//2 * value=2*8=16
}

执行父类的setValue方法:

public void setValue(int value) {//value=16
    this.value= value;//this.value=16
}

到这里,new B()执行完毕。this.value=16

然后执行new B().getValue()

然后执行new B().getValue()
子类B中没有这个方法,调用父类继承来的getValue方法

先执行try块:

1
2
3
4
5
6
7
8
9
public int getValue() {
try {
value ++;//this.value=16 value++之后 this.value=17
return value;//getValue()返回值是17
} finally {
this.setValue(value);
System.out.println(value);
}
}

然后执行finally块:

1
2
3
4
5
6
7
8
9
public int getValue() {
try {
value ++;//this.value=17
return value;//getValue()返回值是17
} finally {
this.setValue(value);//value=17
System.out.println(value);
}
}

先调用当前类的setValue方法:

1
2
3
public void setValue(int value) {//value=17
super.setValue(2 * value);//2 * value=2 * 14=34
}

调用父类的setValue方法:

public void setValue(int value) {//value=24
    this.value= value;//this.value=34
}

然后执行 System.out.println(value);语句,输出34

到这里new B().**getValue()**已经执行完毕,getValue()返回17.

最后执行最外层的数据语句

1
2
3
public static void main(String[] args) {
System.out.println(new B().getValue());//17
}

输出17

考点5:JRE判断程序执行结束的标准 前台线程 后台线程

jre 判断程序是否执行结束的标准是()

  • A 所有的前台线程执行完毕
  • B 所有的后台线程执行完毕
  • C 所有的线程执行完毕
  • D 和以上都无关
显示答案/隐藏答案正确答案: A

前台线程 后台线程 用户线程 守护线程

其实这个题,就是在说守护线程和非守护(用户)线程的问题。后台线程就是守护线程,前台线程就是用户线程。
守护线程:是指在程序运行时在后台提供一种通用服务的线程,这种线程并不是必须的。同时守护线程的线程优先级都很低的。JVM中的GC线程就是一个守护线程,只要JVM启动,GC线程就启动了。

用户线程和守护线程几乎没有什么区别,唯一的区别就在于,如果用户线程都已经退出了,只剩下了守护线程,那么JVM直接就退出了

下面举个例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
class ThreadDemo extends Thread {
public void run() {
System.out.println(Thread.currentThread().getName() + " : begin");
try {
Thread.sleep(2000);
} catch (Exception e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + " : end");
}
}

public class Test {
public static void main(String[] args) {
System.out.println("test : begin");
Thread t = new ThreadDemo();
t.setDaemon(true);
t.start();
System.out.println("test : end");
}
}

程序输出:

1
2
test : begin
test : end

运行结果中不会有Thread-0 : end,是因为,守护线程开启之后,中间睡了2s,这个时候又没有锁,主线程直接就执行完了,
一旦主线程结束,那么JVM中就只剩守护线程了,JVM直接就退出了,不管你守护线程有没有执行完。

jre判断程序是否执行结束的标准是:所有的前台线程执行完毕。

考点6:自增运算符

以下代码执行的结果显示是多少()?

1
2
3
4
5
6
7
8
9
10
11
public class Demoe {
public static void main(String[] args) {
int count = 0;
int num = 0;
for (int i = 0; i <= 100; i++) {
num = num + i;
count = count++;
}
System.out.println("num * count=" + (num * count));
}
}
  • A num * count = 505000
  • B num * count = 0
  • C 运行时错误
  • D num * count = 5050
显示答案/隐藏答案正确答案: B

count = count++;这个先将count这个值0暂存起来,然后count自加1变成1,最后将暂存的值赋值给count,count最终的值为0

考点7:类的加载顺序:先静态后非静态 先父类后子类

关于下列代码的执行顺序,下面描述正确的有哪些选项()

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public class HelloA {
public HelloA() {
System.out.println("A的构造函数");
}
{
System.out.println("A的构造代码块");
}
static {
System.out.println("A的静态代码块");
}
public static void main(String[] args) {
HelloA a = new HelloA();
}
}
  • A 打印顺序A的静态代码块> A的构造函数
  • B 打印顺序A的静态代码块> A的构造代码块
  • C 打印顺序A的构造代码块> A的构造函数
  • D 打印顺序A的构造函数> A的构造代码块
显示答案/隐藏答案正确答案: ABC

类的加载顺序

1.父类静态代码块
2.子类静态代码块
3.父类构造代码块
4.父类构造函数
5.子类构造代码块
6.子类构造方法

总结:先静态后非静态,先父类后子类

考点8:字符集编码 国际化

在Java语言中,下列关于字符集编码(Character set encoding)和国际化(i18n)的问题,哪些是正确的?

  • A 每个中文字符占用2个字节,每个英文字符占用1个字节
  • B 假设数据库中的字符是以GBK编码的,那么显示数据库数据的网页也必须是GBK编码的。
  • C Java的char类型,通常以UTF-16 Big Endian的方式保存一个字符。
  • D 实现国际化应用常用的手段是利用ResourceBundle类
显示答案/隐藏答案正确答案: CD

解析

A 显然是错误的,Java一律采用Unicode编码方式,每个字符无论中文还是英文字符都占用2个字节
B 也是不正确的,不同的编码之间是可以转换的,通常流程如下:
将字符串S以其自身编码方式分解为字节数组,再将字节数组以你想要输出的编码方式重新编码为字符串
例:

GBK字符串转UTF-8字符串

1
String newUTF8Str = new String(oldGBKStr.getBytes("GBK"), "UTF8");

C 是正确的。Java虚拟机中通常使用UTF-16的方式保存一个字符
D 也是正确的。ResourceBundle能够依据Local的不同,选择性的读取与Local对应后缀的properties文件,以达到国际化的目的。
综上所述,答案是 C 和 D。

考点9:字符流 字节流

character流和byte流的区别不包括()

  • A 每次读入的字节数不同
  • B 前者带有缓冲,后者没有。
  • C 前者是字符读入,后者是字节读入。
  • D 二者没有区别,可以互换。
显示答案/隐藏答案正确答案: ABD

字符流和字节流每次读入的字节数是不确定的,可能相同也可能不相同;
字符流和字节流都有缓冲流