JAVA常见问题合集

面筋分类汇总-测开向

面向对象

  • 面向过程和面向对象

  • 面向对象的三大基本特征:封装、继承、多态

 - 封装:隐藏内部细节html

  • 继承:复用现有代码java

  • 多态:改写对象行为程序员

  • JAVA为何是面向对象的,为何还用int等基础类型

    • 面向对象的特征:封装,继承,多态。JAVA语言符合这些特征。
    • 由于在每一个封装体里,基本仍是面向过程的代码思想,所以仍是须要这些基础数据类型。
  • 面向对象软件开发的优势有哪些?

    • 更符合人们思考习惯的思想,更多体现的是指挥者,指挥对象作事情,与面向过程的执行者区别;
    • 能够将复杂问题简单化,保证了较高的开发效率,保证了程序的鲁棒性和可维护性。
    • 易维护、易复用、易扩展,因为面向对象有封装、继承、多态性的特性,能够设计出低耦合的系统。
    • 代码开发模块化,更易维护和修改。
    • 加强代码的可靠性和灵活性。
    • 增长代码的可理解性。
  • 一、封装

    • 封装:隐藏对象的属性和实现细节,仅对外公开接口,控制在程序中属性的读和修改的访问级别,将抽象获得的数据和行为(或功能)相结合,造成一个有机的总体,也就是将数据与操做数据的源代码进行有机的结合,造成“类”,其中数据和函数都是类的成员。
    • 封装的目的:是加强安全性和简化编程,使用者没必要了解具体的实现细节,而只是要经过外部接口,以特定的访问权限来使用类的成员。
    • 面向对象就是使用程序处理事情时以对象为中心去分析;与面向过程不一样,面向过程关心处理的逻辑、流程等问题,而不关心事件主体。而面向对象即面向主体,因此咱们在解决问题时应该先进行对象的封装(对象是封装类的实例,好比张三是人,人是一个封装类,张三只是对象中的一个实例、一个对象)。好比咱们平常生活中的小兔子、小绵羊均可以封装为一个类。
  • 封装的定义和好处有哪些?

    • 一是提升了数据的安全性。用private把类的细节与外界隔离起来,从而实现数据项和方法的隐藏,而要访问这些数据项和方法惟一的途径就是经过类自己,类才有资格调用它所拥有的资源(方法,数据项属性等等)。
    • 二是提升了代码的可用性和可维护性。经过隐藏隔离,只容许外部对类作有限的访问,开发者能够自由的改变类的内部实现,而无需修改使用该类的那些程序。只要那些在类外部就能被调用的方法保持其外部特征不变,内部代码就能够自由改变,各取所需,利于分工。
    • 三就是提升了代码的重用性,封装成工具类之后可以减小不少繁琐的步骤。
  • 抽象的定义?抽象和封装的不一样点?

    • 抽象是把想法从具体的实例中分离出来的步骤,所以,要根据他们的功能而不是实现细节来建立类。Java支持建立只暴漏接口而不包含方法实现的抽象的类。这种抽象技术的主要目的是把类的行为和实现细节分离开。
    • 抽象和封装是互补的概念。一方面,抽象关注对象的行为。另外一方面,封装关注对象行为的细节。通常是经过隐藏对象内部状态信息作到封装,所以,封装能够当作是用来提供抽象的一种策略。
  • 二、继承

    • 继承是指:保持已有类的特性而构造新类的过程。继承后,子类可以利用父类中定义的变量和方法,就像它们属于子类自己同样。
      • 继承是面向对象的基本特征之一,支持单继承,不支持多继承;继承机制容许建立分等级层次的类。
      • 继承就是子类继承父类的特征和行为,使得子类对象(实例)具备父类的实例域和方法,或子类从父类继承方法,使得子类具备父类相同的行为。
      • 继承之间是子父类的关系。继承机制能够很好的描述一个类的生态,也提升了代码复用率,在Java中的Object类是全部类的超类,常称做上帝类。
    • 做用:
      • 继承提升了代码复用性,提供了多态的前提。
  • 单继承和多继承

    • 只支持单继承(一个类只能有一个父类),不支持多继承(多继承用接口实现)。
    • 单继承:java类是单继承的,一个类只容许有一个父类。
      • public class A extends B{ } //继承单个父类
    • 多继承:java接口多继承的,一个类容许继承多个接口。
      • public class A extends B implements C{ } //同时继承父类和接口
      • public class A implements B,C{ } //继承多个接口
    • 支持多重继承
      • class A {}
      • class B extends A{}
      • class C extends B{}
  • 三、多态

    • 在父类中定义的属性和方法被子类继承以后,能够具备不一样的数据类型或表现出不一样的行为,这使得同一个属性或方法在父类及其各个子类中具备不一样的含义。
    • 多态:同一个行为具备多个不一样表现形式或形态的能力。
      • 是指一个类实例(对象)的相同方法在不一样情形有不一样表现形式。多态机制使具备不一样内部结构的对象能够共享相同的外部接口。这意味着,虽然针对不一样对象的具体操做不一样,但经过一个公共的类,它们(那些操做)能够经过相同的方式予以调用。
    • 多态的优势:
      • 消除类型之间的耦合关系
      • 可替换性
      • 可扩充性
      • 接口性
      • 灵活性
      • 简化性
    • 多态存在的三个必要条件:
      • 继承
      • 重写(子类继承父类后对父类方法进行从新定义)
      • 父类引用指向子类对象
    • 子类对象的多态性使用前提:
      • 有类的继承或实现关系;
      • 由子类对父类方法的重写(覆盖)
    • 写一个多态:
 

java基础

  • Integer和int的区别

    • int则是java的一种基本数据类型,Integer是int的包装类
    • int的默认值是0,Integer的默认值是null
    • Integer变量必须实例化后才能使用,而int变量不须要
    • Integer实际是对象的引用,当new一个Integer时,其实是生成一个指针指向此对象;而int则是直接存储数据值
  • java常见的几种运行时异常RuntimeException

    • NullPointerException - 空指针引用异常
    • ClassCastException - 类型强制转换异常。
    • IllegalArgumentException - 传递非法参数异常。
    • ArithmeticException - 算术运算异常
    • ArrayStoreException - 向数组中存放与声明类型不兼容对象异常
    • IndexOutOfBoundsException - 下标越界异常
    • NegativeArraySizeException - 建立一个大小为负数的数组错误异常
    • NumberFormatException - 数字格式异常
    • SecurityException - 安全异常
    • UnsupportedOperationException - 不支持的操做异常
  • Java 异常中的getMessage()和toString()方法的异同

    • e.toString(): 得到异常种类和错误信息
    • e.getMessage():得到错误信息
    • e.printStackTrace():在控制台打印出异常种类,错误信息和出错位置等
  • java中的length和length(),size

  • 重写与重载

    • 重载:
      • 发生在同一个类里面,两个或者是多个方法的方法名相同可是参数不一样的状况。
      • 注:参数顺序不一样也参数列表不一样的状况。
      • 重载与返回值类型和修饰符无关。
    • 重写或覆盖:
      • 是指子类从新定义了父类的方法;
      • 重写必须有相同的方法名,参数列表和返回类型。
      • 子类函数的访问修饰权限不能少于父类的。
  • 栈和堆的区别

    • 1.栈内存存储的是局部变量,基本类型的变量表示的是数据自己;而堆内存存储的是实体,每一个实体对象都有地址值和默认初始化值;
    • 2.栈内存的读取和更新速度要快于堆内存,由于局部变量的生命周期很短;
    • 3.栈内存使用一级缓存,存放的变量生命周期一旦结束就会被释放;而堆内存使用二级缓存,存放的实体会被垃圾回收机制不定时的回收。
  • java集合

  • String中两种初始化方式比较:直接赋值和构造函数初始化

    • 经过直接赋值建立对象是在方法区的常量池:
      • String s1 = "abc";
      • 首先在常量池中查找"abc",若是没有则在常量池建立该对象
      • 在栈中建立s1的引用,将s1直接指向对象"abc"
      • 所以在这里"abc"是常量池中的对象,若是声明另外一个String类型的对象引用,并将它指向对象"abc",则这两个引用指向的是同一个常量池中的对象。
      • 直接赋值:存储在常量池。
      • 屡次直接赋值:将存储在常量池中的"hello"的地址传递给s变量,且常量池已有字符串公用。
    • 经过构造方法建立字符串对象是在堆内存:
      • String s = new String(“abc”);
      • 凡是通过 new 建立出来的对象,都会在堆内存中分配新的空间,建立新的对象,因此s是String类新建立的对象;
      • 每new一次,都会建立新的对象,即便对象值相同。
  • String、StringBuilder与StringBuffer

    • 字符修改上的区别
      • String:字符串常量,不可变字符串,每次对String的操做均可能生成新的String对象,效率低下,并且大量浪费有限的内存空间。
        • 注:对string从新赋值,若是字符串常量池不存在这个新的赋值对象,就会创造新的对象,若是存在,就不会建立。
      • StringBuffer:字符串变量,可变字符串、效率低、线程安全;
      • StringBuilder:字符串变量,可变字符序列、效率高、线程不安全;
        • 注:StringBuffer 和 StringBuilder 类的对象可以被屡次的修改,而且不产生新的未使用对象。
    • 三者在执行速度方面的比较:
      • StringBuilder > StringBuffer > String
    • 继承结构不一样
      • String继承自CharSequence接口,StringBuilder和StringBuffer继承自Appendable接口。
    • 小结:
      • (1)若是要操做少许的数据用 String;
      • (2)多线程操做字符串缓冲区下操做大量数据 StringBuffer;
      • (3)单线程操做字符串缓冲区下操做大量数据 StringBuilder(推荐使用)。
  • HashMap和Hashtable的区别

    • 底层都是数组+链表实现
    • 一、继承的父类不一样: Hashtable继承自Dictionary类,而HashMap继承自AbstractMap类。但两者都实现了Map接口。
    • 二、线程安全性不一样: HashMap是线程不安全的,可用于单线程;Hashtable是线程安全的,可用于多线程。
    • 三、是否提供contains方法
      • HashMap把Hashtable的contains方法去掉了,改为containsValue和containsKey,由于contains方法容易让人引发误解。
      • Hashtable则保留了contains,containsValue和containsKey三个方法,其中contains和containsValue功能相同。
    • 四、key和value是否容许null值:其中key和value都是对象,而且不能包含重复key,但能够包含重复的value。
      • Hashtable中,key和value都不容许出现null值。可是若是在Hashtable中有相似put(null,null)的操做,编译一样能够经过,由于key和value都是Object类型,但运行时会抛出NullPointerException异常,这是JDK的规范规定的。
      • HashMap中,null能够做为键,这样的键只有一个;能够有一个或多个键所对应的值为null。当get()方法返回null值时,多是 HashMap中没有该键,也可能使该键所对应的值为null。所以,在HashMap中不能由get()方法来判断HashMap中是否存在某个键, 而应该用containsKey()方法来判断。
    • 五、两个遍历方式的内部实现上不一样:
      • Hashtable、HashMap都使用了 Iterator。而因为历史缘由,Hashtable还使用了Enumeration的方式 。
    • 六、hash值不一样:
      • 哈希值的使用不一样,HashTable直接使用对象的hashCode。而HashMap从新计算hash值。
    • 七、内部实现使用的数组初始化和扩容方式不一样:
      • HashTable在不指定容量的状况下的默认容量为11,而HashMap为16,Hashtable不要求底层数组的容量必定要为2的整数次幂,而HashMap则要求必定为2的整数次幂。
      • Hashtable扩容时,将容量变为原来的2倍加1,而HashMap扩容时,将容量变为原来的2倍。
  • map的put方法

  • HashMap为何是线程不安全的?

    • HashMap是线程不安全的,咱们应该使用ConcurrentHashMap
    • Hashtable 中的方法是Synchronize的,而HashMap中的方法在缺省状况下是非Synchronize的
    • HashMap的线程不安全主要体如今下面两个方面:
      • 1.在JDK1.7中,当并发执行扩容操做时会形成环形链和数据丢失的状况。
      • 2.在JDK1.8中,在并发执行put操做时会发生数据覆盖的状况。
    • 注:ConcurrentHashMap使用了分段锁技术来提升了并发度,不在同一段的数据互相不影响,多个线程对多个不一样的段的操做是不会相互影响的。每一个段使用一把锁。因此在须要线程安全的业务场景下,推荐使用ConcurrentHashMap,而HashTable不建议在新的代码中使用,若是须要线程安全,则使用ConcurrentHashMap,不然使用HashMap就足够了。
  • 哈希表:哈希函数构造;哈希表解决地址冲突的方法

    • 散列函数构造方法:
      • 1.直接定址法:H(key) = a*key + b
      • 2.除留余数法:H(key) = key % p(p为不大于散列表表长,但最接近或等于表长的质数p)
      • 3.数字分析法:选取r进制数数码分布较为均匀的若干位做为散列地址
      • 4.平方取中法:取关键字的平方值的中间几位做为散列地址
      • 5.折叠法:将关键字分割成位数相同的几部分,而后取这几部份的叠加和做为散列地址
    • 处理冲突的方法:
      • 1.开放定址法(闭哈希表):在冲突的哈希地址的基础上进行处理,获得新的地址值。Hi = (H(key)+di) % m(m表示散列表表长,di为增量序列)
        • 1)线性探测法:dii=1,2,3,…,m-1
        • 2)二次探测法:di=12,-12,22,-22,…,k2,-k2 ( k<=m/2 )
          • 冲突发生时,以原哈希地址为中心,在表的左右进行跳跃式探测,比较灵活。
        • 3)伪随机数法:di=伪随机数序列。
          • 具体实现时,应创建一个伪随机数发生器,(如i=(i+p) % m),并给定一个随机数作起点。
      • 线性探测再散列的优势是:只要哈希表不满,就必定能找到一个不冲突的哈希地址,而二次探测再散列和伪随机探测再散列则不必定。
      • 注:在开放定址的情形下,不能随便物理删除表中已有元素,若删除元素将会截断其余具备相同散列地址的元素的查找地址。若想删除一个元素,给它作一个删除标记,进行逻辑删除。
      • 2.链地址法、拉链法(开哈希表)
        • 将全部哈希地址为i的元素构成一个称为同义词链的单链表,并将单链表的头指针存在哈希表的第i个单元中,于是查找、插入和删除主要在同义词链中进行。链地址法适用于常常进行插入和删除的状况。
      • 3.再哈希法:同时构造多个不一样的哈希函数,发生冲突时,使用其余哈希函数求值。这种方法不易产生汇集,但增长了计算时间。
      • 4.创建公共溢出区:将哈希表分为基本表和溢出表两部分,凡是和基本表发生冲突的元素,一概填入溢出表
  • 数组与链表

    • 数据中数组的内存是顺序存储的,而链表是随机存取的。
    • 数组随机访问效率很高,但插入删除操做的效率比较低。
    • 链表在插入删除操做上相对数组有很高的效率,而若是访问链表中的某个元素,那就要从表头逐个遍历,直到找到所须要的元素为止,因此链表的随机访问效率比数组低。
    • 链表不存在越界问题,数组有越界问题。
    • 数组节省空间可是长度固定。链表虽然变长,可是占了更多的存储空间。
    • 静态)数组从栈中分配内存空间,对于程序员方便快速,可是自由度小。链表从堆中分配空间,自由度大,但申请管理比较麻烦。
  • arraylist和linkedlist的区别

    • ArrayList和LinkedList都实现了List接口,他们有如下的不一样点:
    • ArrayList是基于索引的数据接口,它的底层是数组。它能够以O(1)时间复杂度对元素进行随机访问。与此对应,LinkedList是以元素列表的形式存储它的数据,每个元素都和它的前一个和后一个元素连接在一块儿,在这种状况下,查找某个元素的时间复杂度是O(n)。
    • 相对于ArrayList,LinkedList的插入,添加,删除操做速度更快,由于当元素被添加到集合任意位置的时候,不须要像数组那样从新计算大小或者是更新索引。
    • LinkedList比ArrayList更占内存,由于LinkedList为每个节点存储了两个引用,一个指向前一个元素,一个指向下一个元素。
  • 辨析:replace,replaceAll,replaceFirst

    • replace和replaceAll:
      • 相同点:替换全部匹配的字符串(都是替换全部)
      • 不一样点:replace支持字符替换,字符串替换; replaceAll是正则表达式替换
    • replaceFirst: 同replaceAll同样,也是基于规则表达式的替换
      • 不一样之处是:只替换第一次出现的字符串
  • protected,private,public

  • 判断一个类是否“无用”,则需同时知足三个条件:

    • 该类全部的实例都已经被回收,也就是Java堆中不存在该类的任何实例;
    • 加载该类的ClassLoader已经被回收
    • 该类对应的java.lang.Class对象没有在任何地方被引用,没法在任何地方经过反射访问该类的方法
  • 垃圾回收算法:复制算法、标记-清除算法、标记-整理算法、分代收集算法

    • 如何肯定某个对象是垃圾:引用计数法。
      • 在java中是经过引用来和对象进行关联的,也就是说若是要操做对象,必须经过引用来进行。那么很显然一个简单的办法就是经过引用计数来判断一个对象是否能够被回收。不失通常性,若是一个对象没有任何引用与之关联,则说明该对象基本不太可能在其余地方被使用到,那么这个对象就成为可被回收的对象了。这种方式成为引用计数法。
      • 这种方式的特色是实现简单,并且效率较高,可是它没法解决循环引用的问题,所以在Java中并无采用这种方式(Python采用的是引用计数法)。
    • 垃圾回收算法:复制算法、标记-清除算法、标记-整理算法、分代收集算法
      • 标记-清除算法:
        • 分为两个阶段:标记阶段和清除阶段。标记阶段的任务是标记出全部须要被回收的对象,清除阶段就是回收被标记的对象所占用的空间。
        • 实现起来比较容易,可是有一个比较严重的问题就是容易产生内存碎片,碎片太多可能会致使后续过程当中须要为大对象分配空间时没法找到足够的空间而提早触发新的一次垃圾收集动做。
      • 复制算法:
        • 将可用内存按容量划分为大小相等的两块,每次只使用其中的一块。当这一块的内存用完了,就将还存活着的对象复制到另一块上面,而后再把已使用的内存空间一次清理掉,这样一来就不容易出现内存碎片的问题。
        • 实现简单,运行高效且不容易产生内存碎片,可是却对内存空间的使用作出了高昂的代价,由于可以使用的内存缩减到原来的一半。
        • Copying算法的效率跟存活对象的数目多少有很大的关系,若是存活对象不少,那么Copying算法的效率将会大大下降。
      • 标记-整理算法:
        • 该算法标记阶段和Mark-Sweep同样,可是在完成标记以后,它不是直接清理可回收对象,而是将存活对象都向一端移动,而后清理掉端边界之外的内存。
      • 分代收集算法:
        • 是目前大部分JVM的垃圾收集器采用的算法。
        • 它的核心思想是根据对象存活的生命周期将内存划分为若干个不一样的区域。通常状况下将堆区划分为老年代(Tenured Generation)和新生代(Young Generation),老年代的特色是每次垃圾收集时只有少许对象须要被回收,而新生代的特色是每次垃圾回收时都有大量的对象须要被回收,那么就能够根据不一样代的特色采起最适合的收集算法。
        • 新生代:Copying算法,由于新生代中每次垃圾回收都要回收大部分对象,也就是说须要复制的操做次数较少,可是实际中并非按照1:1的比例来划分新生代的空间的,通常来讲是将新生代划分为一块较大的Eden空间和两块较小的Survivor空间,每次使用Eden空间和其中的一块Survivor空间,当进行回收时,将Eden和Survivor中还存活的对象复制到另外一块Survivor空间中,而后清理掉Eden和刚才使用过的Survivor空间。
        • 老年代:Mark-Compact算法。根据老年代的特色:每次回收都只回收少许对象。
        • 在堆区以外还有一个代就是永久代(Permanet Generation):存储class类、常量、方法描述等。对永久代的回收主要回收两部份内容:废弃常量和无用的类。
    • 典型的垃圾收集器:
      • 垃圾收集算法是 内存回收的理论基础,而垃圾收集器就是内存回收的具体实现。
      • 下面介绍一下HotSpot(JDK 7)虚拟机提供的几种垃圾收集器,用户能够根据本身的需求组合出各个年代使用的收集器。
      • 小结:Serial/Serial Old,ParNew,Parallel Scavenge,Parallel Old,CMS,G1
  • 内存溢出和内存泄露

  • java会不会内存泄漏

    • 内存泄漏:一个再也不被程序使用的对象或变量还在内存中占用存储空间。
    • Java的垃圾回收机制能够回收这类再也不使用的对象。
    • 可是Java还存在内存泄漏的问题。
    • 缘由:
      • 静态集合类,如哈希表:由于是静态的,生命周期与程序一致,在程序结束前不能释放,形成内存泄漏;
      • 变量不合理的做用域:若是一个变量定义的做用范围大于使用范围,可能形成内存泄漏。
      • 其余:创建各类连接后,监听器,单例模式中静态存储单例对象等等。
  • 抽象类

    • 抽象类和抽象方法必须用abstract修饰;
    • 抽象方法:只有方法声明,没有方法体,定义在抽象类中;
    • 格式:修饰符 abstract 返回值类型 函数名(参数列表){}
    • 抽象类不能够被实例化,即不能够用new建立对象。
      • 抽象类经过其子类实例化,而子类须要覆盖掉抽象类中全部抽象方法才能够建立对象,不然该子类也是抽象类。
  • 接口

    • 抽象类和接口:
      • 抽象类中能够定义抽象方法和非抽象方法;
      • 当抽象类中的方法都是抽象的时,能够定义为接口。
    • 接口最重要的体现:解决单继承的弊端。
      • 多继承父类中有方法主体,致使调用时的不肯定性;
      • 接口中没有方法体,由子类来定义。
    • 接口的特色:
      • 接口不能够建立对象;
      • 子类必须覆盖掉接口中的全部的抽象方法后,子类才能够实例化,不然子类是一个抽象类。
    • 固定修饰符:
      • 成员变量(实际上是常量):public static final
      • 成员方法:public abstract
    • 代码体现:
interface A{ void show();}
interface B{ void show();}
class C implements A,B{public void show(){...}}
C c = new C();
c.show();
  • 抽象类和接口的区别

    • 共性:都是不断抽取出来的抽象的概念。
    • 不一样1:
      • 抽象类体现的是继承关系,一个类只能单继承;
      • 接口体现的是实现关系,一个类能够多实现。
    • 不一样2:
      • 抽象类是继承,是is a的关系;
      • 接口是实现,是like a的关系。
    • 不一样3:
      • 抽象类中能够定义非抽象方法,供子类直接使用;
      • 接口的方法都是抽象,接口中的成员都有固定修饰符。
  • 线程的实现方式有哪些 extend Thread、implement runnable、implement callable

  • 序列化和反序列化

    • 定义:
      • Java序列化就是指把Java对象转换为字节序列的过程。
      • Java反序列化就是指把字节序列恢复为Java对象的过程。
    • 做用:
      • 序列化:在传递和保存对象时,保证对象的完整性和可传递性。对象转换为有序字节流,以便在网络上传输或者保存在本地文件中。
      • 反序列化:根据字节流中保存的对象状态及描述信息,经过反序列化重建对象。
    • 总结:核心做用就是对象状态的保存和重建。

设计模式

  • 单例模式

    • 什么状况下会用到:
      • 假若有不少地方都须要使用配置文件的内容,也就是说,不少地方都须要建立 AppConfig对象的实例,这就致使系统中存在多个AppConfig的实例对象,在配置文件内容不少的状况下会严重浪费内存资源。相似AppConfig这样的类,咱们但愿在程序运行期间只存在一个实例对象。
    • 优势:速度快、在使用时不须要建立、直接使用便可。
    • 缺点:可能存在内存浪费

END

相关文章
相关标签/搜索