SE复习

概念

Java是面向对象的语言,但是我们不能像操作对象一样操作基本数据类型,所以我们将基本数据类型包装到各自的包装类中,通过包装类中的一些静态方法就可以操作基本数据类型

拆箱和装箱

  1. 装箱: 将基本类型包装成包装类
  • 构造方法:
    Integer(int value)
    Integer(String s)

  • 静态方法:
    static Integer valueOf(int i)
    static Integer valueOf(String s)
    注:Integer.parseInt()和Integer.valueOf()都可将String转为int,但是前者返回值int,后者返回Integer.

  1. 拆箱:将包装类型转化为基本类型
    int intValue()

  2. 自动装箱拆箱

Integer i = 4;//Integer.valueOf(4)
i = i + 5//i.intvalue() + 5
         //运算完成后,再装箱
  1. 基本类型和字符串类型
    转换为字符串:
  • 基本类型数据 + “”
  • 包装类中的 toString()
  • String.valueOf()
    字符串转基本类型:
  • Integer.parseInt(String s)

String,StringBuilder,StringBuffer

三者的底层都是字符数组,

  • String被final修饰,所以String对象不可改变,这样就造成了每次对String对象操作都会新生成一个String对象,浪费内存,线程安全;
    常用方法
length()
chatAt()
toCharArray()
equals()
equalsIgnoreCase()
split()
  • StringBuilder没有被final修饰,可以改变,节约内存,效率高,但是没有使用锁机制,所以线程不安全;
    常用方法
append()
reverse()
toString()
  • StringBuffer没有被final修饰,可以改变,节约内存,效率低,每个方法都加了synchronized关键字,所以线程安全;
    常用方法
append()
  • 总结:
    (1) 每次操作String对象都要创建新的对象,效率慢;而StringBuilder和StringBuffer始终操作的一个对象所以效率高;
    (2)StringBuilder和StringBuffer区别主要在线程安全和效率上,StringBuffer每个方法都加了synchronized关键字所以线程安全,StringBuilder没有加所以线程不安全,但是因为StringBuffer每次需要判断锁,所以效率慢;
    (3)优先使用StringBuilder;

final,finally,finallize

  • final:可以修饰类,方法,属性,不能修饰构造方法,被修饰的类不能被继承,被修饰的方法不能被重写,被修饰的基本类型变量不能改变,被修饰的引用类型变量的引用地址不能改变,但是变量值可以被改变
    abstract:可以修饰类,方法,不能修饰构造方法,属性,抽象类要被继承,抽象方法要被子类重写
  • finally:异常处理是使用,以便接在try-catch后面,不论有没有异常发生都会执行,通常用来关闭物理连接,如IO流,数据库连接,Socket连接等
  • finallize:是Object类提供的方法,由JVM调用,在垃圾收集器将对象从内存中清除前做必要清理工作,在垃圾收集器删除对象之前被调用。

多线程

  1. 如何保证线程安全?
    通过合理的时间调度以及适当的策略,避免任务和任务间同时对共享资源进行操作,产生冲突。
    具体方法有同步代码块同步方法Lock锁,并发情况下,线程共享的变量改为方法的局部变量。
  2. 请你简要说明一下线程的基本状态以及状态之间的关系?
    新建:new一个线程对象,跟java其他对象一样,值分配了内存;
    等待:在new对象之后,调用start方法前,线程处于等待状态;
    就绪:调用start方法,线程的状态在java虚拟机的可运行池中,等待cpu的使用权;
    运行:获得了cpu的使用权,执行程序;
    阻塞:因为某些原因放弃cpu使用权,java虚拟机不会给其分配cpu使用权,直到这个线程重新回到就绪状态,才有机会cpu使用权
    阻塞状态三种情况:
  • 等待阻塞:线程执行wait方法
  • 同步阻塞:锁对象被其他线程占用
  • 其他阻塞:调用了sleep方法或者发出IO请求时,直到sleep方法超时或者IO 请求处理完毕,才重新回到就绪状态
    死亡状态:run方法执行完毕,或者异常未捕获,则线程进入死亡状态

注:wait和sleep区别
都是处于线程的阻塞状态,wait是Object类中的方法,sleep是Thread类中的方法;调用wait处于阻塞状态时会释放锁对象而调用sleep处于阻塞状态时不会释放锁对象,仍然处于同步状态;调用wait必须通过notify或者notifyAll方法唤醒,而调用sleep可以自己唤醒

  1. 为什么使用线程池?如何使用?
    在有大量的线程任务的情况下,频繁创建和销毁线程池会造成资源浪费和效率低下,利用“池化思想”提前创建好一个有很多线程对象组成的池子,每次有新的线程任务只要从线程池中获取线程,任务完成后再将线程对象放回线程池。节约资源,效率提高。
    使用Executors工具类中的静态方法创建线程池,我经常使用的是newFixedThreadPool方法。可以通过excute和submit两种方法执行线程池中的任务,excute无返回值,submit有返回值。
  2. 线程调度的相关方法
    线程调度分为两种,一种是分时调度,平均分配每个线程占用CPU的时间;一种是抢占式调度,优先让优先级搞得线程使用CPU,线程优先级相同,则随机选择一个线程执行
    wait,sleep,notify,notifyAll
  3. 请问当一个线程进入一个对象的synchronized方法A之后,其它线程是否可进入此对象的synchronized方法B?
    进入非静态的synchronized方法要求获得调用对象的锁,所以已经进入A方法说明对象锁已被取走,其他线程得等待
  4. 请简述一下线程的sleep()方法和yield()方法有什么区别?
  • sleep方法给其他线程运行机会时不考虑优先级,低优先级线程也会有运行机会,而yield方法只会给同优先级或者更高优先级的线程运行机会
  • 线程执行sleep方法后会进入阻塞状态,执行yield方法会进入就绪状态
  • sleep声明抛出InterruptedException,而yield()方法没有声明任何异常
  1. stop()和suspend()方法为何不推荐使用,请说明原因?
  • stop方法不安全,因为会解除线程获得的所有锁定,如果一个同步方法正在执行,突然使用了stop方法,就会终止这个方法的执行,不能保证数据的完整性,使对象处于一种不连贯的状态;
  • suspend方法容易出现死锁现象,因为使用此方法将线程挂起时,仍然保持之前获得的锁定,别的线程想访问锁定的资源,就必须等被挂起线程恢复执行,这样极容易早死锁现象;
  • 正常我们应该设置一个标志,指出改活动应该被挂起还是执行,若被挂起则调用wait方法,执行则调用notify方法
  1. 请简述一下线程池的运行流程,使用参数以及方法策略等
    主要参数有:
    核心线程数,最大线程数,任务缓存队列,线程最大存活时间,拒绝策略。
    流程:当一个线程任务来的时候首先在核心线程池中创建线程,执行此任务;当大于核心线程数量时,进入任务缓存队列,等待创建被执行;
    当核心线程池和任务缓存队列满时,开始增加线程池的线程数;如果再来线程任务,则开启拒绝策略,其中有丢弃任务抛出异常,丢弃任务不抛出异常,丢弃最老任务并且尝试重新执行任务,调用线程执行此任务
  2. 多线程中的i++线程安全吗?请简述一下原因?
    不安全,i++不是原子性操作,分为两步分别是取i值和+1操作,执行任何一部都可能被其他线程抢占。
  3. 请简要说明一下JAVA中cyclicbarrier和countdownlatch的区别分别是什么?
    都是能够实现线程中的等待;
    cyclicbarrier:是通过减计数方式,一个或多个线程等待其他多个线程完成某件事,每执行完一个线程就减一,直到减到0位置,此时继续执行,并且是不可重用的;
    countdownlatch:是通过加计数方式,多个线程完成同一个任务,每当一个线程执行完就加一,直到所有线程都完成这个任务,此时继续执行,并且是可重用的;
  4. 请回答一下Java中有几种线程池?并且详细描述一下线程池的实现过程
    newFixedThreadPool:指定线程池线程数量,当大于线程池最大线程数量时,新的线程任务将进入任务缓存队列;
    newCacheThreadPool:创建一个可缓存线程池,如果线程池长度超过处理需要,可灵活回收空闲线程,若无可回收,则新建线程;
    newSingleThreadPool:每次只创建单一线程,根据任务顺序执行;
    newScheduleThreadPool:适合执行定时和周期性任务;
  5. synchronized 和 lock 有什么区别?
    synchronized是一个关键字,由jvm管理,而lock是一个接口;lock可以实现synchronized所有功能,lock释放锁必须手动调用unlock方法,同时必须在finally代码块中使用,而synchronized则是自动释放锁;synchronized等待不可以被中断而lock等待可以通过设置超时方法或者调用interrupt方法中断;synchronized是非公平锁而lock既可以是公平锁又可以是非公平锁,通过在构造方法中传入boolean来确定;
    volatile和synchronized的区别:
    volatile保证共享变量对所有线程的可见性,就是当A线程对共享变量进行修改后,会将修改后的值从线程本地内存中刷新到主内存,此时其他线程本地内存中存储的此共享变量修改前的值会被清除,重新读取主内存中修改后的变量值;
    主要区别是:
    (1) volatile仅能使用在变量级别,synchronized则可以使用在对象,方法,类;
    (2) volatile仅能实现变量的修改可见性,而synchronized则可以保证变量的修改可见性和原子性;
    (3) volatile不会造成线程的阻塞,而synchronized可能会造成线程的阻塞;
    CAS:
  • Compare And Swap(比较与交换),是一种无锁算法。在不使用锁(没有线程被阻塞)的情况下实现多线程之间的变量同步;
  • 如果内存位置V的值等于预期的A值,则将该位置更新为新值B,否则不进行任何操作。许多CAS的操作是自旋的:如果操作不成功,会一直重试,直到操作成功为止;
  • 乐观锁中可以使用CAS;可以CAS和Volatile配合使用,保证了原子性;
  • 会出现高并发情况下开销大,ABA问题(通过版本号解决)
  1. 请问什么是死锁(deadlock)?
    两个或以上线程各占用了一些共享资源,但是又想使用对方的共享资源就会造成死锁现象。比如线程1锁住了资源A,想使用资源B,线程2锁住了资源B,想使用资源A,这就是死锁。
  2. 为什么会产生死锁?
  • 互斥作用:每一时刻某个资源只能被一个线程所占用;
  • 不可抢占:正在被某个线程使用的资源不能被其他线程强行剥夺;
  • 请求和保持:线程在请求资源的时候同时保持对原有资源的占有;
  • 循环和等待:线程1占有资源A请求资源B,线程2占有资源B请求资源A,形成了等待环路;
    解决:
  • 避免在同步代码块中调用外部的同步方法。
  • 在嵌套多层synchronized同块中,对锁进行排序,使得每次获取锁的顺序是一致的。
  1. 请说明一下synchronized的可重入怎么实现。
    每个锁关联一个线程和计数器,当计数器为0时说明该锁没有被占用,所有线程都可以请求,当某个线程请求成功后,JVM会记下此线程并将计数器加1,当这个线程再次请求这个资源,计数器会继续加1,当线程使用完资源退出一个synchronized方法/块时,计数器会减一,当减为0时,该资源可以被所有线程请求

面向对象

  1. 抽象类和接口的区别
    抽象类:被abstract关键字修饰,抽象类中可以包含抽象方法也可以包含被具体实现的方法;一个类只能继承一个抽象类,并且继承抽象类的子类要实现父类所有抽象方法,如果没有实现父类的所有抽象方法那么这个子类也会成为抽象类;
    接口:接口是被interface关键字修饰,JDK1.8之前,接口中只能包含抽象方法,被public static 所修饰,接口中的变量被public static final 所修饰。JDK1.8之后,如果接口中的方法被default或者static修饰,则可以是具体实现方法,一个类可以实现多个接口,但是需要具体实现接口中的抽象方法
  2. 请说明Java中的方法覆盖(Overriding)和方法重载(Overloading)是什么意思?
    Overriding是存在于子父类中,父类中的方法不能够满足子类的需求那么就需要重写父类的这个方法;
    Overloading存在于同一个类中,方法名相同但是参数列表不同;
  3. 请说明Comparable和Comparator接口的作用以及它们的区别。
    Comparable:是一个排序接口,某个类需要实现这个接口,那么这个类就具有了排序能力,通过实现compareTo()方法来定义排序规则,如果return的是this-被比较对象,则按照升序排序,如果return的是被比较对象-this,则按照降序排序;
    Comparator:是一个比较器接口,对于那些自身没有排序能力的对象即没有实现Comparable接口,则可以在外部通过Comparator接口来排序,主要是利用Comparator接口的compare方法来定义排序规则,compare方法接受两个参数也就是两个待比较的对象,前者-后者为升序,后者-前者为降序;
  4. 请你谈谈如何通过反射创建对象?
  • 方法1:通过类对象调用newInstance()方法,例如:String.class.newInstance()
  • 方法2:通过类对象的getConstructor()或getDeclaredConstructor()方法获得构造器(Constructor)对象并调用其newInstance()方法创建对象,例如:String.class.getConstructor(String.class).newInstance(“Hello”);
  1. 请你谈谈JVM类加载器机制和双亲委派模型
    类加载器:就是将编译成.class字节码文件的类加载到内存中运行;
    主要分为四种类加载器:
  • 启动类加载器,加载一些java的核心类库;
  • 扩展类加载器,加载lib/ext目录下的类,
  • 应用程序类加载器,加载一些已经写好的一些类进入内存
  • 自定义类加载器,可以继承java.lang.classloader类,实现自己的类加载器
    双亲委派模型:
    在这里插入图片描述

当收到一个类加载请求时,会先询问上一级类加载器是否加载过此类,最终会询问道最顶层的启动类加载器,若父类加载器都没有加载过此类,那么子类加载器就会进行加载,这样就可以防止重复加载相同的类,并且防止恶意篡改核心类。
6. 请列举你所知道的Object类的方法并简要说明。

  • clone:创建并返回此对象的副本
  • equals:比较两个对象是否为同一个
  • notify(notifyAll):唤醒等待的线程
  • wait()线程等待
  • getClass():返回一个对象的运行时类
  1. 什么是多态?使用多态的好处是什么?项目中什么什么时候用多态?(什么是封装?使用封装的好处是什么?什么是继承?使用继承的好处是什么?)
    多态存在的条件存在继承关系,子类重写了父类方法,父类的引用指向子类对象。程序的复用性和可扩展性都得到了提高。
    向上转型:其实就是多态,缺点就是不能使用子类的特有方法
    向下转型:将父类强制类型转换为子类
  2. java对象生命周期
    创建阶段,使用阶段,不可见阶段,不可达阶段,可回收阶段,终结阶段,释放阶段
  3. Servlet生命周期
  • 加载和实例化 javax.servlet

  • 初始化 init()

  • 请求处理 service()

  • 服务终止 destroy()
    注:
    (1)什么时候被创建?
    默认情况下第一次被访问的时候创建,可以通过<load-on-startup>来设置被创建时机,当<load-on-startup>设置为负数,第一次被访问时创建,当<load-on-startup>设置为0或者正整数,启动服务器时被创建;
    (2)什么时候被销毁?
    服务器关闭时

  1. 类的生命周期?
    加载、连接、初始化、使用、和卸载
  2. static成员变量方法和非static成员变量方法的区别
    变量:被static修饰的成员变量属于整个类,被这个类实例化的对象所共享,当static修饰的变量改变时,其他实例中该static对象也被改变;static成员变量在类被加载时被创建,而非static成员变量只有实例化对象后才被加载;可以通过类.静态成员变量来调用
    方法:静态方法不可以直接访问非静态变量,非静态方法可以随意访问静态成员变量和非静态成员变量;可以通过类.静态成员方法来调用

JVM

  1. JVM体系结构
  2. 什么是类加载器?
    将.Class文件通过类加载器加载进JVM中的方法区,这个类对象实例化对象时,将这个对象存入堆,引用存入栈。
  3. 解释下Native方法
    Native方法都是存在jvm中的本地方法栈中。因为Java是运行在虚拟机上,无法对操作系统底层进行操作,此时可以通过Native方法调用JNI加载本地方法库中的方法从而用其他语言对底层进行操作,对java语言起到了扩展作用
  4. 什么是JDK,JRE,JVM?
    JDK包含JRE和JVM
    JDK是java标准开发包,提供了编译,执行java程序所需要的所有资源,包括编译器,运行时环境,各种java类库等;
    JRE是java程序运行时环境,可以直接运行java的编译好的.class文件;
    JVM是java虚拟机,是java程序跨平台运行的核心,不同平台上的jvm各不相同,其功能是将编译好的.class文件转换成不同平台的机器码进行执行;
  5. 方法区:
    被所有线程共享,在线程启动时创建,主要存放静态变量,常量,类信息(构造方法,接口定义),常量池
  6. 虚拟机栈
    线程私有,先进后出,队列是先进先出,生命周期和线程同步,不存在垃圾回收问题,存放8大基本类型+对象引用+实例的方法
  7. 常量池,方法区和永久代关系?
  • 方法区物理上存放在堆中,逻辑上方法区和堆是分开的;
  • 永久代包含方法区,jdk1.8之后元空间包含的是方法区
  • jdk 1.6 常量池在方法区中,jdk 1.7 常量池在堆中,jdk 1.8 常量池在元空间中
  • jdk 1.6 常量池在方法区中,大小固定,无法被垃圾回收器回收,OOM风险高;jdk 1.7 常量池在堆中,可以被垃圾回收器回收;jdk 1.8 永久代消失,常量池在元空间中,元空间并不在虚拟机中,而是使用本地内存(也就是说jvm可以使用外边的内存)。因此,默认情况下,元空间的大小仅受本地内存限制,但可以通过以下参数来指定元空间的大小
    永久代:存放JDK自带的 Class对象,是java运行时的一些环境和类信息
  1. 为什么要进行垃圾回收?
    程序员不需要亲自进行内存分配和释放;

  2. 如何判定哪些对象需要进行垃圾回收?
    引用计数法:给对象中添加一个引用计数器,每当有一个地方引用它时,计数器值就加1;当引用失效时,计数器值就减1;任何时刻计数器为0的对象就是不能再被使用的。引用计数法实现简单,判定效率也很高,但是它很难解决对象之间相互循环引用的问题
    可达性分析法:通过一系列的称为“GC Roots”的对象作为起始点,从这些节点开始向下搜索,搜索所走过的路径称为引用链,当一个对象到GC Roots没有任何引用链相连时,则证明此对象是不可用的。例如object5、object6、object7虽然互有关联,但它们到GC Roots是不可达的,所以它们将会被判定为可回收对象。
    在这里插入图片描述

  3. 分代收集算法
    复制算法:
    优点:没有内存碎片,效率高
    缺点:幸存区中多了一块to区,假设对象100%存活,浪费内存空间
    标记清除算法:
    优点:节省内存空间
    缺点:两次扫描,效率低,存在内存碎片
    标记压缩算法:
    优点:节省内存空间,不存在内存碎片
    缺点:三次扫描,效率最低
    分代收集算法:
    年轻代:存活率低,复制算法,经过15次(默认,可改)垃圾回收还存在,进入老年区
    老年代:标记清除+标记压缩
    调优:经过多少次GC进入老年代,经过多少次清除开始压缩

补充

  1. Servlet API中forward()与redirect()区别?
  • forward是服务器端的跳转,在一次请求中完成,而redirect是从客户端跳转,需要重新发起请求;
  • forward发生跳转后,浏览器地址不会发生改变,而redirect发生跳转后,浏览器地址会发生改变;
  • 因为 forward是在服务器端发生跳转,所以效率更高
  1. 什么是servlet?
    Java编写的服务器端程序,客户端发出请求,客户端找到指定的Servlet来处理请求,并将请求结果生成动态web内容完成对请求的相应
  2. get和post?
  • 都是http的请求方式;
  • get只能采用unicode编码,post有多重编码方式
  • get将提交的数据信息显示在浏览器的地址栏中,而post则是将数据信息存在请求体中,所以post方式更加的安全;
  • 由于浏览器对地址长度有限制,所以get请求传输数据大小受到了限制,而post则不会收到限制
  • get 刷新/后退无异常,post 刷新后/后退重新提交表单
  • get能被缓存,post不能缓存
  1. 什么是泛型,为什么要使用泛型?
  • 泛型就是未知的数据类型,当我们不知道要使用什么数据类型的时候就可以临时使用泛型来代替,在创建对象的时候在确定具体的数据类型;
  • 提高了代码的安全性,比如说可以减少强制类型转换的情况,减少运行期异常等;
  1. 什么是泛型的通配符?
    只能在方法的参数中使用;
    上限限定: ? extends E 使用的泛型只能是E类型的子类或者本身
    下限限定:? super E 使用的泛型只能是E类型的父类或者本身在这里插入图片描述

  2. ThreadLocal是什么?
    是一种数据结构,通过其set方法保存的值只有在本线程中才能获取,其他线程获取不到;因为每一个Thread对象都包含一个Threadlocals引用指向一个map,ThreadLocal的set方法在保存value时会首先获取当前线程的Thread对象,然后将value保存到那个map中,这个map专属于这个线程对象,别的线程对象获取不了;
    ThreadLocal应用?
    在spring的事务控制中用到,将connection对象放入map,同一个事务中的方法都从那个map中获取同一个connection对象;
    ThreadLocal如何防止内存泄漏?
    因为Threadlocals引用指向的map的key为弱引用,发生GC时会被回收,如果创建ThreadLocal的线程一直持续运行,那么这个Entry对象中的value就有可能一直得不到回收,发生内存泄露;
    所以在使用完ThreadLocal之后,调用remove方法,将Entry对象及时清除;

  3. == 和 equals 的区别是什么?
    == : 它的作用是判断两个对象的地址是不是相等。即,判断两个对象是不是同一个对象。(基本数据类型 == 比较的是值,引用数据类型 == 比较的是内存地址)

    equals() : 它的作用也是判断两个对象是否相等。但它一般有两种使用情况:

    情况1:类没有覆盖 equals() 方法。则通过 equals() 比较该类的两个对象时,等价于通过“==”比较这两个对象。

    情况2:类覆盖了 equals() 方法。一般,我们都覆盖 equals() 方法来两个对象的内容相等;若它们的内容相等,则返回 true (即,认为这两个对象相等)。

  4. 什么是反射?
    在运行状态中,对于任意一个类,可以动态的获取任意对象的属性和方法。
    Java获取反射的三种方法

  • 通过new对象实现反射机制
   Student stu = new Student();
   Class classobj1 = stu.getClass();
  • 通过路径实现反射机制
Class classobj2 = Class.forName("fanshe.Student");
  • 通过类名实现反射机制
Class classobj3 = Student.class;
  1. Error 和 Exception 区别是什么?
    Error 类型的错误通常为虚拟机相关错误,如系统崩溃,内存不足,堆栈溢出等,一旦这类错误发生,通常应用程序会被终止,仅靠应用程序本身无法恢复;
    Exception 类的错误是可以在应用程序中进行捕获并处理的,通常遇到这种错误,应对其进行处理,使应用程序可以继续正常运行。
  2. 运行时异常和一般异常(受检异常)区别是什么?
    运行时异常包括 RuntimeException 类及其子类,表示 JVM 在运行期间可能出现的异常。 Java 编译器不会检查运行时异常;
    受检异常是Exception 中除 RuntimeException 及其子类之外的异常。 Java 编译器会检查受检异常。
  3. throw 和 throws 的区别是什么?
    throw 关键字用在方法内部,只能用于抛出一种异常,用来抛出方法或代码块中的异常,受查异常和非受查异常都可以被抛出;
    throws 关键字用在方法声明上,可以抛出多个异常;
  4. 常见的 RuntimeException 有哪些?
  • ClassCastException(类转换异常)
  • IndexOutOfBoundsException(数组越界)
  • NullPointerException(空指针)
  • ArithmeticException:数学计算异常
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值