Java考试常见知识点

数据类型

double

double和float比较特殊。一个典型的题目是

System.out.println(0.9);       // 输出0.9
System.out.println(2.0 - 0.1); // 输出0.8999999999999999
System.out.println(2.0 - 1.1);

原理解析
小数转2进制的方法为: 乘2取整,余数再乘2取整, 直到余数为0;
0.9和0.1,使用2进制表示时,是个无限循环小数 (你自己试试就知道了 );
那么System.out.println(0.9)为什么打印的是0.9, 而不是0.8999999999999999?
可以通过下面的语句打印出1.1的二进制形式

System.out.println(Long.toBinaryString(Double.doubleToLongBits(1.1)));

输出
11111111110001100110011001100110011001100110011001100110011010
double的数据结构 https://blog.csdn.net/wzj_whut/article/details/85225186

可以看出, 末尾由...11001的, 变成了...11010, 应该是执行了4舍5入, 也就是1.1实际上是大了一点点的.
1.100000000000000088817841970012523233890533447265625

System.out.println(new BigDecimal(1.1));

所以2.0-1.1实际上比0.9要小

手动算很困难, 再涉及到四舍五入的问题, 只能根据出题人意图靠猜?

char/String的长度问题

不同于C/C++中的char, java中的一个char占用2个字节.

考试了?!

在java中, 上面这段字符串, 是几个char?
答案是6
1个中文字符为1个char, 1个表情符号为2个char, 1个英语字母为1个char

如果转成utf8字节, 是几个字节?
答案是14
1个中文字符为3个字节, 1个表情符号为4个字节, 1个英语字母为1个字节

字符串常量池

    String s1 = "hello";
    String s2 = "hello";
    String s3 =  new String("hello");
    String s4 = "he";
    String s5 = "he" + "llo";
    String s6 = s4 +"llo";
    String s7 = s6.intern();

    System.out.println(s1==s2); // true 
    System.out.println(s1==s3); // false
    System.out.println(s1==s5); // true
    System.out.println(s1==s6); // false
    System.out.println(s1==s7); // true

数值类型转换

两个不再类型的数值进行二元操作时,
其中一个值是double, 则另一个也会转换成double
否则, 其中一个值是float, 则另一个也会转换成float
否则, 其中一个值是long, 则另一个也会转换成long
否则, 其中一个值是int, 则另一个也会转换成int

控制流

带标签的break语句的作用有

  1. 跳出循环体
  2. 跳出代码块

类的构造器处理步骤

  1. 所有数据域被始化为(0, false, null)
  2. 按照声明次序, 执行域初始化语句和初始化块 .
  3. 如果构造器调用了第二个构造器, 则执行第二个构造器
  4. 执行构造器主体

注意点:
a. 如果初始化代码先声明, 也会先执行初始化块
b. 如果代码块, 域初始化语句, 构造主体都初始了同一个域, 这个值就被初始化多次

不可变类

不可变类有哪些?

  1. Character,Short等基本类型的包装器, String, Optional

继承

  1. java不支持多继承, 且只有公有继承

覆盖

  1. 子类的方法可见性不能低于超类的可见性, 举例:
    如果超类方法是protected, 子类必须是protected或public

  2. 如果超类方法声明抛出异常, 子类抛出的异常不能比超类更通用, 举例
    超类声明抛出了IOException, 子类可以重新声明为抛出FileNotFoundException

  3. 子类无法覆盖超类的域变量, 所谓的覆盖, 只是针对方法

多态

  1. 域变量不存在覆盖问题, 当前对象处于哪一态, 就使用哪一态的域变量, 举例
    class D {
        public int b = 1;
    }

    class E extends D {
        public int b = 2;
    }
    
    E e = new E();
    D d = e;
    System.out.println(e.b);  // 2
    System.out.println(d.b);  // 1

反射

反射的作用有哪些?

  1. 在运行时分析类, 包括域, 方法, 构造器
  2. 在运行时分析对象
  3. 编写泛型数组代码
  4. 调用任意方法

接口/lambda/内部类

接口特性

  1. 接口方法, 包括静态方法, 自动设置为public

  2. 接口中的域自动设置为public static final

  3. 什么是标记接口, 有什么作用?
    不包含方法, 但是可以使用instanceof

lambda

函数式接口: 只有一个方法的接口. (不能是抽象类)
lambda捕获的变量必须是final变量

方法引用

构建器引用

    Arrays.sort(strs, String::compareTo);

    strs.stream().map(String::new); 
    
    // 等同于: 
    strs.stream().map(str -> {
        return new String(str);
    });    

内部类

需要注意的是序列化问题

异常

分类

Erorr和Exception
Exception又分为RuntimeException和其它异常

受检异常/非受检异常:
派生于Error或RuntimeException的异常, 称为非受检异常. (由环境导致的错误)
其它的异常, 称为受检异常. (由于代码疏忽导致的错误)

RuntimeException有哪些

  1. ClassCastException
  2. IndexOutOfBoundsException
  3. NullPointerException

注意: OutOfMemoryError不是RuntimeException

try-finally

如果try和finally中都执行return语句, finally的值会覆盖try中的值

try-resource

  1. try-resource中的try语句会抑制其中的异常
  2. 优先使用try-resource代替try-finally

泛型

真相与约束:

  1. 虚拟机中没有泛型, 因为泛型会被擦除
  2. 不能创建泛型数组
  3. 运行时查询类型, 只能产生原始类型

泛型的通配符

extends, super
规则:
super只允许写, 不允许读. (助记: 如果一个类有好多个super, 都不知道super是哪个类, 读出来是什么类型?)
extends只允许读, 不允许写 (助记. 与super相反, 但是可以写入null)

举例:

    List<Child> list1 = new LinkedList<>();
    list1.add(new Child());
    List<? extends Parent> list2 = list1;

    // extends限定, 只允许读, 不允许写
    // list2.add(new Parent());
    // list2.add(new Child());
    Parent x = list2.get(0);
        
    
     List<Parent> list1 = new LinkedList<>();
     List<? super Child> list2 = list1;

     list2.add(new Child());

     // super限定, 只允许写, 不允许读
     //  Parent x = list2.get(0);
     //  Child y = list2.get(0);       

多线程与死锁

线程的状态有哪些?

新创建, 可运行, 被阻塞, 等待, 计时等待, 被终止

线程被终止有原因有?

  1. 正常退出
  2. 出现未被捕获的异常

线程死锁

典型死锁例子

    class A {
        public synchronized void fa(B b) {
            sleep(500); // 为了100%死锁
            b.call();
        }
        public synchronized void call() {
        }
    }

    class B {
        public synchronized void fb(A a) {
            sleep(500); // 为了100%死锁
            a.call();
        }
        public synchronized void call() {
            System.out.println("B call");
        }
    }
    
    final A a = new A();
    final B b = new B();

    Thread ta = new Thread(new Runnable() {
        @Override
        public void run() {
            a.fa(b);
        }
    });
    Thread tb = new Thread(new Runnable() {
        @Override
        public void run() {
            b.fb(a);
        }
    });
    ta.start();
    tb.start();

    ta.join();
    tb.join();

在两个线程刚启动时, a, b都获取了自己的锁
500毫秒之后, a试图获取b的锁, 但是b已经被锁了, 于a阻塞
b也同样, 最后两个线程都被锁住
死锁线程堆栈分析

"Thread-1" #10 prio=5 os_prio=0 tid=0x15ff0000 nid=0x2714 waiting for monitor entry [0x1614f000]
   java.lang.Thread.State: BLOCKED (on object monitor)
	at 死锁$A.call(死锁.java:19)
	- waiting to lock <0x04e29750> (a 死锁$A)
	at 死锁$B.fb(死锁.java:31)
	- locked <0x04e2b128> (a 死锁$B)
	at 死锁$2.run(死锁.java:57)
	at java.lang.Thread.run(Thread.java:745)

"Thread-0" #9 prio=5 os_prio=0 tid=0x15fed800 nid=0xb38 waiting for monitor entry [0x1607f000]
   java.lang.Thread.State: BLOCKED (on object monitor)
	at 死锁$B.call(死锁.java:34)
	- waiting to lock <0x04e2b128> (a 死锁$B)
	at 死锁$A.fa(死锁.java:16)
	- locked <0x04e29750> (a 死锁$A)
	at 死锁$1.run(死锁.java:51)
	at java.lang.Thread.run(Thread.java:745)

上面显示:
Thread-1已经锁住了0x04e2b128, 然后试图去锁0x04e29750
Thread-2已经锁住了0x04e29750, 然后试图去锁0x04e2b128
很明显是死锁

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值