JAVA基础02

1、请你说说ArrayList和LinkedList的区别
        ArrayList的实现是基于数组,LinkedList的实现是基于双向链表,对于随机访问,ArrayListt要优于LinkedList,ArrayList可以根据下标以O(1)时间复杂度对元素进行随机访问,
     而LinkedList需要依靠遍历查找,时间复杂度为O(n),对于插入和删除操作,LinkedList要优于ArrayList,LinkedList在存储相同数量元素情况下,所占内存要比ArrayList高.
2、请你谈谈对HashMap的理解
        在JDK8中,HashMao底层是采用“数组+链表+红黑树”来实现的,HashMap是基于哈希算法来确定元素的索引位置的,向集合中存入数据时,它会计算传入的key的哈希值,
     并利用哈希值取余来确定槽的位置,如果元素发生碰撞,也就是这个槽不为空,则HashMap会通过链表将这些元素组织起来,如果链表长度达到了8,则查找效率已经算慢了,则HashMap会
     创建红黑树来代替这个链表,从而提高查找速度.HashMap中,数组的默认初始容量为16,这个容量会以2的指数进行扩容,具体来说,当数组中的元素达到一定比例的时候,HashMao就会扩容
     而这个比例就是扩容因子,默认为0.75.自动扩容机制,是为了保证HashMao初始时不必要占据太大的内存,而在使用期间又可以实时保证有足够大的空间.采用2的指数进行扩容,是为了利用位运算,
     提高扩容运算效率.
     put()流程:(1)、判断数组,若发现数组为空,则进行首次扩容 
                           (2)、判断头节点,若发现头节点为空,则新建链表节点,存入数组
                           (3)、判断头节点,若发现头节点非空,则将元素插入槽内
                                (1)、若元素的key与头节点一致,则直接覆盖头节点
                                (2)、若元素为树型节点,则将元素追加到树中
                                (3)、若元素为链表节点,则将元素追加到链表中,追加后需要判断链表长度以决定是否转为红黑树.若链表长度达到8,数组容量
                                            未达到64,则扩容,若链表长度达到8,数组容量达到64,则转为红黑树.
                           (4)、插入元素后,判断元素的个数,若发现超过阈值,则进行首次扩容
    HashMap的扩容机制:1、如果数组为空,则进行首次扩容.
                                       2、将元素接入链表后,如果链表长度达到8,并且数组长度小于64,则扩容
                                       3、添加后元素后若数组中元素超过阈值,即比例超出负载因子,则扩容,并且每次扩容时都是将容量翻倍,即创建一个2倍大的新数组,然后将
                                            旧数组中的元素前移到新数组中,由于HashMao中数组的容量为2^n,所以可以利用位运算来计算新容量,效率较高.
    隐患:HashMap是非线程安全的,在多线程环境下,多个线程同时触发HashMap的改变时,有可能会发生冲突,所以在多线程环境下不建议使用HashMap,可以考虑
              使用ConcurrentHashMap.
3、请你说说==与equals()的区别
     (1)、==和equals()都是java中判断两个变量是否相等的方式,如果判断的是两个基本类型的变量,且两者都是数值类型(不一定要求数据类型完全相同),只要
                 两个变量的值相等就会返回true.
     (2)、对于两个引用类型变量而言,只有当他们指向同一个引用时,==才会返回true,==不能用于比较类型上没有父子关系的两个对象.
     (3)、equals()是object类提供的一个实例方法,所以所有的引用变量都能调用equals()方法来判断是否与其他引用变量相等,而equals()同样也要求两个引用变量指向同一个对象
                 才会返回true,但如果是这样的话,equals()也就失去了意义,因此,一般根据业务需求来重写equals()方法,例如String类型对象重写equals()就是判断字符串值是否相同.
4、通过反射可以实现什么
     (1)、java程序中,许多对象在运行时都会有编译时异常和运行时异常两种异常,例如多态情况下Car c=new HANMA();这行代码运行时会生成一个c变量,在编译时该变量的类型
                是Car,而运行时该变量类型是HANMA,另外还有更极端的情况,例如程序在运行时收到了外部传入的一个对象,这个对象的编译时类型是object类型,但程序又需要调用
                这个对象运行时类型的方法,这种情况下有两种解决方案,第一种做法是假设在编译时和运行时都完全知道类型的具体信息,在这种情况下,可以先使用instanceof运算符进行
                判断,再利用强转将其转为其运行时类型的变量.第二种做法是编译时根本无法预知该对象和类可能属于那些类,程序只依靠运行时信息来发现该对象和类的真实信息,这就必须
                使用反射.
     (2)、反射可以实现:1、程序运行时,可以通过反射创建任意一个类的实例,并访问该实例的成员.
                                        2、程序运行时,可以通过反射机制获得任意一个类的class对象,并通过这个对象查看这个类的信息.
                                        3、程序运行时,可以通过反射机制生成一个类的动态代理类或动态代理对象.
     (3)、反射的使用场景:1、使用JDBC时,如果要创建数据库的链接,则需要先通过反射机制加载数据库的驱动程序->Class.forName();
                                            2、多数框架都支持注解/XML配置,从配置中解析出来的类时字符串,需要利用反射机制实例化;
                                            3、面向切面编程AOP的实现方案,是在程序运行时创建目标对象的代理类,这必须由反射机制来实现;
5、请你说说ConcurrentHashMap
     (1)、在jdk8中,ConcurrentHashMap的底层数据结构与HashMap一样,也是采用“数组+链表+红黑树”的形式.同时,它又采用锁定头节点的方式降低了锁粒度,以较小的性能代价实现了
                 线程安全,底层数据结构与HashMap一样.
     (2)、其线程安全实现机制:1、初始化数组或头节点时,ConcurrentHashMap并没有加锁,而是CAS的方式进行原子替换,即原子性操作.
                                                   2、插入数据时会进行加锁处理,但锁定的不是整个数组,而是槽中的头节点.所以ConcurrentHashMap中锁的粒度是槽,而不是整个数组,并发性能较好.
                                                   3、扩容时会进行加锁处理,锁定的仍是头节点,并且支持多个线程同时对数组扩容,提高并发能力,每个线程需先以CAS操作抢任务,争抢一段连续槽位的数据转移
                                                        权,抢到任务后,该线程会锁定槽内的头节点,然后将链表或书中的数据迁移到新的数组里.
                                                   4、查找数据时并不会加锁,所以性能很好,另外在扩容的过程中,依然可以支持查找操作,如果某个槽还未进行迁移,则直接可以从旧数组里找到数据.
                                                        如果某个槽已经迁移完毕,但整个扩容还没结束,则扩容线程会创建一个转发节点存入旧数组,届时查找线程根据转发节点的提示,从新数组中找到目标数据.

     (3)、加分项:ConcurrentHashMap实现线程安全得难点在于多线程并发扩容,即当一个线程在插入数据时,若发现数组正在扩容,那么它就会立即参与扩容操作,完成扩容后再插入数据
                             到新数组中.在扩容的时候,多个线程共同分担数据迁移任务,每个线程负责的迁移数量是(数组长度>>3)/CPU核心数,也就是说,为线程分配的迁移任务,是充分考虑了硬件的
                             处理能力的,多个线程依据硬件的处理能力,平均分摊一部分槽的迁移任务.另外,如果计算出来的迁移数量<16,则强制将其改为16,这是考虑到目前服务器领域主流的CPU运行速度
                             每次处理的任务过少,对于CPU的算力也是一种浪费.
                             
5、请你谈谈对Threadlocal的理解
        1、Threadlocal即线程变量,它将需要并发访问的资源复制多分,让每个线程拥有一份资源.由于每个线程都拥有自己的资源副本,从而也就没有必要对该变量进行同步.Threadlocal提供了线程安全
             的共享机制.在实现上,Thread类声明了threadLocals变量,用于存放当前线程独占的资源,Threadlocal类中定义了该变量的类型ThreadlocalMap,这是一个类似于Map的结构,用于存放键值对
             Threadlocal类中还提供了set和get方法,set方法会初始化ThreadlocalMap并将其绑定在Thread.threadLocals,从而将传入的值绑定到当前线程,在数据存储上,传入的值将作为键值对的value
             而key则是Threadlocal本身,get方法没有提供任何参数,它会以当前Threadlocal对象为key,从Thread.threadLocals中获取与当前线程绑定的数据.
        2、加分点:Threadlocal不能代替同步机制,两者面向的问题领域不同,同步机制是为了同步多个线程对相同资源的并发访问,是多个线程之间进行通信的有效方式.而Threadlocal是为了隔离多个线程
                           的数据共享,从根本上避免多个线程之间对共享资源的竞争,也就不需要同步机制了,一般情况下,如果多个线程之间需要共享资源,以达到线程之间的通信功能,就使用同步机制.如果仅仅
                           需要隔离多个线程之间的共享冲突,则可以使用Threadlocal.
6、请你谈一谈final关键字
        1、final关键字可以用来标志其修饰的类、方法、变量不可变
           (1)、当final修饰类时,该类不能被继承,例如java.lang.Math类就是一个final类,它不能被继承.
           (2)、final修饰的方法不能被重写,final修饰的变量不可被改变,一旦有了初始值,该final变量的值就不能被重新赋值,例如String字符串.
           (3)、final既可以修饰成员变量(包括类变量和实例变量),也可以修饰局部变量、形参.
        2、加分项:
           (1)、对于final修饰的成员变量而言,一旦有了初始值就不能被重新赋值,如果既没有在定义成员变量时指定初始值,也没有再初始化块,构造器中为成员变量指定初始值,那么这个成员变量的值就
                      为默认值(0,0.0,false,null)等.那么这个成员变量就失去了存在的意义,所以java语法规定:final修饰的成员变量必须显示指定初始值.
           (2)、final修饰的实例变量要么在定义该实例变量时指定初始值,要么在普通初始化块或构造器中为该实例变量指定初始值.
           (3)、final修饰的类变量,要么在定义该变量时指定初始值,要么在静态初始化块中指定初始值.
           (4)、实例变量不能在静态初始化块中指定初始值,因为静态初始化块是静态成员,不可以访问实例变量.
           (5)、类变量不能再普通的初始化块中指定初始值,因为类变量在类初始化阶段就已经被初始化了,普通的初始化块不能为其重新赋值,
 7、请你说说重载和重写的区别,构造方法能不能重写
        1、重载要求发生在同一个类中,多个方法之间方法名相同且参数列表不同,需要注意的是,重载与方法的返回值以及访问修饰符无关.
        2、重写发生在父子类中,若子类想和父类方法构成重写关系,则它的方法名、参数列表必须与父类方法相同.另外,返回值类型要<=父类返回值类型,抛出的异常类型要<=父类,访问修饰符则要>=父类
          (public>default>procted>private),但若父类为private,则子类不能重写.
        3、同类中有多个构造器,多个构造器参数列表不同即为构造器重载,构造器重载让java类包含了多个初始化逻辑,从而允许使用不同的构造器来初始化对象.但构造方法不能重写,因为构造方法需要和类
             保持同名,如果允许重写构造方法的话,那么子类中将会存在与类名不同的构造方法,这与构造方法的要求是矛盾的,父类方法和子类方法也有可能会发生重载,因为子类会获得父类的方法,如果子类
             中定义了一个与父类名字相同但参数列表不同的方法,就会新城子类方法和父类方法的重载.(注:private修饰的父类方法不会形成重载).
8、请你谈谈对java集合的理解
        1、java中的集合类分为四大类,分别由四个接口来代表,它们是Set、List、Queue、Map.其中Set、List、Queue都是继承自Collection接口,这些实现类和接口都位于java.util包下
           (1)、Set代表无序的、元素不可重复的集合,其实现类有HashSet、TreeSet
           (2)、List代表有序的,元素可重复的集合,其实现类有ArrayList
           (3)、Queue代表先进先出的队列,其实现类有ArrayDeque
           (4)、Map代表具有映射关系(key-value)的集合,其实现类有HashMap、TreeMap
            注:以上的实现类大多非线程安全,但性能较好,若需要使用线程安全的集合类,则可以使用Collections工具类中的synchronizedXXX()方法,可以将集合类包装成线程安全的集合类.
                   java.util包下的集合类中,也有少数的线程安全的集合类,例如Vector、Hashtable,它们都是非常古老的API,虽然是线程安全的,但性能很差,已经不推荐使用了.从jdk1.5开始,
                  并发包下新增了大量高效的容器,这些容器按照实现机制可以分为三类:1、第一类是以降低锁粒度来提高并发性能的容器,它们的类名以Concurrent开头,如ConcurrentHashMap.
                                                                                                                             2、第二类是采用写时复制技术实现的并发容器,它们的类名以CopyOnWrite开头,如CopyOnWriteArrayList.
                                                                                                                             3、第三类是采用Lock实现的组赛队列,内部创建了两个Condition分别用于生产者和消费者的等待,
                                                                                                                                  这些类都实现了BlockingQueue接口,如ArrayBlockingQueue.
9、请你讲一下java8的新特性
        java8是一个拥有丰富特性的版本,新增了很多特性:
         (1)、Lambda表达式:该特性可以将功能视为方法参数或者将代码视为数据,使用Lambda表达式可以更简洁的表示单方法接口(称为功能接口)的实例,例如创建线程方式
                     ( new Thread(()->{System.out.println("dsada");}).start();).
         (2)、方法引用:方法引用提供了非常有用的语法,可以直接引用已有java类或对象的方法或构造器,例如mybatis-plus的LambdaQueryWrapper类,在附加查找条件时就可以
                       使用方法引用来使用模型对象的方法,避免字段书写错误.方法引用与Lambda联合使用,方法引用可以使语言的构造更紧凑简洁,减少冗余代码.
         (3)、java8对接口进行了改进:允许在接口中定义默认方法,默认方法必须使用default修饰.
         (4)、新增的API:1、Stream API:支持对元素流进行函数式操作,Stream API集成在Collections API中,可以对集合进行批量操作,例如 List<Integer> list=new ArrayList<>();
                                             list.stream().count();对集合中的元素进行求和.
                                        2、Date Time API:加强对日期与时间的处理.
10、请你说说HashMap和Hashtable的区别
          HashMap和Hashtable都是典型的Map实现,它们的区别在于是否线程安全,是否可以存入null值.
        (1)、Hashtable在实现Map接口时保证了线程安全性,而HashMap则是非线程安全的,所以Hashtable的性能不如HashMap,因为为了保证线程安全,它牺牲了一些性能.
        (2)、Hashtable不允许存入null,无论是以null作为key或value都会引起异常,而HashMap是允许存入null的,无论是以null作为key或value都是可以的.
        (3)、虽然Hashtable是线程安全的,但仍不建议在多线程环境下使用Hashtable,因为它的同步方案还不成熟,性能较差,不推荐使用,建议使用ConcurrentHashMap.
11、HashMap是线程安全的吗?如果不是该如何解决?
         1、HashMap是非线程安全的,在多线程环境下,多个线程同时触发HashMap的改变时,可能会发生冲突,导致数据丢失或错乱,所以在多线程情况下建议不适用HashMap.
         2、若想要使用线程安全的HashMap,则有三种方法:
              (1)、使用Hashtable,但不建议.
              (2)、使用Collections将HashMap包装成线程安全的HashMap,Collections类中的synchronizedMap()方法可以将传入的Map包装成线程同步的Map,除此之外,
                          Collections还提供了以下三类方法来返回一个不可变的集合,这三类方法的参数是原有 集合的对象,返回的是该集合的“只读”版本.通过Collections提供的三类方法,
                         可以生成“只读”map.
                         例如emptyMap(): 返回一个空的不可变的Map对象
                               singletonMap():返回一个只包含指定键值对的不可变的Map对象.
                               unmodifiableMap():返回指定的Map对象的不可变视图.
             (3)、ConcurrentHashMap是线程安全且搞笑的HashMap,并且在jdk8中进行了升级,使其在jdk的基础上进一步提高了并发能力.                                          
12、请你说说String类,以及new String()和使用字符串直接赋值的区别
        1、String类是Java最常用的API,它包含了大量处理字符串的方法,比较常用的有: 
           - char charAt(int index):返回指定索引处的字符;
           - String substring(int beginIndex, int endIndex):从此字符串中截取出一部分子字符串;
           - String[] split(String regex):以指定的规则将此字符串分割成数组; - String trim():删除字符串前导和后置的空格;
           - int indexOf(String str):返回子串在此字符串首次出现的索引;
           - int lastIndexOf(String str):返回子串在此字符串最后出现的索引; 
           - boolean startsWith(String prefix):判断此字符串是否以指定的前缀开头;
           - boolean endsWith(String suffix):判断此字符串是否以指定的后缀结尾;
           - String toUpperCase():将此字符串中所有的字符大写; 
           - String toLowerCase():将此字符串中所有的字符小写;
           - String replaceFirst(String regex, String replacement):用指定字符串替换第一个匹配的子串; 
           - String replaceAll(String regex, String replacement):用指定字符串替换所有的匹配的子串。 
       2、String类被final修饰,因此不可被继承,创建字符串有两种方式,一种是使用字符串直接量,即String s="xxxx";,另一种是使用new Sting("xxxx");,当使用直接量时,jvm会使用
            常量池来管理这个字符串,当使用new关键字创建时,jvm会先使用常量池来管理字符串,再调用String类的构造器来创建一个新的String对象,新创建的String对象会被保存再堆内存
            中,对比来说,采用new的方式会多创建出一个对象来,占用了更对的内存.
13、请你说说hashCode()和equals()的区别,为什么重写equals()就要重写hashcode()?
       1、HashCode()方法的主要用途是获取哈希码,equals()主要用来比较两个对象是否相等,两者之间有两个约定,如果两个对象相等,它们必须有相同的哈希码,但如果两个对象的
            哈希码相同,它们却不一定相等.
       2、Object类提供的equals()方法默认是用==比较的,当两个对象指向同一引用时,才返回true,因此与==没区别,所以在业务开发中,一般需要根据业务需求重写equals()方法,
            而equals()方法与HashCode()方法具有联动关系,所以equals()方法重写时,通常也要将hashCode()进行重写,使得这两个方法始终满足相关的约定。
14、说说你对ArrayList的理解
       1、ArrayList是基于数组实现得,它的内部封装了一个Object[]数组,通过默认构造器创建容器时,该数组先被初始化为空数组,之后在首次添加数据时,再将其初始化为长度为10的数组.
            也可以使用有参构造器来创建容器,通过参数指定容器大小.如果添加数据时超出了数组长度,则会先触发自动扩容,再添加数据,扩容就是创建一个新的数组,新数组的长度
            是旧数组长度的1.5倍,再将旧数组拷贝到新数组中.ArrayList支持缩容,但不会自动缩容,即便是ArrayList中只剩下少量数据时也不会主动缩容.如果希望缩容,则需要调用它的
            trimToSize()方法,届时数组将按照元素的实际个数进行缩减.
       2、Set、List、Queue都是Collection的子接口,都继承了父接口的iterator()方法,从而具备了迭代的能力,但是相比另外两个接口,List还单独提供了listiterator()方法,增强了迭代能力
            iterator()方法返回iterator迭代器,listiterator()方法返回Listiterator迭代器,并且Listiterator是iterator的子接口,Listiterator在iterator的基础上,增强了向前遍历的支持,增加了
            在迭代过程中修改数据的支持.
15、请你说说java的特点和优点,为什么要选择java?
      1、java是一门非常纯粹的面向对象的编程语言,它在吸收C++语言的各种优点的同时,去除了c++语言中难理解的多继承、指针等概念.所以java语言在保证了强大的功能性的基础上,还比C++
           更为简单易用.
      2、java拥有跨平台性,可以做到“一次编译,到处运行”,因为有jvm的存在,在运行程序之前,java源码需要经过编译器,将源代码翻译成字节码(.class),但字节码不能直接运行,所以必须
           通过jvm将字节码翻译成平台的机器码运行程序,但跨平台的是java程序,而不是jvm,所以需要在不同平台下安装不同版本的jvm.
      3、java还提供了很多内置的类库,通过这些类库简化了开发人员的程序设计工作,缩短了项目开发时间,最重要的是java提供了垃圾回收器,这也将开发人员从对内存的管理中解脱出来.
      4、java拥有良好的安全性和健壮性,java语言经常被用在网络环境中,为了增强程序安全性,java语言提供了一个防止恶意代码攻击的安全机制(数组边界检测和Bytecode校验等).
      5、java的强类型机制、垃圾回收器、异常处理和安全检查机制使得java拥有很良好的健壮性.
      6、java还提供了对Web应用开发的支持.
16、介绍以下包装类的自动拆装箱
      1、自动拆装箱是jdk1.5提供的功能,自动装箱是指把一个基本类型的数据直接赋值给对应的包装类型,自动拆箱是指把一个包装类型的对象直接赋值给对应的基本类型.通过自动拆装箱功能,可以简化
           基本类型变量和包装类对象之间的转换过程、比如某个方法的参数类型为包装类型,调用时,可以直接传入基本类型参数,不用做任何特殊处理.
      2、Java是一门非常纯粹的面向对象的编程语言,其设计理念是“一切皆对象”。但8种基本数据类型却不具备对象的特性。Java之所以提供8种基本数据类型,主要是为了照顾程序员的传统习惯。
           这8种基本数据类型的确带来了一定的方便性,但在某些时候也会受到一些制约。比如,所有的引用类型的变量都继承于Object类,都可以当做Object类型的变量使用,但基本数据类型却不可以。
           如果某个方法需要Object类型的参数,但实际传入的值却是数字的话,就需要做特殊的处理了。有了包装类,这种问题就可以得以简化。 不同包装类不能直接进行比较,这包括:
           - 不能用==进行直接比较,因为它们是不同的数据类型; - 不能转为字符串进行比较,因为转为字符串后,浮点值带小数点,整数值不带,这样它们永远都不相等; - 
          不能使用compareTo方法进行比较,虽然它们都有compareTo方法,但该方法只能对相同类型进行比较。 整数、浮点类型的包装类,都继承于Number类型,而Number类型分别定义了将数字转换为byte、
          short、int、long、float、double的方法。所以,可以将Integer、Double先转为转换为相同的基本数据类型(如double),然后使用==进行比较。
17、请你说说java基本类型和引用类型
      1、java的数据类型分为基本数据类型和引用数据类型两大类.基本类型共有八大类,又可分为四小类,分别为整数类型(byte,short,int,long)、浮点类型(float,double)、字符型(char)和布尔类型(boolean).
           其中int是最常用的整数类型,double是最为常用的浮点类型,除了布尔类型之外的其他7个类型,都可以看做是数字类型,它们之间可以进行类型转换.
      2、引用类型包括数组、类、接口类型、还有null类型,引用类型就是对一个对象的引用,对象包括实例和数组两种.
      3、byte:1字节,8位,数据范围是-2^7-2^7-1,short:2字节,16位,数据范围是-2^15-2^15-1,int:4字节,32位,数据范围是-2^31-2^31-1.-long:8字节,64位,float:4字节,32位,
           -3.4*10^38-3.4*10^38,double:8字节,64位,char:两字节,16位,boolean:不同的jvm有不同的实现机制.
18、请你说说对线程池的理解
      1、线程池可以有效地管理线程:它可以管理线程的数量,可以避免无节制的创建线程,导致超出系统负荷直至崩溃,它还可以让线程复用,可以大大的减少创建和销毁线程带来的开销.
      2、线程池需要依赖一些参数来控制任务的执行流程,其中最重要的参数有
                 (1)、corePoolSize:核心线程数
                 (2)、workQueue:等待队列
                 (3)、maxinumPoolSize:最大线程数
                 (4)、handler:拒绝策略
                 (5)、KeepAliveTime:空闲线程存活时间
                 (6)、TimeUnit:时间单位
                 (7)、ThreadFactory:线程工厂,用于生成线程
     3、当向线程池提交一个任务之后,线程池的执行流程:
                (1)、判断线程数是否达到核心线程数,若没有则新建线程执行该任务,否则进入下一步
                (2)、判断等待队列是否已满,若没有则将任务放入等待队列,否则进行下一步,等待队列可以有界,也可以无界,若指定了无界的队列,则线程池永远无法进入第三步,相当于放弃了最大线程数
                            这种用法非常危险,若任务在队列中大量堆积,很容易造成内存溢出.
                (3)、判断线程数是否达到最大线程数,若没有则新建线程执行该任务,否则进入下一步
                (4)、采用初始化线程池时指定的拒绝策略,拒绝执行该任务.拒绝策略主要有四个,分别是:让调用线程自己执行任务、直接抛出异常、丢弃任务、删除队列中最老的任务,并将当前任务加入队列.
                (5)、新建的线程处理完当前任务后,不会立刻销毁,而是继续处理等待队列中的任务,如果线程的空闲时间达到了空闲存活时间,则线程池会销毁一部分线程,是线程数量保持核心线程数
      4、线程池的生命周期包含5个状态,RUNNING、SHUTDOWN、STOP、TIDING、TERMINATED.这五种状态的状态值分别是:-1、0、1、2、3.在线程池的生命周期中,它的状态只能由小到大迁移,是不可逆的
               (1)、RUNNING:表示线程池正在运行
               (2)、SHUTDOWN:执行shutdown()时进入该状态,此时队列不会清空,线程池会等待任务执行完毕
               (3)、STOP:执行shutdownNow()时进入该状态,此时线程池会清空队列,不再等待任务的执行
               (4)、TIDING:当前线程池及队列为空时进入该状态,此时线程池会执行钩子函数,目前该函数是一个空的实现
               (5)、TERMINATED:沟子函数执行完毕后、线程进入该状态,表示线程池已死亡.
19、请你说说List与Set的区别
      1、List与Set都是Collection接口的子接口,它们的主要区别在于元素的有序性和重复性:List代表有序的元素可以重复的集合,集合中每个元素都有对应的顺序索引,它默认按照元素的添加顺序设置元素的索引
           并且可以通过索引来访问指定位置的集合元素,另外List允许使用重复元素.
      2、Set代表无序的元素不可重复的集合,它通常不能记住元素的添加顺序,Set集合不允许包含相同的元素,如果试图把两个相同的元素加入同一个Set中,则会添加失败并返回false.
      3、虽然Set代表无序的集合,但是它有支持排序的实现类,即TreeSet,TreeSet可以确保集合元素处于排序状态,并支持自然排序和定制排序两种排序方式,它的底层是由TreeMap实现的,TreeSet也是
           非线程安全的,但是它内部元素值不能为null.
20、说说java中常用的锁及原理
      1、java中加锁有两种方式,分别是synchroznized关键字和Lock接口,而Lock接口的经典实现是ReentrantLock.另外还有ReadWriteLock接口,它的内部设计了两把锁分别用于读写,这两把锁都是Lock类型,
          它的经典实现是ReentrantReadWriteLock,其中synchronized的实现依赖于对象头,Lock接口的实现则依赖于AQS.
      2、synchronized的底层是采用java对象头来存储信息的,对象头包括三部分,分别是MarkWord、Class Metadata、Array length.其中Mark Word用来存储对象的hashCode及锁信息,Class Metadata Address
           用来存储.//
21、请介绍一下访问修饰符
     1、java除了提供的三个访问修饰符分别代表三个访问级别之外还有一个不加修饰符的访问级别,它们访问级别控制从小到大为:private>default>protected>public,它们的访问级别分别如下:
         (1)、private:类中被private修饰的成员只能在当前类中被访问,根据这点,可以用private来修饰我们需要隐藏的成员变量
         (2)、default:如果类中的成员或者一个外部类不使用任何访问修饰符来进行修饰,那么他就是default级别的,default访问控制的类成员或者外部类可以在被相同包下的其他类访问
         (3)、protected:如果一个类成员被protected访问修饰符修饰,那么这个成员不但可以被同一个包下的其他类访问,还可以被其他包下的子类访问,一般来讲,如果一个方法被protected修饰,
                 那么通常是希望它的子类来重写它.
         (4)、public:这是java中最宽泛的访问级别,如果类成员被这个修饰符修饰,那么无论访问类和被访问类在不在一个包下,有没有父子关系,这个类成员都可以被访问到.
     加分项:对于局部变量而言,它的作用域就是他所在的方法,不可能被其它类所访问,所以不能使用访问修饰符来修饰,对于外部类而言,它只有两种控制级别:public和默认,外部类之所以不能使用protected
               和private修饰,是因为外部类没有处于任何类的内部,所以就没有它所在类的内部,所在类的子类两个范围,protected和private没有意义.使用public声明的外部类可以被所有类引用;不使用访问修饰符创建
               的外部类只有同一个包内的类能引用.
22、请你讲一下java NIO
      1、新的输入/输出(NIO)库是在JDK1.4中引入的,NIO弥补了原来同步阻塞I/O的不足,它在标准java代码中提供了高速的、面向块的I/O.
      2、Buffer是一个对象,它包含一些要写入或者要读出的数据,在NIO类库中加入Buffer对象,体现了新库与原I/O的一个重要区别,在面向流的I/O中,可以将数据直接写入或者将数据直接读到Stream对象中.
      3、在NIO中,所有数据都是用缓冲区处理的,在读取数据时,它是直接从缓冲区中读的,在写入数据时,写入缓冲区中,任何时候访问NIO中的数据,都是通过缓冲区进行操作的.
      4、Channel是一个通道,可以通过它读取和写入数据,网络数据通过Channel读取和写入.通道与流的不同之处在于通道是双向的,流只是在一个方向上移动,而且通道可以用于读、写或者同时支持读写.因为Channel是
           所以它可以比流更好的映射底层操作系统的API,特别是在UNIX网络编程模型中,底层操作系统的通道都是双全工的,同时支持读写操作.
      5、Selector会不断地轮询注册在其上的Channel的集合,进行后续的I/O操作,一个多路复用器Selector可以同时轮询多个Channel,由于JDK使用了epoll()代替传统的select实现,所以它并没有最大连接句柄1024/2048
           的限制,这也就意味着只需要一个线程负责Selector的轮询,就可以接入成千上万的客户端,这确实是个非常巨大的进步.
           

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值