Java基础知识

Java基础知识

1.Constructor可以重写吗?

Constructor不允许重写@Override,但是可以重载(overload)。


2.重写(@Override) & 重载(overload)

  • 重写@Override:发生在父子类中,函数名、参数列表必须相同,子类返回值范围必须小于等于父类返回值,子类抛出异常范围必须小于等于父类,子类访问修饰符必须大于等于父类

    父类的privatefinalConstrunctor函数不允许被重写

  • 重载(overload):发生在同一类中,函数名必须相同,参数列表、返回值、修饰符可以不同。


3.String & StringBuilder & StringBuffer

  1. 可变性:String是不可变的,StringBuilderStringBuffer都是可变的

    String内部采用final类型的数组存储字符串,所以是不可变的
    StringBuilderStringBuffer内部用于存储字符串的数组都不是final的。

  2. 线程安全性:String由于是不可变的,所以天生线程安全;StringBuilder不是线程安全的;StringBuffer通过同步锁保证了线程安全

  3. 性能:StringBuilderStringBuffer性能要优10%~15%,但是线程不安全;String每次修改是指向新的对象,而StringBuilder则是每次操作原有对象

使用总结

  1. 操作少量字符串:String
  2. 单线程操作大量字符串:StringBuilder
  3. 多线程操作大量字符串:StringBuffer

4.接口(public interface) & 抽象类(abstract class

  1. 接口必须是public的,抽象类除了private都可以

  2. 接口变量必须是finalstatic的,抽象类则不一定

    接口变量必须是static的,但是接口本身不能是static

  3. 一个类可以实现(implements)多个接口,但是只能继承(extends)一个抽象类


5.BIO & NIO & AIO

1>. BIO(Blocking I/O)

同步阻塞I/O,数据的读取写入必须阻塞在一个线程内完成,不能应付高并发。

2>. NIO(Non-Blocking I/O)

同步非阻塞I/O,支持高并发、高负载
应用操作之后直接返回,不会堵塞在那里。

3>. AIO(Asynchronous I/O)

异步非阻塞I/O:应用操作之后直接返回,不会堵塞在那里,当后台处理完成,操作系统会通知相应的线程进行后续的操作

NIOAIO的区别:
NIO是OS有了I/O资源后,通知调用方进行I/O
AIOOS直接进行I/O,将结果通知给调用方

4>. NIOAIO 的区别

  • NIO是同步非阻塞,调用方会一直等待被调用方返回操作结果,而被调用方只有有了操作结果后才会返回。
  • AIO是异步非阻塞,被调用方会直接返回一个收到请求的消息给调用方,然后再通过事件/回调等机制返回操作结果。

    在网上看到了一个很贴切的比喻——烧开水:

1.你妈妈叫你烧开水,你小时候比较笨,坐在开水壶前等水开,这是同步阻塞
2.等你大一点了,知道等水烧开的这段时间可以忙别的事情,你只需要时不时来看看水烧开了没有,这是同步非阻塞
3.后来你们家用上了水烧开会发出声音的壶,这样你就可以去干别的事情了,等到水开了它自己会发出声音提醒你,这就是异步非阻塞


6.线程状态

  1. NEW:初始状态,线程被构建,但是还没有调用start()方法

  2. RUNNABLE:运行状态,Java线程将OS中的RUNNABLERUNNING笼统地称作RUNNABLE

    NEW -> 调用.start() -> RUNNABLE -> 获得时间片 -> RUNNING

  3. BLOCKED:线程阻塞于synchronized

  4. WAITING:等待notify/notifyAll

    wait(),被notify/notifyAll唤醒,线程重新变为RUNNABLE

  5. TIME_WAITING:超时等待notify/notifyAll

    wait(long timeout) / sleep(long timeout),timeout结束后 / 被notify/notifyAll唤醒,线程重新变为RUNNABLE

  6. TERMINATED:终止—— run()执行完毕 / 抛出异常


7.使用常量或有确定值的对象调用equals()

如果通过null调用equals()会导致NullPointerException,所以如果要调用equals(),那么最好是常量,或者对象一定非null

1
2
3
String s = null;
if(s.equals("xxxx") ) {...} //NullPointerException
if("xxxx".equals(s) ) {...} //通过常量调用

推荐直接使用工具类java.util.Objectsequals()方法

1
2
3
if(Objects.equals(s, "xxxx") ){
//...
}

8.通过Integer而非int创建-128~127

当通过Integer创建一个-128~127的整型对象时,该对象会被缓存起来,当下次创建同样的对象,就会直接从缓存取


9.所有的POJO类属性必须使用包装类

POJO类不给初始值,是为了提醒使用者在创建时显式地赋值,防止出现NullPointerException,但是基本类型本身有初始值,所以POJO类属性必须使用包装类,默认是null

试想现有Student类,属性score设置为int,那么没有给它赋值时,score = 0,但是这显然是不对的,这个同学很可能根本没有考,这时它的score应该是null
考了0分,和没考但是默认你0分,是不同的概念。
score应该设置为Integer


10.不要在foreach循环中调用Collection#remove()/add(),但是可以使用Iterator#remove()

  1. foreach循环的迭代器是fail-fast的——如果修改了集合,立即抛出ConcurrentModificationException

    即使是Java8中的.forEach()也不可以:

1
2
3
4
5
6
7
List<String> lists = new ArrayList<>();
lists.add("aaa");
lists.add("bb");
lists.add("c");
lists.forEach(c -> {
lists.remove(c); //立马抛出:java.util.ConcurrentModificationException
});
  1. 虽然不能调用集合的remove()方法,但是可以调用Iterator#remove()——它不会修改集合
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    List list = new ArrayList<>(Arrays.asList("1", "2") );
    Iterator<String> iterator = list.iterator();
    while(iterator.hasNext() ) {
    String item = iterator.next();
    if(...) {
    //错误的删除:list.remove(item);
    //正确的删除:
    iterator.remove();
    }
    }

11.ArrayList和LinkedList有什么区别?

ArrayListLinkedList
底层数据结构动态数组双向链表
中间插入/删除慢(数组整个移动)快(直接拆链、加链)
随机访问支持(数组下标)不支持(只能遍历)
遍历优先for循环,其次才是forEach迭代LinkedList每次遍历都是从头开始的,用for循环效率极其低下,所以优先Iterator迭代(forEach也是迭代)
内存占用动态数组会预留部分内存节点保存前驱和后继,消耗较大

RandomAccess随机访问接口:
RandomAccess源码中只声明了一个空的接口,接口中任何方法都没有。RandomAccess的目的更像是给其实现类一个标识,表示这个类可以实现随机访问,具体如何实现是由实现类的底层数据结构决定的,比如说ArrayList底层是动态数组,所以它本身就可以实现随机访问。


12.ArrayList和Vector有什么区别?

ArrayListVector
线程安全性不安全安全(同步容器类,所有方法都是synchronized,一次只允许一个线程访问,比起并发容器类来说效率不高

13.HashMap、HashTable、ConcurrentHashMap有什么区别?

HashMapHashTableConcurrentHashMap
线程安全不安全安全(同步容器类,大部分方法都是synchronized,一次只允许一个线程访问,一个线程put另一个线程无法get,比起并发容器类来说效率不高安全(并发容器类,JDK8取消了分段锁,改为synchronized锁定首节点 + CAS
是否支持null key支持不支持不支持
数据结构数组 + 链表 + 红黑树数组 + 链表数组 + 链表 + 红黑树
初始容量大小 & 扩容默认16,每次扩容变为原来的2倍,如果指定了容量,将其扩容为2的次方默认11,之后每次扩容变为原来的2n+1倍,如果指定了容量,直接使用给定值

14.HashSet是怎么实现重复检查的?

  • HashSet底层就是HashMapkey是存储元素,value是一个指向对象的指针
  • 加入一个元素时,首先计算其key的hashCode值,判断对应的数组位置是否有元素,同时还会计算其他元素的hashCode值,如果没有相同的,可以假设没有重复元素
  • 如果有相同的hashCode值,那么久调用equals(),判断两个元素是否相等

15.a++++a的区别

  • a++是先使用a,使用完后再a = a + 1
  • ++a是先让a + 1,再使用a的值
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
int a = 10 >> 1;    //a = 5
int b = a++; //先使用a的原值,所以b = 5,然后a + 1, a = 6
int c = ++a; //先让a + 1,a = 7,c = 7
int d = b * a++; //先使用a的旧值,d = 5 * 7 = 35,然后a + 1,a= 8
System.out.println(a); //8
System.out.println(b); //5
System.out.println(c); //7
System.out.println(d); //35

//=================================================================
//i,j均为数组下标

map.add(string.charAt(j++));
//等价于:
//map.add(string.charAt(j));
//j = j + 1;

16.什么是fail-fast机制?它的原理和应用

  • fail-fast(快速失败)是Java集合中的一种机制,在用迭代器遍历一个集合对象时,如果遍历过程中对集合对象的内容进行了修改(增加/删除/修改),将会抛出ConcurrentModificationException异常
  • 原理:集合内容的修改将会改变modCount值,迭代器使用hasNext()/next()遍历下一个元素前,都会检测modCount变量是否为expectedModCount值,如果不是则抛出异常,终止遍历
  • 应用:java.util包下的集合类都是fail-fast的,不能在多线程下并发修改,也不能在迭代过程中被修改,算是一种安全机制。

    j.u.c包下的容器都是fail-safe的,可以在多线程下并发修改、并发使用

-------------本文结束感谢您的阅读-------------

本文标题:Java基础知识

文章作者:DragonBaby308

发布时间:2019年08月15日 - 22:55

最后更新:2020年01月16日 - 22:28

原始链接:http://www.dragonbaby308.com/JB/

许可协议: 署名-非商业性使用-禁止演绎 4.0 国际 转载请保留原文链接及作者。

急事可以使用右下角的DaoVoice,我绑定了微信会立即回复,否则还是推荐Valine留言喔( ఠൠఠ )ノ
0%