java基础总结 part3

PART3

059 类加载与初始化执行顺序

Q: 类初始化过程

  1. 加载就是在第一次使用类时动态把类加载到内存,加载一个类会看其父类是否已经加载,如果没有则会先加载父类。
  2. 类加载过程如下:分配内存保存类信息->给类变量赋默认值->加载父类->设置父子关系->执行类初始化代码;
  3. 而类初始化过程(先执行父类再执行子类,父类执行时子类静态变量是有默认值 0 的)如下:
    定义静态变量时的赋值语句->静态初始化代码块;这些 OK 以后就进入实例初始化过程:定义实例变量时的赋值语句->实例初始化代码块->构造方法。 所以上面的结果基本就是这个流程,只是寻找要执行的实例方法时是从对象的实际类型信息开始查找的,找不到才会查父类;对于变量无论是类变量还是实例变量都是静态绑定访问的。

060 类加载器机制

Q: 简单说说 java 类加载器的理解及加载机制?

  1. 三个类加载器:启动类加载器,虚拟机实现,主要负责加载java基础类(String,list等); 扩展类加载器:负责加载java的一些扩展类,开发者可以直接调用,应用程序类加载器:负责加载应用程序的类,引用的第三方类;
  2. 调用规则:双亲委派模型: 在 java 类加载器中除了引导类加载器( Bootstrap ClassLoader),所有的类加载器都有一个父类加载器,在子 ClassLoader 加载类时一般会先通过父 ClassLoader 加载,所以在加载一个 class 文件时首先会判断是否已经加载过了,加载过则直接返回 Class 对象(一个类只会被一个 ClassLoader 加载一次),没加载过则先让父 ClassLoader 去加载,如果加载成功返回得到的 Class 对象,父没有加载成功则尝试自己加载,自己加载不成功则抛出 ClassNotFoundException,整个这个加载流程就是双亲委派模型
  3. 双亲委派模型特点: 优先让父 ClassLoader 加载;双亲委派可以从优先级的策略上避免 Java 类库被覆盖的问题
  4. 特殊情况: 可以自定义加载顺序(不建议)就不用遵守双亲委派模型了,继承java.lang.ClassLoader,然后重写父类的findClass(),举例:过通过自定义 ClassLoader 可以实现一些灵活强大的功能,譬如热部署(不重启 Java 程序的情况下动态替换类实现)、应用的模块化和隔离化(不同 ClassLoader 可以加载相同的类,但是互相隔离互不影响,tomcat 就是利用这个特性管理多 web 应用的)、灵活加载等,
  5. JVM 在判定两个 Class 是否相同时不仅会判断两个类名是否相同而且会判断是否由同一个类加载器实例加载的,只有两者同时满足的情况下 JVM 才认为这两个 Class 是相同的,

061 反射

Q: 什么是 java 的反射?

  1. 概述: 反射是在运行时而非编译时动态获取类型的信息(譬如接口信息、成员信息、方法信息、构造方法信息等)然后依据这些动态获取到的信息创建对象、访问修改成员、调用方法等。
  2. 方法: Class.forName(clzss) 方法可以访问返回一个以指定字符串 clzss 为类名的类对象;类名.Class获取 Class 类对象,还可以通过实例.getClass() 方法获取 Class 类对象;Class.newInstance()方法可以创建对象实例

062 反射的优缺点

Q: 如何提高反射的效率?反射优缺点有哪些?

  1. 提高反射效率要考虑的问题如下,首先保证反射 API 最小化,譬如尽量使用 getMethod 直接获取而不是 getMethods 遍历查找获取;其次需要多次动态创建一个类的实例时尽可能的使用缓存。

  2. 优点:运行时动态获取类的实例,大大提高系统的灵活性和扩展性,与 Java 动态编译相结合可以实现无比强大的功能,springAOP

  3. 缺点:性能相对较低,此外使用反射相对来说不安全,破坏了类的封装性(可以通过反射获取这个类的私有方法和属性)

063 Class.forName()与ClassLoader.loadClass()方法

Q:简单说说 java 的 Class.forName 和 ClassLoader.loadClass 方法的区别?

  1. 一个 Java 类加载到 JVM 中会经过三个步骤,装载(查找和导入类或接口的二进制数据)、链接(校验:检查导入类或接口的二进制数据的正确性;准备:给类的静态变量分配并初始化存储空间;解析:将符号引用转成直接引用;)、初始化(激活类的静态变量的初始化 Java 代码和静态 Java 代码块)。
  2. class.forName()方法执行之后已经对被加载类的静态变量分配完了存储空间。而ClassLoader.loadClass方法并没有一定执行完链接这一步
Class.forName(className)实际调用的是:Class.forName(className,true,classLoader);
ClassLoader.loadClass(className)实际调用的是: ClassLoader.loadClass(className,false);
public static Class<?> forName(String name, boolean initialize, ClassLoader loader)

/**
三个参数的含义分别为:
name:要加载 Class 的名字
initialize:是否要初始化
loader:指定的 classLoader
*/
protected Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException

/**
两个参数的含义分别为:
name:class 的名字
resolve:是否要进行链接
*/

064 字节流与字符流

Q: 字节流与字符流有什么区别?

  1. 对于程序运行的底层设备来说永远都只接受字节数据,所以当我们往设备写数据时无论是字节还是字符最终都是写的字节流。字符流是字节流的包装类,所以当我们将字符流向字节流转换时要注意编码问题(因为字符串转成字节数组的实质是转成该字符串的某种字节编码)。
  2. 但是实际上字节流的操作不会经过缓冲区(内存)而是直接操作文本本身的,而字符流的操作会先经过缓冲区(内存)然后通过缓冲区再操作文件。
  3. 使用场景:
    • 字节流:大多数情况下使用字节流会更好,因为字符流是字节流的包装,而大多数时候 IO 操作都是直接操作磁盘文件,所以这些流在传输时都是以字节的方式进行的
    • 字符流:而如果对于操作需要通过 IO 在内存中频繁处理字符串的情况使用字符流会好些,因为字符流具备缓冲区,提高了性能。

065 缓冲区

Q:什么是缓冲区?有什么作用?

  1. 缓冲区就是一段特殊的内存区域,很多情况下当程序需要频繁地操作一个资源(如文件或数据库)则性能会很低,所以为了提升性能就可以将一部分数据暂时读写到缓存区,以后直接从此区域中读写数据即可,这样就显著提升了性能。
  2. 对于 Java 字符流的操作都是在缓冲区操作的,所以如果我们想在字符流操作中主动将缓冲区刷新到文件则可以使用 flush() 方法操作。

066 IO流设计的设计模式

Q: 平时使用的 Java IO 流中涉及到了哪些设计策略和设计模式?

  1. 链接机制:可以将一个流处理器跟另一个流处理器首尾相接,以其中之一的输出作为另一个的输入而形成一个流管道链接
    java
    new DataInputStream(new FileInputStream(file)
  2. 对称性的设计策略: 输入输出对称性,InputStream,OutputStream,Reader,Writer
  3. 装饰者模式:就是给一个对象增加一些新的功能,而且是动态的,要求装饰对象和被装饰对象实现同一个接口,装饰对象持有被装饰对象的实例(各种字符流间装饰,各种字节流间装饰)
    java
    //把InputStreamReader装饰成BufferedReader来成为具备缓冲能力的Reader。
    BufferedReader bufferedReader = new BufferedReader(inputStreamReader);
  4. 适配器设计模式:就是将某个类的接口转换成我们期望的另一个接口表示,目的是消除由于接口不匹配所造成的类的兼容性问题(字符流与字节流间互相适配)
    java
    //把FileInputStream文件字节流适配成InputStreamReader字符流来操作文件字符串。
    FileInputStream fileInput = new FileInputStream(file);
    InputStreamReader inputStreamReader = new InputStreamReader(fileInput);

067 jvm内存结构

Q: Java 的 JVM 内存结构分为哪几个部分?

  1. JVM 内存共分为虚拟机栈、堆、方法区、程序计数器、本地方法栈五个部分
  2. 虚拟机栈:线程私有,每个方法在执行时会创建一个栈帧,存储局部变量,操作数帧,动态链接,方法返回地址等
  3. 堆: 线程共享的,在虚拟机启动时创建的,用于存放对象实例
  4. 方法区: 线程共享的,用于存储已被虚拟机加载的类信息、常量、静态变量等
  5. 程序计数器:线程私有的,当前线程所执行的字节码号指示器,每个线程都有一个独立的程序计数器
  6. 本地方法栈:线程私有,主要虚拟机用到的native方法服务

068 StackOverflowError,OutOfMemoryError

Q: StackOverflowError 与 OutOfMemeryError 的区别?

  1. StackOverflowError: 当启动一个线程时虚拟机会为其分配一个栈空间,java栈以帧为单位保存线程运行状态,当线程调用一个方法时JVM会压入一个新的栈帧到这个线程的栈空间中,只要这个方法还没返回则这个栈帧就会一直存在,如果嵌套调用层次太多(递归调用),随着栈帧的增加导致总和大于JVM设置的-Xss的值就会抛出StackOverflowError
  2. OutOfMemoryError:
    • 堆内存溢出:当需要为对象实例化分配堆内存空间时,而堆的占用已经达到了设置的最大值(-Xmx)抛出OutOfMemoryError异常
    • 方法区溢出: 方法区存放类信息,在类加载器加载class文件到内存时JVM会提取到类的这些信息放到方法区,而此时如果需要存储这些类信息且方法区的内存占用达到最大值(-XX:MaxPermSize)则会抛出OutOfMemoryError

069 round

Math.round(15.5) 等于多少?Math.round(-15.5) 等于多少?

round 不是四舍五入,而是再加0.5向下取整

070 垃圾回收

Q: Java 中垃圾回收的方式有哪些?

  1. 标记清除法: 它的思想是标记哪些要被回收的对象,然后统一回收。特点:标记清除的效率很低;同时产生大量不连续的内存碎片,从而导致以后的程序在分配较大的对象时由于没有充足的连续内存而提前出发一个GC操作
  2. 复制算法: 解决效率问题;复制算法将可用内存按容量划分为相等的两部分,然后每次只使用其中的一块,当一块内存用完后就将还存活的对象复制到第二块内存上,然后一次性清楚完第一块内存,再将第二块上的对象复制到第一块;
  3. 标记整理: 主要是为了解决标记清除法产生大量内存碎片的问题;当对象存活率较高时,也解决了复制算法的效率问题。它的不同之处就是在清除对象的时候现将可回收对象移动到一端,然后清除掉端边界以外的对象,这样就不会产生内存碎片了。
  4. 分代收集法: 大多采用这个方式,根据对象的生存周期,将堆分为新生代和老年代,在新生代中,由于对象生存期端,每次回收都有大量对象死去,采用复制算法,老年代对象存活率较高,没有额外的空间进行分配担保,可以使用标记整理或者标记清除

071 内存泄漏 内存溢出

Q: 内存泄漏与内存溢出的区别产生原因以及解决方案

  1. 内存溢出: 程序在申请内存时没有足够的内存空间供其使用,内存泄漏: 程序在申请内存后无法释放已申请的内存空间,内存泄漏最终可能会导致内存溢出
  2. 内存溢出原因:
    • 内存中加载的数据量过大;
    • 集合类中有对象的引用,使用完没有清理;
    • 代码中存在死循环或者循环产生过多重复的对象实体;
    • 启动参数内存值设定的过小;
  3. 内存溢出解决方案: 修改JVM启动参数,直接增加内存(-Xms,-Xmx参数); 优化程序,释放垃圾(比如数据库各种连接,避免死循环)
  4. 内存泄漏原因:
    • 集合类被一个Static变量引用,同时集合类没有删除自己的元素;
    • 单例模式持有外部本应该被释放的对象
    • Thread 如果Thread的run方法一直在循环的执行不停,而该Thread又持有了外部变量,那么这个外部变量即发生内存泄漏
    • 非静态内部类创建静态实例造成的内存泄漏
    • 资源未关闭造成的内存泄漏

072 读取大文件

Q:Java 如何读取大文件,你有什么建议或者经验?

  1. 文件流边读边用,使用文件流read()方法每次读取指定长度的数据到内存中
  2. 对大文件建立NIO的FileChannel,每次调用read()方法时会先将文件数据u读取到已分配固定长度的ByteBuffer中,接着从中读取数据
  3. 内存文件映射:就是把文件内容映射到虚拟内存的一块区域中,从而可以直接操作内存当中的数据而无需每次都通过 I/O 去物理硬盘读取文件,这种方式可以提高速度
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值