Android开发面试:Java知识答案精解
目录
Java
集合
集合概述
HashMap
ConcurrentHashMap
泛型
反射
注解
IO流
异常、深浅拷贝与Java8新特性
Java异常
深浅拷贝
Java8新特性
并发
线程
线程池
锁
volatile
JVM
内存区域
内存模型
类加载机制
垃圾回收机制
如何判断对象已死
Java
集合
集合概述
- 集合分类:大致分为List、Set、Map和Queue四种体系(Set代表无序、不可重复的集合,List代表有序、重复的集合,Map代表具有映射关系的集合,Queue代表一种队列集合实现)
- 集合和数组区别:a、长度:数组长度在初始化时指定,而集合可以保存数量不确定的数据,同时可以保存具有映射关系的数据;b、元素类型:数组元素既可以是基本类型的值,也可以是对象,而集合里只能保存对象(实际上只是保存对象的引用变量),基本数据类型要转换成对应包装类才能放入集合类中
- 集合类关系:Collection和Map是Java集合框架的根接口,List(List集合里增加了一些根据索引来操作集合元素的方法)、Set(Set集合与Collection集合基本相同,没有提供任何额外方法,实际上Set就是Collection,只是Set不允许包含重复元素)和Queue(Queue模拟队列这种“先进先出”数据结构,通常队列不允许随机访问队列中元素)接口继承自Collection,Map里所有key相当于一个Set集合、所有value相当于一个List集合
- ArrayList与LinkedList:ArrayList以数组实现,查询快增删慢(自动扩容,动态数组节约空间,但数组有容量限制,默认第一次插入元素时创建大小为10的数组,超出限制时会增加50%容量,用 System.arraycopy()复制到新的数组,因此最好能给出数组大小的预估值);LinkedList以双向链表实现,node函数将节点访问复杂度由O(n)变为O(n/2)(链表无容量限制,但双向链表本身使用了更多空间,也需要额外的链表指针操作)
HashMap
- 核心:数组(哈希表)+链表/红黑树,基于Map接口实现、允许null键/值、非同步、不保证有序(比如插入的顺序)、也不保证顺序不随时间变化
- 容量和负载因子:容量(Capacity,容器大小,默认16)、负载因子(loadFactor,容器填满程度的最大比例,默认0.75);当容器内容数目大于 capacity*loadFactory时,就需要调整容器大小为16的2倍
- 为什么扩容,并且为2的倍数:数据结构是数组需要扩容;如果是2的倍数,就可以用位运算替代取余操作,更加高效(HashMap存取时,计算index即(length- 1) & hash,使用&运算符相比%效率更高,如果length为2的倍数,可以最大程度确保index均分)
- put函数:a、首先对key的hashCode()做hash,然后再计算index;b、如果没碰撞直接放到bucket里;c、如果碰撞了,以链表的形式存在buckets;d、如果碰撞导致链表过长(大于等于8 ),就把链表转换成红黑树(提高遍历查询),当红黑树上节点数量小于6个,会重新把红黑树变成单向链表数据结构;e、如果节点已经存在就替换old value(保证key唯一性);f、如果bucket满了(超过 load factor*current capacity ),就要resize
- get函数:a、bucket里的第一个节点,直接命中;b、如果有冲突,则通过key.equals(k)去查找对应的entry;c、若为树,则在树中通过key.equals(k)查找,O(logn);若为链表,则在链表中通过key.equals(k)查找,O(n)
- hash函数:a、在get和put的过程中,计算下标时,先对hashCode进行hash()操作,然后再通过hash值进一步计算下标(n-1)&hash;b、hash()操作中hashcode()的高16bit不变,低16bit和高16bit做异或(在n - 1为15(0x1111)时,其实散列真正生效的只是低4bit的有效位,容易碰撞,设计者于是从速度、功效、质量考虑,这么做可以在bucket的n比较小时,也能保证考虑到高低bit都参与到hash的计算, 同时不会有太大的开销);c、哈希冲突解决,Java8之前使用链表O(n),Java8中采用红黑树O(logn)(现在大多数的hashCode的分布已经很不错)
- HashMap为什么使用String、int作为key:a、都是final修饰的类,能够保证Hash值的不可更改性和计算准确性;b、内部已重写了equals()、hashCode()等方法,能够有效的减少Hash碰撞(Integer的hashCode就是值本身)
- HashMap与Hashtable有什么区别:a、Hashtable线程安全,效率低,HashMap非线程安全(多个线程同时resize可能会引起死循环、put操作覆盖),效率高;b、null可以作为HashMap的key和value,不可以作为Hashtable的key;c、Hashtable初始容量11,负载因子0.75,扩容2n+1;d、Hashtable没有链表转红黑树的机制
- HashMap与SpareArray:key为int时考虑使用SpareArray,SpareArray使用2个数组存储key和value。好处:a、没有自动拆装箱;b、数据量不大时采用二分查找效率高,不需要开辟内存空间来额外存储外部映射,节省内存
- LinkedHashMap:数组(哈希表)+双向链表/红黑树,双向链表能够保证访问顺序,内部使用LruCache做最近最少使用的移除,继承自HashMap(put函数在LinkedHashMap中未重新实现,只是实现了afterNodeAccess和afterNodeInsertion两个回调函数,get函数则重新实现并加入了afterNodeAccess来保证访问顺序)
- HashMap、LinkedHashMap、TreeMap:HashMap不保证数据有序, LinkedHashMap保证数据插入顺序,TreeMap保证Map的key大小顺序
ConcurrentHashMap
- 核心:Java8的实现已经抛弃了Segment分段锁机制,利用CAS+Synchronized来保证并发更新的安全,底层依然采用数组table+链表+红黑树的存储结构
- 解决问题:HashMap多线程put操作并发死循环问题;以及Hashtable、Collections.synchronizedMap(hashMap)加锁效率低问题
- 数组table初始化:不会在构造时初始化,会延缓到第一次put行为,第一次put操作的线程会执行Unsafe.compareAndSwapInt方法修改sizeCtl(默认0)为-1,有且只有一个线程能够修改成功,其它线程通过Thread.yield()让出CPU时间片等待table初始化完成
- 数组table扩容:构建一个nextTable,大小为table两倍,把table数据复制到nextTable中
- put函数:采用CAS+synchronized实现并发插入或更新操作,key和value均不能为null
- get函数:a、判断table是否为空,如果为空,直接返回null;b、计算key的hash值,并获取指定table中指定位置的Node节点,通过遍历链表或则树结构找到对应的节点,返回value值
泛型
- 泛型含义:泛型本质就是参数化类型,这种类型可以用在类、接口和方法的创建中,分别称为泛型类、泛型接口、泛型方法,是一种语法糖(自动拆装箱、变长参数、内部类等也是语法糖)
- 泛型好处:让类型更加安全。a、编译时类型检查,将错误暴露在编译期,不用等到运行时,有助于开发者更容易找到错误,提高程序可靠性;b、运行时自动类型转换,避免强制类型转换出现ClassCastException;c、更加语义化(比如:List清楚知道存储的是String对象)和能写出更加通用化的代码(引入泛型后并未增加代码的冗余性)
- 类型通配符:上限(List:表示集合中所有元素都是Shape类型或其子类)、下限(List:表示集合中所有元素都是Circle类型或其父类)
- 泛型擦除:a、Java泛型是不变的(Fruit是Apple父类,但List不是List父类),是类型擦除的,可以看做伪泛型;b、无法在程序运行时获取到一个对象的具体类型;c、在静态方法、静态初始化块或静态变量的声明和初始化中,不允许使用类型形参;d、由于系统中并不会真正生成泛型类,所以instanceof运算符后不能使用泛型类
反射
- 反射机制:运行时,动态获取类信息,以及动态调用对象
- 反射机制应用场景:a、逆向代码(反编译);b、与注解相结合的框架(Retrofit);c、单纯的反射机制应用框架(EventBus);d、动态生成类框架(Gson)
- 3种方式获取Class对象:a、Class.forName("全限定类名");b、类名.class;c、对象.getClass()
- 反射API:a、getDeclaredFields与getFields——获取所有属性与获取所有public属性;b、getDeclaredMethods与getMethods——所有方法与所有public方法;c、getDeclaredConstructors与getConstructors——所有构造方法与所有public构造方法;d、getAnnotation(Deprecated.class)——获取指定注解;e、newInstance——创建实例对象;f、method.setAccessible(true)与method.invoke(obj,args)——取消访问权限检查与调用对应方法;g、field.setInt(obj,6)——设置对象的属性值为6
- 代理模式:静态代理(代理类在编译时就实现好,是一个实际的class文件)、动态代理(代理类在运行时动态生成的类字节码,并加载到JVM中)
- 动态代理:主要涉及两个类Proxy、InvocationHandler,都在java.lang.reflect包下,内部主要通过反射实现
- Proxy:这是生成代理类的主类,通过Proxy类生成的代理类都继承自Proxy类,是所有动态代理类的父类,并且提供了用户创建动态代理类静态方法getProxyClass和创建代理对象静态方法newProxyInstance
- InvocationHandler:调用动态代理类中方法时,将会直接转接到执行自定义InvocationHandler中的invoke()方法
注解
- 元数据:是关于数据的数据,对数据进行说明描述,添加到程序元素如方法、字段、类和包上的额外信息
- 注解Annotation:就是Java平台的元数据,允许在Java代码中添加自定义注释,并允许通过反射,以编程方式访问元数据
- 常见注解:@Override(可适用元素为方法,仅仅保留在Java源文件中)、@Deprecated、@SuppressWarnings
- 元注解:@Documented被提取成文档、@Inherited某个类定义@Xxx注解时使用@Inherited修饰,那么其子类自动被该@Xxx注解修饰、@Retention(SOURCE、CLASS、RUNTIME)注解保留时长、@Target(CONSTRUCTOR、FIELD、METHOD等)注解所适用的程序元素类型
- 注解保留策略为RetentionPolicy.RUNTIME时,才可在运行期通过反射机制来使用
IO流
- 输入输出流:输入流(只能从中读取数据,而不能向其写入数据)、输出流(只能向其写入数据,而不能从中读取数据)
- 字节流与字符流:字符流的由来——因为数据编码不同,而有了对字符进行高效操作的流对象,本质其实就是基于字节流读取时,去查了指定的码表。处理纯文本数据优先考虑使用字符流,除此之外都使用字节流
- 节点流与处理流:节点流(FileInputStream低级流,可以从/向一个特定IO设备(如磁盘、网络)读/写数据的流)、处理流(BufferedInputStream高级流,包装节点流,通过封装后的流来实现数据读/写功能)
- IO流分类:分为如下4种抽象基类,InputStream输入字节流、OutputStream输出字节流、Reader输入字符流、Writer输出字符流
- IO流关闭:在执行完输入流操作后,要调用close()方法来关闭,因为程序里打开的IO资源不属于内存资源,垃圾回收机制无法回收该资源,所以应该显式关闭文件IO资源;在执行完输出流操作后,要调用close()方法来关闭,关闭输出流除了可以保证流的物理资源被回收之外,还能将输出流缓冲区数据flush到物理节点里(因为在执行 close()方法之前,会自动执行输出流的flush()方法
- RandomAccessFile优缺点和使用场景:优点(既可以读取文件内容,也可以向文件输出数据,同时支持自由访问文件任意位置(内部包含一个记录指针来实现随机访问))、缺点(只能读写文件,不能读写其他IO节点)、使用场景(网络请求中多线程下载及断点续传)
- NIO:New IO,新型IO可替代标准IO,并提供与标准IO不同工作方式,标准IO基于字节流和字符流进行操作,而NIO基于通道(Channel)和缓冲区(Buffer)进行操作,数据总是从通道读到缓冲区中,或从缓冲区写入通道
异常、深浅拷贝与Java8新特性
Java异常
- 是什么:是Java提供的一种识别及响应错误的一致性机制,Java异常机制可以使程序中异常处理代码和正常业务代码分离,保证程序代码更加优雅,并提高程序健壮性
- 关键字:try(用于监听。将要被监听的代码(可能抛出异常的代码)放在try语句块之内,当 try语句块内发生异常时,异常就被抛出)、catch(用于捕获异常。catch用来捕获try语句块中发生的异常)、finally(总是会被执行。用于回收在try块里打开的资源(如数据库连接、网络连接和磁盘文件))、throw(用于抛出异常)、throws(用在方法签名中,用于声明该方法可能抛出的异常)
- finally与return执行顺序:a、只有finally块执行完成之后,才会回来执行try或catch块中的return或throw语句(finally语句在return语句执行之后return返回之前执行);b、如果finally中使用了return或者throw等终止方法的语句,则就不会跳回执行,直接停止;c、如果finally语句中没有return语句覆盖返回值,那么原来的返回值可能因为finally里的修改而改变(改变引用)也可能不变(改变值)
- 异常框架:a、Throwable是Java 语言中所有错误或异常的超类;b、Throwable包含Error和Exception两个子类;c、Exception类本身以及它的子类中,除了"运行时异常"之外的其它子类都属于被检查异常;d、RuntimeException是Exception子类,编译器不会检查运行时异常(没有通过throws声明抛出RuntimeException异 常",也"没有通过try...catch...处理该异常",也能通过编译);e、Error用于指示合理的应用程序不应该试图捕获的严重问题,和RuntimeException一样,编译器也不会检查Error
深浅拷贝
- Java中有三种类型对象拷贝:浅拷贝(Shallow Copy)、深拷贝 (Deep Copy)、延迟拷贝(Lazy Copy)
- 浅拷贝:按位拷贝对象,会创建一个新对象,这个对象有着原始对象属性值的一份精确拷贝(如果属性是基本类型,拷贝的就是基本类型的值;如果属性是引用类型,拷贝的就是内存地址 ,因此如果其中一个对象改变了这个地址, 就会影响到另一个对象)、实现方式(需要拷贝的类实现Clonable接口并重写clone()方法,并在方法中调用super.clone())
- 深拷贝:会拷贝所有的属性,并拷贝属性指向的动态分配内存(当对象和它所引用的对象一起拷贝时即发生深拷贝,深拷贝相比于浅拷贝速度较慢并且花销较大)、实现方式(a、new实现:需要拷贝的类实现Clonable接口并重写clone()方法,并在方法中new一个对象(类中有引用对象才会发生深拷贝);b、序列化实现)
- 延迟拷贝:是浅拷贝和深拷贝的一个组合,从外面看起来就是深拷贝,但是只要有可能它就会利用浅拷贝的速度,当原始对象中的引用不经常改变的时候可以使用延迟拷贝
- 深浅拷贝选择:a、如果对象属性全是基本类型、对象引用任何时候都不会被改变,可以使用浅拷贝;b、如果对象有引用属性,就要基于具体需求来选择浅拷贝还是深拷贝,如果对象引用经常改变,就要使用深拷贝
Java8新特性
- Lambda表达式和函数式接口:函数接口指只有一个函数的接口,这样的接口可以隐式转换为Lambda表达式;只要某个开发者在该接口中添加一个函数,则该接口就不再是函数式接口,进而导致编译失败,为了克服这种代码层面的脆弱性,并显式说明某个接口是函数式接口,Java8 提供了一个特殊的注解@FunctionalInterface(Java库中的所有相关接口都已经带有这个注解)
- 接口新增特性:除了常量和抽象方法,在Java8增加了默认方法和静态方法(在Java9增加了私有方法)
- Java8抽象类和接口区别:a、继承:抽象类只能单继承,接口可以被接口多重继承、被类多实现;b、设计理念不同:抽象类表示的是"is-a"关系(这个对象是什么),接口表示的是"like-a"关系(这个对象能做什么);c、变量:接口中定义的变量默认是public static final型且必须给其初值,实现类中不能重新定义,也不能改变其值;抽象类中的变量是普通变量,其值可以在子类中重新定义,也可以重新赋值
并发
线程
- 创建线程的4种方式:实现Runnable接口(实现run方法,调用时作为参数传递给Thread构造方法)、继承Thread类(实现run方法)、使用Java8的Lambda(new Thread(()-> System.out.println(Thread.currentThread().getName())).start())、使用Callable和Future(Callable和Runnable类似,JDK1.5提供以弥补调用线程没有返回值的情况)
- join方法:线程加入,描述(可以使线程之间并行执行变为串行执行,比如:a、在线程a中调用线程b.join()方法,那么线程a就会进入阻塞状态直到线程b执行完以后,线程a才会结束阻塞状态,也叫等待b线程死亡;b、如果线程a中调用线程b.join(10),那么a线程会等待b线程执行10毫秒,10毫秒过后a和b线程并行执行)、原理(join方法通过Object.wait方法实现。当a线程中调用b.join时,a线程会获得线程b的锁(wait意味着拿到该对象的锁),调用线程b对象的wait(等待时间),直到该线程b对象唤醒a线程(也就是线程b执行完毕退出时))、注意(由于join方法中会抛出异常,所以还需要try-catch来捕捉处理异常)
- interrupt方法:线程中断,interrupt作用其实也不是中断线程,而是通知线程应该中断,一个线程不应该由其他线程来强制中断或停止,而是应该由线程自己自行停止
- yield方法:线程让步,让掉当前线程CPU执行的时间片,让当前线程或其它线程运行(是让自己或其他线程运行,并不是单纯让给其他线程)
- sleep方法:线程睡眠,让当前线程睡眠指定毫秒,在指定时间内当前线程处于阻塞状态
- sleep()和wait()区别:区别(sleep为线程方法,而wait为Object方法,他们功能相似)、本质区别(sleep不释放锁,wait释放锁)、用法上不同(sleep(milliseconds)可以用时间指定来使他自动醒过来,如果时间不到你只能调用interreput()来终止线程;wait()可以用notify()/notifyAll()直接唤起)
- 线程同步:一共synchronized(代码块同步、方法同步)和ReentrantLock(ReentrantLock lock = new ReentrantLock(); lock.lock();lock.unlock())两种锁,来实现线程同步问题,乐观锁ReentrantLock,悲观锁synchronzed、Lock
线程池
- 线程池优势:a、降低系统资源消耗(通过重用已存在线程,降低线程创建和销毁造成的消耗);b、提高系统响应速度(当有任务到达时,无需等待新线程创建便能立即执行);c、方便线程并发管控(线程若是无限制创建,不仅会额外消耗大量系统资源,更是占用过多资源而阻塞系统或oom等状况,从而降低系统稳定性,而线程池能有效管控线程,统一分配、调优,提高资源使用率);d、更强大的功能(线程池提供了定时、定期以及可控线程数等功能的线程池,使用方便简单)
- ThreadPoolExecutor参数:corePoolSize(核心线程数,默认情况下核心线程一直存活在线程池中,即便处于闲置状态;除非将ThreadPoolExecutor的allowCoreThreadTimeOut属性设为true,这时候处于闲置的核心线程在等待新任务到来时会有超时策略,一旦超过keepAliveTime所设置的超时时间,闲置核心线程就会被终止)、maximumPoolSize(最大线程数,包含核心线程数+非核心线程数,活动线程达到这个数以后,后续新任务将会被阻塞)、keepAliveTime(非核心线程闲置时的超时时长,对于非核心线程,闲置时间超过这个时间,非核心线程就会被回收;当allowCoreThreadTimeOut属性设为true时,这个超时时间才会对核心线程产生效果)、unit(用于指定keepAliveTime参数的时间单位)、workQueue(保存等待执行任务的阻塞队列,通过线程池execute方法提交的Runable对象都会存储在该队列中)、threadFactory(线程工厂,为线程池提供新线程的创建)、handler(是RejectedExecutionHandler对象,当任务队列已满,且线程池中活动线程已达到限定最大值或是无法成功执行任务,这时ThreadPoolExecutor会调用RejectedExecutionHandler的rejectedExecution方法)
- 线程池执行流程:提交任务——核心线程数是否已满(未满就创建核心线程执行任务)——任务队列是否已满(未满就加入队列)——线程池是否已满(未满就创建非核心线程执行任务)——拒绝执行(调用handler.rejectedExecution通知调用者)
- newFixedThreadPool():内部ThreadPoolExecutor实现,new ThreadPoolExecutor(nThreads, nThreads, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue())——只有核心线程,并且这些线程都不会被回收,不存在超时机制,采用LinkedBlockingQueue在于其无界性(对于任务队列大小也没有限制)
- newCachedThreadPool():(0, Integer.MAX_VALUE,60L, TimeUnit.SECONDS, new SynchronousQueue()) ——没有核心线程,最大线程数为整数最大值,闲置60s的线程会被回收,采用SynchronousQueue(内部是个空集合,任务队列大小为0,如果现有线程无法接收任务,将会创建新线程来执行任务)
- newScheduledThreadPool():(corePoolSize, Integer.MAX_VALUE, 0, NANOSECONDS,new DelayedWorkQueue())——核心线程数固定,最大线程数为整数最大值,非核心线程处于限制状态时会立即被回收,创建一个可定时执行或周期执行任务的线程池,返回值是ScheduledExecutorService
- ScheduledExecutorService:schedule方法(延迟一定时间后执行任务)、scheduleAtFixedRate(延迟一定时间后,以一定间隔周期性执行任务,时间间隔是从上一个任务开始执行到下一个任务开始执行的间隔,这一系列任务的触发时间都是可预知)、scheduleWithFixedDelay(时间间隔是从上一个任务执行结束到下一个任务开始执行的间隔,这一系列任务的触发时间不可预知)
- newSingleThreadExecutor():new ThreadPoolExecutor(1, 1,0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue())
- 线程池使用:CPU密集型任务(线程池中线程个数应尽量少,如配置N+1个线程的线程池)、IO密集型任务(IO操作速度远低于CPU速度,CPU绝大多数时间处于空闲状态,那么线程池可以配置尽量多线程,以提高CPU利用率,如2*N)、混合型任务(拆分成CPU密集和IO密集,当这两类任务执行时间相差无几时,通过拆分再执行吞吐率高于串行执行吞吐率,但若这两类任务执行时间有数据级差距,那么没有拆分意义)
锁
- 死锁产生条件:互斥条件(一个资源每次只能被一个线程使用)、请求与保持条件(一个线程因请求资源而阻塞时,对已获得的资源保持不放)、不剥夺条件(线程已获得的资源,在未使用完之前,不能强行剥夺)、循环等待条件(若干线程之间形成一种头尾相接的循环等待资源关系)
- 死锁分类和解决办法:静态的锁顺序死锁(所有需要多个锁的线程,都要以相同顺序来获得锁)、动态的锁顺序死锁(使用System.identifyHashCode来定义锁的顺序,确保所有线程都以相同顺序获得锁)、协作对象之间发生的死锁(需要使用开放调用(开放调用就是调用某个外部方法时不需要持有锁),避免在持有锁的情况下调用外部方法)
- 重入锁:当一个线程得到一个对象后,再次请求该对象锁时是可以再次得到该对象锁的,也就是说自己可以再次获取自己的内部锁,Java内置锁(synchronized)和Lock(ReentrantLock)都是可重入的
- 公平锁:优先级低的线程可能一直无法获取到CPU执行权,公平锁就可以保证线程按照时间先后顺序执行,避免产生饥饿现象,但公平锁效率比较低,因为需要维护一个有序队列以实现顺序执行,ReentrantLock便是一种公平锁,通过在构造方法中传入true就是公平锁,传入false就是非公平锁,默认false
- synchronized和Lock的区别:a、Lock是一个接口,而synchronized是Java中的关键字,synchronized是内置的语言实现;b、synchronized在发生异常时,会自动释放线程占有的锁,因此不会导致死锁现象发生,而Lock在发生异常时,如果没有主动通过unLock()去释放锁,则很可能造成死锁现象,因此使用Lock时需要在finally块中释放锁;c、Lock可以让等待锁的线程响应中断,而synchronized却不行,使用synchronized时,等待的线程会一直等待下去,不能够响应中断;d、通过Lock可以知道有没有成功获取锁,而synchronized却无法办到;e、Lock可以提高多个线程进行读操作的效率
volatile
- 原子性:一个或多个操作,要么全部执行且执行过程不会被任何因素打断,要么都不执行
- 可见性:当多个线程访问同一个变量时,一个线程修改了这个变量的值,其他线程能够立即看到修改后的值
- 有序性:程序执行的顺序按照代码的先后顺序执行
- 指令重排序:处理器为了提高程序运行效率,可能会对输入代码进行优化,它不保证程序中各个语句执行先后顺序同代码中顺序一致,但它会保证程序最终执行结果和代码顺序执行结果是一致的;指令重排序不会影响单个线程的执行,但是会影响到线程并发执行的正确性
- 锁与原子性、可见行、有序性:a、Java内存模型只保证基本读取和赋值是原子性操作,如果要实现更大范围操作的原子性,可以通过synchronized和Lock来实现;b、通过synchronized和Lock也能够保证可见性,同一时刻只有一个线程获取锁然后执行同步代码,并且在释放锁之前会将对变量的修改刷新到主存当中;c、synchronized和Lock同样能保证有序性
- volatile与原子性、可见性、有序性:a、volatile不能确保对变量任何操作都是原子性的,而且自增操作不是原子性操作,Java5带来的atomic系列如AtomicInteger利用CAS(Compare And Swap)能保证操作的原子性;b、volatile保证可见性,当一个共享变量被volatile修饰时,它会保证修改的值会立即被更新到主存,当有其他线程需要读取时,它的缓存会无效,就会去内存中读取新值;c、volatile可以保证有序性,禁止指令进行重排序,一个方法体中volatile变量所在语句的前/后面语句执行顺序不会变,但前/后面语句的内部多条语句执行顺序不做保证
- volatile应用场景:基于多线程下,a、作为状态开关;b、单例模式中的double check(第一次判空为了效率避免加锁,第二次判空为了并发安全,volatile避免new语句的指令重排序操作)
JVM
内存区域
- 方法区(公有):存储已被虚拟机加载的类信息、常量、静态常量、即时编译器编译后代码等数据,包含常量池(用户存放编译器生成的各种字面量和符号引用),异常状态OutOfMemoryError
- 堆(公有):是JVM所管理内存中最大的一块,唯一目的就是存放实例对象,几乎所有的对象实例都在这里分配;Java堆是垃圾收集器管理的主要区域,很多时候也被称为“GC堆”,异常状态OutOfMemoryError
- 虚拟机栈(线程私有):描述的是Java方法执行的内存模型;每个方法在执行时都会创建一个栈帧,用户存储局部变量表,操作数栈,动态连接,方法出口等信息;每个方法从调用直至完成的过程,就对应着一个栈帧在虚拟机栈中入栈到出栈的过程;对这个区域定义了两种异常状态OutOfMemoryError、StackOverflowError
- 本地方法栈(线程私有):与虚拟机栈所发挥的作用相似,区别不过是虚拟机栈为虚拟机执行Java方法,而本地方法栈为虚拟机使用到Native方法服务
- 程序计数器(线程私有):一块较小的内存,当前线程所执行字节码的行号指示器;字节码解释器工作时,就是通过改变这个计数器的值来选取下一条需要执行的字节码指令
内存模型
- Java内存模型目的:屏蔽各种硬件和操作系统的内存访问差异,以实现让Java程序在各种平台下都能达到一致的内存访问效果
- 主要目标:定义程序中各个变量访问规则,即在虚拟机中将变量存储到内存和从内存中取出变量这样的底层细节(此处的变量与Java变成中所说的变量有所区别,它包括了实例字段,静态字段和构成数组对象的元素,但不包括局部变量和方法参数)
- 主内存与线程工作内存的变量:Java内存模型规定所有变量都存储在主内存中,每条线程在自己工作内存中,保存被该线程所使用到的变量(这些变量是从主内存中拷贝而来),线程对变量所有操作(读取、赋值)都必须在工作内存中进行,不同线程之间也无法直接访问对方工作内存中变量,线程间变量值的传递均需要通过主内存来完成
类加载机制
- 定义:把class文件加载到内存,并对其进行验证、准备、解析和初始化,最终形成可被虚拟机直接使用的Java类型
- 类的生命周期:加载、连接(验证,准备,解析)、初始化、使用和卸载
- 触发类加载时机:new,static字段,static方法,反射,初始化类时先初始化父类
- 类加载具体过程:加载(a、通过全限定类名,类加载器获取类的二进制字节流;b、将这个字节流所代表的静态存储结构,转换为方法区内的运行时数据结构;c、在内存中生成代表这个类的Class对象,作为方法区这个类的各种数据访问入口)、验证(Class文件格式、元数据验证、字节码验证、符号引用验证)、准备(在方法区为类变量分配内存并设置初始值)、解析(虚拟机将常量池内的符号引用替换为直接引用的过程,动态和静态解析)、初始化(执行类构造器(与类的构造函数不同,不需要显示调用父类构造器,虚拟机会保证在子类方法执行之前,父类构造器已经执行完毕)、static代码块、给类变量赋值)
- 类加载器分类:主要分为两类,启动类加载器(Bootstrap ClassLoader,C++语言实现(针对HotSpot),负责加载Java核心类,将存放在\lib目录或-Xbootclasspath参数指定路径中的类库加载到内存中)、其他类加载器(由Java语言实现,继承自抽象类ClassLoader。扩展类加载器——Extension ClassLoader,Java语言实现,负责加载Java扩展核心类之外的类,加载\lib\ext目录或java.ext.dirs系统变量指定路径中的所有类库;应用程序类加载器——Application ClassLoader,Java语言实现,默认加载器,负责加载用户类路径classpath上指定类库,通过 ClassLoader.getSystemClassLoader()方法直接获取
- Android类加载器:Android中最主要类加载器有如下4个,BootClassLoader(加载Android Framework层中的class字节码文件,类似java的Bootstrap ClassLoader但通过Java实现,代码在ClassLoader文件内部)、PathClassLoader(加载已经安装到系统中Apk的class字节码文件(类似Java的App ClassLoader))、DexClassLoader(加载制定目录的class字节码文件(类似Java中的Custom ClassLoader))、BaseDexClassLoader(PathClassLoader和DexClassLoader的父类)
- 双亲委派模型:自定义类加载器——Application ClassLoader——Extension ClassLoader——Bootstrap ClassLoader
- 双亲委派模型代码实现:ClassLoader中loadClass方法实现了双亲委派模型,自定义类加载器就主要重写findClass方法
垃圾回收机制
- 垃圾收集算法:标记-清除算法(标记和清除效率都不高、产生大量内存碎片)、复制算法(为了解决效率问题,内存划分为大小相等两块,分配效率高但内存缩小一半)、标记-整理算法(对象存活率高时复制算法效率变低,标记过程仍然与”标记-清除“算法一样,但后续步骤不是直接对可回收对象进行清理,而是让所有存活对象都向一端移动,然后直接清理掉边界以外的内存)、分代收集算法(新生代使用复制算法、老年代采用标记清除或标记整理进行Java堆内存回收)
- JVM中的年代:新生代(1个Eden区存放新建对象和2个Survivor区(From Survivor区与To Survivor区)保存还存活对象,默认内存划分比例为8:1,Minor GC时“Eden”存活对象被复制到“To”,“From”区年龄未达到阈值的对象被复制到“To”区,然后“From”和“To”会交换角色保证名为To的Survivor区域是空的)、老年代(“From”区年龄达到阈值的对象会被移动到老年代中;“To”区被填满之后,会将所有对象移动到老年代中)
- Minor GC和Full/Major GC:Minor GC(发生在新生代的垃圾收集动作,非常频繁Major)、Full/Major GC(发生在老年代,Major GC通常会伴随至少一次有Minor GC,且Major GC速度比Minor GC慢10倍以上)
- 空间分配担保:在发生Minor GC之前,虚拟机会先检查老年代最大可用连续空间,是否大于新生代所有对象总空间,大于那么可以确保Minor GC是安全的,小于则可能进行Full GC
如何判断对象已死
- 引用计数法:给对象添加一个引用计数器,当多一个引用则计数器就加1,引用失效则计数器减1,计数器为0表示对象不再使用,JVM未使用这种方式,因为很难解决对象间互循环引用的问题
- 可达性分析算法:GC Roots对象(Java栈对象、Native栈对象、方法区static对象和final对象)、算法描述(通过一些列GC Roots对象作为起始点,从这些节点开始向下搜索,所走过的路径称为引用链,当一个对象到GC Roots没有任何引用链相连时(就是从GC Roots到这个对象是不可达),则证明此对象是不可用的,它们会被判定为可回收对象)、宣告对象死亡的两次标记过程(对象没有与GC Roots相连接,会被第一次标记并进行一次筛选(此对象是否有必要执行finalize方法),如果对象在finalie()中重新与引用链上任何一个对象建立关联,那在第二次标记时它将会被移除出“即将回收”集合,但如果没建立联系就会被回收)
- 对象是否存活与引用相关:4大引用,强引用(OOM也不会回收对象)、软引用(内存不足时(OOM之前)回收,如:图片缓存)、弱引用(下一次GC时回收,防止内存泄漏)、虚引用(随时可能被回收,为一个对象设置虚引用的唯一目的,就是能在这个对象被GC回收时能得到一个系统通知)
Java
集合
集合概述
HashMap
ConcurrentHashMap
泛型
反射
注解
IO流
异常、深浅拷贝与Java8新特性
Java异常
深浅拷贝
Java8新特性
并发
线程
线程池
锁
volatile
JVM
内存区域
内存模型
类加载机制
垃圾回收机制
如何判断对象已死
相关文章:
Android开发面试:Java知识答案精解
目录 Java 集合 集合概述 HashMap ConcurrentHashMap 泛型 反射 注解 IO流 异常、深浅拷贝与Java8新特性 Java异常 深浅拷贝 Java8新特性 并发 线程 线程池 锁 volatile JVM 内存区域 内存模型 类加载机制 垃圾回收机制 如何判断对象已死 Java 集合 …...
Windows上一款特别好用的画图软件
安装 废话不多说,打开windows的应用商店,搜索draw.io,点击获取即可。 画图 draw.io的布局左边是各种图形组件,中间是画布,右边是属性设置,文件扩展名是.drawio。 点击左边列表中的图形可以将它添加到画…...
html--学习
javascrapt交互,网页控制JavaScript:改变 HTML 图像本例会动态地改变 HTML <image> 的来源(src):点亮灯泡<script>function changeImage() {elementdocument.getElementById(myimage) #内存变量࿰…...
关于递归处理,应该怎么处理,思路是什么?
其实问题很简单,就是想要循环遍历整个data对象,来实现所有name转成label,但是想到里面还有children属性,整个children里面可能还会嵌套很多很多的name,如此循环,很难搞,知道使用递归,…...
重磅!牛客笔试客户端可防ChatGPT作弊
上线俩月,月活过亿。爆火的ChatGPT能代写文,撕代码,善玩梗,秒答题,几乎“无所不能”,争议也随之而来。调查显示,截至2023年1月,美国89%的大学生利用ChatGPT应付作业,53%的…...
春季训练营 | 前端+验证直通车-全实操项目实践,履历加成就业无忧
“芯动的offer”是2023年E课网联合企业全新推出集训培优班(线下),针对有一定基础(linux、verilog、uvm等)在校学生以及想要通过短时间的学习进入到IC行业中的转行人士,由资深IC设计工程师带教,通…...
2.详解URL
文章目录视图函数1.1endpoint简介1.2 装饰器注册路由源码浅析1.3 另一种注册路由的方式---app.add_url_rule()1.4 视图函数中添加自定义装饰器2 视图类2.1 视图类的基本写法3 详细讲解注册路由的参数3.1常用的参数3.2不常用的参数(了解)视图函数 1.1endpoint简介 endpint参数…...
Android特别的数据结构(二)ArrayMap源码解析
1. 数据结构 public final class ArrayMap<K,V> implements Map<K,V> 由两个数组组成,一个int[] mHashes用来存放Key的hash值,一个Object[] mArrays用来连续存放成对的Key和ValuemHashes数组按非严格升序排列初始默认容量为0减容ÿ…...
减少if else
1. 三目运算符 可以理解为条件 ?结果1 : 结果2 里面的?号是格式要求。也可以理解为条件是否成立,条件成立为结果1,否则为结果2。 实例: public String handle(int code) {if (code 1) {return "success";} else {return &quo…...
硕士毕业论文常见的排版小技巧
word排版陆续更新吧,更具我所遇到的一些小问题,总结上来 文章目录1.避免题注(图或者表的标题)与图或表格分不用页注意点:光标移动到表的题注后面2.设置论文的页眉关键点:需要将每一章节末尾,都要…...
JAVA开发(数据类型String和HasMap的实现原理)
在JAVA开发中,使用最多的数据类型恐怕是String 和 HasMap两种数据类型。在开发的过程中我们每天都使用的不亦乐乎。但是相信很多人都没有考虑过String数据类型的实现原理或者说是在数据结构中的存储原理,还有一个就是是HashMap,也很少有人去了…...
Hbase 映射到Hive
目录 一、环境配置修改 关闭掉hbase,zookeeper和hive服务 进入hive312/conf 修改hive-site.xml配置, 在代码最后添加配置 将hbase235的jar包全部拷贝到hive312的lib目录,并且所有的是否覆盖信息全部输入n,不覆盖 查看hive312下…...
14_MySQL视图
1. 常见的数据库对象2. 视图概述2.1 使用视图的好处视图一方面可以帮我们使用表的一部分而不是所有的表,另一方面也可以针对不同的用户制定不同的查询视图。比如,针对一个公司的销售人员,我们只想给他看部分数据,而某些特殊的数据…...
做程序界中的死神,斩魂刀始解
标题解读:标题中的死神,是源自《死神》动漫里面的角色,斩魂刀是死神的武器,始解是斩魂刀的初始解放形态,卐解是斩魂刀的觉醒解放形态,也是死神的大招。意旨做程序界中程序员的佼佼者,一步一步最…...
顺序表——“数据结构与算法”
各位CSDN的uu们你们好呀,今天小雅兰的内容是数据结构与算法里面的顺序表啦,在我看来,数据结构总体上是一个抽象的东西,关键还是要多写代码,下面,就让我们进入顺序表的世界吧 线性表 顺序表 线性表 线性表&…...
嵌入式Linux从入门到精通之第十六节:U-boot分析
简介 u-boot最初是由PPCBoot发展而来的,可以引导多种操作系统、支持多种架构的CPU,它对PowerPC系列处理器的支持最为完善,而操作系统则对Linux系统的支持最好目前已成为Armboot和PPCboot的替代品。 特点: 主要支持操作系统:Linux、NetBSD、 VxWorks、QNX、RTEMS、ARTOS、L…...
UART 串口通信
第18.1讲 UART串口通信原理讲解_哔哩哔哩_bilibili 并行通信 一个周期同时发送8bit的数据,占用引脚资源多 串行通信 串行通信的通信方式: 同步通信 同一时钟下进行数据传输 异步通信 发送设备和接收设备的时钟不同 但是需要约束波特率(…...
【硬件】P沟道和N沟道MOS管开关电路设计
场效应管做的开关电路一般分为两种,一种是N沟道,另一种是P沟道,如果电路设计中要应用到高端驱动的话,可以采用PMOS来导通。P沟道MOS管开关电路PMOS的特性,Vgs小于一定的值就会导通,当Vgs<0,即Vs>Vg,管…...
中移杭研一面经历
文章目录 1、常用的Java元注解@Documented@Target@Retention@Override@Deprecated@Inherited@Repeatable@Native2、Java注解的原理3、spring boot starter开发过程1、原理浅谈2、大概步骤3、项目介绍1、常用的Java元注解 @Documented @Documented 是一个标记注解,没有成员变…...
如何成为一名全栈工程师:专业建议与技能要求
作为一名全栈工程师,你需要拥有跨越前端、后端、数据库等多个领域的技能,并能够将它们整合起来构建出完整的应用程序。因此,成为一名全栈工程师需要你掌握多种技术,具备较强的编程能力和系统设计能力。下面,我将从以下…...
MySQL架构篇
一、进阶学习环境说明 1.1 MySQL服务器环境 Linux虚拟机:CentOS 7 MySQL:MySQL5.7.30 在Linux服务器中安装MySQL: ps.如果有自己的云服务器,可忽略前两步,直接进行第三步 1.2 服务器日志文件说明 MySQL是通过文件系统对…...
Redhat7.6安装weblogic10.3.6(超详细,有图文)
一、环境 linux版本:Redhat 7.6 weblogic版本:WLS10.3.6 jdk版本:jdk1.8.0 下载网址:https://www.oracle.com/technetwork/middleware/weblogic/downloads/index.html 1.安装vsftpd服务,将部署环境使用JDK文件和wls服务文件…...
dashboard疏散主机提示报错:无法疏散主机...处理方法、openstack虚拟机状态卡在重启处理方法、openstack在数据库修改虚拟机状态的方法
文章目录dashboard疏散主机提示报错:无法疏散主机...处理方法报错说明【状态卡在reboot状态】解决方法【登录nova数据库修改虚拟机信息】首先获取nova数据库的密码登录nova数据库并做修改验证信息是否修改成功再次迁移并验证报错说明【虚拟机状态error也会导致疏散失…...
力扣:轮转数组(详解)
前言:内容包括:题目,代码实现,大致思路,代码解读 题目: 给定一个整数数组 nums,将数组中的元素向右轮转 k 个位置,其中 k 是非负数。 示例 1: 输入: nums [1,2,3,4,5,6,7], k 3…...
Vue计算属性Computed
30. Vue计算属性Computed 1. 定义 Computed属性是Vue中的一个计算属性,是一种基于其它属性值计算而来的属性值,具有缓存机制,在依赖的属性值发生变化时会重新计算。 使用computed属性可以避免在模板中书写过多的计算逻辑,提高代…...
实验四:搜索
实验四:搜索 1.填格子 题目描述 有一个由数字 0、1 组成的方阵中,存在一任意形状的封闭区域,封闭区域由数字1 包围构成,每个节点只能走上下左右 4 个方向。现要求把封闭区域内的所有空间都填写成2 输入要求 每组测试数据第一…...
本地开发vue项目联调遇到访问接口跨域问题
本地开发vue项目联调遇到访问接口跨域问题 修改本地的localhost 一:按winr打开运行窗口,输入drivers ,然后回车 二:打开etc文件夹,然后用记事本的方式打开里面的hosts文件, 三:这时我们就可…...
Vue键盘事件的使用
前言 在vue中,我们经常会用到键盘事件,不管是我们按下某个键,其实都是一次键盘事件的调用,下面就介绍下Vue中的键盘事件 先写一段代码,这里我选择的键盘事件是keyup,当然用keydown也是没问题的 问题来了,…...
抓包工具fiddler详细使用教程
各位做测试的同学想必对抓包工具fiddler并不陌生,但是很多同学可能没有总结过它的用法,下面我总结了fiddler一些常用的用法。 Web端抓包配置 打开Fiddler,Tools -> Fiddler Options -> HTTPS 配置完后记得要重启Fiddler 选中Decrpt …...
raspberry Pi 连接蓝牙(小爱同学)
参数valueraspberry pi MOdel4B,4Gbbluetooth MOdel小爱同学writeTime2023年 2月11日 下午13:14分raspberry System ModelLinux raspberrypi 5.15.61-v8 #1579 SMP PREEMPT Fri Aug 26 11:16:44 BST 2022 aarch64 GNU/Linux 连接蓝牙 请在小爱同学app上…...
html动态网页效果代码/信阳seo
打印机和传真(Print Spooler)不可用的解决办法 突然不能打印,并提示Operation cannot be completed(操作不能完成) 单击“开始”-“打印机和传真”时,提示如下: Spooler subsystem app has enco…...
西安网站建设首选/seo研究中心qq群
1、解决问题 在实际的应用场景中,经常会出现需要把系统的附件文件存储到Tomcat路径之外的磁盘目录下。这个时候就可以实现通过配置server.xml文件实现附件存储位置与Tomcat目录分离。 2、配置Server.xml 首先找到tomcat目录下conf目录下的server.xml文件࿰…...
百度上能收到的企业名称网站怎么做/足球世界排名
Jane Street Jane Street是华尔街最神秘的交易公司,以关注科技和股票交易而闻名,去年他们总交易额达到了5万亿美元。 Jane Street公司成立于2000年,目前拥有600多名员工,每天股权交易量高达130亿美元。有消息人士透露ÿ…...
网站开发形式选择/制作链接的小程序
炫彩界面库-帮助文档 v1.8.6.1 - [首页](#)- [相关页面](#)- [模块](#)- [数据结构](#)- [文档首页](#)Button - 动画按钮### 说明Button 动画按钮.### 示例代码struct my_image_info{HIMAGE hImage1;HIMAGE hImage2;HIMAGE hImage3;HIMAGE hImage4;HIMAGE hImage5;HIMAGE hIm…...
网站打不开 .../真实有效的优化排名
http://blog.csdn.net/kexiuyi/article/details/53292358...
理财网站建设的毕业论文/宁波seo外包推广软件
之前为了能够处理命令交互,编译了静态链接的expect,结果遇到了大问题导致在没有tcl的机器上还是无法运行。找了半天,发现empty是一个不错的替代。 优势 首先,empty是一个纯C实现的,不依赖任何其他库的工具。这对于需要…...