Java基础

  • String为什么是final的?
    • 答:因为String被高度使用,如果允许被继承,可能会降低程序的性能。
  • HashMap源码,实现原理,底层结构
    • 不是线程安全的,如果要线程安全:
      • Map map = Collections.synchronizedMap(new HashMap());
      • 或者Hashtable(写入较慢,效率较低,key和value不允许为null)
    • 数据结构:数组+链表(解决hash冲突)
    • Null:table[0]
    • 长度length:2的整数次幂,原因:length是偶数的话,数组下标可奇可偶,length是奇数的话,数组下标都是偶数。
    • 数组下标:h&(length-1)就相当于对length取模(但比除法运算取模效率高)
    • 目标:使不同hash发生碰撞的概率较小,使元素在哈希表中均匀散列
    • 扩容:当元素个数超过数组大小*loadFactor装载因子时,【扩容100%】
    • 【ArrayList扩容:50%】
    • 【Vector扩容:100%】
    • 【HashTable扩容:100%+1】
    • 非线程安全:fail-fast策略:在迭代过程中,判断modCount和expectedCount是否相等,如果不相等就表示有其他线程修改了Map(仅用于检测程序错误)
  • Java集合类
    • List:有序,可重复
    • Set:无序,不可重复(AbstractSet、Hash、Tree)
    • Map:键值对,键唯一(AbstractMap、Hash、Tree)
    • ArrayList&LinkedList:LinkedList(使用双向链表,要移动指针)经常用在增删操作较多而查询操作很少的情况下,ArrayList(基于动态数组,要移动数据)则相反。
    • Vector&ArrayList:Vector线程安全,性能差
    • HashMap&TreeMap:TreeMap会整理顺序(按照自然顺序或者自定义顺序)
    • ConcurrentHashMap:锁分段技术,一个线程锁住一段数据。get不需要加锁,因为get方法将要使用的共享变量都定义成volatile
      • Volatile:能够在线程之间保持可见性,能够被多线程同时读,并且保证不会读到过期的值,但是只能被单线程写
      • Happen before原则:对volatile字段的写入操作先于读操作,因此读到的一定是最新的
    • Arrays.sort和Collection.sort都是用的Timsort
  • Java中的队列都有哪些?
    • 阻塞队列&普通队列:阻塞队列为空时,获取的操作/线程会被阻塞;队列为满时,添加的操作/线程会被阻塞
  • 反射中,Class.forName和classLoader的区别
    • Class.forName会对类进行初始化,解释,执行类的static块(可以带参数控制是否加载),可以决定哪个classLoader来请求这个类型
    • classLoader只加载不出实话,只有在newInstance才会执行static,用当前的classLoader去请求
  • Java7、Java8的新特性
    • Java7:switch可以使用字符串,范性实例化类型自动推断,语法上支持集合,新增一些获取环境信息的方法
    • Java8:给接口添加非抽象的方法实现,用default关键字,lambda
  • 数组&链表的操作效率
    • 数组在随机访问上的效率比链表高,链表的插入和删除效率较高
    • 数组内存要求高,必须有连续内存;链表内存利用率较高
  • Java内存泄漏
    • jvm回收不应用的对象,但是如果一直应用旧的对象,还在创建新的对象,就容易内存溢出
    • OutOfMemoryError:内存泄漏的标志。继续监听GC的活动,看内存使用量是否持续增加。
    • Jstat:性能分析(classLoader,compiler,gc相关信息,可以时时监控资源和性能)
    • Jstack:输出某个线程的堆栈信息,找到有问题的位置(死锁,阻塞,资源…)
    • Jmap:某个线程内存分配的详细情况
  • String StringBuffer StringBuilder
    • String不可变(每次都是新生成一个对象,改变指针指向的)
      • Why?字符串常量池,新的String指向常量池中的值,如果可变的话,就会发生混乱,影响别的String;String不可变使String对象的hashCode不可变,可以放心缓存,性能优化手段;String被用作参数,url,path等,如果String可变,那么就不安全
    • StringBuffer(线程安全,可以同步,推荐,但是拼接较慢)
    • StringBuilder(非线程安全,不保证同步,优先使用)
  • 异常的结构
    • Try,catch,finally(用于回收,一定会执行)
    • Throw:抛出一个具体的异常;throws:声明可能抛出的异常
    • 运行时异常:空指针,数组越界,类型强制转换……
    • 非~:输入输出异常,SQL异常(不处理不能通过编译)
  • String ==
    • String s1 = “abc”;
    • String s2 = “abc”;
    • String s3 = new String(“abc”);
    • s1==s2(true):以这种形式创建了s1以后,s1放在常量池中,当创建s2时,Java底层会在常量池中查找,如果存在,则将s2指向这个值
    • s2==s3(false):s3是新建的对象,与s2是不同的对象
  • String类常用方法
    • str.length
    • str.charAt
    • str.substring
    • str.compareTo
    • str.equals
    • str.concat
    • str.indexOf
    • str.replace
    • str.split
  • Java引用类型:
    • 强引用:Person jack = new Person(“Jack”);——绝对不会被回收
    • 软引用Soft:SoftReference personSoftReference = new SoftReference<>(jack);图片缓存等——内存不够才会回收
    • 弱引用Weak:WeakReference personSoftReference = new WeakReference(jack);——GC一运行就回收了
    • 虚引用Pantom:一创建就回收了
  • 抽象类和接口的区别
    • 共同点:都不能实例化,被其他类实现和继承;都可以包含抽象方法,实现和普通子类来实现这些抽象方法。
    • 抽象类可以包含普通方法,接口里只能包含抽象、静态、默认方法
    • 抽象类可以定义普通成员变量,接口不行(只能静态static)
    • 抽象类可以包含构造器,让子类调用构造器来完成初始化,接口不能包含
    • 抽象类可以包含初始化块,接口里不能
    • 一个类最多只有一个直接父类,但是可以实现多个接口,来弥补单继承的不足
  • java基本数据类型所占字节
    • Byte:1
    • Short:2
    • Char:2
    • Int:4
    • Long:8
    • Float:4
    • Double:8
    • Boolean:不定
  • Hash冲突的解决:找一找数据结构的教材看
    • 开放定址法
  • == .equals() .hashCode()
    • Object类中的equals方法和==是一样的,String,Integer类是重写了equals方法,比较值是否相等~
    • 可以重写Object类的equals方法
      • Instanceof:对象运算符,判断左边对象是否是右边的类创建的对象
    • 重写equals方法,一定要重写hashCode方法。对象相同->成员变量相同->hashCode相同
    • 如果两个对象的hashCode相同,它们并不一定equals相同
    • HashSet中的add(基于HashMap的put):hashCode或者equals或者==满足一个即可
    • Remove:使用对象的hashCode去找到对象(如果重写了hashCode时,把对象的属性值作为运算数,那么修改对象的属性值的时候,hashCode就会发生改变,这样在原先的hashSet中就找不到这个对象了!)——内存泄漏!
  • Int Integer
    • int是基本数据类型,integer是包装类
    • integer必须实例化后才能使用
    • integer是对象的引用,new以后生成指针指向该对象,int是直接存储数据
    • integer默认null,int默认0
    • integer的具体情况可以参考String(非new产生的话,直接放入常量池中)
      • Integer i = new Integer(100); Integer j = 100; ==> j!=i
      • Integer i = new Integer(100); Integer j = new Integer(100); ==> j!=I
  • Java多态
    • 前提:继承关系
    • 覆盖(父类与子类用相同的方法名和参数,也就是说子类重写了父类的方法)
    • 父类数据类型的引用指向子类对象:Animal am = new Cat();
    • 成员变量:父类;
    • 成员方法:编译的时候看父类,运行的时候看子类(也就是说多态后不能使用子类特有的属性和方法!否则编译不能通过的!)
    • 静态方法:父类(父类的静态方法不能被子类重写覆盖)
    • 如果要用子类特有的属性和方法,就强制变回去:Cat ct = (Cat) am;
  • 内部类
    • 内部类是一个相对独立的实体
    • 内部类可以直接访问外部类元素,外部类需要通过内部类引用简介访问内部类元素(new InnerClass())
    • OuterClass.InnerClass innerClass=new OuterClass().new InnerClass();//在外部类外面/外部类中的main里创建内部类
    • InnerClass innerClass=new InnerClass()//在外部类里面创建
    • 种类:
      • 成员内部类(没有static,可以访问外部类的所有成员)
      • 方法内部类(作用域只有某个方法)
      • 匿名内部类(继承一个抽象类或者实现一个接口,没有构造方法,没有访问修饰符等…)它属于方法内部类
      • 静态内部类(可以有静态成员,但是不能访问外部类的非静态成员..)
    • 作用:
      • 实现隐藏,非内部类不允许有private与protected权限,内部类可以
      • 内部类拥有外部类的所有权限
      • 实现多重继承
      • 如果这个类要继承一个类,还要实现一个接口,但是继承的类和接口里有同名方法的话:外部类继承一个类,内部类作为闭包实现接口,
      • Incrementable getCallbackReference() {
      • return new Closure(); }
  • 抽象类
    • Abstract class 。。。。
    • 和final private static不能共存
    • 抽象类不能new创建对象,抽象类的抽象方法要被使用,必须由子类复写后,建立子类对象调用。如果子类只覆盖部分抽象方法,那么该子类还是一个抽象类
    • 意义:类型隐藏,实现多态(抽象类继承)
  • 抽象类和接口的使用场景
    • 抽象类:既需要统一的接口,有需要实例变量或缺省的方法
    • 简单来说就是,不需要每个实现类实现所有接口,可以用抽象类,子类可以选择方法来实现;或者还需要变量来区分关系
    • 其他情况都用接口
    • (类,描述这个东西是什么,接口描述它能做什么事)
  • 抽象类可以没有属性和方法吗?
    • 可以(?但是有啥意义啊?)
  • 接口的意义
    • 统一访问
    • 接口是单继承的,比多继承简单很多
    • 接口不必限制类的类型
  • 泛型中,extends和super的区别
    • ? extends T上界通配符。返参的类型是T和T的父类,无法入参,比如:一个水果盘子(返回水果或者Object)放在里面的可以是苹果,但是不能存入苹果,可读不可写
    • ? super T下界通配符。入参的类型是T和T的子类,返参类型只能是Object,可写不太好读
    • PECS原则:
      • 频繁往外读取内容的,适合用extends
      • 频繁往里插入的,适合用supper
  • 进程和线程的区别
    • 进程是cpu资源分配的最小单位,线程是cpu调度的最小单位
    • 一个进程至少有一个线程
    • 多个线程共享内存,同时执行,可以提高效率
    • 但是多个线程不是独立的应用
  • final,finally,finalize的区别
    • Final:不能有新的子类的类的修饰符
    • Finally:try catch中最后无论如何必须执行的
    • Finalize:垃圾收集器从内存中清除对象的方法
  • Java序列化
    • Jvm停止运行后仍然可以持久化的对象(保存为一组字节)
    • 只要类实现了java.io.Serializable接口,那么它就可以被序列化
      • Public String toString()
      • 写入:新建一个ObjectOutputStream oos对象
      • oos.writeObject(object)
      • IOUtils.closeQuietly(oos)
      • 读取(反序列化):新建ObjectInputStream ois对象
    • 序列化不保存静态变量
    • 能否反序列化:类路径和功能代码一致;序列化ID一致
    • Transient关键字的作用是阻止变量被序列化
    • ArrayList的序列化
    • Traisient elementData的原因:ArrayList自动增长长度,会造成很多null元素
    • 为什么要重写writeObject:因为把元素transient了,所以要把正常的元素持久化
    • Oos怎么调用ArrayList中的write, read?通过反射
  • Serializable 和Parcelable 的区别(P是安卓的,我就说我不知道)
  • Java静态内部类的设计意图:
    • 不依赖外部类(没有外部类的实体也可以new),不使用外在类的非静态属性和方法,可以有静态属性和方法
    • 加强了类的封装性和代码可读性
    • 更节省资源,减少内部类指向外部类的引用
  • String转化成Integer源码分析
    • 首先判断第一个字符是否是’-’号
    • digit = Character.digit(s.charAt(i++),radix);//如果是合法的字符,返回数字,否则返回-1
    • 判断digit是否-1,即字符串非法,抛出异常
    • 判断最后一位之前是否溢出,如果溢出,抛出异常1
    • 最后一位之前都没溢出,最后一位溢出,抛出异常2
    • 判断是否是空字符串,如果为空,抛出异常
    • 判断正负
      • 如果是负数(’-‘开头),判断位数是否大于1,如果只有’-‘,抛出异常
      • 如果是正数,直接返回结果的负数
Table of Contents