第一章 JavaSE
第一章 JavaSE
第二章 JDBC
第三章 MVC
第四章 JavaScript
第五章 计算机网络
第六章 数据结构
第七章 数据库
Java基础知识点https://blog.csdn.net/gutianwei1/article/details/111183439
点击链接link
文章目录
前言
时间截止2020年底,关于Java基础、关键字、集合、线程、锁、JDK、JVM、GC、IO流的面试知识点总结一、Java基础(一)
1.Java和PHP的区别
(1)PHP是服务器端执行的脚本语言,适用于web开发领域。
(2)PHP的库函数是C语言实现,而Java核心类库是(jdk/lib/rt.jar)是用Java编写。
(3)PHP内置模板引擎,自身就是模板语言,JavaWeb要使用JSP容器,如Tomcat或第三方模板引擎。
(4)PHP不管是多进程还是多线程的PHPweb,都不需要PHP开发者关心和控制,这些都是由PHP-FPM实现。PHP一个worker进程崩溃,master进程会自动新建一个新的worker进程。
(5)Java多线程编程需要Java开发者参与,稍有不慎(如没有捕获异常)就会导致JVM崩溃退出。
2.Java中如何支持正则表达式
(1)String类提供了正则表达式的方法:
①matches();
②replaceAll();
③replaceFirst();
④split();
(2)Pattern类提供正则表达式的对象:Pattern p=Pattern.compile("");
3.正则表达式及其用途
(1)用于描述符和某些负责规则字符串的工具。
4.比较Java和JavaScript
(1)Java是Sun公司推出的面向对象语言,适合互联网应用程序开发;JavaSript是NetScape公司开发的一种可嵌入web网页运行的基于对象和事件驱动的解释型语言。
(2)Java前身是Oak,JavaScript前身LiveScript。
(3)基于对象和面向对象:Java是面向对象,即使开发简单程序也必须设计对象;JavaScript是基于对象和事件驱动的语言,因而本身提供了丰富的内部对象,如String、Array、Date、Boolean、Number、Math、Object、Error
(4)JavaScript支持三种对象:
①内置对象
②浏览器对象:window、document窗口、history浏览过的页面、location当前URL
③自定义对象
(5)解释和编译:
①Java执行前必须编译。
②JavaScript是解释性语言,不用编译,有浏览器解释执行。
(6)强类型和弱类型:
①Java是强类型变量,变量编译前必须声明。
②JavaScript示弱类型变量,使用变量前可以不用声明,解释器运行时推断。
5.&和&&的区别
(1)&
①逻辑与,两边都为true,结果为true。
②位运算符,两边不为Boolean类型时,表示按位“与”操作
(2)&&
①逻辑与
②短路功能:如果左边表达式为false,则不判断右边表达式真假。
如:str!=null&&!str.equals(""),两者顺序不可颠倒,也不能用&。因为第一个条件不成立,则str=null,此时str调不出.equals方法,就不能进行equals比较,否则产生NullPointerException
二、Java基础(二)
1.数组Array和列表ArrayList的区别
(1)Array可以包含基本类型和对象类型,ArrayList只能包含对象类型。
(2)Array大小是固定的,ArrayList大小是动态可变的(默认扩展因子1.5)。
(3)Array提供更多的方法
2.值传递和引用传递
(1)基本数据类型是值传递,值就保存在变量中。
(2)引用数据类型是引用传递,变量保存的是对象的地址。
3.为什么会出现4.0-3.6=0.40000001
计算机在计算十进制小数时要先转换成二进制小数进行计算。二进制小数无法精确表达十进制小数。
4.十进制的数在内存中以补码形式存在
5.Lamda表达式优缺点(Java8中推出)
(1)优点:简洁易并行计算,可能是未来的趋势。
(2)缺点:不易调试,需要单独学习。
三、关键字
1.final
(1)final变量:
①基本变量:值一旦初始化就不能被更改。
②引用变量:初始化后不能再指向另一个对象,但对象的内容可以被更改。
(2)final方法:可被子类继承,但不能被修改(不能重写)。
(3)final类:不能被继承。
2.synchronized和lock
(1)synchronized:
①Java关键字,修饰方法或代码块是保证同一时刻最多只有一个线程执行该代码。
②发生异常时自动释放线程占有的锁,不会发生死锁。
③使用synchronized是等待线程会一直等待,不能响应中断。
(2)lock:
①一个接口。
②发生异常若没有主动通过unLock()释放锁,很可能造成死锁。因此要在finally块中释放锁。
③可以让等待锁的线程响应中断。
④可以知道有没有成功获取锁。
3.synchronized
(1)修饰普通方法,线程之间相互没有关系,可任意执行自己对象的锁。
(2)修饰静态方法,锁定了class类。多个线程都是相同的一把锁,即哪个线程先执行代码哪个线程就持有了该方法所属的对象锁,其他对象无法执行。
四、面向对象(一)
1.如何通过反射创建对象
(1)通过类对象调用newInstance()方法。如String.class.newInstance()
(2)通过类对象的getConstructor()或getDeclaredConstructor()方法获得构造器(constructor)对象,并调用其newInstance()方法创建对象。
如:String.class.getConstructor(String.class).newInstance(“str”);
2.是否可以在static环境中访问非static变量
不可以。
static变量在Java中属于类,在所有实例中的值是一样的。当类被虚拟机载入的时候,会对static变量进行初始化。若不用实例来访问非static变量,编译器会报错。因为这些变量还没被创建出来,没有和任何实例关联上。
3.extend和supper泛型限定符
(1)<? extend Fruit>,上界只能get,不能add(不能add出除了null之外的对象,包括Object)
<? super Apple>,下界只能add,不能get。
(2)<? extend Fruit>表示继承Fruit的所有子类,具体哪个子类无法确定,所以不能add类型,但get时无论什么子类都可追溯到Fruit,也就是所有子类向上转型为Fruit。
(3)<? super Apple>表示Apple的所有父类,一直追溯到Object,所有不能add Apple的父类,但可以add Apple及子类,不管什么子类都可以向上转型为Apple及所有的父类。但get时,Apple父类太多接不住。
(4)编译器支持向上转型,但不支持向下转型。
4.泛型
“参数化类型”,将类型由原来的具体类型参数化。
五、面向对象(二)
1.类加载机制
加载---->验证---->准备---->解析---->初始化
其中“验证、准备、解析”称为连接
(1)加载:将.class文件加载到虚拟机内存的过程
(2)验证:确保class文件的字节流信息符合虚拟机要求
①文件格式验证:字节流符合class文件格式规范,且能被当前虚拟机处理。
②元数据验证:对字节码信息进行语义分析,确保其符合Java语言规范。如类中字段是否与父类冲突。
③字节码验证:对数据流和控制流分析,确保程序语义合法。主要对类的方法分析,保证方法在运行时不会危害虚拟机。
符号引用验证:对类自身以外的信息进行校验,确保解析能够完成。
(3)准备:为类变量分配内存并设置初始值
①类变量(static)分配内存,但实例变量不会,实例变量随对象实例化被分配到堆中。但final修饰的static不包含,其在编译时已被分配。
②初始值指各数据类型的默认值,而不是代码的赋值。
boolean:false;short:0;int:0;float:0.0f;long:0l;double:0.0d
(4)解析:主要是将虚拟机常量池中的符号引用化为直接引用。
①符号引用:以一组符号描述引用的目标(任何无歧义的字面量,如代号)
②直接引用:可以指向目标的指针、相对偏移量。和虚拟机实现的内存有关,不同虚拟机直接引用一般不同。
③解析主要针对:类或接口、字段、类方法、接口方法、方法类型、方法句柄、调用点限定符,7类。
(5)初始化:执行类构造器方法的过程,主要为类的静态变量赋予正确的初始值(JVM负责)
①JVM初始化:
A. 假如该类未被加载和连接,则先加载并连接该类。
B.假如该类的直接父类未被初始化,则先初始化其直接父类。
C.假如类中有初始化语句,则依次执行初始化语句。
②类初始化时机:
A.创建该类的实例new
B.访问某个类或接口的静态变量,或对静态变量赋值。
C.调用类的静态方法
D.反射
2.类加载器
(1)类加载器
Bootstrap ClassLoader——>Extension ClassLoader——>Application ClassLoader——>User ClassLoader
启动类加载器——>扩展类加载器——>应用程序类加载器——>自定义类加载器
(2)自定义类加载器的两种创建方法
①遵守双亲委派模型:
继承ClassLoader,重写findClass()方法,在findClass()方法中调用defineClass()方法。
②破坏双亲委派模型:
继承ClassLoader,重写loadClass()方法。
2.双亲委派模型
(1)双亲委派原则:
当一个类加载器收到类加载任务,会先交给其父类加载器完成,直到传到顶层加载器。只有当父类加载器无法完成时,才会逐级尝试加载任务。(类似于红包上交原则)
(2)好处:
①避免重复加载。父类已加载,子类就不用再次加载。
②更安全,防止用户随意定义类加载器来加载核心API。
比如rt.jar包中的类java.long.Object,不管哪个加载器加载该类,最终都会委托给顶层的启动类加载器,保证不同的类加载器得到的都是同一个Object对象。
3.String、StringBuilder、StringBuffer
(1)String不可变,StringBuilder和StringBuffer可变。
(2)String和StringBuffer线程安全,StringBuilder线程不安全。
(3)Sring连接操作底层由StringBuilder实现。
(4)StringBuffer比StringBuilder多了个synchronized修饰符。
(5)StringBuilder和StringBuffer都继承自AbstractStringBuilder类。
4.Object类的方法
(1)Object()默认构造方法。
(2)equals()指定某个对象是否与此对象"相等"。
(3)finalize()垃圾回收器回收垃圾前调用。
(4)hashCode()返回对象的哈希值。
(5)notify(),notifyAll()
(6)toString()返回对象的字符串表示。
(7)wait()
5、类和对象的区别
(1)类是某一类事物的描述,是抽象的。
对象是一个实在个体,是类的一个实例。
(2)类是一组函数和变量的集合体,即类是一组具有相同属性对象的集合体。
对象是函数、变量的集合体。
六、集合
1.如果一直在尾部添加元素,ArrayList和LinkedList哪个效率高?
(1)千万级数据以内,LinkedList快。
千万级以上,ArrayList快得多。
(2)猜想:ArrayList扩容会使效率降低。
(3)事实:
LinkedList每次增加会new一个Node对象来存新增加的元素。当数据量小时,时间不明显,而ArrayList需扩容。当数据量大时,new对象的时间大于扩容对象所需的时间。
2.Collection和Collections的区别
(1)Collection是集合类的上级接口,继承于他的接口有LIst、Set、Queue
(2)Collections是针对集合类的一个帮助类,提供一系列静态方法,实现各种集合的搜索、排序、线程安全化等操作。
3.Iterator(迭代器)和ListIterator的区别
(1)Iterator可用来遍历Set和List集合,且对集合只能前向遍历。
(2)ListIterator只能遍历List,可前向也可后向遍历。实现了Iterator接口,并包含其他功能。如:增加元素、替换元素…
4.迭代器
(1)Iterator提供了统一遍历操作集合元素的统一接口,Iterator每个集合都通过实现Iterable接口中iterator()方法返回Iterator接口的实例,然后对集合的元素进行迭代操作。
(2)在迭代元素时,不能通过集合的方法删除元素,否则会抛出ConcurrentModificationException异常,但可以通过Iterator接口中的remove()方法进行删除。
注:Collection继承Iterable,Iterable接口里定义了返回iterator的方法。
5.ArrayList是否会越界
ArrayList是基于动态数组的不安全集合,并发add()可能出现数组下标越界异常。
七、线程
1.如何保证线程安全
(1)合理的时间调度,避开共享资源的存取冲突。
(2)在并行任务设计上可以设计一个规则来保证一个客户的计算工作和数据访问只会被一个线程或一台工作机完成,而不是将一个工作分配给多个线程去完成。
2.什么是线程池
(1)事先将创建的若干线程放入一个容器中,需要的时候从池中获取线程,不用自行创建。使用完毕后不需要销毁线程而是放入池中,从而减少创建、销毁线程对象的开销。
(2)Executors接口生成一些常用线程池:
①newSingleThreadExecutor:创建一个单线程的线程池。只有一个线程在工作,若因异常而结束,会有一个新的线程替代它。此线程池保证所有任务按照任务的提交顺序完成。
②newFixedThreadPool:创建一个大小固定的线程池。每次提交一个任务就创建一个线程,直到线程达到线程池最大大小。一旦达到最大值就不会改变,若有线程因异常而结束,会有新线程补充。
③newCachedThreadPool:创建一个可缓存的线程池。如果线程池的大小超过处理任务所需线程数,会回收部分空闲(60s不执行)的线程,当任务增加,线程池自动添加新线程。此线程池大小无限制,有JVM所能创建最大线程数决定。
3.线程同步和异步
如果系统中存在临界资源(资源数量少于竞争资源的线程数量的资源),例如正在写的数据以后可能被另一个线程读到,或者正在读的数据可能已被另一个线程写过。那么这些数据必须进行同步存取。
当应用程序在对象上调用了一个要花费很长时间来执行的方法,并且不希望让程序等待方法的返回时,就应该使用异步编程。
很多情况下采用异步往往更有效率,事实上同步就是指阻塞式操作,异步就是非阻塞式操作。
4.线程调度的相关方法
(1)wait()使一个线程处于等待(阻塞)状态,并释放所持有的对象锁。
(2)sleep()使一个正在运行的线程处于睡眠状态,是一个静态方法,要处理InterRuptedException(中断异常)。
(3)notify()唤醒一个处于等待状态的线程,但并不能确切的唤醒某个等待线程,由线程优先级决定。(4)notifyAll()唤醒所有处于等待的线程,但并不是将对象的锁给所有线程,而是让他们竞争,只有获得锁的线程才能进入就绪状态。
5.一个线程进入一个对象synchronized方法A后,其他线程是否可进入此对象的synchronized方法B?
不能。
其他线程只能访问对象的非同步方法。非静态方法上的synchronized修饰符要求执行方法时获得对象锁。
6.sleep()和yield()的区别
(1)①sleep()给其他线程运行机会时不考虑线程优先级。
②yield()方法只给相同优先级或更高优先级的线程以运行机会。
(2)①执行sleep后进入阻塞状态。
②执行yield后进入就绪状态。
(3)①sleep方法抛出InterRuptedException
②yeild方法没有声明任何异常。
(4)sleep()方法比yield()方法更有移植性(与CPU调度有关)。
7.sleep()和wait()的区别
(1)sleep是线程类(Thread)的方法,会把执行机会让给其他线程,但仍持有对象锁。
(2)wait是Object类的方法,会放弃对象锁。
8.实现一个线程
(1)继承Thread类,重写run()方法。
(2)实现Runnable接口,重写run()方法。
(3)实现Callable接口,重写call()方法,利用Futrue Task包装Callable,并作为task传入Thread构造函数。
(4)通过线程池创建线程。
八、锁
1.synchronized和lock的区别
(1)synchronized:
①Java关键字,修饰方法或代码块是保证同一时刻最多只有一个线程执行该代码。
②发生异常时自动释放线程占有的锁,不会发生死锁。
③使用synchronized是等待线程会一直等待,不能响应中断。
(2)lock:
①一个接口。
②发生异常若没有主动通过unLock()释放锁,很可能造成死锁。因此要在finally块中释放锁。
③可以让等待锁的线程响应中断。
④可以知道有没有成功获取锁。
2.产生死锁的条件
(1)互斥条件:进程要求对所分配的资源进行排他性控制,即在一段时间内某资源仅被一进程所占有。
(2)请求和保持条件:当进程因请求资源而阻塞时,对已获得的资源保持不放。
(3)不剥夺条件:进程已获得的资源在未使用完之前不能剥夺,只能在使用完时自己释放。
(4)环路等待条件:在发生死锁时,必然存在一个“进程——资源”的环形链。
3.预防死锁
(1)以确定的顺序获得锁。
(2)超时放弃。Lock接口提供tryLock()方法,在固定时常等待锁,否则主动释放之前获得的所有锁。
(3)破坏请求保持条件:一次性分配所有资源。
(4)破坏不可剥夺条件:当某进程获得部分资源,但得不到其他资源则释放已占有的资源。
(5)破坏环路等待条件:系统给每类资源赋予一个编号,每个进程按编号递增顺序请求资源,释放则相反。
4.避免死锁
(1)系统处于安全状态(存在能让所有进程执行完的序列),可避免死锁。
(2)系统处于不安全状态,可能进入死锁(解决方案:银行家算法)。
(3)银行家算法:
状态:①Resource(向量):系统中的资源总量
②Available(向量):未分配给进程的每种资源的总量
③Claim(矩阵):进程对资源的需求
④Allocation(矩阵):当前分配给进程的资源
安全状态:至少有一个资源分配序列不会导致死锁。
算法:当进程请求一组资源时,假设同意该请求,从而改变了系统的状态,然后确定其结果是否还处于安全状态。如果是,同意该请求;如果不是,阻塞该进程直到同意该请求后系统状态仍安全。
5.检测死锁
(1)首先为每个进程和每个资源指定一个唯一的号码。
(2)然后建立资源分配表和进程等待表。
(3)资源分配图不完全简化则是死锁状态。
6.解除死锁
(1)剥夺资源:从其他进程剥夺足够资源给死锁进程,以解除死锁状态。
(2)撤销进程:直接撤销进程或撤销代价最小的进程,直至有足够的资源可用,死锁状态消失为止。
注:代价指优先级、运行代价、进程重要性等。
7.锁和同步的区别
(1)用法:
①synchronized即可加在方法上,也可加在特定代码块上,是托管给JVM执行的。
②lock需要显示地指定起始、终止位置。是通过代码实现的,比synchronized有更精确的线程语义。
(2)锁机制:
①synchronized获取锁和释放锁的方式都是在块结构中,当获取多个锁时,必须以相反的顺序释放,并且是自动解锁。
②lock需要手动释放,且必须在finally中释放。
8.synchronized的可重入
(1)每个锁关联一个线程持有者和一个计数器。当计数器为0时表示该锁没有被任何线程持有,那么任何线程都可能获得该锁而调用相应方法。
(2)当一个线程请求成功后,,JVM会记下持有锁的线程,并将计数器记为1。此时,其他线程请求该锁,则必须等待,而持有锁的线程如果再次请求该锁,就可以再次拿到该锁,同时计数器递增。当线程退出一个synchronized方法或块时,计数器会递减,如果计数器为0则释放该锁
九、JDK
1.JDK和JRE的区别
(1)JDK是Java开发工具包,是完整的Java软件开发包,包含JRE、编译器和其他工具。
(2)JRE是Java运行时环境,是将要执行Java程序的虚拟机,同时包含执行applet所需的浏览器插件。
十、JVM
1.JDK1.8之后的JVM内存结构
(1)永久代(方法区):PermGen Space
方法区和永久代的关系就像Java中接口和类的关系,类实现了接口,而永久带就是HotSpot虚拟机对虚拟机规范中方法区的一种实现方式。
(2)三种垃圾回收
①minor GC:对新生代进行垃圾回收
②major GC:对老年代进行垃圾回收
③full GC:对新生代、老年代和永久代都进行垃圾回收
(3)元空间
①1.8之后取消永久代,改为元空间。类的元信息被存储在元空间中。元空间没有使用堆内存,而是在于堆不相连的本地内存区域。
②理论上系统可以使用的内存有多大,元空间就有多大,不会出现永久代存在时的内存溢出问题。
(4)为什么移除永久代
①大小是启动时固定好的,很难进行调优,-XX:MaxPermSize设置多少合适?
②永久代中元数据的位置会随一个full GC发生移动,比较消耗虚拟机性能。
③将永久代分出,可以简化full GC以及对以后的并发隔离类元数据等方面进行优化。
(5)元数据
在Java中元数据以标签的形式存在于Java代码中,元数据标签的存在不影响代码的编译和运行,它只是被用来生成其他文件或描述代码间关系的数据。
2.什么是虚拟机
Java虚拟机是一个可以执行Java字节码的虚拟机进程。Java源文件被编译成能被执行的字节码文件。
3.JVM最大内存限制(JDK1.8之前的)
(1)堆内存分配
①JVM初始分配的内存由-Xms指定,默认为物理内存的1/64
②JVM最大分配的内存由-Xmx指定,默认为物理内存的1/4
③默认空余堆内存小于40%时,JVM就会增大堆直到-Xmx的最大限制。
④空余堆内存大于70%时,JVM会减少堆直到-Xms的最小限制。
⑤因此服务器一般设置-Xms、-Xmx相等以避免在每次GC后调整堆的大小。
(2)非堆内存分配
①JVM使用-XX:PermSize设置非堆内存初始值,默认是物理内存的1//64
②由XX:MaxPermSize设置最大非堆内存的大小,默认是物理内存的1/4
4.JVM哪些对象可作为ROOT对象
(1)虚拟机栈中的引用对象
(2)本地方法栈JNI(native方法)引用对象。
(3)方法区中类静态变量引用的对象。
(4)方法区常量引用对象。
5.eden区和survial区
(1)目前主流的虚拟机实现都采用了分代收集的思想。把整个堆区分为新生代和老年代,新生代又被划分为Eden区、From Survivor和To Survivor三块区域。
(2)Eden:From Survivor:To Survivor=8:1:1,对象总是在Eden区出生,From Survivor保存当前的幸存对象,To Survivor为空。
(3)一次GC后:
①Eden区活着的对象和From Survivor存储的对象被复制到To Survivor
②清空Eden和From Survivor
③From变To,To变From
(4)可以看出,只有在Eden空间快满时才会触发Minor GC。而Eden空间占新生代的绝大部分,所以Minor GC的频率得以降低。当然使用Survivor的方式有一定代价,如10%的空间浪费、复制对象的开销等。
6.JVM分区
(1)程序计数器:
每个线程都有单独的程序计数器,通过改变计数器的值来确定下一条指令。
(2)栈:
每个方法在执行的时候会创建一个栈帧,用于存储局部变量表、操作数栈、动态链接、方法返回地址等信息。
(3)本地方法栈:
同栈,但只为本地方法服务。
(4)堆:
创建对象实例的区域,经常发生垃圾回收。
(5)方法区(1.8之后变为元空间,存储虚拟机加载的类信息):
是各个线程共享的内存区域,不需要连续内存,并可动态扩展。用于存放已被JVM加载的类信息、常量等数据,方法区是堆的一个逻辑部分,又被称为“非堆”。
①运行时常量池属于方法区的一部分
②常量池即是文本字符串、被声明为final的常量值。
注:1.8之后将运行时常量池和常量池放在了堆中。
7.为什么Java被称为“平台无关的编程语言”
.java文件经编译后为.class文件(字节码文件),不同的平台装有不同的JVM,JVM可以将.class文件解释成对应平台的机器码,利用机器码来操作硬件。
十一、GC
1.垃圾回收的原理
当创建对象时,GC就开始监控该对象的地址、大小以及使用情况。通常GC采用有向图的方式记录和管理堆中的所有对象。通过这种方法确定哪些对象是“可达的”。当一些对象确定为“不可达”时,GC就有责任回收这些内存空间。
2.垃圾回收器可以马上回收内存吗
可以。也可以手动执行System.gc(),通知GC运行,但Java语言规范不保证GC一定执行。
3.Java会存在内存泄露吗
内存泄漏的主要原因是,先前申请了内存空间而忘记释放。获取一段程序需要分配大量的内存空间,从而把计算机的内存空间消耗殆尽而导致发生内存泄漏。即不再被使用的对象或变量一直被占据在内存中。
(1)长生命周期对象持有短生命周期对象的引用。例如,缓存系统。加载一个对象放在缓存系统中,一直不去使用对象,但一直被缓存引用,所以不会被回收,导致内存泄漏。
(2)各种连接。比如数据库连接,除非显示调用close()方法将其连接关闭,否则不会自动GC回收。
(3)HsahMap、ArrayList,如果被声明成static时,其生命周期会和应用程序一样长,也容易发生内存泄漏。
4.垃圾回收的优点
(1)垃圾回收可以有效地防止内存泄露,有效的使用可以使用的内存。
(2)垃圾回收器通常是作为一个单独的低级别的线程运行,不可预知的情况下对内存堆中已死亡的或长时间没有使用的对象进行清除和回收。
5.GC算法的评判标准
(1)吞吐量:单位时间内的处理能力。
(2)最大暂停时间:因执行GC而暂停执行程序所需的时间。
(3)堆的使用效率:可用的堆越大,GC运行越快;相反利用有限的堆,GC花费的时间越长。
(4)访问的局部性:在存储器层级构造中,越是高速存取的存储器容量会越小。由于程序的局部性原理,将经常用到的数据放在堆中较近的位置,可以提高程序的运行效率。
6.可达性
(1)可达性就是通过一系列“GC Roots"对象为起点,从这些节点开始向下搜索,搜索走过的路径称为引用链。当一个对象到GC Roots没有任何引用链相连时,则该对象不可用。
(2)可作为Roots的对象
①栈中的引用对象。
②本地栈中的JNI引用的对象。
③方法区中的静态成员。
④方法区中的常量引用对象。
7.引用计数法
(1)在对象创建时绑定一个计数器,每当有一个引用指向该对象,计数器加1,;每当一个指向它的引用失效,计数器减1.当计数器为0时,进行垃圾回收。
(2)优点:
①简单
②最大暂停时间短
(3)缺点:
①不全面,容易漏掉循环引用对象
②占用额外内存空间
8.标记清除法
(1)为每个对象存储一个标记位,记录对象的状态(活着或死亡)。第一阶段为每个对象更新标记位,检查对象是否死亡,第二阶段对死亡对象进行清除,执行GC操作。
(2)优点:
①只需找到一个活着的引用即可。
②比引用计数法更全面,且不需要移动对象的位置。
(3)缺点:
①为判断对象是否死亡,最大暂停时间长。
②标记阶段和清除阶段都要遍历对象,算法复杂度高。
③没有移动对象,导致可能出现很多碎片空间未被利用。
9.标记整理法
(1)是标记清除算法的升级版。在第二阶段对所有存活对象进行整理,放入另一处空间,然后把所有对象全部清除。
(2)优点:不会像标记清除那样产生大量碎片空间。
(3)缺点:若存活对象过多,整理阶段会有较多复制操作,导致算法效率降低。
10.复制算法
(1)将内存平均分成两部分,然后只使用其中一部分。当这部分内存满时,将内存中所有存活对象复制到另一个内存中,然后清空之前的内存。
(2)优点:实现简单,不产生碎片。
(3)缺点:每次运行总有一半内存是空的。
十二、IO流
1.IO流概念
IO流是实现输入和输出的基础,可以方便的实现数据的输入和输出操作。
2.IO流分类
(1)按流向分:
①输入流:InputStream、Reader(读取数据)
②输出流:OutputStream、Writer(写入数据)
(2)按操作单元:
①字节流:InputStream、OutputStream
②字符流:Reader、Writer
(3)按流的角色分:
①节点流:从一个特定的IO设备(磁盘、网络)读/写数据,也称为低级流。
②处理流:对一个已知存在的流进行连接和封装,通过封装后的流来实现数据的读/写功能,也称为高级流。
3.常见流的分类
4.字节流和字符流哪个好
(1)大多数情况下字节流更好。因为多数IO操作是直接操作磁盘文件,所以这些流是以字节的方式进行传输。
(2)如果IO在内存中频繁处理字符串则使用字符流更好,字符流具备缓冲区,可提高性能。
5.字节流和字符流的区别
(1)字节流处理单元为1个字节,操作字节和数组。
(2)字符流处理单元为2个字节的Unicade字符(统一码),操作字符、字符数组、或字符串。
(3)字节流操作不会经过缓冲区,而是直接操作文本本身,而字符流的操作会先经过缓冲区(内存),然后通过缓冲区操作文件。
6.缓冲区
缓冲区就是一段特殊的内存区域,很多情况下程序频繁操作一个资源(文件、数据库)则性能很低。为提升性能就可以将一部分数据暂时读写到缓冲区,再从缓存区操作资源。
7.BufferReader是处理流的缓冲流,可以将读取的内容存在内存中,典型方法是readLine(),用来读取一行。
8.InputStream的read()返回的是什么?read(byte[] data)?
(1)read方法返回所读取的字节的int型(0~255)
(2)read(byte[] data)将读取的存储在这个数组,返回的就是传入参数的这个数。
9.OutputStream的writer()方法、write(byte b[],int off,int len )?
(1)write将指定字节传入数据源
(2)byte b[]是byte数组
b[off]传入的第一个字符
b[off+len-1]传入的最后一个字符,len是实际长度
十三、异常
1.异常
(1)异常机制是指当程序出现错误后,程序如何处理。具体来说,异常机制提供了程序退出的安全通道。
(2)Throwable:error,exception(受检异常IOException、SQLException、ClassNotFoundException非受检异常RuntimeException)
(3)Exception可以被程序本身处理,Error无法处理
2.Error
是程序无法处理的错误,表示运行应用程序中较严重问题。大多数错误与代码编写者执行的操作无关,而表示代码运行时 JVM(Java 虚拟机)出现的问题。
如OutOfMemoryError,Java虚拟机运行错误(Virtual MachineError)、类定义错误(NoClassDefFoundError)等。这些错误是不可查的
3.Exception
(1)非受检异常RuntimeException:可以选择捕获处理,也可以不处理。如空指针错误,数据下标越界,方法传递参数错误,字符串转数字错误,数据类型转换错误;算术条件异常
(2)受检异常IOException、SQLException、ClassNotFoundException:必须进行处理的异常,不处理不能编译通过。文件已结束异常,文件未找到异常。
4.异常处理机制
(1)Throws:声明异常,不处理,交给上层。
(2)Throw:实例化异常对象,抛出异常。
(3)Try,catch,finally:捕获异常。
5.什么时候捕获异常,什么时候向上抛出异常?
应该考虑当前作用域是否有有能力处理这一异常,如果没有,则应将该异常继续向上抛出,交由更上层的作用域来处理。
6.断言
(1)调试方法,只用于开发测试阶段,保证正确性,异常保证健壮性
(2)assertionError:不能捕获的异常。
(3)Assert 条件:表达式 条件为false时,抛出异常