热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

极客“Java核心技术”笔记part1

问题1:谈谈你对Java平台的理解,“Java是解释执行”这句话正确吗?对于Java平台的理解,可以从很多方面谈一下。上图


问题1:谈谈你对Java平台的理解,“Java是解释执行”这句话正确吗?

对于Java平台的理解,可以从很多方面谈一下。上图是一个相对宽泛的蓝图,可以作为回答这个问题的蓝图。

这个说法是不准确的。Java的源代码,首先经过Javac编译器编译成字节码文件,然后,在运行时,通过JVM内嵌的解释器将字节码转换为最终的机器码。但是,常见的JVM,比如我们大多数情况使用的Oracle JDK提供的Hotspot JVM都提供了JIT(just in time)编译器,也就是通常所说的动态编译器,JIT能够在运行时将热点代码编译成机器码,这种情况下部分热点代码就属于编译执行,而不是解释执行。

继续说一下,解释执行和编译执行的问题。字节码 + JVM是Java实现跨平台的基础。JVM会通过类加载器(双亲委托机制)加载字节码,解释或者编译执行。主流的Java版本中,如JDK8实际是解释和编译混合的一种模式。通常,运行在server模式的JVM,会进行上万次调用以收集足够的信息进行高效的编译,client模式的这个上线是1500次。Hotspot JVM内置了两个JIT compiler,c1(client模式 - 适用于对启动速度敏感的应用,比如Java桌面应用) 和 c2(server模式 - 适用于长时间运行的服务器端应用设计)。默认采用所谓的分层编译。

其实还有一种新的编译方式,即AOT(Ahead of time),直接将字节码编译成机器码,这样就避免了JIT预热等各个方面的开销。Android 的ART使用的就是类似AOT的方式,就是比较占用空间。

另外,JVM作为一个强大的平台,不仅仅只有Java语言可以运行在JVM上,本质上合规的字节码都可以运行,Java语言自身也为此提供了便利。我们可以看到类似Groovy、JRuby、Jython、Scala、Clojure等大量JVM语言,活跃在不同的场景。

问题2:Exception和Error有什么区别,另外,运行时异常与一般异常有什么区别?

 

 1、NoClassDefFoundError和ClassNotFoundException有什么区别?

最明显的区别就是:一个是Exception,一个是Error。

JDK API里面的解释:
a)NoClassDefFoundError 
当 Java 虚拟机或 ClassLoader 实例试图在类的定义中加载(作为通常方法调用的一部分或者作为使用 new 表达式创建的新实例的一部分),但无法找到该类的定义时,抛出此异常。 
当前执行的类被编译时,所搜索的类定义存在,但运行时无法再找到该定义。

b)ClassNotFoundException 
当应用程序试图使用以下方法通过字符串名加载类时,抛出该异常: 
* Class 类中的 forName 方法。 
* ClassLoader 类中的 findSystemClass 方法。 
* ClassLoader 类中的 loadClass 方法。 
2、随着Java语言得发展,引入了一些更加便利的特性,比如try-with-resources和multiple catch。示例如下:

try (BufferedReader br = new BufferedReader(…);BufferedWriter writer = new BufferedWriter(…)) {// Try-with-resources
// do something
catch ( IOException | XEception e) {// Multiple catch// Handle it
}

3、异常处理的基本原则

  • 尽量不要捕获类似Exception这样的通用异常,而是应该捕获特定异常;进一步讲,除非深思熟虑了,否则不要捕获Throwable或者Error,这样很难保证我们能够正确处理OOM;
  • 不要生吞异常;因为如果不把异常跑出来,或者也没有输出日志之类的,可能会导致非常难以诊断的诡异情况;

看下如下代码:

try {// 业务代码// …
} catch (IOException e) {e.printStackTrace();
}

记住:在产品代码中,通常是不允许这样处理的。因为在稍微复杂一点的生产系统中,标准出错不是合适的输出选项,因为你很难判断到底输出到哪里去了。尤其是分布式系统,如果发生异常,但是无法找到堆栈轨迹,这纯属为诊断设置障碍了。最好使用产品日志,详细地输出到日志系统里。

4、从性能的角度审视Java的异常处理机制

  • try-catch代码段会产生额外的性能开销,或者换个角度说,往往会影响JVM对代码进行优化,所以建议仅捕获有必要的代码段,尽量不要一个大的try包住整段的代码;同时,避免利用异常机制控制代码流程,这远比我们通常意义上的条件语句要低效。
  • Java每实例化一个Exception,都会对当时的栈进行快照,这是一个相对比较重的操作。如果发生的非常频繁,开销就不能被忽略了。当我们的服务出现反应变慢、吞吐量下降的时候,检查发生最频发的Exception也是一种思路。

5、匿名内部类,访问局部变量时,局部变量为啥要用final来修饰吗?

Java inner class实际会copy一份,不是去直接使用局部变量,final可以防止出现数据一致性问题。

问题3:强引用、软引用、弱引用、虚引用的区别

虚引用仅仅是提供了一种确保对象被finalize以后(处于虚可达状态),做某些事情的机制,比如,通常用来做所谓的Post-Mortem清理机制。Java平台自身的Cleaner机制等。

判断对象可达性,是JVM垃圾收集器决定如何处理对象的一部分考虑。

所有引用类型,都是抽象类java.lang.ref.Reference的子类,提供了get()方法。除了虚引用(因为get永远返回null),如果对象还没有被销毁,都可以通过get方法获取原有对象。这意味着,利用软引用和弱引用,我们可以将访问到的对象,重新指向强引用,也就是人为的改变对象的可达性状态。所以,对于软引用、弱引用之类,垃圾回收器可能会存在二次确认的问题,以保证处于该状态的对象,没有改变为强引用。

谈到各种引用的编程,就必然要提到引用队列。我们会创建各种引用并关联到响应对象,可以选择是否需要关联引用队列,JVM会在特定时机将引用enqueue到队列里,我们可以从队列里获取引用,然后进行相关后续逻辑。尤其是虚引用,get方法只返回null,如果再不指定引用队列,基本就没有意义了。

Object counter = new Object();
ReferenceQueue refQueue &#61; new ReferenceQueue<>();
PhantomReference p &#61; new PhantomReference<>(counter, refQueue);
counter &#61; null;
System.gc();
try {// Remove 是一个阻塞方法&#xff0c;可以指定 timeout&#xff0c;或者选择一直阻塞Reference ref &#61; refQueue.remove(1000L);if (ref !&#61; null) {// do something}
} catch (InterruptedException e) {// Handle it
}

Java软引用什么时候被回收

问题4&#xff1a;String、StringBuilder、StringBuffer

1、StringBuilder与StringBuffer&#xff08;线程安全&#xff09;

StringBuilder与StringBuffer底层都是利用可修改的(char&#xff0c;JDK9以后是byte)数组&#xff0c;二者都继承自AbstractStringBuilder&#xff0c;里面包含了基本的操作&#xff0c;区别在于最终的方法是否加了Synchronized。注意&#xff1a;数组的大小默认是16&#xff0c;超过则进行扩容。

2、字符串拼接

String strByBuilder &#61; new
StringBuilder().append("aa").append("bb").append("cc").append("dd").toString();String strByConcat &#61; "aa" &#43; "bb" &#43; "cc" &#43; "dd";

非静态的拼接逻辑在JDK8中会自动被javac转换为StringBuilder操作&#xff1b;而在JDK9里面&#xff0c;则体现了思路的变化。Java9利用InvokeDynamic&#xff0c;将字符串拼接的优化与javac生成的字节码解耦&#xff0c;假设未来JVM增强相关运行时实现&#xff0c;将不再需要依赖javac的任何修改。

3、字符串缓存String.intern()

String在Java6以后提供了intern()方法&#xff0c;目的是提示JVM把相应字符串缓存起来&#xff0c;以备重复使用。在我们创建字符串对象并调用intern()方法的时候&#xff0c;如果已经有缓存的字符串&#xff0c;就会返回缓存里的实例&#xff0c;否则将其缓存起来。

上述看起来不错&#xff0c;实际却是大跌眼镜。一般使用Java6这种历史版本并不推荐大量使用intern()&#xff0c;为什么&#xff1f;魔鬼存在于细节之中&#xff0c;被缓存的字符串是保存在所谓PermGen里的&#xff0c;也就是臭名昭著的“永久代”&#xff0c;这个空间是很有限的&#xff0c;也基本不会被FullGC之外的垃圾收集照顾。所以&#xff0c;如果使用不当&#xff0c;OOM就会光顾。

在后续的版本中&#xff0c;这个缓存被放置在了堆中&#xff0c;这样就极大的避免了永久代占满的问题&#xff0c;甚至永久代在JDK8中被MetaSpace&#xff08;元数据区&#xff09;替代了。而且&#xff0c;默认缓存大小也在不断地扩大中&#xff0c;从最初的1009&#xff0c;到7u40以后被修改为60013。

Intern是一种显式地重拍机制&#xff0c;但是有一定的副作用&#xff0c;因为需要开发者写代码时明确调用&#xff0c;一是不方便&#xff0c;每一个都显式调用非常麻烦&#xff1b;另外就是我们很难保证效率&#xff0c;应用开发阶段很难清楚地预计字符串的重复情况&#xff0c;有人认为这是一种污染代码的实践。

幸好在Oracle JDK 8u20之后&#xff0c;推出了一个新的特性&#xff0c;也就是G1 GC下的字符串排重。它是通过将相同数据的字符串指向同一份数据做出来的&#xff0c;是JVM的底层的改变&#xff0c;并不需要Java类库做什么修改。注意&#xff1a;这个功能默认是关闭的&#xff0c;需开启。

4、字符串创建的机理

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

举例&#xff1a;String str1 &#61; "123"; //通过直接量赋值方式&#xff0c;放入字符串常量池
String str2 &#61; new String(“123”);//通过new方式赋值方式&#xff0c;不放入字符串常量池

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

 


推荐阅读
  • Java在运行已编译完成的类时,是通过java虚拟机来装载和执行的,java虚拟机通过操作系统命令JAVA_HOMEbinjava–option来启 ... [详细]
  • Nginx使用AWStats日志分析的步骤及注意事项
    本文介绍了在Centos7操作系统上使用Nginx和AWStats进行日志分析的步骤和注意事项。通过AWStats可以统计网站的访问量、IP地址、操作系统、浏览器等信息,并提供精确到每月、每日、每小时的数据。在部署AWStats之前需要确认服务器上已经安装了Perl环境,并进行DNS解析。 ... [详细]
  • 本文介绍了RPC框架Thrift的安装环境变量配置与第一个实例,讲解了RPC的概念以及如何解决跨语言、c++客户端、web服务端、远程调用等需求。Thrift开发方便上手快,性能和稳定性也不错,适合初学者学习和使用。 ... [详细]
  • 本文介绍了Web学习历程记录中关于Tomcat的基本概念和配置。首先解释了Web静态Web资源和动态Web资源的概念,以及C/S架构和B/S架构的区别。然后介绍了常见的Web服务器,包括Weblogic、WebSphere和Tomcat。接着详细讲解了Tomcat的虚拟主机、web应用和虚拟路径映射的概念和配置过程。最后简要介绍了http协议的作用。本文内容详实,适合初学者了解Tomcat的基础知识。 ... [详细]
  • 本文介绍了如何清除Eclipse中SVN用户的设置。首先需要查看使用的SVN接口,然后根据接口类型找到相应的目录并删除相关文件。最后使用SVN更新或提交来应用更改。 ... [详细]
  • 本文讨论了在VMWARE5.1的虚拟服务器Windows Server 2008R2上安装oracle 10g客户端时出现的问题,并提供了解决方法。错误日志显示了异常访问违例,通过分析日志中的问题帧,找到了解决问题的线索。文章详细介绍了解决方法,帮助读者顺利安装oracle 10g客户端。 ... [详细]
  • 本文介绍了解决Netty拆包粘包问题的一种方法——使用特殊结束符。在通讯过程中,客户端和服务器协商定义一个特殊的分隔符号,只要没有发送分隔符号,就代表一条数据没有结束。文章还提供了服务端的示例代码。 ... [详细]
  • 本文介绍了如何使用php限制数据库插入的条数并显示每次插入数据库之间的数据数目,以及避免重复提交的方法。同时还介绍了如何限制某一个数据库用户的并发连接数,以及设置数据库的连接数和连接超时时间的方法。最后提供了一些关于浏览器在线用户数和数据库连接数量比例的参考值。 ... [详细]
  • 原文地址:https:www.cnblogs.combaoyipSpringBoot_YML.html1.在springboot中,有两种配置文件,一种 ... [详细]
  • 本文介绍了Hyperledger Fabric外部链码构建与运行的相关知识,包括在Hyperledger Fabric 2.0版本之前链码构建和运行的困难性,外部构建模式的实现原理以及外部构建和运行API的使用方法。通过本文的介绍,读者可以了解到如何利用外部构建和运行的方式来实现链码的构建和运行,并且不再受限于特定的语言和部署环境。 ... [详细]
  • 本文介绍了计算机网络的定义和通信流程,包括客户端编译文件、二进制转换、三层路由设备等。同时,还介绍了计算机网络中常用的关键词,如MAC地址和IP地址。 ... [详细]
  • 本文介绍了在Windows环境下如何配置php+apache环境,包括下载php7和apache2.4、安装vc2015运行时环境、启动php7和apache2.4等步骤。希望对需要搭建php7环境的读者有一定的参考价值。摘要长度为169字。 ... [详细]
  • Android源码深入理解JNI技术的概述和应用
    本文介绍了Android源码中的JNI技术,包括概述和应用。JNI是Java Native Interface的缩写,是一种技术,可以实现Java程序调用Native语言写的函数,以及Native程序调用Java层的函数。在Android平台上,JNI充当了连接Java世界和Native世界的桥梁。本文通过分析Android源码中的相关文件和位置,深入探讨了JNI技术在Android开发中的重要性和应用场景。 ... [详细]
  • 本文介绍了如何使用C#制作Java+Mysql+Tomcat环境安装程序,实现一键式安装。通过将JDK、Mysql、Tomcat三者制作成一个安装包,解决了客户在安装软件时的复杂配置和繁琐问题,便于管理软件版本和系统集成。具体步骤包括配置JDK环境变量和安装Mysql服务,其中使用了MySQL Server 5.5社区版和my.ini文件。安装方法为通过命令行将目录转到mysql的bin目录下,执行mysqld --install MySQL5命令。 ... [详细]
  • 解决nginx启动报错epoll_wait() reported that client prematurely closed connection的方法
    本文介绍了解决nginx启动报错epoll_wait() reported that client prematurely closed connection的方法,包括检查location配置是否正确、pass_proxy是否需要加“/”等。同时,还介绍了修改nginx的error.log日志级别为debug,以便查看详细日志信息。 ... [详细]
author-avatar
陈建希佳旭
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有