2024年java基础36

java基础361 谈谈你对 Java 平台的理解 image png Java 是一种面向对象的语言 最显著的特性有两个方面 一个就是一次编译 到处运行 Write once run anywhere 能够非常容易的获得跨平台能力 另一个就是垃圾收集 GC Garbage Collection Java 通过 GC 回收分配内存 大部分情况下程序员不需要操心内存的分配和回收 什么是 JRE Java

大家好,我是讯享网,很高兴认识大家。



1. 谈谈你对Java平台的理解。

image.png

Java是一种面向对象的语言,最显著的特性有两个方面:

  • 一个就是一次编译,到处运行(Write once, run anywhere),能够非常容易的获得跨平台能力;
  • 另一个就是垃圾收集(GC,Garbage Collection),Java通过GC回收分配内存,大部分情况下程序员不需要操心内存的分配和回收;

什么是JRE(Java Runtime Environment)/JDK(Java Development Kit)?

JRE:java运行环境,包含了JVM,Java内裤,以及一些模块;
    JDK:JRE的一个超集,提供了更多工具,比如编辑器、各种诊断工具;
    
JVM < JRE < JDK

  1. Java是解释执行,这句话正确么?
    image.png

Java是解释执行,这个说法不太准确。我们开发java的源代码,通过Javac编译成字节码,然后在运行时通过JVM内嵌的解释器将字节码转换为最终的机器码。

但是常见的JVM(Oracle JDK 提供的Hotspot JVM),都提供了动态编译器JIT(Just in Time),JIT能够在运行时将热点代码编译成机器码,这种情况下部分热点代码就属于编译执行,而不是解释执行。

  • -Xmixed:解释和编译混合模式;
  • -Xint:只进行解释执行,不对代码进行编译,抛弃JIT可能带来的性能优势;
  • -Xcomp:关闭解释器或者叫做最大优化级别。但这种模式并不一定是最高效的,他会导致JVM启动变慢,同时有些JIT编译器优化方式,如分支预测,如果不进行profiling,往往不能进行有效优化。
    AOT(Ahead-of-Time Compilation):直接将字节码编译成机器代码,避免了JIT预热等各方面的开销。在Oracle JDK 9 引入了实验性的AOT特征,并增加了新的jaotc工具;利用下面的命令把某个类或者某个模块编译成为 AOT 库。
 
讯享网 

然后,在启动时直接指定就可以了。

讯享网

Exception和Error有什么区别?

Exception和Error都继承自java.lang.Throwable。在Java中只有Throwable的实例才可以被抛出和捕获,是异常处理机制的基本组成类型。

  • Error描述了java运行时系统的内部错误和资源耗尽错误。大多数错误与代码编写者执行的操作无关,而表示代码运行时 JVM(Java 虚拟机)出现的问题。应用程序不应该抛出这种类型的对象。
  • Exception是程序正常运行中可以预料的意外情况,并且应该被捕获进行相应的处理。Exception分为可检查异常(checked)和不可检查异常(unchecked),可检查异常必须在代码中显示的捕获处理,这是编译期检查的一部分。不可检查异常就是运行时异常(java.lang.RuntimeException)类似IndexOutOfBoundsException、NullPointerException通常是可以编码避免的逻辑错误,具体根据需要来判断是否需要进行捕获,并不会在编译期强制要求。
  1. 捕获异常时需要注意几点:
  • 一个是尽量不要捕获类似于Exception这样的通用异常,否则有可能会把不想捕获的异常也捕获到例如本想捕获NoSuchMethodException,结果把NullPointerException也捕获到了,这样就无法判断到底是类对象为空还是没有这个方法,干扰问题的定位。
  • 不要使用e.printStackTrace();这样的代码,特别是在产品代码中,因为用这个方法最后异常堆栈轨迹是不知道打印到哪里去的。
  • Throw early,catch late原则,具体可以查看文章这里不再展开说。
  • try…catch…finally块尽量只用来包裹需要抛出异常的那一句或者几句代码。有的时候程序员或许因为程序代码的可读性和美观性,将一大段代码用try…catch…finally包裹,实际上这样对性能的损耗是非常大的。
  1. 理解ClassNotFoundException 与NoClassDefFoundError的区别
    ClassNotFoundException的产生原因主要是:
    Java支持使用反射方式在运行时动态加载类,例如使用Class.forName方法来动态地加载类时,可以将类名作为参数传递给上述方法从而将指定类加载到JVM内存中,如果这个类在类路径中没有被找到,那么此时就会在运行时抛出ClassNotFoundException异常。
    解决该问题需要确保所需的类连同它依赖的包存在于类路径中,常见问题在于类名书写错误。
    另外还有一个导致ClassNotFoundException的原因就是:当一个类已经某个类加载器加载到内存中了,此时另一个类加载器又尝试着动态地从同一个包中加载这个类。通过控制动态类加载过程,可以避免上述情况发生。

NoClassDefFoundError产生的原因在于:
如果JVM或者ClassLoader实例尝试加载(可以通过正常的方法调用,也可能是使用new来创建新的对象)类的时候却找不到类的定义。要查找的类在编译的时候是存在的,运行的时候却找不到了。这个时候就会导致NoClassDefFoundError.
造成该问题的原因可能是

  • 打包过程漏掉了部分类,或者jar包出现损坏或者篡改。解决这个问题的办法是查找那些在开发期间存在于类路径下但在运行期间却不在类路径下的类。
  • 配置出现问题 classpath,包权限,xml配置
  • 类静态初始化出现问题,检查是否有java.lang.ExceptionInInitializerError错误
    详细请参考:https://blog.csdn.net/jamesjxin/article/details/

谈谈final、finally、 finalize有什么不同?

1。final修饰的类,不可被继承,修饰的方法不可被重写,修饰的变量不可多次赋值。通过final能够得到性能上的优化,但是不明显,如果大量使用可能会干扰代码,不能表达出本来具有的含义。故不使用。匿名内部类,访问局部变量要求传入的参数,必须是final是要保证数据一致性问题。
2。finally。代码中总是会执行的代码段。除了退出虚拟机外。
3。finalize。在虚拟机回收改对象前进行调用。此种方式不可取。因为java虚拟机不知道在什么时候才对对象进行回收。

final修饰类,变量,方法等;目的是不可变,这样做使得安全、提高性能。

强引用、软引用、弱引用、幻象引用有什么区别?

牛人总结:

在Java语言中,除了基本数据类型外,其他的都是指向各类对象的对象引用;Java中根据其生命周期的长短,将引用分为4类。

1 强引用

特点:我们平常典型编码Object obj = new Object()中的obj就是强引用。通过关键字new创建的对象所关联的引用就是强引用。 当JVM内存空间不足,JVM宁愿抛出OutOfMemoryError运行时错误(OOM),使程序异常终止,也不会靠随意回收具有强引用的“存活”对象来解决内存不足的问题。对于一个普通的对象,如果没有其他的引用关系,只要超过了引用的作用域或者显式地将相应(强)引用赋值为 null,就是可以被垃圾收集的了,具体回收时机还是要看垃圾收集策略。

2 软引用

特点:软引用通过SoftReference类实现。 软引用的生命周期比强引用短一些。只有当 JVM 认为内存不足时,才会去试图回收软引用指向的对象:即JVM 会确保在抛出 OutOfMemoryError 之前,清理软引用指向的对象。软引用可以和一个引用队列(ReferenceQueue)联合使用,如果软引用所引用的对象被垃圾回收器回收,Java虚拟机就会把这个软引用加入到与之关联的引用队列中。后续,我们可以调用ReferenceQueue的poll()方法来检查是否有它所关心的对象被回收。如果队列为空,将返回一个null,否则该方法返回队列中前面的一个Reference对象。

应用场景:软引用通常用来实现内存敏感的缓存。如果还有空闲内存,就可以暂时保留缓存,当内存不足时清理掉,这样就保证了使用缓存的同时,不会耗尽内存。

3 弱引用

弱引用通过WeakReference类实现。 弱引用的生命周期比软引用短。在垃圾回收器线程扫描它所管辖的内存区域的过程中,一旦发现了具有弱引用的对象,不管当前内存空间足够与否,都会回收它的内存。由于垃圾回收器是一个优先级很低的线程,因此不一定会很快回收弱引用的对象。弱引用可以和一个引用队列(ReferenceQueue)联合使用,如果弱引用所引用的对象被垃圾回收,Java虚拟机就会把这个弱引用加入到与之关联的引用队列中。

应用场景:弱应用同样可用于内存敏感的缓存。

4 虚引用

特点:虚引用也叫幻象引用,通过PhantomReference类来实现。无法通过虚引用访问对象的任何属性或函数。幻象引用仅仅是提供了一种确保对象被 finalize 以后,做某些事情的机制。如果一个对象仅持有虚引用,那么它就和没有任何引用一样,在任何时候都可能被垃圾回收器回收。虚引用必须和引用队列 (ReferenceQueue)联合使用。当垃圾回收器准备回收一个对象时,如果发现它还有虚引用,就会在回收对象的内存之前,把这个虚引用加入到与之关联的引用队列中。
ReferenceQueue queue = new ReferenceQueue ();
PhantomReference pr = new PhantomReference (object, queue);
程序可以通过判断引用队列中是否已经加入了虚引用,来了解被引用的对象是否将要被垃圾回收。如果程序发现某个虚引用已经被加入到引用队列,那么就可以在所引用的对象的内存被回收之前采取一些程序行动。

应用场景:可用来跟踪对象被垃圾回收器回收的活动,当一个虚引用关联的对象被垃圾收集器回收之前会收到一条系统通知。


  1. 强引用:项目中到处都是。
  2. 软引用:图片缓存框架中,“内存缓存”中的图片是以这种引用来保存,使得JVM在发生OOM之前,可以回收这部分缓存
  3. 虚引用:在静态内部类中,经常会使用虚引用。例如,一个类发送网络请求,承担callback的静态内部类,则常以虚引用的方式来保存外部类(宿主类)的引用,当外部类需要被JVM回收时,不会因为网络请求没有及时回来,导致外部类不能被回收,引起内存泄漏
  4. 幽灵引用:这种引用的get()方法返回总是null,所以,可以想象,在平常的项目开发肯定用的少。但是根据这种引用的特点,我想可以通过监控这类引用,来进行一些垃圾清理的动作。不过具体的场景,还是希望峰哥举几个稍微详细的实战性的例子?

String/StringBuffer/StringBuilder:

1 String的设计与实现考量
(1) String的创建机理
由于String在Java世界中使用过于频繁,Java为了避免在一个系统中产生大量的String对象,引入了字符串常量池。其运行机制是:创建一个字符串时,首先检查池中是否有值相同的字符串对象,如果有则不需要创建直接从池中刚查找到的对象引用;如果没有则新建字符串对象,返回对象引用,并且将新创建的对象放入池中。但是,通过new方法创建的String对象是不检查字符串池的,而是直接在堆区或栈区创建一个新的对象,也不会把对象放入池中。上述原则只适用于通过直接量给String对象引用赋值的情况。

注意:String提供了intern()方法。调用该方法时,如果常量池中包括了一个等于此String对象的字符串(由equals方法确定),则返回池中的字符串。否则,将此String对象添加到池中,并且返回此池中对象的引用。

[B] 针对常量池的优化。当2个String对象拥有相同的值时,他们只引用常量池中的同一个拷贝。当同一个字符串反复出现时,这个技术可以大幅度节省内存空间。

2 StringBuffer/StringBuilder

StringBuffer和StringBuilder都实现了AbstractStringBuilder抽象类,拥有几乎一致对外提供的调用接口;其底层在内存中的存储方式与String相同,都是以一个有序的字符序列(char类型的数组,jdk9之后是byte数组)进行存储,不同点是StringBuffer/StringBuilder对象的值是可以改变的,并且值改变以后,对象引用不会发生改变;两者对象在构造过程中,首先按照默认大小申请一个字符数组,由于会不断加入新数据,当超过默认大小后,会创建一个更大的数组,并将原先的数组内容复制过来,再丢弃旧的数组。因此,对于较大对象的扩容会涉及大量的内存复制操作,如果能够预先评估大小,可提升性能。

唯一需要注意的是:StringBuffer是线程安全的,但是StringBuilder是线程不安全的。可参看Java标准类库的源代码,StringBuffer类中方法定义前面都会有synchronize关键字。为此,StringBuffer的性能要远低于StringBuilder。

3 应用场景

[A]在字符串内容不经常发生变化的业务场景优先使用String类。例如:常量声明、少量的字符串拼接操作等。如果有大量的字符串内容拼接,避免使用String与String之间的“+”操作,因为这样会产生大量无用的中间对象,耗费空间且执行效率低下(新建对象、回收对象花费大量时间)。

[B]在频繁进行字符串的运算(如拼接、替换、删除等),并且运行在多线程环境下,建议使用StringBuffer,例如XML解析、HTTP参数解析与封装。

[C]在频繁进行字符串的运算(如拼接、替换、删除等),并且运行在单线程环境下,建议使用StringBuilder,例如SQL语句拼装、JSON封装等。

谈谈java反射机制,动态代理是基于什么原理

  1. 反射原理
    JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性;这种动态获取的信息以及动态调用对象的方法的功能称为java语言的反射机制。
  2. 动态代理分类:JDK动态代理原理,cglib动态代理原理
  3. 优缺点,适用场景
    代理模式(通过代理静默地解决一些业务无关的问题,比如远程、安全、事务、日志、资源关闭……让应用开发者可以只关心他的业务)
    静态代理:事先写好代理类,可以手工编写,也可以用工具生成。缺点是每个业务类都要对应一个代理类,非常不灵活。
    动态代理:运行时自动生成代理对象。缺点是生成代理代理对象和调用代理方法都要额外花费时间。
    JDK动态代理:基于Java反射机制实现,必须要实现了接口的业务类才能用这种办法生成代理对象。新版本也开始结合ASM机制。
    cglib动态代理:基于ASM机制实现,通过生成业务类的子类作为代理类。
    适用场景:动态代理(AOP、RPC)、提供第三方开发者扩展能力(Servlet容器,JDBC连接)、第三方组件创建对象(DI)……

int和Integer有什么区别?

  1. 基本类型和包装类型
    int 是我们常说的整形数字,是 Java 的 8 个原始数据类型。
    Integer是int对应的包装类,它有一个int类型的字段存储数据,并且提供了基本操作,比如数学运算、int和字符串之间转换等。在Java 5中,引入了自动装箱和自动拆箱功能(boxing/unboxing),Java可以根据上下文,自动进行转换,极大地简化了相关编程。对于整数,javac替我们自动把装箱转换为Integer.valueOf(),把拆箱替换为Integer.intValue()。
  2. 包装类型的缓存
    就像上一讲谈到的String,Java也为Integer提供了值缓存,默认情况下只会缓存-128到127之间的值。
 

上述代码中第一行与第二行的写法取值使用了值缓存,而第三行的写法则没有利用值缓存。结合刚刚讲到的自动装箱、拆箱的知识,第一行代码用到的自动装箱,等价于调用了Integer.valueOf()。

讯享网
  1. 适应场景,性能影响
    [1] 基本类型均具有取值范围,在大数*大数的时候,有可能会出现越界的情况。
    [2] 基本类型转换时,使用声明的方式。例:long result= * 24 * 365;结果值一定不会是你所期望的那个值,因为 * 24已经超过了int的范围,如果修改为:long result= L * 24 * 365;就正常了。
    [3] 慎用基本类型处理货币存储。如采用double常会带来差距,常采用BigDecimal、整型(如果要精确表示分,可将值扩大100倍转化为整型)解决该问题。
    [4] 优先使用基本类型。原则上,建议避免无意中的装箱、拆箱行为,尤其是在性能敏感的场合,
    [5] 如果有线程安全的计算需要,建议考虑使用类型AtomicInteger、AtomicLong 这样的线程安全类。部分比较宽的基本数据类型,比如 float、double,甚至不能保证更新操作的原子性,可能出现程序读取到只更新了一半数据位的数值。

对比Vector、ArrayList、LinkedList有何区别?

对比Hashtable、HashMap、TreeMap有什么不同?

image.png
  1. Collection 各种类的实现原理,使用场景
  2. List 的各种实现,Set 的各种实现,Queue的各种实现
  3. 理解Java提供的各种排序算法 Collections.sort() Arrays.sort()

如何保证集合是线程安全的? ConcurrentHashMap如何实现高效地线程安全?

Java提供了哪些IO方式? NIO如何实现多路复用?

Java有几种文件拷贝方式?哪一种最高效?

谈谈接口和抽象类有什么区别?

谈谈你知道的设计模式?

小讯
上一篇 2025-01-02 20:36
下一篇 2024-12-31 08:39

相关推荐

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容,请联系我们,一经查实,本站将立刻删除。
如需转载请保留出处:https://51itzy.com/kjqy/6803.html