一、Java基础知识-1.3其他特性

1.3、其他特性

1.3.1、Java和JavaScript有哪些区别?

答:

  1. 产生背景不同:最初Java是由Sun公司开放的,前身叫Oak语言,1995年5月正式对外发布并开源;而JavaScript是Netscape(网景)公司的产品,是为了扩展Netscape浏览器的功能而开发的解释性语言,最初被命名为LiveScript,因为Netscape和Sun合作,Netscape公司管理层希望它外观看起来像Java,因此更名为JavaScript。
  2. 对象设计不同:Java是真正的面向对象的语言,即使开发简单的程序,也必须要设计对象;而JavaScript是脚本语言,可以用来开发与网络无关的、与用户交互的复杂软件,JavaScript是一种基于对象(Object-Based)和事件驱动(Event-Driven)的编程语言,它内置了非常丰富的对象供开发人员使用。
  3. 运行机制不同:Java源码在运行之前必须要经过编译,而JavaScript是一种解释性编程语言,源码不需要经过编译,直接由浏览器解释执行。目前的浏览器几乎都使用了JIT(即时编译)技术来提升JavaScript的运行效率。
  4. 数据类型检查时期不同:Java是静态语言,在编译期间对数据类型进行检查,而JavaScript是动态语言,在运行期间才检查数据类型。也就是说,Java采用强类型变量检查,所有的变量在编译之前必须先声明,而JavaScript中的变量是弱类型的,甚至在使用前变量都可以不声明。JavaScript的解释器会在运行时自动判断数据类型。

扩展:
静态语言是指在编译期间对数据类型进行检查的语言。这种语言在编写程序时需要声明所有变量的数据类型。典型的静态语言如C,C++、Java。
动态语言是指在运行期间才去做数据类型检查的语言。在用动态语言编程的时候并不需要给变量指定数据类型,该语言会在第一次将数据赋值给变量的时候在内部将数据类型记录下来。典型的动态语言如Python,Ruby。

1.3.2、什么是受检异常和非受检异常?

答:
受检异常:表示在编译的时候强制检查的异常,遇到这种异常需要显式的通过try/catch来捕捉或者通过throws抛出,否则程序无法通过编译;而非受检异常,表示编译器不需要强制检查的异常,这种异常不需要显式的捕捉。

扩展:
Java里,所有的异常都继承自java.lang.Throwable类,Throwable类有两个直接子类:Error类和Exception类。
Error用来表示程序底层或者硬件相关的错误,这种错误和程序本身无关,比如常见的OOM异常。因为Error这种异常和程序本身无关,所以不需要检查,属于非受检异常。
Exception表示程序中的异常,一般都是由程序不严谨导致的,比如NullPointException。Exception类下面派生了RuntimeException类和其他异常类,但RuntimeException是运行时异常,也属于非受检异常。
所以,除了Error和RuntimeException及其RuntimeException的派生类,其他异常都属于受检异常。比如IOException、SQLException。之所以Java设计一些在编译时需要强制检查的异常,其实就是为了保证程序的稳定性和可靠性

1.3.3、fail-fast机制和fail-safe机制分别有什么作用?

答:
fail-fast和fail-safe是多线程并发操作集合时的失败处理机制。
fail-fast表示快速失败,在集合遍历过程中,一旦发现容器中的数据被修改了(这里说的容器指的是集合),会立刻抛出ConcurrentModificationException(并发修改异常),从而导致遍历失败。java.util包下的集合类都是fail-fast机制的,常见的有HashMap、ArrayList等

示例:
public static void main(String[] args) {
    Map<String,String> emp = new HashMap<String,String>();
    emp.put("name","kongZi");
    emp.put("sex","male");
    emp.put("age","18");
    Iterator iterator = emp.keySet().iterator();
    while(iterator.hasNext()) {
        System.out.println(emp.get(iterator.next()));
        emp.put("work","java");
    }
}
运行结果:
male
Exception in thread "main" java.util.ConcurrentModificationException
...

fail-safe表示安全失败,在这种机制下,集合遍历时出现集合元素的修改不会抛出ConcurrentModificationException(并发修改异常),原因是采用fail-safe机制的集合容器,在遍历时不是直接在集合内容上访问的,而是先复制原有集合的内容,在复制的集合上进行遍历。由于迭代时是对复制的集合进行遍历的,所以在遍历过程中对原集合的修改不会被迭代器检测到。java.util.concurrent包下的容器都是fail-safe机制的,可以在多线程下并发使用、并发修改,常见的有ConcurrentHashMap、CopyOnWriteArrayList等

示例:
public static void main(String[] args) {
    CopyOnWriteArrayList<Integer> list
         = new CopyOnWriteArrayList<>(new Integer[] {1,7,9,11});
    Iterator iterator = list.iterator();
    while(iterator.hasNext()) {
        Integer i = (Integer)iterator.next();
        System.out.println(i);
        if(i == 7) {
            list.add(15); //15并不会被打印
        }
    }
}
运行结果:
1
7
9
11

1.3.4、如何理解序列化和反序列化?

答:
序列化:就是把内存里面的对象转化为字节流,以便用来实现存储或传输。
反序列化:就是根据从文件或者网络上的对象上获得的对象的字节流,根据字节流保存的对象描述信息和状态重新构建一个对象。

扩展:
序列化的目的就是解决网络通信中对象传输的问题。本质上就是把当前JVM进程里的一个对象,跨网络传输到另外一个JVM进程里。
序列化的前提是保证通信双方对于对象的可识别性,所以一般情况下,我们会把对象先转化成通用的解析格式,比如JSON、XML等,再把它们转化为数据流进行网络传输。
市面上开源的序列化技术有很多,比如JSON、XML、Protobuf等,实际应用时选择哪种序列化技术更好,需要考虑以下几点:

  1. 序列化之后数据的大小,因为数据大小会影响传输性能
  2. 序列化耗时时长,耗时较长会影响业务性能
  3. 是否支持跨平台和跨语言
  4. 技术的成熟度,越成熟的技术使用的公司越多,也就越稳定
示例:JSON序列化和反序列化技术

1.Java对象序列化为json字符串:
//obj是一个Object
import com.alibaba.fastjson.JSON;
String jsonString = JSON.toJSONString(obj);

2.json字符串反序列化为Java对象(这里使用阿里的fastjson技术)
引入依赖
<dependency>
	<groupId>com.alibaba</groupId>
	<artifactId>fastjson</artifactId>
	<version>1.2.47</version>
 </dependency>
反序列化
import com.alibaba.fastjson.JSONObject;
People people = JSONObject.parseObject(jsonStr, People.class); // People类是自己定义的类

1.3.5、什么是SPI?它有什么用?

答:
SPI,全称是Service Provider Interface,是一种基于接口的动态扩展机制,Java SPI相当于Java里面提供了一套接口,然后由第三方来实现这个接口从而完成功能的扩展。
简单的说,我们可以定义一个标准接口,然后交给第三方来实现这个接口。程序运行的时候,会根据配置信息动态加载第三方实现的类,从而完成功能的动态扩展。

扩展:
Java SPI机制有一个非常典型的例子,就是数据库驱动java.jdbc.Driver。JDK里面定义了数据库驱动Driver,它是一个接口,JDK并没有提供实现。比如定义的Oracle的接口是oracle.jdbc.OracleDriver,定义的Mysql的接口是com.mysql.jdbc.Driver。程序根据集成的驱动实现连接到对应的数据库,这些实现是由第三方数据库厂商完成的。程序运行的时候会根据我们声明的驱动类型,动态加载对应的扩展实现,从而完成对应数据库的连接。
实现Java SPI需要满足以下的基本格式:

  1. 需要定义一个接口,作为扩展的标准
  2. 在classpath目录下创建META-INF/service目录
  3. 在创建的目录下,以接口的全限定名命名配置文件,文件内容是这个接口的实现类
  4. 在应用程序里,使用ServiceLoad就可以根据接口名称找到classpath所有的扩展实现,然后根据上下文场景选择实现类完成功能的调用

Java SPI机制也有缺点:不能根据需求只加载需要的扩展实现,每次都会加载扩展接口的所有实现类并进行实例化,实例化会造成性能开销,并且加载一些不需要的实现类会导致内存资源的浪费

1.3.6、finally语句块一定会被执行吗?

答:
finally语句块是和try语句块组合使用的。通常情况下,不管有没有触发异常,finally语句块中的代码都会执行,所以我们会把资源的释放和业务日志的打印放在finally语句块中。
但是有两种情况finally语句块是不会被执行的:

  1. 程序没有进入到try语句块中而因为异常导致程序终止。出现这个问题主要是因为开发人员在编写代码的时候异常捕获的范围不够。
  2. 在try语句块或catch语句块中执行了System.exit(0)语句,导致JVM直接退出。

1.3.7、什么是内存溢出?什么是内存泄漏?

答:

  • 内存泄露(memory leak):指程序在申请内存后,无法释放已申请的内存空间,内存泄露堆积会导致内存被占光
  • 内存溢出(out of memory):指程序在申请内存时,没有足够的内存空间供其使用,出现 out of memory
  • memory leak 最终会导致 out of memory

扩展:
内存泄漏:程序运行结束后,没有释放所占用的内存空间。一次内存泄漏似乎不会有太大影响,但内存泄漏不断累积,最终可用内存会变得越来越少,从而导致内存溢出。
比如说,总内存大小是100MB,有40MB的内存一直无法回收,那么可用的只有60MB 。这40MB的就是内存泄漏。内存泄漏,就是程序运行结束后没有释放的内存
内存溢出: 程序运行时,在申请内存空间时,没有足够的内存空间供其正常使用,程序运行停止,并抛出OutOfMemoryError。
比如程序运行时申请了一个10MB 空间,但是当前可用内存只有5MB,程序无法正常执行,这就是内存溢出。内存溢出,可以理解为程序运行需要的内存大于当前可用内存
内存溢出的解决办法:

  1. 修改JVM启动参数,直接增加内存。( -Xms 、-Xmx 参数一定不要忘记加)
  2. 检查错误日志,查看 “OutOfMemory” 错误前是否有其它异常或错误
  3. 对代码进行走查和分析,找出可能发生内存溢出的位置
重点排查以下几处:
1. 检查对数据库查询中,是否有一次获得全部数据的查询。
一般来说,如果一次取十万条记录到内存,就可能引起内存溢出。这个问题比较隐蔽,在上线前,数据库中数据较少,不容易出问题,但是在上线后,数据库中的数据多了,一次查询就有可能引起内存溢出。因此对于数据库查询尽量采用分页的方式查询;
2. 检查代码中是否有死循环或递归调用;
3. 检查是否有大循环重复产生新的实体对象;
4. 检查ListMAP等集合对象是否有使用完后,未清除的问题。
ListMAP等集合对象会始终存有对象的引用,使得这些对象不能被GC回收;
  1. 使用内存查看工具动态查看内存使用情况,比如使用Jmap获取内存堆信息。
  • 18
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值