一.java基础
1.1 为什么java是跨平台的
因为java代码是平台无关的,jvm是平台相关的,不同平台都有不同的jvm实现。 java代码需要先经过编译器前端编译(.class),然后经过虚拟机后端编译(二进制),不同的操作系统上面要安装对应的JVM,运行对应的二进制文件即可。
1.2 jdk,jre,jvm
JDK :Java Development Kit是提供给Java开发人员使用的,其中包含了Java的开发工具,也包括了JRE。所以安装了JDK,就无需再单独安装JRE了。其中的开发工具:编译工具(javac.exe),打包工具(jar.exe)等
JRE :Java Runtime Environment包括Java虚拟机和Java程序所需的核心类库等。,Jre大部分都是 C 和 C++ 语言编写的,他是我们在编译java时所需要的基础的类库.核心类库主要是java.lang
包:包含了运行Java程序必不可少的系统类,如基本数据类型、基本数学函数、字符串处理、线
程、异常处理类等,系统缺省加载这个包,如果想要运行一个开发好的Java程序,计算机中只需要安装JRE即可。
Jvm:Java Virtual Machine是Java虚拟机,在倒数第二层 由他可以在(最后一层的)各种平台上运行 ,Java程序需要运行在虚拟机上,不同的平台有自己的虚拟机,因此Java语言可以实现跨平台。
1.3 基本数据类型
1.4 访问修饰符
private : 在同一类内可见。使用对象:变量、方法。 注意:不能修饰类(外部类)
default (即缺省,什么也不写,不使用任何关键字): 在同一包内可见,不使用任何修饰符。
使用对象:类、接口、变量、方法。
protected : 对同一包内的类和所有子类可见。使用对象:变量、方法。 注意:不能修饰类
(外部类)。
public : 对所有类可见。使用对象:类、接口、变量、方法
1.5 面向对象三大特征
封装把一个对象的属性私有化,同时提供一些可以被外界访问的属性的方法,如果属性不想被外界
访问,我们大可不必提供方法给外界访问。
继承是使用已存在的类的定义作为基础建立新类的技术,新类的定义可以增加新的数据或新的功
能,也可以用父类的功能,但不能选择性地继承父类。通过使用继承我们能够非常方便地复用以前
的代码。
多态 多态是同一个行为具有多个不同表现形式或形态的能力。多态就是同一个接口,使用不同的实例而执行不同操作,
1.6 == 和 equals 的区别是什么
== : 它的作用是判断两个对象的地址是不是相等。即,判断两个对象是不是同一个对象。(基本数
据类型 == 比较的是值,引用数据类型 == 比较的是内存地址)
equals() : 它的作用也是判断两个对象是否相等。但它一般有两种使用情况:
情况1:类没有覆盖 equals() 方法。则通过 equals() 比较该类的两个对象时,等价于通过
“==”比较这两个对象。
情况2:类覆盖了 equals() 方法。一般,我们都覆盖 equals() 方法来两个对象的内容相等;
若它们的内容相等,则返回 true (即,认为这两个对象相等)。
hashCode()与equals()的相关规定 如果两个对象相等,则hashcode一定也是相同的 两个对象相等,对两个对象分别调用equals方法都返回true 两个对象有相同的hashcode值,它们也不一定是相等的 因此,equals 方法被覆盖过,则 hashCode 方法也必须被覆盖 hashCode() 的默认行为是对堆上的对象产生独特值。如果没有重写 hashCode(),则该 class 的两个对象 无论如何都不会相等(即使这两个对象指向相同的数据)
1.7 Integer a= 127 与 Integer b = 127相等吗
如果整型字面量的值在-128到127之间,那么自动装箱时不会new新的Integer对象,而是直接引用常量池中的 Integer对象,是true如果>127结果是false
1.8 反射是什么?
JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任 意一个对象,都能够调用它的任意一个方法和属性;这种动态获取的信息以及动态调用对象的方法 的功能称为java语言的反射机制。缺点是性能比较差 应用场景::①我们在使用JDBC连接数据库时使用Class.forName()通过反射加载数据库的驱动程序; ②Spring框架也用到很多反射机制,最经典的就是xml的配置模式。Spring 通过 XML 配置模式装 载 Bean 的过程:1) 将程序内所有 XML 或 Properties 配置文件加载入内存中; 2)Java类里面解析 xml或properties里面的内容,得到对应实体类的字节码字符串以及相关的属性信息; 3)使用反射机 制,根据这个字符串获得某个类的Class实例; 4)动态配置实例的属性
1.9 说一下java异常
Throwable 是 Java 语言中所有错误与异常的超类。包含两个子类:Error(错误)和 Exception(异常),它们通常用于指示发生了异常情况。Throwable 包含了其线程创建时线程执行堆栈的快照,它提供了 printStackTrace() 等接口用于获取堆栈跟踪数据等信息。
Error(错误) 定义:Error 类及其子类。程序中无法处理的错误,表示运行应用程序中出现了严重的错误。 特点:此类错误一般表示代码运行时 JVM 出现问题。通常有 Virtual MachineError(虚拟机运行 错误)、NoClassDefFoundError(类定义错误)等。比如 OutOfMemoryError:内存不足错 误;StackOverflowError:栈溢出错误。此类错误发生时,JVM 将终止线程。 这些错误是不受检异常,非代码性错误。因此,当此类错误发生时,应用程序不应该去处理此类错 误。按照Java惯例,我们是不应该实现任何新的Error子类的!
Exception(异常)
程序本身可以捕获并且可以处理的异常。Exception 这种异常又分为两类:运行时异常和编译时异
常。
运行时异常
定义:RuntimeException 类及其子类,表示 JVM 在运行期间可能出现的异常。
特点:Java 编译器不会检查它。也就是说,当程序中可能出现这类异常时,倘若既"没有通过
throws声明抛出它",也"没有用try-catch语句捕获它",还是会编译通过。比如
NullPointerException空指针异常、ArrayIndexOutBoundException数组下标越界异常、
ClassCastException类型转换异常、ArithmeticExecption算术异常。此类异常属于不受检异常,
一般是由程序逻辑错误引起的,在程序中可以选择捕获处理,也可以不处理。虽然 Java 编译器不
会检查运行时异常,但是我们也可以通过 throws 进行声明抛出,也可以通过 try-catch 对它进行
捕获处理。如果产生运行时异常,则需要通过修改代码来进行避免。
RuntimeException 异常会由 Java 虚拟机自动抛出并自动捕获(就算我们没写异常捕获语句运行
时也会抛出错误!!),此类异常的出现绝大数情况是代码本身有问题应该从逻辑上去解决并改进
代码。
编译时异常
定义: Exception 中除 RuntimeException 及其子类之外的异常。
特点: Java 编译器会检查它。如果程序中出现此类异常,比如 ClassNotFoundException(没有找
到指定的类异常),IOException(IO流异常),要么通过throws进行声明抛出,要么通过trycatch
进行捕获处理,否则不能通过编译。在程序中,通常不会自定义该类异常,而是直接使用系
统提供的异常类。该异常我们必须手动在代码里添加捕获语句来处理该异常。
JVM 是如何处理异常的? 在一个方法中如果发生异常,这个方法会创建一个异常对象,并转交给 JVM,该异常对象包含异 常名称,异常描述以及异常发生时应用程序的状态。创建异常对象并转交给 JVM 的过程称为抛出 异常。可能有一系列的方法调用,最终才进入抛出异常的方法,这一系列方法调用的有序列表叫做 调用栈。 JVM 会顺着调用栈去查找看是否有可以处理异常的代码,如果有,则调用异常处理代码。当 JVM 发现可以处理异常的代码时,会把发生的异常传递给它。如果 JVM 没有找到可以处理该异常的代 码块,JVM 就会将该异常转交给默认的异常处理器(默认处理器为 JVM 的一部分),默认异常处 理器打印出异常信息并终止应用程序。
1.10 代理
1.静态代理
2.Java动态代理
代理类是在程序运行时创建的代理方式称为动态代理,相比于静态代理而言,动态代理的优势可以对代理类中的方法进行统一的处理,而不用修改每个代理类中的方法。在java动态代理中主要涉及两个类,java.lang.reflect.InvocationHandler和java.lang.reflect.Proxy,需要创建一个代理类来实现InvocationHandler接口,这个接口中只有一个方法invoke,我们在对委托类中所有的方法调用都会变成是调用invoke方法。这样我们就可以在invoke方法中添加一些特定的逻辑进行处理。
优点:解决了静态代理中冗余的代理实现类问题。
缺点:JDK 动态代理是基于接口设计实现的,如果没有接口,会抛异常。
3.CGlib动态代理
JDK的动态代理依赖于接口,当我们只有类而没有接口的时候,我们需要借助一个三方框架CGlib来实现动态代理。CGLIB代理是针对类来实现代理的,原理是对指定的委托类生成一个子类并重写其中业务方法来实现代理。但因为采用的是继承,所以不能对final修饰的类进行代理。final修饰的类不可继承。
CGLib采用了非常底层的字节码技术,其原理是通过字节码技术为一个类创建子类,并在子类中采用方法拦截的技术拦截所有父类方法的调用,顺势织入横切逻辑,来完成动态代理的实现。实现方式实现MethodInterceptor接口,重写intercept方法,通过Enhancer类的回调方法来实现。
但是CGLib在创建代理对象时所花费的时间却比JDK多得多,所以对于单例的对象,因为无需频繁创建对象,用CGLib合适,反之,使用JDK方式要更为合适一些。同时,由于 CGLib 由于是采用动态创建子类的方法,对于final方法,无法进行代理。
二.集合
Collection和Map<K,V>是java.util框架中的两个根接口,代表了两种不同的数据结构:集合和映射表。而List、Set则是继承自Collection下最核心的两个接口,List有序可重复并可以通过整数索引来访问,Set不包含重复元素。
List
ArrayList底层数组
1.默认初始容量10,扩容 扩容因子1.5,连续内存
定义了transient obj[],和size
2.扩容机制:
第一次add:Array.copyof 一个容量为10的数组
当数组装满add:size+size>>1(1.5倍)Array.copyof新数组
缩容:没有自动缩容,可以手动缩容trimToSize()
删除元素时,将元素index后的元素全部前移一位(System.arraycopy index前后两个数组为一个新的数组),最后一位index = null
3.arraylist 和vector: a 1.5倍扩容,线程不安全。v 2倍扩容,线程安全
在多线程的场景中可以直接使用Vector(同步方法锁)类,
也可以使用Collections.synchronizedList(list)方法来返回一个线程安全的list(同步代码块锁)。
SynchronizedList和Vector最主要的区别是:
1.SynchronizedList有很好的扩展和兼容功能。他可以将所有的List的子类转成线程安全的类 。
2.使用SynchronizedList的时候,进行遍历时要手动进行同步处理。3.SynchronizedList可以指定锁定的对象。
CopyOnWriteArrayList 读不加锁,但是写会复制集合,内存开销大
3.LinkedList:底层双向链表,查询时间复杂度O(n)适用增删,只需要改动链表指针关系 查询,折半算法
三.jvm
3.1jvm内存模型 Hot Spot
Java虚拟机在执行Java程序的过程中会把它所管理的内存划分为若干个不同的数据区域。这些区域都有各自的用途,以及创建和销毁的时间,有的区域随着虚拟机进程的启动而存在,有些区域则是依赖用户线程的启动和结束而建立和销毁。
堆::线程共享,在虚拟机启动时创建,用来存放对象实例,几乎所有的对象实例都在这里分配内存。对于大多数应用来说,Java堆(Java Heap)是Java虚拟机所管理的内存中最大的一块。
- Java堆是垃圾收集器管理的主要区域,因此很多时候也被称做“GC堆”。如果从内存回收的角度看,由于现在收集器基本都是采用的分代收集算法,所以Java堆中还可以细分为:新生代和老年代;新生代又有Eden空间、From Survivor空间、To Survivor空间三部分。
- Java 堆不需要连续内存,并且可以通过动态增加其内存,增加失败会抛出 OutOfMemoryError 异常。
虚拟机栈:线程私有,虚拟机栈描述的是Java方法执行的内存模型:每个方法被执行的时候都会同时创建一个栈帧(Stack Frame)用于存储局部变量表(基本数据类型,对象引用)、操作栈、动态链接、方法出口等信息。每一个方法被调用直至执行完成的过程,就对应着一个栈帧在虚拟机栈中从入栈到出栈的过程。
该区域可能抛出以下异常:
当线程请求的栈深度超过最大值,会抛出 StackOverflowError 异常;
栈进行动态扩展时如果无法申请到足够内存,会抛出 OutOfMemoryError 异常。
本地方法栈:为虚拟机使用到的Native 方法服务,异常同虚拟机栈
方法区::用于存放已被加载的类信息、常量、静态变量、即时编译器编译后的代码等数据
对这块区域进行垃圾回收的主要目标是对常量池的回收和对类的卸载,但是一般比较难实现,HotSpot 虚拟机把它当成永久代(Permanent Generation)来进行垃圾回收。
方法区逻辑上属于堆的一部分,但是为了与堆进行区分,通常又叫“非堆”。
程序计数器:线程私有,如果线程正在执行的是一个Java方法,这个计数器记录的是正在执行的虚拟机字节码指令的地址;如果正在执行的是Natvie方法,这个计数器值则为空(undefined)所占空间的大小不会随程序的执行而发生改变,所以此区域不会出现OutOfMemoryError的情况。
注
在 JDK1.7之前,HotSpot 使用永久代实现方法区;HotSpot 使用 GC 分代实现方法区带来了很大便利;从 JDK1.7 开始HotSpot 开始移除永久代。其中符号引用(Symbols)被移动到 Native Heap中,字符串常量和类引用被移动到 Java Heap中。在 JDK1.8 中,永久代已完全被元空间(Meatspace)所取代。元空间的本质和永久代类似,都是对JVM规范中方法区的实现。不过元空间与永久代之间最大的区别在于:元空间并不在虚拟机中,而是使用本地内存。因此,默认情况下,元空间的大小仅受本地内存限制。
作者:军儿呀 链接:juejin.cn/post/684490… 来源:掘金 著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
3.2 GC
1.0标记-清除算法:标记出所有需要回收的对象(或标记所有存活的对象),统一回收
缺点:1.效率不高,堆中大量的对象,其中大部分需要回收,需要进行大量标记和回收动作
2.清理之后,有大量的内存空间碎片化,导致后续分配大对象时不得不提前再次GC
2.0标记-复制算法:将内存划分为相等的两块区域,其中一块用完了就将存活的对象复制到另一半,并可以按序整理.
缺点:只有一半内存可用(现在的新生代收集原理),对象存活率较高市,复制操作增加,效率降低
3.0标记整理算法:标记出所有存活的对象,移动到内存的一边,直接清理另一边.移动对象并更新对象引用必须全程暂停用户应用程序,(STW,STOP THE WORLD)(老年代收集原理)
对象存活判定:
1.引用计数器算法:在对象中添加一个引用计数器,有一个地方引用,加一,引用失效,减一.
缺点,需要配合大量额外处理才能保证正确,例如互相循环引用,所以主流jvm都不使用
2.可达性分析:通过一系列称为"GC root"的跟对象作为起始节点集合,从这些节点开始根据引用关系向下搜索,搜索过程走过的路径称为"引用链",如果某个对象到GC root 没有任何引用链相连,证明对象不可能再使用,不可达,可以回收.(STW)
GC root:
1、JVM stack虚拟机栈(栈帧中的本地变量表)中引用的对象
比如: 各个线程被调用的方法中使用到的参数、局部变量,临时变量等
2、nativ method stack JNI( 通常说的native方法) 引用的对象
3、runtime constant pool运行时常量池
4、static references in method area(堆 jdk8)中类静态属性引用的对象
比如: Java 类的引用类型静态变量
(堆 jdk8) 中常量引用的对象
比如: 字符串常量池 (String Table ) 里的引用
5、Clazz Java 虚拟机内部的引用。基本数据类型对应的 Class 对象, 一些常驻的异常对象( 如:NullPointerException 、OutOfMemoryError) , 系统类加载器
6、所有被同步锁 synchronized 持有的对象
7.反映jvm内部情况的JMXBean,JVMTI中注册的回调,本地代码缓存等.
8.临时GC root :某一块区域垃圾回收时,将关联区域的对象一并加入
引用:
强引用是指引用赋值,即Objecto bj = new Object()。无论任何情况下,只要强引用关系还存在,垃圾收集器就永远不会回收掉被引用的对象。
软引用是用来描述一些还有用,但非必须的对象。只被软引用关联着的对象,在系统将要发生内存溢出异常前,会把这些对象列进回收范围之中进行第二次回收,如果这次回收还没有足够的内存,才会抛出内存溢出异常;
SoftReference reference = new SoftReference<>(new String("zszxz"));
弱引用当垃圾收集器开始工作,无论当前内存是否足够,都会回收掉被弱引用关联的对象;
WeakReference weakReference = new WeakReference<>(new String("zszxz"));
虚引用为一个对象设置虚引用关联的唯一目的只是为了能在这个对象被收集器回收时收到一个系统通知,除此之外没有任何作用,不决定对象的生命周期;
ReferenceQueue queue = new ReferenceQueue();
PhantomReference reference = new PhantomReference(new String("zszxz"),queue);/ˈfæntəm/
二次标记:GC root 没有引用,第一次标记,判断此对象是否有必要执行finalize(),假如对象没有覆盖finalize()方法,或finalize()已经被jvm调用过,才会GC掉
常见的7种垃圾收集器有:
serial收集器、ParNew收集器(seria收集器的升级版,新生代)、serial Old收集器。
Parallel Scavenge收集器、Parallel Old 收集器。(吞吐量优先)
CMS(Concurrent Mark-Sweep)收集器。老年代。(停顿时间优先)
G1收集器(Garbage First)。整个堆。
其中Serial收集器为串行收集器,其他均为并行收集器。 blog.csdn.net/qian520ao/a…
3.3栈上分配(思想)
逃逸分析: 逃逸分析的主要作用就是分析对象作用域。 当一个对象在方法中被定义后,它可能被外部方法所引用,例如作为调用参数传递到其他方法中,这种行为就叫做 方法逃逸。甚至该对象还可能被外部线程访问到,例如赋值被类变量或可以在其他线程中访问的实例变量,称为 线程逃逸。 标量替换 指:若一个对象被证明不会被外部访问,并且这个对象可以被拆解成若干个基本类型的形式,那么当程序真正执行的时候可以不创建这个对象,而是采用直接创建它的若干个被这个方法所使用到的成员变量来代替,将对象拆分后,除了可以让对象的成员变量在栈上分配和读写之外,还可以为后续进一步的优化手段创造条件。 同步消除 指:若一个变量被证明不会逃逸出线程,那么这个变量的读写就肯定不会出现竞争的情况,那么对这个变量实施的同步措施也就可以消除掉。 现在最多能做到方法逃逸分析