目录
2.6 continue、break 和 return 的区别是什么?
二刷Java基础。
Java SE:(Java Platform,Standard Edition),Java平台标准版。包含了支持Java应用程序开发和运行的核心类库和虚拟机等核心组件。可以用来构建简单的程序。
Java EE:(Java Platform,Enterprise Edition),Java平台企业版。建立在Java SE基础上,包含了支持企业级应用程序开发和部署的标准和规范(Servlet,JSP,JDBC,JPA等)。可用来开发大型分布式,可移植,壮健的Java应用。
简单来说,JavaEE是JavaSE的PLUS版。
JVM:Java虚拟机,是运行Java字节码的虚拟机。针对不同的操作系统(Windows,Linux,macOS),JVM有不同的实现。目的是相同的字节码在不同平台得到相同结果,这就是Java“一次编译,随处可以运行”且跨平台的支持。

JVM不是只有一种,只要满足JVM规范,每个公司都可以开发自己的专属的JVM。
JDK:(Java Development Kit)是提供给Java开发者使用,可以用来编译和运行Java程序。包含了JRE和源码编译器Javac以及一些其他工具javadoc(文档注释工具)、jdb(调试器)、javap(反编译工具)等等。
JRE:(Java Runtime Environment)是Java运行时环境,是运行已编译的.class文件所需内容的集合,主要包含JVM和Java基础类库(Class Library)。

JVM虚拟机能够理解的代码就是字节码,就是class文件。Java通过字节码的方式,一定程度上解决了传统解释型语言效率低问题,同时又保留了解释型语言可移植的优点。
Java 程序从源代码到运行的过程如下图所示:

我们需要格外注意的是 .class->机器码 这一步。在这一步 JVM 类加载器首先加载字节码文件,然后通过解释器逐行解释执行,这种方式的执行速度会相对比较慢。而且,有些方法和代码块是经常需要被调用的(也就是所谓的热点代码),所以后面引进了 JIT(Just in Time Compilation) 编译器,而 JIT 属于运行时编译。当 JIT 编译器完成第一次编译后,其会将字节码对应的机器码保存下来,下次可以直接使用。而我们知道,机器码的运行效率肯定是高于 Java 解释器的。这也解释了我们为什么经常会说 Java 是编译与解释共存的语言 。

HotSpot 采用了惰性评估(Lazy Evaluation)的做法,根据二八定律,消耗大部分系统资源的只有那一小部分的代码(热点代码),而这也就是 JIT 所需要编译的部分。JVM 会根据代码每次被执行的情况收集信息并相应地做出一些优化,因此执行的次数越多,它的速度就越快。
JDK、JRE、JVM、JIT 这四者的关系如下图所示。


为什么说 Java 语言“编译与解释并存”?
这是因为 Java 语言既具有编译型语言的特征,也具有解释型语言的特征。因为 Java 程序要经过先编译,后解释两个步骤,由 Java 编写的程序需要先经过编译步骤,生成字节码(.class 文件),这种字节码必须由 Java 解释器来解释执行。
JDK9引入了一个新的编译模式AOT(Ahead Of Time Compliation)。和 JIT 不同的是,这种编译模式会在程序被执行前就将其编译成机器码,属于静态编译(C、 C++,Rust,Go 等语言就是静态编译)。AOT 避免了 JIT 预热等各方面的开销,可以提高 Java 程序的启动速度,避免预热时间长。并且,AOT 还能减少内存占用和增强 Java 程序的安全性(AOT 编译后的代码不容易被反编译和修改),特别适合云原生场景。
JIT 与 AOT 两者的关键指标对比:

可以看出,AOT 的主要优势在于启动时间、内存占用和打包体积。JIT 的主要优势在于具备更高的极限处理能力,可以降低请求的最大延迟。
既然 AOT 这么多优点,那为什么不全部使用这种编译方式呢?
我们前面也对比过 JIT 与 AOT,两者各有优点,只能说 AOT 更适合当下的云原生场景,对微服务架构的支持也比较友好。除此之外,AOT 编译无法支持 Java 的一些动态特性,如反射、动态代理、动态加载、JNI(Java Native Interface)等。然而,很多框架和库(如 Spring、CGLIB)都用到了这些特性。如果只使用 AOT 编译,那就没办法使用这些框架和库了,或者说需要针对性地去做适配和优化。举个例子,CGLIB 动态代理使用的是 ASM 技术,而这种技术大致原理是运行时直接在内存中生成并加载修改后的字节码文件也就是 .class 文件,如果全部使用 AOT 提前编译,也就不能使用 ASM 技术了。为了支持类似的动态特性,所以选择使用 JIT 即时编译器。
可能在看这个问题之前很多人和我一样并没有接触和使用过 OpenJDK 。那么 Oracle JDK 和 OpenJDK 之间是否存在重大差异?下面我通过收集到的一些资料,为你解答这个被很多人忽视的问题。
首先,2006 年 SUN 公司将 Java 开源,也就有了 OpenJDK。2009 年 Oracle 收购了 Sun 公司,于是自己在 OpenJDK 的基础上搞了一个 Oracle JDK。Oracle JDK 是不开源的,并且刚开始的几个版本(Java8 ~ Java11)还会相比于 OpenJDK 添加一些特有的功能和工具。
下面这段话摘自 Oracle 官方在 2012 年发表的一个博客:
问:OpenJDK 存储库中的源代码与用于构建 Oracle JDK 的代码之间有什么区别?
答:非常接近 - 我们的 Oracle JDK 版本构建过程基于 OpenJDK 7 构建,只添加了几个部分,例如部署代码,其中包括 Oracle 的 Java 插件和 Java WebStart 的实现,以及一些闭源的第三方组件,如图形光栅化器,一些开源的第三方组件,如 Rhino,以及一些零碎的东西,如附加文档或第三方字体。展望未来,我们的目的是开源 Oracle JDK 的所有部分,除了我们考虑商业功能的部分
既然 Oracle JDK 这么好,那为什么还要有 OpenJDK?
答:
OpenJDK 是开源的,开源意味着你可以对它根据你自己的需要进行修改、优化,比如 Alibaba 基于 OpenJDK 开发了 Dragonwell8:https://github.com/alibaba/dragonwell8open in new window
OpenJDK 是商业免费的(这也是为什么通过 yum 包管理器上默认安装的 JDK 是 OpenJDK 而不是 Oracle JDK)。虽然 Oracle JDK 也是商业免费(比如 JDK 8),但并不是所有版本都是免费的。
OpenJDK 更新频率更快。Oracle JDK 一般是每 6 个月发布一个新版本,而 OpenJDK 一般是每 3 个月发布一个新版本。(现在你知道为啥 Oracle JDK 更稳定了吧,先在 OpenJDK 试试水,把大部分问题都解决掉了才在 Oracle JDK 上发布)
虽然,Java 和 C++ 都是面向对象的语言,都支持封装、继承和多态,但是,它们还是有挺多不相同的地方:

标识符就是一个名字呗,可以是变量的名字,也可以是方法名字。
关键字就是特殊的标识符,Java已经指定了它的用法。
访问控制:private、protected、public
类、方法、和变量修饰符:abstract、class 、extends、final、 implements、 interface、 native、 new 、static 、strictfp 、synchronized 、transient、 volatile 、enum
程序控制:break 、continue 、return、 do、 while、 if、 else、 for、 instanceof、 switch、 case、 default 、assert
错误处理:try 、catch、 throw 、throws 、finally
包相关:import 、package
基本类型:boolean、byte、char、double、float、int、long、short
变量引用:super、this、void
保留字:goto、const
++或--
四种用法
总结一下就是符号在前就是先操作自己,再返回,符号在后就是先返回再操作自己。
注:
java中使用补码来表示二进制数。在补码表示中,最高位是符号位:0-正数,1-负数。
补码规则为:正数最高位是0,其余的各位表示其数值本身;
负数是该数值的绝对值的补码按位取反+1所得。
Java 中有三种移位运算符:

<<:左移运算符,左移变大,x< 4 << 2 = 4*2^2 = 16 -4 << 2 = -4*2^2 = -16 >>:带符号右移,右移变小,x>>n相当于x/2^n后向下取整。高位补符号位,低位丢弃。 9 >> 2 = 9/2^2 = 2 -9 >> 2 = -9/2^2 = -3 >>>:无符号右移,忽略符号位,空位补0 9 >>> 2 = 2 -9 >>> 2 = 1073741821 由于 移位操作符实际上支持的类型只有 如果移位的位数超过数值所占有的位数会怎样? 当 int 类型左移/右移位数大于等于 32 位操作时,会先求余(%)后再进行左移/右移操作。 x< continue:跳出当前循环,执行下一次循环 break:跳出循环,执行下面的代码。 return:结束该方法,返回。 Java 中有 8 种基本数据类型,分别为: 设占的位数为n,4种整数型能表示的范围从-2^n-1 ~ 2^n-1 - 1 这八种基本类型都有包装类型,分别为Byte,Short,Integer,Long,Float,Double,Character,Boolean Java 基本数据类型的包装类型的大部分都用到了缓存机制来提升性能。 两种浮点数类型的包装类 记住:所有整型包装类对象之间值的比较,全部使用 equals 方法比较。 什么是自动拆装箱? Integer i = 10; //装箱 装箱其实就是调用了 包装类的 注意:如果频繁拆装箱的话,也会严重影响系统的性能。我们应该尽量避免不必要的拆装箱操作。 为什么会出现这个问题呢? 这个和计算机保存浮点数的机制有很大关系。我们知道计算机是二进制的,而且计算机在表示一个数字时,宽度是有限的,无限循环的小数存储在计算机时,只能被截断,所以就会导致小数精度发生损失的情况。这也就是解释了为什么浮点数没有办法用二进制精确表示。 就比如说十进制下的 0.2 就没办法精确转换成二进制小数: // 0.2 转换为二进制数的过程为,不断乘以 2,直到不存在小数为止, 静态变量也就是被 静态变量是通过类名来访问的,例如 通常情况下,静态变量会被 1、调用方式 在外部调用静态方法时,可以使用 不过,需要注意的是一般不建议使用 因此,一般建议使用 2、访问类成员是否存在限制 静态方法在访问本类的成员时,只允许访问静态成员(即静态成员变量和静态方法),不允许访问实例成员(即实例成员变量和实例方法),而实例方法不存在这个限制。 重载就是同样的一个方法能够根据输入数据的不同,做出不同的处理 重写就是当子类继承自父类的相同方法,输入数据一样,但要做出有别于父类的响应时,你就要覆盖父类方法 从 Java5 开始,Java 支持定义可变长参数,所谓可变长参数就是允许在调用方法时传入不定长度的参数。就比如下面的这个 public static void method1(String... args) { 另外,可变参数只能作为函数的最后一个参数,但其前面可以有也可以没有任何其他参数。 public static void method2(String arg1, String... args) { 参考文章:Java基础常见面试题总结(上)
double,float 在二进制中的表现比较特殊,因此不能来进行移位操作。int和long,编译器在对short、byte、char类型进行移位前,都会将其转换为int类型再操作。
x<<42等同于x<<10,x>>42等同于x>>10,x >>>42等同于x >>> 102.6 continue、break 和 return 的区别是什么?
3. 基本数据类型
3.1 Java中的八种基本数据类型
byte、short、int、longfloat、doublecharboolean。基本类型 位数 字节 byte 8 1 short 16 2 int 32 4 long 64 8 float 32 4 double 64 8 char 16 2 boolean 1 3.2 基本类型和包装类型的区别

static 修饰 )存放在 Java 虚拟机的堆中。包装类型属于对象类型,我们知道几乎所有对象实例都存在于堆中。null ,而基本类型有默认值且不是 null。== 比较的是值。对于包装数据类型来说,== 比较的是对象的内存地址。所有整型包装类对象之间值的比较,全部使用 equals() 方法。3.2 包装类型的缓存机制了解么?
Byte,Short,Integer,Long 这 4 种包装类默认创建了数值 [-128,127] 的相应类型的缓存数据,Character 创建了数值在 [0,127] 范围的缓存数据,Boolean 直接返回 True or False。Float,Double 并没有实现缓存机制。3.3 自动装箱与拆箱了解吗?原理是什么?
int n = i; //拆箱
valueOf()方法,拆箱其实就是调用了 xxxValue()方法。Integer i = 10 等价于 Integer i = Integer.valueOf(10)int n = i 等价于 int n = i.intValue();3.4 为什么浮点数运算的时候会有精度丢失的风险?
// 在这个计算过程中,得到的整数部分从上到下排列就是二进制的结果。
0.2 * 2 = 0.4 -> 0
0.4 * 2 = 0.8 -> 0
0.8 * 2 = 1.6 -> 1
0.6 * 2 = 1.2 -> 1
0.2 * 2 = 0.4 -> 0(发生循环)
...3.5 如何解决浮点数运算的精度丢失问题?
BigDecimal 可以实现对浮点数的运算,不会造成精度丢失。通常情况下,大部分需要浮点数精确运算结果的业务场景(比如涉及到钱的场景)都是通过 BigDecimal 来做的。4. 变量和方法
4.1 成员变量与局部变量的区别?

public,private,static 等修饰符所修饰,而局部变量不能被访问控制修饰符及 static 所修饰;但是,成员变量和局部变量都能被 final 所修饰。static 修饰的,那么这个成员变量是属于类的,如果没有使用 static 修饰,这个成员变量是属于实例的。而对象存在于堆内存,局部变量则存在于栈内存,静态变量存在方法区。final 修饰的成员变量也必须显式地赋值),而局部变量则不会自动赋值。4.2 静态变量有什么作用?
static 关键字修饰的变量。它可以被类的所有实例共享,无论一个类创建了多少个对象,它们都共享同一份静态变量。也就是说,静态变量只会被分配一次内存,即使创建多个对象,这样可以节省内存。StaticVariableExample.staticVar(如果被 private关键字修饰就无法这样访问了)。final 关键字修饰成为常量。4.3 字符型常量和字符串常量的区别?
4.4 静态方法为什么不能调用非静态成员?
4.5 静态方法和实例方法有何不同?
类名.方法名 的方式,也可以使用 对象.方法名 的方式,而实例方法只有后面这种方式。也就是说,调用静态方法可以无需创建对象 。对象.方法名 的方式来调用静态方法。这种方式非常容易造成混淆,静态方法不属于类的某个对象而是属于这个类。类名.方法名 的方式来调用静态方法。4.6 重载和重写有什么区别?
4.7 什么是可变长参数?
printVariable 方法就可以接受 0 个或者多个参数。
//......
}
//......
}