华清远见-重庆中心-JAVA高级阶段总结

华清远见-重庆中心-JAVA高级阶段总结

1、Java容器都有哪些?

一、什么是容器?

容器就是用来存放和管理对象的对象。

二、容器有哪些?

  • 数组

数组是一种典型的容器,但是数组的容量是有限的。

  • Java.Util下的集合容器

    • Collection接口:List、Set、、Queue接口

    • Map接口

    • Iteror迭代器

2、Collection 和 Collections 有什么区别?

  • Collection是一个集合接口。它提供了对集合对象进行基本操作的通用接口方法。Collection接口在Java类库中有很多具体的实现。Collection接口的意义是为各种具体的集合提供最大化的统一操作方式。

  • Collections是一个包装类。它包含各种有关集合操作的静态多态方法。此类不能实例化,就像一个工具类,服务于Java的Collection框架

3、List、Set、Map 之间的区别是什么?

  • List和Set都是单列集合,继承自Collection接口,Map是双列集合,是键值对的形式,独立接口
  • List是有序集合,Set和Map都是无序集合,List集合的元素可以重复,Set和Map的键不能重复,但是Map的值可以重复(注意:如果在Map中添加重复的键,那么改建对应的值会被覆盖)
  • List的实现类有:LinkedList,ArratList,Vector。
    • Vector的底层逻辑和ArrayList一样都是动态数组,因此查询的效率高,添加删除的操作效率低,相比ArrayList,Vector在单线程中更加安全,但是效率上就会降低
  • Set的实现类有HashSet,TreeSet,LinkedHashSet。这三个类都是不允许重复的,但是只有HashSet是无序的,TreeSet和大小顺序有关,LinkedHashSet和插入顺序有关

4、 HashMap 和 Hashtable 有什么区别?

(1)hash值不同

  • HashMap:重新计算Hash值
  • HashTable:直接使用对象的hashCode

(2)两个遍历方式的内部实现不同

  • HashTable、HashMap两者都是使用了Iterator,但是,HashTable除了使用了Iteratoer之外,还使用了Enumeration

(3)是否提供contians方法

  • HashTable:HashTable和HashMao不同,它保留了contains,containsValue以及conrainsKey3个方法
  • HasMap:它去掉了HashTable的contains方法,改为了containsKey和containsValue

(4)内部实现使用的数组初始化扩容方式不同

  • HashMap:在不指定容量的情况下默认容量为16;要求一定为2的整数次幂;扩容时,将容量变为原来的2倍

  • HashTable:在不指定容量的情况下的默认容量为11;不要求底层数组的容量一定要为2的整数次幂;扩容时将容量变为原来的2倍加1。

  • HashTable中hash数组默认大小是11,增加的方式是old*2+1

(5)key和value是否允许null值

  • HashTable:key和value都不允许出现null值
  • HashMap:null能够作为键,这样的键只有一个,能够有一个或者是多个键所对应的值为null

(6)线程安全性不同

  • HashMap:在缺省情况下是非Synchronize的;使用HashMap的时候就需要自己增加同步处理;HashMap是线程不安全的
  • HashTable:Synchronize在多线程并发的情况下,能够直接使用HashTable,不要自己为它的方法实现同步

(7)继承的父类不同

  • HashMap:继承AbstractMap类
  • HashTable:继承Dictionary类

5、如何决定使用 HashMap 还是 TreeMap?

  • TreeMap<K,V>的Key值是要求实现java.lang.Comparable,所以迭代的时候TreeMap默认是按照Key值升序排序的;TreeMap的实现是基于红黑树结构。适用于自然顺序或自定义顺序遍历键(Key)

  • HashMap<K,V>的Key值实现散列hashCode(),分布是散列的、均匀的,不支持排序;数据结构主要是桶(数组),链表或红黑树。使用于在Map中插入、删除和定位元素

结论

如果需要得到一个有序的结果时就应该使用TreeMap(因为HashMap中元素的排列顺序是不固定的)。除此之外,由于HashMap有更好的性能,所以大多不需要排序的时候我们会使用HashMap

6、 说一下 HashMap 的实现原理?

HashMap 在底层将 key-value 当成一个整体进行处理,这个整体就是一个 Entry 对象。HashMap 底层采用一个 Entry[] 数组来保存所有的 key-value 对,当需要存储一个 Entry 对象时,会根据hash算法来决定其在数组中的存储位置,在根据equals方法决定其在该数组位置上的链表中的存储位置;当需要取出一个Entry时,也会根据hash算法找到其在数组中的存储位置,再根据equals方法从该位置上的链表中取出该Entry

7、说一下 HashSet 的实现原理?

  • (1)是基于HashMap实现的,默认构造函数是构建一个初始容量为16,负载因子为0.75的HashMap。封装了一个HashMap对象来存储所有的集合元素,所有放入HashSet中的集合元素实际上由HashMap的Key来保存,而HashMap的Value则存储了一个PRESENT,它是一个静态的Object对象。
//Dummy value to associate with an Object in the backing Map
private static final Object PRESEN = new Object();
  • (二)当我们试图把某个类的对象当成HashMap的Key,或试图将这个类的对象放入HashSet中保存时,重写该类的equals(Objec obj)方法和HashCode()方法很重要,而且这两个方法的返回值必须保持一致:当该类的两个HashCode()返回值相同时,它们通过equals()方法比较也应该返回true。通常来说,所有计算hashCode()返回值的关键属性,都应该用于作为equals()比较的标准

8、ArrayList 和 LinkedList 的区别是什么?

  • 相同点:

都是List接口下的实现类,都是单列集合;都是线程不安全的。

  • 不同点:

(1)数据结构实现:ArrayList 是动态数组的数据结构实现,而 LinkedList 是双向链表(凡是出现linked都是基于双向链表的)的数据结构实现。数组大小固定,不能动态拓展。 内存空间要求高,必须有足够的连续内存空间。数组可能浪费内存空间,容量存满的时候再添加元素需要扩充,没存满又造成部分内存空间的浪费;而链表不存在长度固定,也不需要扩容,内存空间利用率高,但是每个元素都有前驱节点和后驱节点的空间消耗。

(2)随机访问效率:对于随机访问的get(index)和set(index),ArrayList要优于LinkedList,因为LinkedList要移动指针.而数组可以直接通过数组名【index】获取或者设置元素。

(3)增加和删除效率:在非首尾的增加和删除操作,LinkedList 要比 ArrayList 效率要高,因为 ArrayList 中间和头部增删,会涉及数组元素位置的移动,效率比较低。

(4)内存空间占用:LinkedList 比 ArrayList 更占内存,因为 LinkedList 的节点除了存储数据,还存储了两个引用,一个指向前一个元素,一个指向后一个元素,数据量大的话会占用不少内存空间。

综合来说,在需要频繁读取集合中的元素时,更推荐使用 ArrayList,而在插入和删除操作较多时,更推荐使用 LinkedList。

9、如何实现数组和 List 之间的转换?

  • 数组转list

    • 遍历

    注意:

    在数组转list过程中,如果数组是8大基本数据类型,将数据类型转换为对应的封装类,避免转换为list后存储元素是数组而不是数组的元素

    int[] arr = {2.0,4,9};
    ArrayList<Object> list = new ArrayList();
    for(int arry : arr){
        list.add(arry);
    
    • Arrays.asList()方法(有局限)

    数组如果是原始数据类型,需要转为封装类,如int转为Integer

    如果使用原始数据类型直接使用,输出的是地址,因为list集合存放的是数组

    int[] arr = {2,0,4,9};
    //集合封装的是Integer
    List<Integer> integers = Arrays.asLister(arr);
    Arrays.asList(arr).forEach(System::println);
    

    注意:通过Arrays.aList()方式,将数组转换为Lsit后,不能对Lsit增删,只能查改,否则抛异常

    • Collections.addAll()
    Integer[] arr = {2,0,5,3,6};
    	List<Integer> collect  = Arrays.stream(arr).collect(Collectors.toList());
    	collect.forEach(System.out::println);
    
    • Java8 stream流

    可以使用流stream来将下列数组转为Lsit,分别是int[],long[],double[],其他short[]、byte[]、char[],在JDK1.8中暂不支持

    Integer[] arr = {2,5,4,7,8,9};
    	List<Integer> collect = Arrays.stream(arr).collect(Collectors.toList());
    	collect.forEach(System,out::println);
    
  • list转数组

    • toArray()

    直接使用toArray()不带参方法但是其返回值类型Obfject,在进行强转的时候会报出java.lang.ClassCastException类转换异常

    ArrayList<String> list = new ArrayList();
    	list.add("q");
    	String[] objects = (String[])list.toArray();
    	for(String object : objects){
            System.out.println(object);
        }
    
    • toArray(T[] a)(推荐)
    ArrayList<String> list = new ArrayList();
    	list.add("q");
    	String[] strings = new String[list.size()];
    	String[] string1 = list.toArray(strings);
    System,out.prinln(Arrays.toString(Strings1));
    

10、 Array 和 ArrayList 有何区别?

  • 存储类型不同

    Array:只可存储基本数据类型和对象

    ArrayList:只能存储对象

  • 大小不同

    Array:被设置为固定大小

    ArrayList:是一个可变数组,大小可自动调整

  • 对象所包含的方法不同

    Array:所包含的方法没有ArrayList多

    ArrayList有很多操作方法:addAll、removeAll、iteration等

11、哪些集合类是线程安全的?

线程安全是确保类的内部状态以及从方法返回的值在多个线程并发调用时是正确的类。Java中线程安全的集合类有:Stack、Vector、Properties、Hashtable等

12、迭代器 Iterator 是什么?

(1)迭代器模式,是Java中常用的设计模式之一。可以用于顺序访问集合对象的元素,而不必知道结合对象的底层实现。

(2)Iterator是可以遍历集合的对象,为各种容器提供了公共的操作接口,隔离对容器的遍历操作和底层实现,从而实现解耦。

13、 Iterator 怎么使用?有什么特点?

用法:

(1)Iterator()要求容器返回一个Iterator。Iterator将准备好返回序列的第一个元素。
(2)使用next()获得序列中的下一个元素
(3)使用hasNext()检查序列中是否还有元素。
(4)使用remove()将迭代器新近返回的元素删除。

特点:

(1) Iterator遍历集合元素的过程中不允许线程对集合元素进行修改,否则会抛出ConcurrentModificationEception的异常。
(2)Iterator遍历集合元素的过程中可以通过remove方法来移除集合中的元素,删除的是上一次Iterator.next()方法返回的对象。
(3)Iterator必须依附于一个集合类对象而存在,Iterator本身不具有装载数据对象的功能。
(4)next()方法,该方法通过游标指向的形式返回Iterator下一个元素。

14、 Iterator 和 ListIterator 有什么区别?

1、Iterator 可遍历 Set 和 List 集合; ListIterator 只能遍历 List。

2、Iterator 只能单向遍历;ListIterator 可双向遍历(向前/后遍历)。

3、ListIterator 继承自 Iterator 接口,添加新功能,比如添加一个元素、替换一个元素、获取前面或后面元素的索引位置

15、怎么确保一个集合不能被修改?

使用 Collections.unmodifiableCollection(Collection c) 方法来创建一个只读集合,这样改变集合的任何操作都会抛出 Java. lang. UnsupportedOperationException 异常;从而确保集合不会被修改。

16、线程和进程的区别?

  • 从属关系不同

    进程是正在运行程序的实例,进程包含了线程,而线程不能包含进程

  • 描述侧重点不同

    进程是操作系统分配资源的基本单位,而线程是操作系统调度的基本单位

  • 共享资源不同:

    多个进程间不能共享资源,每个进程有自己的堆、栈、虚存空间(页表)、文件描述符等信息,而线程可以共享进程资源文件(堆和方法区)

  • 上下文切换速度不同

    线程上下文切换速度很快(上下文切换指的是从一个线程切换到另一个线程),而进程的切换速度比较慢

  • 操纵者不同

    一般情况下进程的操纵者是操作系统,而线程的操纵者是变成人员

17、创建线程有哪几种方式?有什么区别?

  • 继承Thread类,重写run方法

    • 创建一个类,继承Thread类

    • 重写Thread类中的run()方法

    • 创建自定义的线程子类对象后,调用start()方法

  • 实现Runnable接口

    • 1.自定义一个类,实现Runnable接口
    • 2.重写run()方法,将多线程要执行的内容写在该方法中
    • 3.创建Runnable接口的实现类对象
    • 4.使用构造方法Thread(Runnable target)或Thread(Runnable target,String name)将上一步创建 的Runnable实现类对象包装为Thread对象
  • 使用匿名内部类

    • 如果不想创建一个Runnable接口的实现类,就可以使用匿名内部类充当Runnable接口的实现类

18、说一下 runnable 和 callable 有什么区别?

相同点:

1、两者都是接口

2、两者都需要调用Thread.start启动线程

不同点:

1、如上面代码所示,callable的核心是call方法,允许返回值,runnable的核心是run方法,没有返回值

2、call方法可以抛出异常,但是run方法不行

3、因为runnable是java1.1就有了,所以他不存在返回值,后期在java1.5进行了优化,就出现了callable,就有了返回值和抛异常

4、callable和runnable都可以应用于executors。而thread类只支持runnable

19、在 Java 程序中怎么保证多线程的运行安全?

(1)使用手动锁lock

(2)使用线程安全的类

(3)使用自动锁synchronized关键字

(4)使用volatile关键字

20、什么是死锁?

定义两个线程类,线程A先获取资源A后,在获取资源B;线程B先获取资源B后,再获取资源A。 如果对资源A和资源B使用了synchronized进行同步,就会在线程A获取资源A的时候,线程B无法获取资 源A,相反线程B在获取资源B的时候,线程A无法获取资源B,所以两个线程都不会得到另一个资源。

21、多线程产生死锁的 4 个必要条件

(1)互斥条件:一个资源同时只能被一个线程所使用.
(2)请求与保持条件: 例如请求第二把锁的时候, 保持自身的第一把锁不去释放.
()3不剥夺条件: 进程已获得的资源(例如已经获得的锁),没有外界的力量来去剥夺这把锁.
(4)循环等待条件: 两个线程时, 是你等我释放锁, 我等你释放锁. 多个线程时, 是头尾相接的等待

22、 怎么防止死锁?

(1)让两个线程获取资源的顺序保持一致。

(2)让两个线程在获取资源A和B之前,再获取第三个资源,对第三个资源使用synchronized进行同步,这样 某个线程在获取第三个资源后,将后续内容执行完毕,其他线程才能开始执行。

23、synchronized 和 Lock 有什么区别?

(1)Synchronized 是Java的一个关键字,而Lock是java.util.concurrent.Locks 包下的一个接口;

(2)Synchronized 使用过后,会自动释放锁,而Lock需要手动上锁、手动释放锁。

(3)Lock提供了更多的实现方法,而且 可响应中断、可定时, 而synchronized 关键字不能响应中断;

(4)synchronized关键字是非公平锁,即,不能保证等待锁的那些线程们的顺序,而Lock的子类ReentrantLock默认是非公平锁,但是可通过一个布尔参数的构造方法实例化出一个公平锁;

(5)synchronized无法判断,是否已经获取到锁,而Lock通过tryLock()方法可以判断,是否已获取到锁;

(6)Lock可以通过分别定义读写锁提高多个线程读操作的效率.

(7)二者的底层实现不一样:synchronized是同步阻塞,采用的是悲观并发策略;Lock是同步非阻塞,采用的是乐观并发策略(底层基于volatile关键字和CAS算法实现)

24、 sleep() 和 wait() 有什么区别?

相同点:
sleep()和wait()都可以暂停线程的执行。

不同点:

  • sleep()是Thread类的静态方法
  • wait()是Object类的方法
  • sleep()是不释放锁的
  • wait()是释放锁的
  • sleep()常用于一定时间内暂停线程执行
  • wait()常用于线程交互和通信
  • sleep()方法睡眠指定时间之后,线程会自动苏醒
  • wait()方法被调用后,可以通过notify()或nitifyAll()来唤醒wait的线程

25、对象的四种引用?

(1)强引用

String str = new String("hello world");

(2)软引用

Java 中所有的引用都是 Reference 的子类,它是个抽象类,软引用对应的实现类是 SoftReference。我们可以将一个对象作为参数来创建 SoftReference 对象,这样就可以将这个对象的引用指定为软引用了。然后可以通过 SoftReference 对象的 get() 方法来获取传入的对象。

String str = new String("Hello World");
Reference<String> softReference = new SoftReference<>(str);
System.out.println("softReference.get--->" + softReference.get());

(3)弱引用

弱引用跟软引用的区别的在于,软引用只会在内存空间不足时 GC 才会回收,而弱引用的话只要 GC 扫描到该引用所在内存区域,无论内存空间是否充足都会回收。

(4)虚引用

虚引用又称幽灵引用、影子引用,它无法通过 get 方法获取到实例,如果一个对象仅持有虚引用,那它跟没有引用一样。虚引用主要跟 ReferenceQueue 一起搭配使用,用来跟踪对象被 GC 回收。

26、什么是 Java 序列化?什么情况下需要序列化?

序列化就是把Java对象存储在某一地方(硬盘、网络),也就是将对象的内容进行流化

反序列化:就是把二进制数据反序列化成对象数据

当 Java 对象需要在网络上传输 或者 持久化存储到文件中时,就需要对 Java 对象进行序列化处理。

序列化的实现:类实现 Serializable 接口,这个接口没有需要实现的方法。实现 Serializable 接口是为了告诉 jvm 这个类的对象可以被序列化。

注意:

  • 某个类可以被序列化,则其子类也可以被序列化
  • 声明为 static 和 transient 的成员变量,不能被序列化。static 成员变量是描述类级别的属性,transient 表示临时数据
  • 反序列化读取序列化对象的顺序要保持一致
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值