JAVA知识

什么是Java虚拟机?为什么Java被称作是“平台无关的编程语言”?

java虚拟机:执行java字节码的虚拟进程
因为java源文件经过javac编译成的**.class字节码**的跨平台性,jvm能将相同的字节码转换成对应平台的机器码

JDK和JRE的区别是什么?

JRE: Java Runtime Environment 运行环境,它包括Java虚拟机、Java核心类库和支持文件。
JDK:Java Development Ki
t开发工具包
(JDK)是完整的Java软件开发包,包含了JRE,编译器和其他的工具(比如:JavaDoc,Java调试器),可以让开发者开发、编译、执行Java应用程序。

“static”关键字是什么意思?Java中是否可以覆盖(override)一个private或者是static的方法?

Static表示静态的意思,可用于修饰成员变量和成员函数,被静态修饰的成员函数只能访问静态成员,不可以访问非静态成员。
静态是随着类的加载而加载的,因此可以直接用类进行访问
重写是子类中的方法和子类继承的父类中的方法一样(函数名,参数,参数类型,反回值类型),但是子类中的访问权限要不低于父类中的访问权限。重写的前提是必须要继承private修饰不支持继承,因此被私有的方法不可以被重写。静态方法形式上可以被重写,即子类中可以重写父类中静态的方法。但是实际上从内存的角度上静态方法不可以被重写

是否可以在static环境中访问非static变量?

因为 static 成员是在类被加载时被加载的,而非 static 成员则是在类被实例化时创建的,只能通过实例进行访问。因为静态的成员属于类,随着类的加载而加载到静态方法区内存,当类加载时,此时不一定有实例创建,没有实例,就不可以访问非静态的成员。当在 static 环境中访问非 static 成员时,此时实例很可能还没有被创建,所以不能访问非 static 成员。

Java支持的数据类型有哪些?什么是自动拆装箱?

Java支持的数据类型包括两种:一种是基本数据类型(8种),包含byte,char,short, boolean ,int , long, float,double;另一种是引用类型:如String等,其实是对象的引用,JVM中虚拟栈中存的是对象的地址,创建的对象实质在堆中,通过地址来找到堆中的对象的过程,即为引用类型。自动装箱就是Java编译器在基本数据类型和对应的对象包装类型间的转化,即int转化为Integer,自动拆箱是Integer调用其方法将其转化为int的过程

Java中的方法覆盖(Overriding)和方法重载(Overload)是什么意思?

  • Java中的方法重载发生在同一个类里面两个或者是多个方法的方法名相同但是参数不同的情况。
    方法重载的原则:方法名称必须相同参数列表必须不同个数不同、或类型不同、参数类型排列顺序不同等)。方法的返回类型可以相同也可以不相同。仅仅返回类型不同不足以成为方法的重载。重载是发生在编译时的,因为编译器可以根据参数的类型来选择使用哪个方法。

  • 方法覆盖是说子类重新定义了父类的方法。方法覆盖必须有相同的方法名,参数列表和返回类型。覆盖者可能不会限制它所覆盖的方法的访问。方法重写的原则:重写方法的方法名称、参数列表必须与原方法的相同,返回类型可以相同也可以是原类型的子类型(从Java SE5开始支持);重写方法不能比原方法访问性差(即访问权限不允许缩小);重写方法不能比原方法抛出更多的异常;被重写的方法不能是final类型,因为final修饰的方法是无法重写的;被重写的方法不能为private,否则在其子类中只是新定义了一个方法,并没有对其进行重写;被重写的方法不能为static。如果父类中的方法为静态的,而子类中的方法不是静态的,但是两个方法除了这一点外其他都满足重写条件,那么会发生编译错误;反之亦然。即使父类和子类中的方法都是静态的,并且满足重写条件,但是仍然不会发生重写,因为静态方法是在编译的时候把静态方法和类的引用类型进行匹配。重写是发生在运行时的,因为编译期编译器不知道并且没办法确定该去调用哪个方法,JVM会在代码运行的时候作出决定。

  • 重写和重载的不同:
    方法重写要求参数列表必须一致,而方法重载要求参数列表必须不一致。
    方法重写要求返回类型必须一致(或为其子类型),方法重载对此没有要求。
    方法重写只能用于子类重写父类的方法,方法重载用于同一个类中的所有方法。
    方法重写对方法的访问权限和抛出的异常有特殊的要求,而方法重载在这方面没有任何限制。
    父类的一个方法只能被子类重写一次,而一个方法可以在所有的类中可以被重载多次
    重载是编译时多态,重写是运行时多态。

Java中,什么是构造方法?什么是构造方法重载?什么是复制构造方法?

新对象被创建的时候,构造方法会被调用,构造函数是为了初始化对象的。每一个类都有构造方法。在程序员没有给类提供构造方法的情况下,Java编译器会为这个类创建一个默认的构造方法。构造函数的函数名和类名一致,默认的构造函数没有参数,没有返回值,构造函数的函数体内,没有内容
Java中构造方法重载和方法重载很相似。可以为一个类创建多个构造方法。构造函数的重载是函数名与类名相同,参数类型不同,参数不同。同样的作用也是为了初始化对象的。 每一个构造方法必须有它自己唯一的参数列表。
Java中没有拷贝构造函数的概念!

Java支持多继承么?

Java中类不支持多继承,只支持单继承(即一个类只有一个父类)多方法。java只支持单继承,这是由于安全性的考虑,如果子类继承的多个父类里面有相同的方法或者属性,子类将不知道具体要继承哪个。 但是java中的接口支持多继承,,即一个子接口可以有多个父接口。而接口可以多实现,是因为接口只定义方法,而没有具体的逻辑实现,多实现也要重新实现方法。(接口的作用是用来扩展对象的功能,一个子接口继承多个父接口,说明子接口扩展了多个功能,当类实现接口时,类就扩展了相应的功能)。

面向对象的六大原则

单一职责(即一个类只负责一项职责)
里氏替换(所有引用基类的地方必须能透明地使用其子类的对象,也就是说子类可以扩展父类的功能,但不能改变父类原有的功能)
开闭原则
依赖倒置(尽量面向接口编程.)
接口隔离
迪米特法则(类的方法和属性能用私有的就尽量私有化)

接口和抽象类的区别是什么?

接口和抽象类的区别: 1、定义接口的关键字是interface ,抽象类的关键字是abstract class 2、接口中的方法都是抽象绝对抽象的,抽象类中可以有方法的实现(即非抽象方法) 3、一个类可以实现多个接口,但只能继承一个抽象类,所以一般来说,可以使用接口实现的尽量使用接口实现。比如Runnable就比Thread使用频率高的多 4、接口和抽象类中都可以定义变量,但是接口中定义的必须是public、static、****Final的,默认final的;抽象类中的变量跟普通类中的没有区别。 5、抽象类可以包含静态方法,但是接口中不可以! 6、接口中的方法必须是public的,抽象类中的方法可以是private、public 和 protected的 7、从设计上来说,接口是对一系列行为的抽象,而抽象类是对事物的抽象。 两者相同点: 1.抽象类和接口都不能直接实例化,如果要实例化,抽象类变量必须指向实现所有抽象方法的子类对象,接口变量必须指向实现所有接口方法的类对象。 2.抽象类里的抽象方法必须全部被子类所实现,如果子类不能全部实现父类抽象方法,那么该子类还只能是抽象类。同样,一个类实现接口的时候,如不能全部实现接口方法,那么该类也只能为抽象类。 两者不同点 : 什么时候使用抽象类和接口? 1.如果有一些需要默认实现的方法并且要求子类重写,那么使用抽象类。 2.如果想实现多重继承,那么必须使用接口 3.如果基本功能在不断改变,那么就需要使用抽象类

什么是值传递和引用传递?

值传递(pass by value)是指在调用函数时将实际参数复制一份传递到函数中,这样在函数中如果对参数进行修改,将不会影响到实际参数。
引用传递(pass by reference)是指在调用函数时将实际参数的地址直接传递到函数中,那么在函数中对参数所进行的修改,将影响到实际参数。
Java中方法参数传递方式是按值传递,无引用传递
如果参数是基本类型,传递的是基本类型的字面量值的拷贝
如果参数是引用类型,传递的是该参量所引用的对象在堆中地址值的拷贝
num是基本类型,值就直接保存在变量中。而str是引用类型,变量中保存的只是实际对象的地址。一般称这种变量为"引用",引用指向实际对象,实际对象中保存着内容。

进程和线程的区别是什么?

进程是分配资源的基本单位,而线程是独立运行和调度的基本单位。
任意时刻,一个CPU只能运行一个进程,进程获得资源后进行分配,由不同的线程来执行和协作。

  1. 进程:程序的一次执行
  2. 线程:CPU的基本调度单位
    线程与进程的区别归纳:
    a.地址空间和其它资源:进程间相互独立,同一进程的各线程间共享。某进程内的线程在其它进程不可见。一个进程的内存空间是可以被线程共享的,线程间的同步是为了防止竞争(因同时修改导致数据的不一致),所以要使用互斥锁,防止多个线程同时读写某一块内存区域。
    进程的内存空间一般是独立的,进程之间是一般不能分享彼此的资源的,进程想要互相通信,必须通过进程间通信(Inter-process communication,IPC)的机制来完成。进程在执行过程中拥有独立的内存单元,而多个线程共享内存,从而极大地提高了程序的运行效率
    每个独立的线程有一个程序运行的入口、顺序执行序列和程序的出口。但是线程不能够独立执行,必须依存在应用程序中,由应用程序提供多个线程执行控制
    b.通信:进程间通信IPC,线程间可以直接读写进程数据段(如全局变量)来进行通信——需要进程同步和互斥手段的辅助,以保证数据的一致性。
    c.调度和切换:线程上下文切换比进程上下文切换要快得多。
    d.在多线程OS中,进程不是一个可执行的实体。
    1.一个程序至少有一个进程,一个进程至少有一个线程
    2.线程的划分尺度小于进程,使得多线程程序的并发性高
    3.进程在执行过程中拥有独立的内存单元,而多个线程共享内存,从而极大地提高了程序的运行效率
    4.每个独立的线程有一个程序运行的入口、顺序执行序列和程序的出口。但是线程不能够独立执行,必须依存在应用程序中,由应用程序提供多个线程执行控制
    5.多线程的意义在于一个应用程序中,有多个执行部分可以同时执行。但操作系统并没有将多个线程看做多个独立的应用,来实现进程的调度和管理以及资源分配

创建线程有几种不同的方式?你喜欢哪一种?为什么?

1,继承Thread类,重写run方法;
2,实现Runnable接口,重写run方法,但是比继承Thread类好用,实现接口还可以继承类,避免了单继承带来的局限性;
3,实现callable接口,重写call方法,有返回值。
4,使用实现了Executor接口的ThreadPoolExecutor来创建线程池
一般情况下,常见的是第二种。

  • Runnable接口有如下好处:
    *①避免点继承的局限,一个类可以继承多个接口
    *②适合于资源的共享

概括的解释下线程的几种可用状态。

  1. 新建( new ):新创建了一个线程对象。
  2. 可运行( runnable ):线程对象创建后,其他线程(比如 main 线程)调用了该对象 的 start ()方法。该状态的线程位于可运行线程池中,等待被线程调度选中,获 取 cpu 的使用权 。
  3. 运行( running ):可运行状态( runnable )的线程获得了 cpu 时间片( timeslice ) ,执行程序代码。
  4. 阻塞( block ):阻塞状态是指线程因为某种原因放弃了 cpu 使用权,也即让出了 cpu timeslice ,暂时停止运行。直到线程进入可运行( runnable )状态,才有 机会再次获得 cpu timeslice 转到运行( running )状态。阻塞的情况分三种:
    (一). 等待阻塞:运行( running )的线程执行 o . wait ()方法, JVM 会把该线程放 入等待队列( waitting queue )中
    (二). 同步阻塞:运行( running )的线程在获取对象的同步锁时,若该同步锁 被别的线程占用,则 JVM 会把该线程放入锁池( lock pool )中。
    (三). 其他阻塞: 运行( running )的线程执行 Thread . sleep ( long ms )或 t . join ()方法,或者发出了 I / O 请求时, JVM 会把该线程置为阻塞状态。 当 sleep ()状态超时、 join ()等待线程终止或者超时、或者 I / O 处理完毕时,线程重新转入可运行( runnable )状态。
  5. 死亡( dead ):线程 run ()、 main () 方法执行结束,或者因异常退出了 run ()方法,则该线程结束生命周期。死亡的线程不可再次复生。

同步方法和同步代码块的区别是什么?

区别:
同步方法默认用this或者当前类class对象作为锁;同步方法锁的是当前对象,当一个线程使用该对象的同步方法时,会获得该对象的锁,其他线程不能访问该对象的同步方法
同步方法:(粗粒度锁):

    1.修饰一般方法: public synchronized void method (){...},获取的是当前调用               对象this上的锁

    2.修饰静态方法: public static synchronized void method (){...},获取当前类的             字节码对象上的锁

同步代码块可以选择以什么来加锁,比同步方法要更细颗粒度,我们可以选择只同步会发生同步问题的部分代码而不是整个方法;同步代码块(细粒度锁):

     synchronized ( obj ) {...},同步代码块可以指定获取哪个对象上的锁。

同步方法使用关键字 synchronized修饰方法,而同步代码块主要是修饰需要进行同步的代码,用 s**ynchronized(object){代码内容}**进行修饰;

在监视器(Monitor)内部,是如何做线程同步的?程序应该做哪种级别的同步?

监视器和锁在Java虚拟机中是一块使用的。在 java 虚拟机中, 每个对象( Object 和 class )通过某种逻辑关联监视器,每个监视器和一个对象引用相关联, 为了实现监视器的互斥功能, 每个对象都关联着一把锁. 一旦方法或者代码块被 synchronized 修饰, 那么这个部分就放入了监视器的监视区域,监视器监视一块同步代码块,确保一次只有一个线程执行同步代码块线程在获取锁之前不允许执行同步代码。另外 java 还提供了显式监视器( Lock )和隐式监视器( synchronized )两种锁方案。

什么是死锁(deadlock)?

所谓死锁是指多个进程因竞争资源而造成的一种僵局(互相等待),若无外力作用,这些进程都将无法向前推进。死锁产生的4个必要条件:
互斥条件:进程要求对所分配的资源(如打印机)进行排他性控制,即在一段时间内某资源仅为一个进程所占有。此时若有其他进程请求该资源,则请求进程只能等待。
不剥夺条件:进程所获得的资源在未使用完毕之前,不能被其他进程强行夺走,即只能 由获得该资源的进程自己来释放(只能是主动释放)。
请求和保持条件:进程已经保持了至少一个资源,但又提出了新的资源请求,而该资源已被其他进程占有,此时请求进程被阻塞,但对自己已获得的资源保持不放
循环等待条件:存在一种进程资源的循环等待链,链中每一个进程已获得的资源同时被 链中下一个进程所请求。

如何确保N个线程可以访问N个资源同时又不导致死锁?

多线程产生死锁需要四个条件,分别是互斥性,保持和请求,不可剥夺性还有要形成闭环,这四个条件缺一不可,只要破坏了其中一个条件就可以破坏死锁,其中最简单的方法就是线程都是以同样的顺序加锁和释放锁,也就是破坏了第四个条件指定获取锁的顺序,并强制线程按照指定的顺序获取锁。

Java集合类框架的基本接口有哪些?

总共有两大接口:Collection 和Map ,一个元素集合,一个是键值对集合; 其中List和Set接口继承了Collection接口,一个是有序元素集合,一个是无序元素集合; 而ArrayList和 LinkedList 实现了List接口,HashSet实现了Set接口,这几个都比较常用; HashMap 和HashTable实现了Map接口,并且HashTable是线程安全的,但是HashMap性能更好;

为什么集合类没有实现Cloneable和Serializable接口?

克隆(cloning)或者是序列化(serialization)的语义和含义是跟具体的实现相关的。因此,应该由集合类的具体实现来决定如何被克隆或者是序列化。

什么是迭代器(Iterator)?

迭代器是一种设计模式,它是一个对象,它可以遍历并选择序列中的对象,而开发人员不需要了解该序列的底层结构。迭代器通常被称为“轻量级”对象,因为创建它的代价小。

Java中的Iterator功能比较简单,并且只能单向移动:

(1) 使用方法iterator()要求容器返回一个Iterator。第一次调用Iterator的next()方法时,它返回序列的第一个元素。注意:iterator()方法是java.lang.Iterable接口,被Collection继承。
  (2) 使用next()获得序列中的下一个元素。
  (3) 使用hasNext()检查序列中是否还有元素。
  (4) 使用remove()将迭代器新返回的元素删除。
  Iterator是Java迭代器最简单的实现,为List设计的ListIterator具有更多的功能,它可以从两个方向遍历List,也可以从List中插入和删除元素。

iterator和ListIterator的区别是什么?

Iterator可用来遍历Set和List集合,但是ListIterator只能用来遍历List
Iterator对集合只能是前向遍历,ListIterator既可以前向也可以后向
ListIterator实现了Iterator接口,并包含其他的功能,比如:增加元素,替换元素,获取前一个和后一个元素的索引,等等。
在这里插入图片描述

快速失败(fail-fast)和安全失败(fail-safe)的区别是什么?

一:快速失败(fail—fast)
在用迭代器遍历一个集合对象时,如果遍历过程中对集合对象的结构进行了修改(增加、删除),则会抛出Concurrent Modification Exception
原理:迭代器在遍历时直接访问集合中的内容,并且在遍历过程中使用一个 modCount 变量。集合在被遍历期间如果结构发生变化,就会改变modCount的值。每当迭代器使用hashNext()/next()遍历下一个元素之前,都会检测modCount变量是否为expectedmodCount值,是的话就返回遍历;否则抛出异常,终止遍历。
注意:这里异常的抛出条件是检测到 modCount!=expectedmodCount 这个条件。如果集合发生变化时修改modCount值刚好又设置为了expectedmodCount值,则异常不会抛出。因此,不能依赖于这个异常是否抛出而进行并发操作的编程,这个异常只建议用于检测并发修改的bug。
场景:java.util包下的集合类都是快速失败的,不能在多线程下发生并发修改(迭代过程中被修改)。
二:安全失败(fail—safe)
采用安全失败机制的集合容器,在遍历时不是直接在集合内容上访问的,而是先复制原有集合内容,在拷贝的集合上进行遍历
原理:由于迭代时是对原集合的拷贝进行遍历,所以在遍历过程中对原集合所作的修改并不能被迭代器检测到,所以不会触发Concurrent Modification Exception。
缺点:基于拷贝内容的优点是避免了Concurrent Modification Exception,但同样地,迭代器并不能访问到修改后的内容,即:迭代器遍历的是开始遍历那一刻拿到的集合拷贝,在遍历期间原集合发生的修改迭代器是不知道的。
场景:java.util.concurrent包下的容器都是安全失败,可以在多线程下并发使用,并发修改

Java中的HashMap的工作原理是什么?

Java中的HashMap是以键值对(key-value)的形式存储元素的。HashMap需要一个hash函数,它使用hashCode()和equals()方法来向集合/从集合添加和检索元素。当调用put()方法的时候,HashMap会计算key的hash值,然后把键值对存储在集合中合适的索引上。如果key已经存在了,value会被更新成新值。HashMap的一些重要的特性是它的容量(capacity),负载因子(load factor)和扩容极限(threshold resizing)

hashCode()和equals()方法的重要性体现在什么地方?

hashcode和equals组合在一起确定元素的唯一性
通过hashCode和equals方法保证元素的唯一性,当重写equals方法时,必须重写hashCode方法,因为如果不重写这两个方法,就会默认使用Object的方法,一般是不相同的,所以就会导致存储了重复值,与hashset、hashmap等性质冲突。
哈希表判断通过hashCode和equals方法判断元素是否相同的步骤如下所示:
先看hashCode的值是否相同,因为这个是逻辑内存地址,如果
不相同:则表示对象不相同
如果相同:继续在桶结构中执行equals方法,equals方法返回;True:表示对象相同, Fasle:表示对象不相同
只有hashCode和equals方法都返回true,才表示对象相同

HashMap和Hashtable有什么区别?

HashMap和Hashtable都实现了Map接口,因此很多特性非常相似。但是,他们有以下不同点:

1、HashMap是非线程安全的,HashTable是线程安全的。
2、HashMap的键和值都允许有null值存在,而HashTable则不行。
3、因为线程安全的问题,HashMap效率比HashTable的要高。
4、Hashtable是同步的,而HashMap不同步。因此,HashMap更适合于单线程环境,而Hashtable适合于多线程环境。
HashMap提供了可供应用迭代的键的集合,因此,HashMap是快速失败的。另一方面,Hashtable提供了对键的列举(Enumeration)。
一般现在不建议用HashTable, ①是HashTable是遗留类,内部实现很多没优化和冗余。②即使在多线程环境下,现在也有同步的ConcurrentHashMap替代,没有必要因为是多线程而用HashTable。

数组(Array)和列表(ArrayList)有什么区别?什么时候应该使用Array而不是ArrayList?

存储内容比较:
Array数组可以包含基本类型和对象类型
ArrayList却只能包含对象类型
但是需要注意的是:Array数组在存放的时候一定是同种类型的元素。ArrayList就不一定了,因为ArrayList可以存储Object。
空间大小比较:
它的空间大小是固定的,空间不够时也不能再次申请,所以需要事前确定合适的空间大小。
ArrayList的空间是动态增长的,如果空间不够,它会创建一个空间比原空间大一倍的新数组,然后将所有元素复制到新数组中,接着抛弃旧数组。而且,每次添加新的元素的时候都会检查内部数组的空间是否足够。(比较麻烦的地方)。
方法上的比较:
ArrayList作为Array的增强版,当然是在方法上比Array更多样化,比如添加全部addAll()、删除全部removeAll()、返回迭代器iterator()等。
适用场景:
如果想要保存一些在整个程序运行期间都会存在而且不变的数据,我们可以将它们放进一个全局数组里,但是如果我们单纯只是想要以数组的形式保存数据,而
不对数据进行增加等操作,只是方便我们进行查找的话,那么,我们就选择ArrayList
。而且还有一个地方是必须知道的,就是如果我们需要对元素进行频繁的移动或删除,或者是处理的是超大量的数据,那么,使用ArrayList就真的不是一个好的选择,因为它的效率很低,使用数组进行这样的动作就很麻烦,那么,我们可以考虑选择LinkedList

ArrayList和LinkedList有什么区别?

ArrayList和LinkedList都实现了List接口,他们有以下的不同点:
ArrayList是基于索引的数据接口,它的底层是数组。它可以以O(1)时间复杂度对元素进行随机访问。与此对应,LinkedList是以元素列表的形式存储它的数据,每一个元素都和它的前一个和后一个元素链接在一起,在这种情况下,查找某个元素的时间复杂度是O(n)
相对于ArrayList,LinkedList的插入,添加,删除操作速度更快,因为当元素被添加到集合任意位置的时候,不需要像数组那样重新计算大小或者是更新索引。
LinkedList比ArrayList更占内存,因为LinkedList为每一个节点存储了两个引用,一个指向前一个元素,一个指向下一个元素。
也可以参考ArrayList vs. LinkedList。

Comparable和Comparator接口是干什么的?列出它们的区别。

Comparable & Comparator 都是用来实现集合中元素的比较、排序的,只是 Comparable 是在集合内部定义的方法实现的排序,Comparator 是在集合外部实现的排序,所以,如想实现排序,就需要在集合外定义 Comparator 接口的方法或在集合内实现 Comparable 接口的方法。
Comparator位于包java.util下,而Comparable位于包 java.langComparable 是一个对象本身就已经支持自比较所需要实现的接口(如 String、Integer 自己就可以完成比较大小操作,已经实现了Comparable接口) 自定义的类要在加入list容器中后能够排序,可以实现Comparable接口,在用Collections类的sort方法排序时,如果不指定Comparator,那么就以自然顺序排序, 这里的自然顺序就是实现Comparable接口设定的排序方式。 而 Comparator 是一个专用的比较器,当这个对象不支持自比较或者自比较函数不能满足你的要求时,你可以写一个比较器来完成两个对象之间大小的比较。 可以说一个是自已完成比较,一个是外部程序实现比较的差别而已。 用 Comparator 是策略模式(strategy design pattern),就是不改变对象自身,而用一个策略对象(strategy object)来改变它的行为。
comparable接口是在内部类通过重写compareTo方法实现的,而comparator接口则是在外部类通过重写compare与equal方法实现的。
comparable接口实现较简单高内聚。但对于多元素排序不方便,因为在重写compareTo方法时事先定义好了元素比较顺序;comparator接口实现较复杂,可能定义多个外部类,但对于多元素使用起来很方便。

什么是Java优先级队列(Priority Queue)?

优先级队列中的元素可以按照任意的顺序插入,却总是按照排序的顺序进行检索。无论何时调用remove方法,总会获得当前优先级队列中的最小元素,但并不是对所有元素都排序。它是采用了(一个可以自我调整的二叉树),执行增加删除操作后,可以让最小元素移动到根。每次从队列中取出的是具有最高优先权的元素。如果不提供 Comparator 的话,优先队列中元素默认按自然顺序排列,也就是数字默认是小的在队列头,字符串则按字典序排列(参阅 Comparable ),也可以根据 Comparator 来指定,这取决于使用哪种构造方法。优先级队列不允许 null 元素。最后,PriorityQueue不是线程安全的,入队和出队的时间复杂度是O(log(n))

你了解大O符号(big-O notation)么?你能给出不同数据结构的例子么?

大O符号描述了当数据结构里面的元素增加的时候,算法的规模或者是一个渐进上界
大O符号也可用来描述其他的行为,比如:内存消耗。因为集合类实际上是数据结构,我们一般使用大O符号基于时间,内存和性能来选择最好的实现。大O符号可以对大量数据的性能给出一个很好的说明。

如何权衡是使用无序的数组还是有序的数组?

查找复杂度:有序数组O(log n)(二分查找) ,无序数组 O(n)(需要比较,移动数据)
插入复杂度:有序数组O(n)(循环遍历) ,无序数组 O(1)(放到末尾)

Java集合类框架的最佳实践有哪些?

如何选择集合框架:数组/集合;集合中map or collections;collecions中list or set;list中Arraylist or Linkedlist
在这里插入图片描述

Enumeration接口和Iterator接口的区别有哪些?

Enumeration速度是Iterator的2倍,同时占用更少的内存。但是,Iterator远远比Enumeration****安全,因为其他线程不能够修改正在被iterator遍历的集合里面的对象。同时,Iterator允许调用者删除底层集合里面的元素,这对Enumeration来说是不可能的。因为 Iterator 支持 fail-fast 机制,当你在遍历的时候,如果另起一个线程来修改它(集合的内容)的结构,这时迭代器会立马感知到,引起快速失败,抛出ConcurrentModificationException异常。而 Enumeration 不支持

HashSet和TreeSet有什么区别?

1、HashSet是由一个hash表来实现的,因此,它的元素是无序的。add(),remove(),contains()方法的时间复杂度是O(1)。HashSet对速度进行了优化,提供了最快的查找速度,无特殊说明一般默认是用这个Set放到HashSet中的元素要保证唯一,应该重写hashCode方法和equals方法,但是不能保证元素有序,底层实现是哈希结构
2、TreeSet底层实现是红黑树(自平衡二叉树),不但能保证元素唯一,还能元素保证有序,存放到TreeSet中的元素应该实现Comparable接口,重写compareTo方法,否则会抛出ClassCastException按照该方法指定的规则维持元素的顺序。TreeSet是由一个树形的结构来实现的,它里面的元素是有序的。因此,add(),remove(),contains()方法的时间复杂度是O(logn)
3、LinkedHashSet,底层实现是哈希表和链表,保持了HashSet的速度,还能按照插入元素的顺序维持元素顺序

Java中垃圾回收有什么目的?什么时候进行垃圾回收?

垃圾回收的目的是识别并且丢弃应用不再使用的对象来释放和重用资源,为更好的释放和管理内存。垃圾回收线程是一个守护线程(指在程序运行的时候在后台提供一种通用服务的线程)
触发主GC(Garbage Collector,垃圾回收)的条件:
(1)当应用程序空闲时,即没有应用线程在运行时,GC会被调用。
(2)Java堆内存不足时,GC会被调用。
要根据垃圾回收机制的两种说法(引用计算法)(根搜索算法)但是引用计数法是不准确有漏洞的因为当两个已经死亡的对象之间有相互引用,这样就不会回收会浪费内存;所以根搜索算法更加准确因为根搜索算法是从一个GC ROOT作为起点往下搜索,搜索的路径是引用链,当一个对象无法通过引用链GC RROT时候就可以判断该对象可以回收了。
程序员可以主动申请垃圾回收,使用代码System.gc();但是由于垃圾回收是由JVM负责的,因此请求可能被JVM可以拒绝

System.gc()和Runtime.gc()会做什么事情?

调用 System.gc() 实际上等效于调用: Runtime.getRuntime().gc()这两个方法用来提示JVM要进行垃圾回收。但是,立即开始还是延迟进行垃圾回收是取决于JVM的。

finalize()方法什么时候被调用?析构函数(finalization)的目的是什么?

释放对象占用的内存之前,垃圾收集器会调用对象的finalize()方法。object 类的一个方法,在垃圾回收期执行时会调用被回收对象的finalize方法,可以覆盖此方法来实现对其他资源的回收,例如关闭文件等,需要注意的是,一旦垃圾回收期准备好释放对象占用的内存空间,将首先调用其finalize方法,并且在下一次垃圾回收动作发生时才会真正回收对象占用的内存
析构finalization,比如你在调用了一些native的方法,可以在finaliztion里去调用释放函数,析构finalization用于调用本地方法时,在finalization中释放对象占有的资源

如果对象的引用被置为null,垃圾收集器是否会立即释放对象占用的内存?

不会立即释放对象占用的内存。 如果对象的引用被置为null,只是断开了当前线程栈帧中对该对象的引用关系,而 垃圾收集器是运行在后台的线程,只有当用户线程运行到安全点(safe point)或者安全区域才会扫描对象引用关系,扫描到对象没有被引用则会标记对象,这时候仍然不会立即释放该对象内存,因为有些对象是可恢复的(在 finalize方法中恢复引用 )。只有确定了对象无法恢复引用的时候才会清除对象内存。

Java堆的结构是什么样子的?什么是堆中的永久代(Perm Gen space)?

堆是动态内存分配意义上的堆——用于管理动态生命周期的内存区域。是运行时数据区,所有类的实例和数组都是在堆上分配内存。它在JVM启动的时候被创建。对象所占的堆内存是由自动内存管理系统也就是垃圾收集器回收
堆内存是由存活和死亡的对象组成的。存活的对象是应用可以访问的,不会被垃圾回收。死亡的对象是应用不可访问尚且还没有被垃圾收集器回收掉的对象。一直到垃圾收集器把这些对象回收掉之前,他们会一直占据堆内存空间。
Young:主要是用来存放新生的对象
Old:主要存放应用程序中生命周期长的内存对象
Permanent:是指内存的永久保存区域,主要存放Class和Meta的信息,Class在被 Load的时候被放入PermGen space区域.
在jdk1.8之后永久代被元空间代替(Metaspace),原因是在方法区中实现垃圾回收的条件比较苛刻,因此存在着内存溢出的风险。在jdk1.8之后,当方法区内存使用较多时,元空间会使用物理内存,减少了风险。

串行(serial)收集器和吞吐量(throughput)收集器的区别是什么?

串行收集器应用于小型应用程序,一般是单线程方式扫描和复制吞吐量收集器应用于并行环境中,适用于中大型规模数据的应用程序。串行收集器的关注点是尽可能地缩短垃圾收集时用户线程的停顿时间,而吞吐量收集器则的目标是达到一个可控制的吞吐量。此外,吞吐量收集器具有自适应调节策略的能力。

在Java中,对象什么时候可以被垃圾回收?

当一个对象到GC Roots不可达时,在下一个垃圾回收周期中尝试回收该对象,如果该对象重写了finalize()方法,并在这个方法中成功自救(将自身赋予某个引用),那么这个对象不会被回收。但如果这个对象没有重写finalize()方法或者已经执行过这个方法,也自救失败,该对象将会被回收

JVM的永久代中会发生垃圾回收么?

永生代也是可以回收的,条件是 1.该类的实例都被回收。 2.加载该类的classLoader已经被回收 3.该类不能通过反射访问到其方法,而且该类的java.lang.class没有被引用 当满足这3个条件时,是可以回收,但回不回收还得看jvm。

Java中的两种异常类型是什么?他们有什么区别?

Throwable包含了错误(Error)和异常(Excetion两类)
Exception又包含了运行时异常(RuntimeException, 又叫非检查异常)和非运行时异常(又叫检查异常)
(1) Error是程序无法处理了, 如果OutOfMemoryError、OutOfMemoryError等等, 这些异常发生时, java虚拟机一般会终止线程 .
(2) 运行时异常RuntimeException都是RuntimeException类及其子类,如 NullPointerException、IndexOutOfBoundsException等, 这些异常是不检查的异常, 是在程序运行的时候可能会发生的, 所以程序可以捕捉, 也可以不捕捉. 这些错误一般是由程序的逻辑错误引起的, 程序应该从逻辑角度去尽量避免.运行异常的特点是 Java编译器不去检查它,也就是说,当程序中可能出现这类异常时,即使没有用try…catch语句捕获它,也没有用throws字句声明抛出它,还是会编译通过
(3) 检查异常是运行时异常以外的异常, 也是Exception及其子类, 这些异常从程序的角度来说是必须经过捕捉检查处理的, 否则不能通过编译. 如IOException、SQLException等.这种异常的特点是要么用 try…catch捕获处理,要么用throws语句声明抛出,否则编译不会通过。
链接:https://www.nowcoder.com/questionTerminal/a679dbc19a6a41c580a50e86fbdfc185
来源:牛客网

Java中Exception和Error有什么区别?

Error类和Exception类的父类都是throwable类,他们的区别是:
Error类一般是指与虚拟机相关的问题,如系统崩溃,虚拟机错误,内存空间不足,方法调用栈溢等。对于这类错误的导致的应用程序中断,仅靠程序本身无法恢复和和预防,遇到这样的错误,建议让程序终止
Exception类表示程序可以处理的异常,可以捕获且可能恢复。遇到这类异常,应该尽可能处理异常,使程序恢复运行,而不应该随意终止异常。
Exception类又分为运行时异常(Runtime Exception)和受检查的异常(Checked Exception ),运行时异常;ArithmaticException,IllegalArgumentException,编译能通过,但是一运行就终止了,程序不会处理运行时异常,出现这类异常,程序会终止。而受检查的异常,要么用try。。。catch捕获,要么用throws字句声明抛出,交给它的父类处理,否则编译不会通过。

throw和throws有什么区别?

1、Throw用于方法内部,Throws用于方法声明上
2、Throw后跟异常对象,Throws后跟异常类型
3、Throw后只能跟一个异常对象,Throws后可以一次声明多种异常类型

  1. Throw用于抛出异常对象,后面跟的时异常对象。是语句抛出一个异常。多用在函数内
    语法:throw(异常对象)
    throw e;
    2.Throws是方法可能抛出异常的声明(用在声明方法时,表示该方法可能要抛出异常)
    语法:修饰符 返回值类型 方法名 参数类型 throws异常类
    public void test throws Exception1,Exception2(){}

异常处理完成以后,Exception对象会发生什么变化?

异常处理完,下一轮判断gc roots是否可达–>是否能调用finalize复活,要是没有就被垃圾回收掉

finally代码块和finalize()方法有什么区别?

finally作为异常处理的一部分,只能用在try/catch语句快中,finally代码块中的语句一定会被执行,经常被用来释放资源,如IO流和数据库资源的释放。
finalize是Object类的一个方法,该方法在Object类中声明:
protected void finalize() throws Throwable { }
在垃圾回收器执行时会调用被回收对象的finalize()方法,可以覆盖此方法来实现对其资源的回收。注意:一旦垃圾回收器准备释放某个对象占用的空间,将首先调该对象的finalize()方法,并且在下一次垃圾回收动作发生时,才真正将该对象占用的内存回收。

final

final是关键字,final可以修饰类、方法、属性。
如果一个类被final修饰,那么这个类就是最终类,不能派生出新的子类,不能作为父类被继承,该类中的所有方法都不能被重写,但是final类中的成员变量是可以改变的,要想final类中的成员变量的不可以改变,必须给成员变量添加final修饰。因此,一个类不能同时被final和abstract修饰,这两个关键字相互矛盾。
如果final修饰方法,那么这个方法是最终方法,不允许任何子类重写该方法,但子类仍可以使用该方法,注意:final参数用来表示这个参数在这个函数内部不允许被修改。
final修饰属性,被final修饰的变量不可变。这里的不可变有两重含义:引用不可变和对象不可变。final指的是引用不可变,即它只能指向初始化时指向的那个对象,而不关心指向对象内容的变化。因此,被final修饰的变量必须初始化,该变量其实就是常量。

什么是Applet?

Java应用小程序,可以直接嵌入到网页中,并能够产生特殊的效果,applet经编译后会产生.class文件,把.class文件嵌入到html页面中,用户在链接网页时,applet便会伴随网页一起下载到用户计算机运行。
applet主要用来创建动态交互的web应用程序。

解释一下Applet的生命周期

applet可以经历下面的状态:
Init:每次被载入的时候都会被初始化。
Start:开始执行applet。
Stop:结束执行applet。
Destroy:卸载applet之前,做最后的清理工作。

当applet被载入的时候会发生什么?

Applet和普通的Java应用程序有什么区别?

首先,创建applet控制类的实例,然后初始化applet,最后开始运行

(1).applet不能单独的运行,必须依附并且嵌入到html代码中,并且在兼容java的浏览器中才能运行;而java应用程序经过编译后,可以在任意支持java虚拟机的平台独立运行
(2).applet里面没有main,而每个java应用程序必有一个main
(3).applet的解释器是嵌入到浏览器中的一部分,必须通过浏览器或者applet观察器才能执行。而java解释器可以时它边解释边执行 。
(4).applet可以使用浏览器或者appletvivewer直接提供图形界面,而java应用程序必须编写相应的代码才行。 (5).applet不能直接操作主机磁盘的io读和写,而java应用程序可以

Java applet有哪些限制条件?

类库或本地方法、读写文件、读取系统属性、网络连接、程序
主要是由于安全的原因,给applet施加了以下的限制:
applet不能够载入类库或者定义本地方法
applet不能在宿主机上读写文件。
applet不能读取特定的系统属性
applet不能发起网络连接,除非是跟宿主机。
applet不能够开启宿主机上其他任何的程序

什么是不受信任的applet?

不受信任的applet是不能访问或是执行本地系统文件的Java applet,默认情况下,所有下载的applet都是不受信任的。

从网络上加载的applet和从本地文件系统加载的applet有什么区别?

当applet是从网络上加载的时候,applet是由applet类加载器载入的,它受applet安全管理器的限制。
当applet是从客户端的本地磁盘载入的时候,applet是由文件系统加载器载入的。
文件系统载入的applet允许在客户端读文件,写文件,加载类库,并且也允许执行其他程序,但是,却通不过字节码校验

static

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

String、StringBuffer、StringBuilder

String:字符串常量,对其改变,相当于创建一个新的对象
StringBuffer:字符串变量,线程安全,在StringBuilder在加上synchronized修饰
StringBuilder:字符串变量,非线程安全

Student s=new Student()内存中做了哪些事

1.加载student.class加载到内存
2.在栈内存为s创建空间
3.堆内存为学生对象申请空间
4.成员变量默认初始化
5.成员变量显示初始化
6.构造方法给成员变量初始化
7.数据初始化完毕,将堆内存的地址赋值给栈内存的s变量

java如何进行对象实例化

1.存在继承关系,先父类后子类
2.先静态变量、静态代码块,再构造函数
3.父类初始化完成,才子类
无继承关系:
静态变量、静态代码块、默认初始化、显示初始化、非静态代码块、构造函数
有继承关系:
父类静态变量、静态代码块—子类静态变量、静态代码块–父类默认初始化—子类默认初始化—父类显示初始化、非静态代码块、构造函数----子类显示初始化、非静态代码块、构造函数

jdk7 Hashcode字符串攻击

HashMap动态使用一个treemap来替换掉

String重写Object的Hashcode和toString

equal方法被重写时,必须重写hashcode,相对等的两个对象必须有相同的hashcode
不重写hashcode会导致存储散列集合时,原对象.equal(新对象),如果不重写,集合中会存储两个值相等的对象。

异常

在这里插入图片描述

==、equals、hashcode

1.==针对变量的值,基本数据类型比较数值,引用类型比较地址(即是否为同一对象)
2.equals针对对象的内容;字符串序列相同返回true;引用类型必须指向同一引用对象
3.hashcode比较对象的,区别equals,hashcode用户一般不调用
应用:集合中添加新元素,先用hashcode比较物理地址无元素则添加,有元素再用equals,相同则不添加,不同则散列其他地址。调用equals次数降低
在这里插入图片描述

集合

hashxx保证元素唯一性:hashcode、equals
treeset保证元素唯一性:排序compareTo return0
在这里插入图片描述

抽象类和接口

抽象类:
有构造方法
可以存在抽象方法
存在普通属性、方法,静态属性和方法
抽象类和抽象方法被abstract修饰
不能被实例化
接口:
无构造方法
方法一定是抽象方法
方法 public abstract;
只有常量,变量被public static final修饰
继承多个接口
接口不能继承类

在这里插入图片描述
HashMap底层实现和扩容
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
**扩容:**初始化16,负载因子0.75,超过160.75=12,则扩大一倍216

图的存储方式,优缺点

邻接表和邻接矩阵
邻接矩阵:二维数组,aij=1/0;易判断两点之间的关系,易求得度;占用空间大
邻接表:链表;易得到顶点的出度,不易得到入度和关系

Object类中方法

在这里插入图片描述

Try catch finally执行顺序

在这里插入图片描述

四种引用类型

强引用:垃圾回收器永远不回收
软引用:内存足够时,垃圾回收器不回收,内存不足时,回收
弱引用:垃圾回收器线程扫描到所管内存,不管内存足不足够,都回收
虚引用:任何时候都可能被回收
虚引用必须和引用队列相结合

for each 和for

for each:需要实现iterable接口,本质上是指针操作
for:非数组存储方式时,开销大
在这里插入图片描述

线程池:

空闲等等状态,线程复用
在这里插入图片描述

多线程好处

时间、开销小于进程、CPU多核、简化程序
在这里插入图片描述

创建多线程的方式

在这里插入图片描述

线程安全

在这里插入图片描述
在这里插入图片描述

多线程同步的实现方法

1.synchronized
2.lock:lock()阻塞锁;trylock()非阻塞锁;lockinterrupty()获取不到用interrupt方法
3.wait和notify

在这里插入图片描述

关闭线程的方式

1.设置一个flag标志位,flag=false时关闭
在这里插入图片描述
2.在run方法中使用interrupt捕获异常让线程安全退出,用来打破线程非运行状态的阻塞
在这里插入图片描述

synchronized与lock区别

synchronized是关键字,自动释放锁的占用,可以用在方法和代码块上;托管给JVM执行,等待线程释放,;
lock是类,用户手动去释放锁,在finally中释放锁;lock锁通过代码实现;
在这里插入图片描述
在这里插入图片描述

synchronized

获取对象的锁

在这里插入图片描述
在这里插入图片描述

volatile

修饰的变量不保留拷贝,直接访问主内存中,保证变量修改的实时可见性,被修饰意味着这个变量随时会被其他线程修改
修饰的成员变量,每次被线程访问,强迫从共享内存中重读该成员变量的值。

在这里插入图片描述

volatile与synchronized区别

volatile:变量,不具备原子特性,不会造成线程阻塞
synchronized:变量和方法,原子和可见,会造成线程阻塞,编译器优化
在这里插入图片描述

JVM垃圾处理方法

1.标记-清除:效率不高,大量内存碎片
2.复制:内存一分为2,将存活的复制到另一个;实现简单,运行高效;内存减小为一半
3.标记-整理:不清楚,存活对象向一端移动,更新引用对象的指针,对象移动,成本较高;不会产生内存碎片
4.分代收集:新生代:复制;老年代:标记-整理
在这里插入图片描述

JVM垃圾回收

java堆
年轻代:新生,生命周期短;分为Eden和Survivor(From、To)from到to经历复制算法
年老代:生命周期长
持久代:静态文件

在这里插入图片描述
在这里插入图片描述

JAVA内存划分

在这里插入图片描述
程序计数器:线程私有,非native保存当前需要执行的指令地址
java栈:内存模型,局部变量表,操作数栈,运行时常量池的引用,方法返回地址,附加信息
本地方法栈:线程私有
方法区:线程共享;类的信息,静态变量、常量、编译后的代码
堆:对象实例本身及数组;只有一个;线程共享

在这里插入图片描述

查内存泄漏

在这里插入图片描述

JAVA内存泄漏

无用对象持续占有内存,占有的内存得不到释放,造成内存空间的浪费
原因:
1.静态集合类 :静态变量的生命周期和应用程序一样,引用对象的object不被释放,占用vector
2.集合里面的属性被修改,再有remove()无效
3.增加监听器,释放时忘记删除监听器
4.各种连接,如果不显示close(),不会自动被回收;数据库,io,网络连接
5.不正确使用单例模式,单例对象持有外部对象的引用,这个外部对象无法被JVM正常回收

GC判定方法

引用计数和可达性分析
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值