本节从整体来看一下Java中的精髓.
Java介绍
略
Java和JDK的关系
JDK(Java Development Kit) Java开发工具包,它包括:编译器,Java运行环境(JRE, Java Runtime Environment), JVM(Java虚拟机),监控和诊断工具等,而Java则表示一种开发语言.
Java程序是怎么执行的?
- 先把Java代码编译成字节码,也就是把 .java类型的文件编译成 .class类型的文件.这个过程的大致执行流程: Java源代码 -> 词法分析器 -> 语法分析器 -> 语义分析器 -> 字节码生成器 ->最终生成字节码,其中任何一个节点执行失败就会造成编译失败;
- 把class文件放置到Java虚拟机,这个虚拟机通常指的是Oracle官方自带的Hotspot JVM;
- Java虚拟机使用类加载器(Class Loader)装载class文件;
- 类加载完成之后,会进行字节码校验,字节码校验通过JVM解释器会把字节码翻译成机器码交由操作系统执行.但不是所有代码都是解释执行的,JVM对此做了优化, 比如, 以Hotspot虚拟机来说, 它本身提供了JIT (Just In Time)也就是我们通常所说的动态编译器,它能够在运行时将热点代码编译成机器码,这个时候字节码就变成了编译执行.
Java程序执行流程图如下:
Java虚拟机是如何判定热点代码的?
Java虚拟机判定热点代码的方式有两种:
- 基于采样的热点判定
主要是虚拟机会周期性的检查各个线程的栈顶,若某个或某些方法经常出现在栈顶,那这个方法就是"热点方法".这种判定方式的优点是实现简单;缺点是很难精确一个方法的热度,容易受到线程阻塞或外界因素的影响.
- 基于计数器的热点判定
主要就是虚拟机给每一个方法甚至代码块建立了一个计数器,统计方法的执行次数,超过了一定的阈值则标记此方法为热点方法.
Hotspot虚拟机使用的基于计数器的热点探测方法.它使用了两类计数器:方法调用计数器和回边计数器,当达到一定阈值时就会触发JIT编译.
方法调用计数器: 在client模式下的阈值是1500次,Server 是10000次,可以通过虚拟机参数: -XX: CompileThreshold = N 对其进行设置.但是JVM还存在热度衰减, 时间段内调用方法的次数较少,计数器就减小.
回边计数器: 主要统计的是方法中循环体代码执行的次数.
由上面的知识点我们可以看出,要想做到对Java了如指掌,必须要好好学习Java虚拟机,那除了Java虚拟机外,还有哪些知识是我们Java工程师必须掌握的知识呢?
1.Java基础中的核心内容
字符串和字符串常量池的深入理解,Array的操作和排序算法,深克隆和浅克隆,各种IO操作,反射和动态代理(JDK自身动态代理和CGLIB)等。
2.集合
集合和String是编程中最常用的数据类型,关于集合的知识,主要包含:链表(LinkedList)、TreeSet、栈(Stack)、队列(双端、阻塞、非阻塞队列、延迟队列)、HashMap、TreeMap等,它们的使用和底层存储数据机构都是热门内容。
3.多线程
多线程使用和线程安全的知识,它包括:死锁、6种线程池的使用与差异、ThreadLocal、synchronized、Lock、JUC(java.util.concurrent包)、CAS (Compare and Swap)、ABA问题等。
4.热门框架
Spring、Spring MVC、MyBatis、SpringBoot
5.分布式编程
消息队列(RabbitMQ、Kafka)、Dubbo、Zookeeper、SpringCloud等。
6.数据库
MySQL常用引擎的掌握、MySQL前缀索引、回表查询、数据存储结构、最左匹配原则、MySQL的问题分析和排除方案、MySQL读写分离的实现原理以及MySQL的常见优化方案等。Redis的使用场景、缓存雪崩和缓存穿透的解决方案、Redis过期淘汰策略和主从复制的实现方案等。
7.Java虚拟机
虚拟机的组成、垃圾回收算法、各种垃圾回收器的区别、Java虚拟机分析工具的掌握、垃圾回收器常用的调优参数等。
8.其他
常用算法的掌握、设计模式的理解、网络知识和常见Linux命令的掌握等。
相关题目
1.Java语言都有哪些特点?
答:Java语言包含以下特点。
- 面向对象,程序容易理解,开发简单,方便;
- 跨平台,可运行在不同服务器类型上,比如:Linux、Windows、Mac等;
- 执行性能好,运行效率高;
- 提供大量API扩展,语言强大;
- 有多线程支持,增加了响应和实时交互能力;
- 安全性好,自带验证机制,确保程序的可靠性和安全性。
2.Java跨平台实现的原理是什么?
答:要了解Java跨平台实现原理之前,必须先要了解Java的执行过程,Java的执行过程如下:
Java执行流程:Java源代码(.java)-> 编译 -> Java字节码(.class)->通过JVM(Java虚拟机)运行Java程序。每种类型的服务器都会运行一个JVM,Java程序只需要生成JVM可以执行的代码即可,JVM底层屏蔽了不同服务器类型之间的差异,从而可以在不同类型的服务器上运行一套Java程序。
3.JDK、JRE、JVM有哪些区别?
答:了解了JDK、JRE、JVM的定义也就明白了它们之间的区别,如下所述。
JDK:Java Development Kit (Java开发工具包)的简称,提供了Java的开发环境和运行环境;
JRE:Java Runtime Environment (Java运行环境)的简称,为Java的运行提供了所需环境;
JVM:Java Virtual Machine (Java虚拟机)的简称,是一种用于计算设备的规范,他是一个虚构出来的计算机,是通过在实际的计算机上仿真模拟各种计算机功能来实现的,简单来说就是所有的Java程序都是运行在JVM(Java虚拟机)上的。
总体来说,JDK提供了一整套的Java运行和开发环境,通常使用对象为Java的开发者,当然JDK也包含了JRE;而JRE为Java运行的最小运行单元,一般安装在Java服务器上,所以JDK和JRE可以从用途上进行理解和区分。JVM不同于JDK和JRE,JVM是Java程序运行的载体,Java程序只有通过JVM才能正常的运行。
4.Java中如何获取明天此刻的时间?
答:JDK8之前使用Calendar.add()方法获取,代码如下:
Calendar calendar = Calendar.getInstance(); calendar.add(Calendar.DATE,1); System.out.println(calendar.getTime());
JDK8有两种获取明天时间的方法。
方法一,使用 LocalDateTime.plusDays() 方法获取,代码如下:
LocalDateTime today = LocalDateTime.now(); LocalDateTime tomorrow = today.plusDays(1); System.out.println(tomorrow);
方法二,使用 LocalDateTime.minusDays() 方法获取,代码如下:
LocalDateTime today = LocalDateTime.now(); LocalDateTime tomorrow = today.minusDays(-1); System.out.println(tomorrow);
minusDays()方法未当前时间减去n天,传负值就相当于当前时间加n天。
5.Java中如何跳出多重嵌套循环?
答:Java中跳出嵌套循环得两种方式:
- 方法一:定义一个标号,使用break加标号得方式
- 方法二:使用全局变量终止循环
方法一,示例代码:
myfor: for (int i = 0; i < 100; i++) { for (int j = 0; j < 100 ; j++) { System.out.println("J:" + j); if (j == 10){ // 跳出多重循环 break myfor; } } }
方法二,示例代码:
boolean flag = true; for (int i = 0; i < 100 && flag; i++) { for (int j = 0; j < 100; j++) { System.out.println("J:" + j); if(j == 10){ // 跳出多重循环 flag = false; break; } } }
6.char变量能不能存储一个中文汉字?为什么?
答:char变量可以存贮一个汉字,因为Java中默认使用的编码是Unicode,一个char类型占2个字节(16bit),所以放一个中文是没问题的。
7.Java中会存在内存泄漏吗?请简单描述一下。
答:一个不再被程序使用的对象或变量一直被占据在内存中就造成了内存泄漏。
Java中得内存泄漏的常见情景如下:
- 长生命周期对象持有短生命的引用,比如,缓存系统,我们加载了一个对象放在缓存中,然后一直不使用这个缓存,由于缓存的对象一直被缓存引用得不到释放,就造成了内存泄漏。
- 各种连接未调用关闭方法,比如数据库Connection连接,未显性地关闭,就会造成内存泄漏。
- 内部类持有外部类,如果一个外部类的实例对象的方法返回了一个内部类的实例对象,这个内部类对象被长期引用了,即使那个外部类实例对象不再被使用,但由于内部类持有外部类的实例对象,这个外部类对象将不会被垃圾回收,这也会造成内存泄漏;
- 改变哈希值,当一个对象被存储进HashSet集合中以后,就不能修改这个对象中的那些参与计算哈希值的字段了,否则对象修改后的哈希值与最初存储进HashSet集合中的哈希值就不同了,在这种情况下,即使在contains方法使用该对象的当前引用作为的参数去HashSet集合中检索对象,也将返回找不到对象的结果,这也将导致无法从HashSet集合中单独删除当前对象,造成内存泄露。