语言基础
JAVA运行环境
- JVM
Java 虚拟机(JVM)是运行 Java 字节码的虚拟机。
- JDK
JDK 是 Java Development Kit,它是功能齐全的 Java SDK。它拥有 JRE 所拥有的一切,还有编译器(javac)和工具,能够创建和编译程序。
- JRE
JRE 是 Java 运行时环境。它是运行已编译 Java 程序所需的所有内容的集合,包括 Java 虚拟机(JVM),Java 类库,java 命令和其他的一些基础构件。但是,它不能用于创建新程序。
oracle jdk与openjdk
Oracle JDK由Oracle公司开发,Oracle JDK采用了商业实现OpenJDK是Java SE平台版的开源和免费实现,虽然OpenJDK的部分功能有所缺失,但整体相差不大
关键字
- 完全小写的字母
- 被高亮的单词
标识符
- 类的名称
- 变量名称
- 方法名称
常量与变量
常量
在程序运行期间,固定不变的量
数据类型
- 基本数据类型
- 整数型
- byte short int long 这些类型的包装类型的valueOf都作了缓存
- 浮点型
- float double
- 字符型
- char
- 布尔型
- boolean
- 整数型
- 引用数据类型
类型名称 | 默认值 | 大小 | 最小值 | 最大值 | 包装类 | 缓存区间 |
---|---|---|---|---|---|---|
boolean | false | 1B | 0(false) | 1(true) | Boolean | 无 |
byte | (byte)0 | 1B | -128 | 127 | Byte | -128~127 |
char | '\u0000' | 2B | '\u0000' | '\uFFFF' | Character | (char)0 ~ (char)127 |
short | (short)0 | 2B | -32768 | 32767 | Short | -128 ~ 127 |
int | 0 | 4B | -2^31 | 2^31-1 | Integer | -128 ~ 127 |
long | 0L | 8B | -2^63 | 2^63-1 | Long | -128 ~ 127 |
float | 0.0f | 4B | 1.4e-45 | 3.4e+38 | Float | 无 |
double | 0.0d | 8B | 4.9e-324 | 1.798e+308 | Double | 无 |
浮点数的表示
packet-beta 0-22: "有效数字(23位)" 23-30: "指数(8位)" 31: "符号(1位)"
指数 = $X + 2^{x的二进制位数-1}$
有效数字 = 使用原码存储
由于某些数字不能由有限二进制位精确表示 所以会出现1f-0.9f != 0.1 这种情况
变量
程序运行期间,内容可以发生改变的量
数据类型转换
- 隐式转换
数据范围从小到大
- 显式转换
范围小的类型 范围小的变量名 = (范围小的类) 原本范围大的数据;
数据溢出
当被转换的数值范围大于目标类型时,就会发生数据溢出,导致一部分数据丢失
- byte short char 在运算时都会被提升为int类型
ASCII 码表
使用数字表示某些字符
运算符
进行特定操作的符号
算术运算符
+ - * / %
加号的三种用法:
数值加法
字符计算
字符串连接
优先级问题
String s = "123" + 20 + 30;
自增自减运算符
- 单独使用
- 混合使用
赋值运算符
- 基本
- 复合
比较运算符
> < >= <= == !=
逻辑运算符
&& || !
- 短路
当 a && b 中的a为false时 就不会去计算b表达式
位运算符
// 以下所有操作都代表的是在2进制操作下System.out.println(2 << 2); // 左移 8System.out.println(2 >> 1); // 右移 2System.out.println(0xffffffff >>> 3); // 无符号右移(忽略掉符号位,对符号位也会移动)System.out.println(8 | 0); // 位或 8System.out.println(8 & 0); // 位与 0System.out.println(8 ^ 0); // 异或 8System.out.println(~8); // 位非 -9
方法
方法签名包括方法名称与参数列表 是方法的唯一标识
方法调用
- 直接调用
- 赋值调用
- 递归调用
参数的传递
在JAVA中 参数都是通过值来进行传递的
- 形参:方法定义阶段
- 实参:方法调用阶段
要避免使用Object作为可变参数
入参保护:对于入参数据量进行判断控制 处理不了 直接返回错误
参数校验:对于外部的输入 都需要校验 基于防御式编程理念 但是对于一些底层的方法就不必校验 越靠近外部 越需要校验
修饰符
访问权限修饰符
- private
- public
- protected
static
- 属于类本身
可以被用来:
- 修饰类变量
- 注意线程安全问题
- 修饰方法
- 方法可以通过类直接调用
- 修饰代码块
- 使用到静态变量的代码块可以在任何位置
初始化时机
- 父类的静态变量和静态块比子类优先初始化;
- 静态变量和静态块比类构造器优先初始化。
问:如何证明 static 静态变量和类实例无关
答:不需要创建该类的实例就可以使用静态变量与静态方法
问:变量和方法被 static 和 final 两个关键字修饰,为什么这么做
答:static可以直接使用这些方法与变量,final则是变量地址不可变,方法不可覆写,提升稳定性
问:catch 中发生了未知异常,finally 还会执行么
答:会,但是异常会被catch吞掉
final
transient
用来修饰类变量,意思是当前变量是无需进行序列化的
default
修饰之后该方法就代表是一个默认实现,如果其他类继承该接口,就可以不用实现该方法,直接使用这个默认实现
abstract
- 抽象类及抽象方法
synchronized
方法重载
指在同一个类中,允许存在一个以上的同名方法,只要它们的参数列表不同即可,与修饰符和返 回值类型无关
重载可以在编译时确定调用的方法 也被称为静态绑定
void test(){ f(1);}// 重载方法的选择:void f(int i){} // 1. 精确匹配void f(long i){} // 2. 基本类型会自动转换成更大的数据类型void f(Integer i){} // 3. 自动装箱拆箱void f(Object i){} // 4. 向上转型进行匹配void f(Integer... i){} // 5. 最终通过可变参数匹配
方法重写
对于子类 其可以改变父类的方法实现
- 父类无法调用在父类没有定义的方法
- 父类可以调用到子类重写的父类的方法
子类重写的条件:
- 只能针对非静态 非final 非构造方法
- 访问权限不能变小
- 重写方法的返回值类型要 `T super 父类的返回值类型`
- 受检异常的类型同上
- 方法签名需要与父类一致
构造方法
- 不能继承 不能覆写 不能直接调用
- 方法名称必须与类名相同
- 没有返回类型
- 默认提供了一个无参构造
- 可以为private
类内方法
外部使用静态成员时 尽量使用 类名.静态成员 来调用
静态方法:
- 不能使用实例成员
- 不能使用super与this关键字
getter 与 setter
为什么使用:
- 满足面向对象的封装特性
- 利于统一控制
- 权限...
几种情况警惕:
- 在方法中添加了业务逻辑会增加排查问题的难度
- 同时定义getxxx与isxxx会迷惑程序员
- 方法参数名称与成员变量名称相同 这点在IDE的使用下不是什么问题
特殊方法
finalize方法
很早之前就已经被弃用了,不仅会导致垃圾回收变慢,甚至会导致死锁等问题产生
一个替代品是从 JDK9 引入的java.lang.ref.Cleaner
public class CleaningExample implements AutoCloseable { // A cleaner, preferably one shared within a library private static final Cleaner cleaner = Cleaner.create(); static class State implements Runnable { State() { // initialize State needed for cleaning action } public void run() { // cleanup action accessing State, executed at most once System.out.println("清理操作"); } } private final State state; private final Cleaner.Cleanable cleanable; public CleaningExample() { this.state = new State(); this.cleanable = cleaner.register(this, state); } public void close() { cleanable.clean(); } public static void main(String[] args) { try (var a = new CleaningExample()){} }}
这个机制也有缺陷,跟一些第三方框架一样,都是通过虚引用来实现对资源的清理
main方法
同步异步
异步处理的任务是非时间敏感的 异步调用需要通过诸如轮询的方式获取执行结果 轮询会增加机器的压力
流程控制
顺序结构
分支结构
- if
- if-else
- if-else if-else
- switch
java8 switch 支持的表达式:
- enum
- String
- Integer
- Short
- Byte
- Character
- char
- byte
- short
- int
循环结构
- for循环
for(初始化表达式①; 布尔表达式②; 步进表达式④){ 循环体③}
- while循环
初始化表达式① while(布尔表达式②){ 循环体③ 步进表达式④ }
- do...while循环
初始化表达式① do{ 循环体③ 步进表达式④ }while(布尔表达式②)
- for 和 while 的小区别:
- 控制条件语句所控制的那个变量,在for循环结束后,就不能再被访问到了,而while循环结束还可以继 续使用,如果你想继续使用,就用while,否则推荐使用for。原因是for循环结束,该变量就从内存中消 失,能够提高内存的使用效率。 在已知循环次数的时候使用推荐使用for,循环次数未知的时推荐使用while
跳出语句
- continue
- break
- 用在循环中
- 用在switch中
死循环
嵌套循环
数组
数组就是存储数据长度固定的容器,保证多个数据的数据类型要一致。
定义
int arr = new int[3];int arr = new int[]{1,2,3};
访问
int a = arr[2];int l = arr.length;
== 与 equals
== 运算符比较的是两个对象的地址equals默认实现也是比较地址,如果重写了equals,可以根据相应的逻辑来判断两个对象是否相等
hashCode 与 equals
- 如果两个对象相等,则 hashcode 一定也是相同的
- 两个对象相等,对两个对象分别调用 equals 方法都返回 true
- 两个对象有相同的 hashcode 值,它们也不一定是相等的
- 因此,**equals 方法被覆盖过,则 hashCode 方法也必须被覆盖**
- hashCode() 的默认行为是对堆上的对象产生独特值。如果没有重写 hashCode(),则该 class 的两个对象无论如何都不会相等(即使这两个对象指向相同的数据)
只要覆写 equals,就必须覆写 hashCode;因为 Set 存储的是不重复的对象,依据 hashCode 和 equals 进行判断,所以 Set 存储的对象必须覆写这两个方法;
如果自定义对象作为 Map 的键,那么必须覆写 hashCode 和 equals。说明:String 已覆写 hashCode 和 equals 方法,所以我们可以愉快地使用 String 对象作为 key 来使用。
有些数据结构比较两个元素相同时是先进行hashcode比较,然后才是equals
final关键字
- 修饰类:类无法继承
- 修饰变量:赋值之后无法修改
- 修饰方法:无法被子类重写