Android开发面试:Java知识答案精解

目录

Java

集合

集合概述

HashMap

ConcurrentHashMap

泛型

反射

注解

IO流

异常、深浅拷贝与Java8新特性

Java异常

深浅拷贝

Java8新特性

并发

线程

线程池

volatile

JVM

内存区域

内存模型

类加载机制

垃圾回收机制

如何判断对象已死


Java

集合

集合概述

  1. 集合分类:大致分为List、Set、Map和Queue四种体系(Set代表无序、不可重复的集合,List代表有序、重复的集合,Map代表具有映射关系的集合,Queue代表一种队列集合实现)
  2. 集合和数组区别:a、长度:数组长度在初始化时指定,而集合可以保存数量不确定的数据,同时可以保存具有映射关系的数据;b、元素类型:数组元素既可以是基本类型的值,也可以是对象,而集合里只能保存对象(实际上只是保存对象的引用变量),基本数据类型要转换成对应包装类才能放入集合类中
  3. 集合类关系:Collection和Map是Java集合框架的根接口,List(List集合里增加了一些根据索引来操作集合元素的方法)、Set(Set集合与Collection集合基本相同,没有提供任何额外方法,实际上Set就是Collection,只是Set不允许包含重复元素)和Queue(Queue模拟队列这种“先进先出”数据结构,通常队列不允许随机访问队列中元素)接口继承自Collection,Map里所有key相当于一个Set集合、所有value相当于一个List集合
  4. ArrayList与LinkedList:ArrayList以数组实现,查询快增删慢(自动扩容,动态数组节约空间,但数组有容量限制,默认第一次插入元素时创建大小为10的数组,超出限制时会增加50%容量,用 System.arraycopy()复制到新的数组,因此最好能给出数组大小的预估值);LinkedList以双向链表实现,node函数将节点访问复杂度由O(n)变为O(n/2)(链表无容量限制,但双向链表本身使用了更多空间,也需要额外的链表指针操作)

HashMap

  1. 核心:数组(哈希表)+链表/红黑树,基于Map接口实现、允许null键/值、非同步、不保证有序(比如插入的顺序)、也不保证顺序不随时间变化
  2. 容量和负载因子:容量(Capacity,容器大小,默认16)、负载因子(loadFactor,容器填满程度的最大比例,默认0.75);当容器内容数目大于 capacity*loadFactory时,就需要调整容器大小为16的2倍
  3. 为什么扩容,并且为2的倍数:数据结构是数组需要扩容;如果是2的倍数,就可以用位运算替代取余操作,更加高效(HashMap存取时,计算index即(length- 1) & hash,使用&运算符相比%效率更高,如果length为2的倍数,可以最大程度确保index均分)
  4. put函数:a、首先对key的hashCode()做hash,然后再计算index;b、如果没碰撞直接放到bucket里;c、如果碰撞了,以链表的形式存在buckets;d、如果碰撞导致链表过长(大于等于8 ),就把链表转换成红黑树(提高遍历查询),当红黑树上节点数量小于6个,会重新把红黑树变成单向链表数据结构;e、如果节点已经存在就替换old value(保证key唯一性);f、如果bucket满了(超过 load factor*current capacity ),就要resize
  5. get函数:a、bucket里的第一个节点,直接命中;b、如果有冲突,则通过key.equals(k)去查找对应的entry;c、若为树,则在树中通过key.equals(k)查找,O(logn);若为链表,则在链表中通过key.equals(k)查找,O(n)
  6. 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的分布已经很不错)
  7. HashMap为什么使用String、int作为key:a、都是final修饰的类,能够保证Hash值的不可更改性和计算准确性;b、内部已重写了equals()、hashCode()等方法,能够有效的减少Hash碰撞(Integer的hashCode就是值本身)
  8. HashMap与Hashtable有什么区别:a、Hashtable线程安全,效率低,HashMap非线程安全(多个线程同时resize可能会引起死循环、put操作覆盖),效率高;b、null可以作为HashMap的key和value,不可以作为Hashtable的key;c、Hashtable初始容量11,负载因子0.75,扩容2n+1;d、Hashtable没有链表转红黑树的机制
  9. HashMap与SpareArray:key为int时考虑使用SpareArray,SpareArray使用2个数组存储key和value。好处:a、没有自动拆装箱;b、数据量不大时采用二分查找效率高,不需要开辟内存空间来额外存储外部映射,节省内存
  10. LinkedHashMap:数组(哈希表)+双向链表/红黑树,双向链表能够保证访问顺序,内部使用LruCache做最近最少使用的移除,继承自HashMap(put函数在LinkedHashMap中未重新实现,只是实现了afterNodeAccess和afterNodeInsertion两个回调函数,get函数则重新实现并加入了afterNodeAccess来保证访问顺序)
  11. HashMap、LinkedHashMap、TreeMap:HashMap不保证数据有序, LinkedHashMap保证数据插入顺序,TreeMap保证Map的key大小顺序

ConcurrentHashMap

  1. 核心:Java8的实现已经抛弃了Segment分段锁机制,利用CAS+Synchronized来保证并发更新的安全,底层依然采用数组table+链表+红黑树的存储结构
  2. 解决问题:HashMap多线程put操作并发死循环问题;以及Hashtable、Collections.synchronizedMap(hashMap)加锁效率低问题
  3. 数组table初始化:不会在构造时初始化,会延缓到第一次put行为,第一次put操作的线程会执行Unsafe.compareAndSwapInt方法修改sizeCtl(默认0)为-1,有且只有一个线程能够修改成功,其它线程通过Thread.yield()让出CPU时间片等待table初始化完成
  4. 数组table扩容:构建一个nextTable,大小为table两倍,把table数据复制到nextTable中
  5. put函数:采用CAS+synchronized实现并发插入或更新操作,key和value均不能为null
  6. get函数:a、判断table是否为空,如果为空,直接返回null;b、计算key的hash值,并获取指定table中指定位置的Node节点,通过遍历链表或则树结构找到对应的节点,返回value值

泛型

  1. 泛型含义:泛型本质就是参数化类型,这种类型可以用在类、接口和方法的创建中,分别称为泛型类、泛型接口、泛型方法,是一种语法糖(自动拆装箱、变长参数、内部类等也是语法糖)
  2. 泛型好处:让类型更加安全。a、编译时类型检查,将错误暴露在编译期,不用等到运行时,有助于开发者更容易找到错误,提高程序可靠性;b、运行时自动类型转换,避免强制类型转换出现ClassCastException;c、更加语义化(比如:List清楚知道存储的是String对象)和能写出更加通用化的代码(引入泛型后并未增加代码的冗余性)
  3. 类型通配符:上限(List:表示集合中所有元素都是Shape类型或其子类)、下限(List:表示集合中所有元素都是Circle类型或其父类)
  4. 泛型擦除:a、Java泛型是不变的(Fruit是Apple父类,但List不是List父类),是类型擦除的,可以看做伪泛型;b、无法在程序运行时获取到一个对象的具体类型;c、在静态方法、静态初始化块或静态变量的声明和初始化中,不允许使用类型形参;d、由于系统中并不会真正生成泛型类,所以instanceof运算符后不能使用泛型类

反射

  1. 反射机制:运行时,动态获取类信息,以及动态调用对象
  2. 反射机制应用场景:a、逆向代码(反编译);b、与注解相结合的框架(Retrofit);c、单纯的反射机制应用框架(EventBus);d、动态生成类框架(Gson)
  3. 3种方式获取Class对象:a、Class.forName("全限定类名");b、类名.class;c、对象.getClass()
  4. 反射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
  5. 代理模式:静态代理(代理类在编译时就实现好,是一个实际的class文件)、动态代理(代理类在运行时动态生成的类字节码,并加载到JVM中)
  6. 动态代理:主要涉及两个类Proxy、InvocationHandler,都在java.lang.reflect包下,内部主要通过反射实现
  7. Proxy:这是生成代理类的主类,通过Proxy类生成的代理类都继承自Proxy类,是所有动态代理类的父类,并且提供了用户创建动态代理类静态方法getProxyClass和创建代理对象静态方法newProxyInstance
  8. InvocationHandler:调用动态代理类中方法时,将会直接转接到执行自定义InvocationHandler中的invoke()方法

注解

  1. 元数据:是关于数据的数据,对数据进行说明描述,添加到程序元素如方法、字段、类和包上的额外信息
  2. 注解Annotation:就是Java平台的元数据,允许在Java代码中添加自定义注释,并允许通过反射,以编程方式访问元数据
  3. 常见注解:@Override(可适用元素为方法,仅仅保留在Java源文件中)、@Deprecated、@SuppressWarnings
  4. 元注解:@Documented被提取成文档、@Inherited某个类定义@Xxx注解时使用@Inherited修饰,那么其子类自动被该@Xxx注解修饰、@Retention(SOURCE、CLASS、RUNTIME)注解保留时长、@Target(CONSTRUCTOR、FIELD、METHOD等)注解所适用的程序元素类型
  5. 注解保留策略为RetentionPolicy.RUNTIME时,才可在运行期通过反射机制来使用

IO流

  1. 输入输出流:输入流(只能从中读取数据,而不能向其写入数据)、输出流(只能向其写入数据,而不能从中读取数据)
  2. 字节流与字符流:字符流的由来——因为数据编码不同,而有了对字符进行高效操作的流对象,本质其实就是基于字节流读取时,去查了指定的码表。处理纯文本数据优先考虑使用字符流,除此之外都使用字节流
  3. 节点流与处理流:节点流(FileInputStream低级流,可以从/向一个特定IO设备(如磁盘、网络)读/写数据的流)、处理流(BufferedInputStream高级流,包装节点流,通过封装后的流来实现数据读/写功能)
  4. IO流分类:分为如下4种抽象基类,InputStream输入字节流、OutputStream输出字节流、Reader输入字符流、Writer输出字符流
  5. IO流关闭:在执行完输入流操作后,要调用close()方法来关闭,因为程序里打开的IO资源不属于内存资源,垃圾回收机制无法回收该资源,所以应该显式关闭文件IO资源;在执行完输出流操作后,要调用close()方法来关闭,关闭输出流除了可以保证流的物理资源被回收之外,还能将输出流缓冲区数据flush到物理节点里(因为在执行 close()方法之前,会自动执行输出流的flush()方法
  6. RandomAccessFile优缺点和使用场景:优点(既可以读取文件内容,也可以向文件输出数据,同时支持自由访问文件任意位置(内部包含一个记录指针来实现随机访问))、缺点(只能读写文件,不能读写其他IO节点)、使用场景(网络请求中多线程下载及断点续传)
  7. NIO:New IO,新型IO可替代标准IO,并提供与标准IO不同工作方式,标准IO基于字节流和字符流进行操作,而NIO基于通道(Channel)和缓冲区(Buffer)进行操作,数据总是从通道读到缓冲区中,或从缓冲区写入通道

异常、深浅拷贝与Java8新特性

Java异常

  1. 是什么:是Java提供的一种识别及响应错误的一致性机制,Java异常机制可以使程序中异常处理代码和正常业务代码分离,保证程序代码更加优雅,并提高程序健壮性
  2. 关键字:try(用于监听。将要被监听的代码(可能抛出异常的代码)放在try语句块之内,当 try语句块内发生异常时,异常就被抛出)、catch(用于捕获异常。catch用来捕获try语句块中发生的异常)、finally(总是会被执行。用于回收在try块里打开的资源(如数据库连接、网络连接和磁盘文件))、throw(用于抛出异常)、throws(用在方法签名中,用于声明该方法可能抛出的异常)
  3. finally与return执行顺序:a、只有finally块执行完成之后,才会回来执行try或catch块中的return或throw语句(finally语句在return语句执行之后return返回之前执行);b、如果finally中使用了return或者throw等终止方法的语句,则就不会跳回执行,直接停止;c、如果finally语句中没有return语句覆盖返回值,那么原来的返回值可能因为finally里的修改而改变(改变引用)也可能不变(改变值)
  4. 异常框架:a、Throwable是Java 语言中所有错误或异常的超类;b、Throwable包含Error和Exception两个子类;c、Exception类本身以及它的子类中,除了"运行时异常"之外的其它子类都属于被检查异常;d、RuntimeException是Exception子类,编译器不会检查运行时异常(没有通过throws声明抛出RuntimeException异 常",也"没有通过try...catch...处理该异常",也能通过编译);e、Error用于指示合理的应用程序不应该试图捕获的严重问题,和RuntimeException一样,编译器也不会检查Error

深浅拷贝

  1. Java中有三种类型对象拷贝:浅拷贝(Shallow Copy)、深拷贝 (Deep Copy)、延迟拷贝(Lazy Copy)
  2. 浅拷贝:按位拷贝对象,会创建一个新对象,这个对象有着原始对象属性值的一份精确拷贝(如果属性是基本类型,拷贝的就是基本类型的值;如果属性是引用类型,拷贝的就是内存地址 ,因此如果其中一个对象改变了这个地址, 就会影响到另一个对象)、实现方式(需要拷贝的类实现Clonable接口并重写clone()方法,并在方法中调用super.clone())
  3. 深拷贝:会拷贝所有的属性,并拷贝属性指向的动态分配内存(当对象和它所引用的对象一起拷贝时即发生深拷贝,深拷贝相比于浅拷贝速度较慢并且花销较大)、实现方式(a、new实现:需要拷贝的类实现Clonable接口并重写clone()方法,并在方法中new一个对象(类中有引用对象才会发生深拷贝);b、序列化实现)
  4. 延迟拷贝:是浅拷贝和深拷贝的一个组合,从外面看起来就是深拷贝,但是只要有可能它就会利用浅拷贝的速度,当原始对象中的引用不经常改变的时候可以使用延迟拷贝
  5. 深浅拷贝选择:a、如果对象属性全是基本类型、对象引用任何时候都不会被改变,可以使用浅拷贝;b、如果对象有引用属性,就要基于具体需求来选择浅拷贝还是深拷贝,如果对象引用经常改变,就要使用深拷贝

Java8新特性

  1. Lambda表达式和函数式接口:函数接口指只有一个函数的接口,这样的接口可以隐式转换为Lambda表达式;只要某个开发者在该接口中添加一个函数,则该接口就不再是函数式接口,进而导致编译失败,为了克服这种代码层面的脆弱性,并显式说明某个接口是函数式接口,Java8 提供了一个特殊的注解@FunctionalInterface(Java库中的所有相关接口都已经带有这个注解)
  2. 接口新增特性:除了常量和抽象方法,在Java8增加了默认方法和静态方法(在Java9增加了私有方法)
  3. Java8抽象类和接口区别:a、继承:抽象类只能单继承,接口可以被接口多重继承、被类多实现;b、设计理念不同:抽象类表示的是"is-a"关系(这个对象是什么),接口表示的是"like-a"关系(这个对象能做什么);c、变量:接口中定义的变量默认是public static final型且必须给其初值,实现类中不能重新定义,也不能改变其值;抽象类中的变量是普通变量,其值可以在子类中重新定义,也可以重新赋值

并发

线程

  1. 创建线程的4种方式:实现Runnable接口(实现run方法,调用时作为参数传递给Thread构造方法)、继承Thread类(实现run方法)、使用Java8的Lambda(new Thread(()-> System.out.println(Thread.currentThread().getName())).start())、使用Callable和Future(Callable和Runnable类似,JDK1.5提供以弥补调用线程没有返回值的情况)
  2. 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来捕捉处理异常)
  3. interrupt方法:线程中断,interrupt作用其实也不是中断线程,而是通知线程应该中断,一个线程不应该由其他线程来强制中断或停止,而是应该由线程自己自行停止        
  4. yield方法:线程让步,让掉当前线程CPU执行的时间片,让当前线程或其它线程运行(是让自己或其他线程运行,并不是单纯让给其他线程)
  5. sleep方法:线程睡眠,让当前线程睡眠指定毫秒,在指定时间内当前线程处于阻塞状态
  6. sleep()和wait()区别:区别(sleep为线程方法,而wait为Object方法,他们功能相似)、本质区别(sleep不释放锁,wait释放锁)、用法上不同(sleep(milliseconds)可以用时间指定来使他自动醒过来,如果时间不到你只能调用interreput()来终止线程;wait()可以用notify()/notifyAll()直接唤起)
  7. 线程同步:一共synchronized(代码块同步、方法同步)和ReentrantLock(ReentrantLock lock = new ReentrantLock(); lock.lock();lock.unlock())两种锁,来实现线程同步问题,乐观锁ReentrantLock,悲观锁synchronzed、Lock

线程池

  1. 线程池优势:a、降低系统资源消耗(通过重用已存在线程,降低线程创建和销毁造成的消耗);b、提高系统响应速度(当有任务到达时,无需等待新线程创建便能立即执行);c、方便线程并发管控(线程若是无限制创建,不仅会额外消耗大量系统资源,更是占用过多资源而阻塞系统或oom等状况,从而降低系统稳定性,而线程池能有效管控线程,统一分配、调优,提高资源使用率);d、更强大的功能(线程池提供了定时、定期以及可控线程数等功能的线程池,使用方便简单)
  2. ThreadPoolExecutor参数:corePoolSize(核心线程数,默认情况下核心线程一直存活在线程池中,即便处于闲置状态;除非将ThreadPoolExecutor的allowCoreThreadTimeOut属性设为true,这时候处于闲置的核心线程在等待新任务到来时会有超时策略,一旦超过keepAliveTime所设置的超时时间,闲置核心线程就会被终止)、maximumPoolSize(最大线程数,包含核心线程数+非核心线程数,活动线程达到这个数以后,后续新任务将会被阻塞)、keepAliveTime(非核心线程闲置时的超时时长,对于非核心线程,闲置时间超过这个时间,非核心线程就会被回收;当allowCoreThreadTimeOut属性设为true时,这个超时时间才会对核心线程产生效果)、unit(用于指定keepAliveTime参数的时间单位)、workQueue(保存等待执行任务的阻塞队列,通过线程池execute方法提交的Runable对象都会存储在该队列中)、threadFactory(线程工厂,为线程池提供新线程的创建)、handler(是RejectedExecutionHandler对象,当任务队列已满,且线程池中活动线程已达到限定最大值或是无法成功执行任务,这时ThreadPoolExecutor会调用RejectedExecutionHandler的rejectedExecution方法)
  3. 线程池执行流程:提交任务——核心线程数是否已满(未满就创建核心线程执行任务)——任务队列是否已满(未满就加入队列)——线程池是否已满(未满就创建非核心线程执行任务)——拒绝执行(调用handler.rejectedExecution通知调用者)
  4. newFixedThreadPool():内部ThreadPoolExecutor实现,new ThreadPoolExecutor(nThreads, nThreads, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue())——只有核心线程,并且这些线程都不会被回收,不存在超时机制,采用LinkedBlockingQueue在于其无界性(对于任务队列大小也没有限制)
  5. newCachedThreadPool():(0, Integer.MAX_VALUE,60L, TimeUnit.SECONDS, new SynchronousQueue()) ——没有核心线程,最大线程数为整数最大值,闲置60s的线程会被回收,采用SynchronousQueue(内部是个空集合,任务队列大小为0,如果现有线程无法接收任务,将会创建新线程来执行任务)
  6. newScheduledThreadPool():(corePoolSize, Integer.MAX_VALUE, 0, NANOSECONDS,new DelayedWorkQueue())——核心线程数固定,最大线程数为整数最大值,非核心线程处于限制状态时会立即被回收,创建一个可定时执行或周期执行任务的线程池,返回值是ScheduledExecutorService
  7. ScheduledExecutorService:schedule方法(延迟一定时间后执行任务)、scheduleAtFixedRate(延迟一定时间后,以一定间隔周期性执行任务,时间间隔是从上一个任务开始执行到下一个任务开始执行的间隔,这一系列任务的触发时间都是可预知)、scheduleWithFixedDelay(时间间隔是从上一个任务执行结束到下一个任务开始执行的间隔,这一系列任务的触发时间不可预知)
  8. newSingleThreadExecutor():new ThreadPoolExecutor(1, 1,0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue())
  9. 线程池使用:CPU密集型任务(线程池中线程个数应尽量少,如配置N+1个线程的线程池)、IO密集型任务(IO操作速度远低于CPU速度,CPU绝大多数时间处于空闲状态,那么线程池可以配置尽量多线程,以提高CPU利用率,如2*N)、混合型任务(拆分成CPU密集和IO密集,当这两类任务执行时间相差无几时,通过拆分再执行吞吐率高于串行执行吞吐率,但若这两类任务执行时间有数据级差距,那么没有拆分意义)

  1. 死锁产生条件:互斥条件(一个资源每次只能被一个线程使用)、请求与保持条件(一个线程因请求资源而阻塞时,对已获得的资源保持不放)、不剥夺条件(线程已获得的资源,在未使用完之前,不能强行剥夺)、循环等待条件(若干线程之间形成一种头尾相接的循环等待资源关系)
  2. 死锁分类和解决办法:静态的锁顺序死锁(所有需要多个锁的线程,都要以相同顺序来获得锁)、动态的锁顺序死锁(使用System.identifyHashCode来定义锁的顺序,确保所有线程都以相同顺序获得锁)、协作对象之间发生的死锁(需要使用开放调用(开放调用就是调用某个外部方法时不需要持有锁),避免在持有锁的情况下调用外部方法)
  3. 重入锁:当一个线程得到一个对象后,再次请求该对象锁时是可以再次得到该对象锁的,也就是说自己可以再次获取自己的内部锁,Java内置锁(synchronized)和Lock(ReentrantLock)都是可重入的
  4. 公平锁:优先级低的线程可能一直无法获取到CPU执行权,公平锁就可以保证线程按照时间先后顺序执行,避免产生饥饿现象,但公平锁效率比较低,因为需要维护一个有序队列以实现顺序执行,ReentrantLock便是一种公平锁,通过在构造方法中传入true就是公平锁,传入false就是非公平锁,默认false
  5. synchronized和Lock的区别:a、Lock是一个接口,而synchronized是Java中的关键字,synchronized是内置的语言实现;b、synchronized在发生异常时,会自动释放线程占有的锁,因此不会导致死锁现象发生,而Lock在发生异常时,如果没有主动通过unLock()去释放锁,则很可能造成死锁现象,因此使用Lock时需要在finally块中释放锁;c、Lock可以让等待锁的线程响应中断,而synchronized却不行,使用synchronized时,等待的线程会一直等待下去,不能够响应中断;d、通过Lock可以知道有没有成功获取锁,而synchronized却无法办到;e、Lock可以提高多个线程进行读操作的效率

volatile

  1. 原子性:一个或多个操作,要么全部执行且执行过程不会被任何因素打断,要么都不执行
  2. 可见性:当多个线程访问同一个变量时,一个线程修改了这个变量的值,其他线程能够立即看到修改后的值
  3. 有序性:程序执行的顺序按照代码的先后顺序执行
  4. 指令重排序:处理器为了提高程序运行效率,可能会对输入代码进行优化,它不保证程序中各个语句执行先后顺序同代码中顺序一致,但它会保证程序最终执行结果和代码顺序执行结果是一致的;指令重排序不会影响单个线程的执行,但是会影响到线程并发执行的正确性
  5. 锁与原子性、可见行、有序性:a、Java内存模型只保证基本读取和赋值是原子性操作,如果要实现更大范围操作的原子性,可以通过synchronized和Lock来实现;b、通过synchronized和Lock也能够保证可见性,同一时刻只有一个线程获取锁然后执行同步代码,并且在释放锁之前会将对变量的修改刷新到主存当中;c、synchronized和Lock同样能保证有序性
  6. volatile与原子性、可见性、有序性:a、volatile不能确保对变量任何操作都是原子性的,而且自增操作不是原子性操作,Java5带来的atomic系列如AtomicInteger利用CAS(Compare And Swap)能保证操作的原子性;b、volatile保证可见性,当一个共享变量被volatile修饰时,它会保证修改的值会立即被更新到主存,当有其他线程需要读取时,它的缓存会无效,就会去内存中读取新值;c、volatile可以保证有序性,禁止指令进行重排序,一个方法体中volatile变量所在语句的前/后面语句执行顺序不会变,但前/后面语句的内部多条语句执行顺序不做保证
  7. volatile应用场景:基于多线程下,a、作为状态开关;b、单例模式中的double check(第一次判空为了效率避免加锁,第二次判空为了并发安全,volatile避免new语句的指令重排序操作)

JVM

内存区域

  1. 方法区(公有):存储已被虚拟机加载的类信息、常量、静态常量、即时编译器编译后代码等数据,包含常量池(用户存放编译器生成的各种字面量和符号引用),异常状态OutOfMemoryError
  2. 堆(公有):是JVM所管理内存中最大的一块,唯一目的就是存放实例对象,几乎所有的对象实例都在这里分配;Java堆是垃圾收集器管理的主要区域,很多时候也被称为“GC堆”,异常状态OutOfMemoryError
  3. 虚拟机栈(线程私有):描述的是Java方法执行的内存模型;每个方法在执行时都会创建一个栈帧,用户存储局部变量表,操作数栈,动态连接,方法出口等信息;每个方法从调用直至完成的过程,就对应着一个栈帧在虚拟机栈中入栈到出栈的过程;对这个区域定义了两种异常状态OutOfMemoryError、StackOverflowError
  4. 本地方法栈(线程私有):与虚拟机栈所发挥的作用相似,区别不过是虚拟机栈为虚拟机执行Java方法,而本地方法栈为虚拟机使用到Native方法服务
  5. 程序计数器(线程私有):一块较小的内存,当前线程所执行字节码的行号指示器;字节码解释器工作时,就是通过改变这个计数器的值来选取下一条需要执行的字节码指令

内存模型

  1. Java内存模型目的:屏蔽各种硬件和操作系统的内存访问差异,以实现让Java程序在各种平台下都能达到一致的内存访问效果
  2. 主要目标:定义程序中各个变量访问规则,即在虚拟机中将变量存储到内存和从内存中取出变量这样的底层细节(此处的变量与Java变成中所说的变量有所区别,它包括了实例字段,静态字段和构成数组对象的元素,但不包括局部变量和方法参数)
  3. 主内存与线程工作内存的变量:Java内存模型规定所有变量都存储在主内存中,每条线程在自己工作内存中,保存被该线程所使用到的变量(这些变量是从主内存中拷贝而来),线程对变量所有操作(读取、赋值)都必须在工作内存中进行,不同线程之间也无法直接访问对方工作内存中变量,线程间变量值的传递均需要通过主内存来完成

类加载机制

  1. 定义:把class文件加载到内存,并对其进行验证、准备、解析和初始化,最终形成可被虚拟机直接使用的Java类型
  2. 类的生命周期:加载、连接(验证,准备,解析)、初始化、使用和卸载
  3. 触发类加载时机:new,static字段,static方法,反射,初始化类时先初始化父类
  4. 类加载具体过程:加载(a、通过全限定类名,类加载器获取类的二进制字节流;b、将这个字节流所代表的静态存储结构,转换为方法区内的运行时数据结构;c、在内存中生成代表这个类的Class对象,作为方法区这个类的各种数据访问入口)、验证(Class文件格式、元数据验证、字节码验证、符号引用验证)、准备(在方法区为类变量分配内存并设置初始值)、解析(虚拟机将常量池内的符号引用替换为直接引用的过程,动态和静态解析)、初始化(执行类构造器(与类的构造函数不同,不需要显示调用父类构造器,虚拟机会保证在子类方法执行之前,父类构造器已经执行完毕)、static代码块、给类变量赋值)
  5. 类加载器分类:主要分为两类,启动类加载器(Bootstrap ClassLoader,C++语言实现(针对HotSpot),负责加载Java核心类,将存放在\lib目录或-Xbootclasspath参数指定路径中的类库加载到内存中)、其他类加载器(由Java语言实现,继承自抽象类ClassLoader。扩展类加载器——Extension ClassLoader,Java语言实现,负责加载Java扩展核心类之外的类,加载\lib\ext目录或java.ext.dirs系统变量指定路径中的所有类库;应用程序类加载器——Application ClassLoader,Java语言实现,默认加载器,负责加载用户类路径classpath上指定类库,通过 ClassLoader.getSystemClassLoader()方法直接获取
  6. 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的父类)
  7. 双亲委派模型:自定义类加载器——Application ClassLoader——Extension ClassLoader——Bootstrap ClassLoader
  8. 双亲委派模型代码实现:ClassLoader中loadClass方法实现了双亲委派模型,自定义类加载器就主要重写findClass方法

垃圾回收机制

  1. 垃圾收集算法:标记-清除算法(标记和清除效率都不高、产生大量内存碎片)、复制算法(为了解决效率问题,内存划分为大小相等两块,分配效率高但内存缩小一半)、标记-整理算法(对象存活率高时复制算法效率变低,标记过程仍然与”标记-清除“算法一样,但后续步骤不是直接对可回收对象进行清理,而是让所有存活对象都向一端移动,然后直接清理掉边界以外的内存)、分代收集算法(新生代使用复制算法、老年代采用标记清除或标记整理进行Java堆内存回收)
  2. JVM中的年代:新生代(1个Eden区存放新建对象和2个Survivor区(From Survivor区与To Survivor区)保存还存活对象,默认内存划分比例为8:1,Minor GC时“Eden”存活对象被复制到“To”,“From”区年龄未达到阈值的对象被复制到“To”区,然后“From”和“To”会交换角色保证名为To的Survivor区域是空的)、老年代(“From”区年龄达到阈值的对象会被移动到老年代中;“To”区被填满之后,会将所有对象移动到老年代中)
  3. Minor GC和Full/Major GC:Minor GC(发生在新生代的垃圾收集动作,非常频繁Major)、Full/Major GC(发生在老年代,Major GC通常会伴随至少一次有Minor GC,且Major GC速度比Minor GC慢10倍以上)
  4. 空间分配担保:在发生Minor GC之前,虚拟机会先检查老年代最大可用连续空间,是否大于新生代所有对象总空间,大于那么可以确保Minor GC是安全的,小于则可能进行Full GC

如何判断对象已死

  1. 引用计数法:给对象添加一个引用计数器,当多一个引用则计数器就加1,引用失效则计数器减1,计数器为0表示对象不再使用,JVM未使用这种方式,因为很难解决对象间互循环引用的问题
  2. 可达性分析算法:GC Roots对象(Java栈对象、Native栈对象、方法区static对象和final对象)、算法描述(通过一些列GC Roots对象作为起始点,从这些节点开始向下搜索,所走过的路径称为引用链,当一个对象到GC Roots没有任何引用链相连时(就是从GC Roots到这个对象是不可达),则证明此对象是不可用的,它们会被判定为可回收对象)、宣告对象死亡的两次标记过程(对象没有与GC Roots相连接,会被第一次标记并进行一次筛选(此对象是否有必要执行finalize方法),如果对象在finalie()中重新与引用链上任何一个对象建立关联,那在第二次标记时它将会被移除出“即将回收”集合,但如果没建立联系就会被回收)
  3. 对象是否存活与引用相关:4大引用,强引用(OOM也不会回收对象)、软引用(内存不足时(OOM之前)回收,如:图片缓存)、弱引用(下一次GC时回收,防止内存泄漏)、虚引用(随时可能被回收,为一个对象设置虚引用的唯一目的,就是能在这个对象被GC回收时能得到一个系统通知)


Java

集合

集合概述

HashMap

ConcurrentHashMap

泛型

反射

注解

IO流

异常、深浅拷贝与Java8新特性

Java异常

深浅拷贝

Java8新特性

并发

线程

线程池

volatile

JVM

内存区域

内存模型

类加载机制

垃圾回收机制

如何判断对象已死


Android开发面试系列文章:


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
基于PyTorch的Embedding和LSTM的自动写诗实验LSTM (Long Short-Term Memory) 是一种特殊的循环神经网络(RNN)架构,用于处理具有长期依赖关系的序列数据。传统的RNN在处理长序列时往往会遇到梯度消失或梯度爆炸的问题,导致无法有效地捕捉长期依赖。LSTM通过引入门控机制(Gating Mechanism)和记忆单元(Memory Cell)来克服这些问题。 以下是LSTM的基本结构和主要组件: 记忆单元(Memory Cell):记忆单元是LSTM的核心,用于存储长期信息。它像一个传送带一样,在整个链上运行,只有一些小的线性交互。信息很容易地在其上保持不变。 输入门(Input Gate):输入门决定了哪些新的信息会被加入到记忆单元中。它由当前时刻的输入和上一时刻的隐藏状态共同决定。 遗忘门(Forget Gate):遗忘门决定了哪些信息会从记忆单元中被丢弃或遗忘。它也由当前时刻的输入和上一时刻的隐藏状态共同决定。 输出门(Output Gate):输出门决定了哪些信息会从记忆单元中输出到当前时刻的隐藏状态中。同样地,它也由当前时刻的输入和上一时刻的隐藏状态共同决定。 LSTM的计算过程可以大致描述为: 通过遗忘门决定从记忆单元中丢弃哪些信息。 通过输入门决定哪些新的信息会被加入到记忆单元中。 更新记忆单元的状态。 通过输出门决定哪些信息会从记忆单元中输出到当前时刻的隐藏状态中。 由于LSTM能够有效地处理长期依赖关系,它在许多序列建模任务中都取得了很好的效果,如语音识别、文本生成、机器翻译、时序预测等。
### 回答1: 《Verilog HDL实用精解》是一本设计人员必备的指南书籍。Verilog HDL是硬件描述语言的一种,它是用于设计数字系统的。本书主要针对初学者和中级设计人员,包括了Verilog HDL的基础知识和设计技术,帮助读者快速掌握该语言。 本书可以帮助读者进行系统级设计,可以应用于各种数字电路的设计,如嵌入式系统、通信控制、计算机处理器等。该书内容涵盖了从基础的Verilog HDL知识到高级设计技术,包括组合逻辑电路、时序电路、存储器和存储器控制器、数字信号处理等方面的内容。 读者可以通过本书了解到如何进行仿真和验证,在设计过程中避免一些常见的错误,提高设计的可靠性。此外,本书还提供了大量的实际案例,帮助读者利用所学知识设计出高性能、低功耗、高可靠性的数字系统。 总之,《Verilog HDL实用精解》是一本全面、易懂、实用的设计指南。读者可以透过该书学习到设计数字系统的基础知识和设计技术,快速提高自己的设计水平,成为一名优秀的设计高手。 ### 回答2: 《Verilog HDL实用精解PDF》是一本非常优秀的电路设计书籍,对于想要成为设计高手的人来说,是一本非常宝贵的资料。Verilog HDL是一种高级硬件描述语言,它可以用于数字电路开发,故而在电路设计中应用广泛。 这本书的内容非常详细,对于不同层次的读者来说都非常有帮助。对初级读者来说,本书提供了对Verilog HDL的简单解释,包括语言元素,语法规则和应用场景等内容。对于有经验的读者来说,本书更是提供了实用的技术示例和项目实战经验,让读者能够快速地掌握设计技能。 此外,本书还提供了许多实例,这些实例既包括了简单的电路,也包括了复杂的设计,能让读者更好地理解设计思路,从而提高自己的设计能力。 总体来说,《Verilog HDL实用精解PDF》是一本非常好的电路设计书籍,读完后可以帮助你掌握Verilog HDL的基础知识,从而使你成为一个更强大的设计高手。 ### 回答3: Verilog HDL (Hardware Description Language) 是一种常用于硬件描述的编程语言。《Verilog HDL实用精解》是一本关于Verilog HDL应用的书籍,通过实用的案例和实践,帮助读者掌握Verilog HDL的使用技巧。 该书分为四部分,分别是基础篇、综合篇、布局篇和实践篇。基础篇主要介绍Verilog HDL的基础语法和基本概念,包括模块、端口、信号、赋值、条件语句、循环语句等。综合篇主要介绍了如何将Verilog HDL代码综合到门级电路,包括时序逻辑、组合逻辑、状态机设计、FPGA 等。 布局篇主要介绍Verilog HDL的物理实现和布局布线的技术,包括布局优化、时序优化、功耗优化、时钟树设计等。实践篇则通过一些实际案例,让读者深入了解Verilog HDL代码的设计和实现过程。 通过阅读《Verilog HDL实用精解》,读者可以全面了解Verilog HDL的应用,熟练掌握硬件设计的技能,轻松成为设计高手。该书内容丰富、实用性强,能够满足读者不同阶段和程度的学习需求。同时,该书也是一本值得硬件设计爱好者和从业人员一起阅读和交流的书籍。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值