Java基础知识及其扩展笔记
- 零 l 写在前面
- 一 l JVM
- 二 l Java 基础
- 1、【1.1.4】Java的类不能多继承,接口可以
- 2、【1.1.4】Java的字符串有length()方法得到长度,所以不用在最后加上'\0'结束符
- 3、【1.1.7】java的包和javax的包没有差别
- 4、【1.2.1】变量空间
- 5、【1.2.4】关键字是特殊的标识符,标识符是变量、方法等的名字
- 6、【1.2.7】泛型与类型擦除
- 7、【1.2.8】==与equals
- 8、【1.2.9】equals与hashcode
- 9、【1.4.2】形参是传实参的值还是实参的引用
- 10、【1.4.4】浅拷贝、深拷贝
- 11、【2.4】接口、抽象类细化比较
- 12、【2.5.1】String、StringBuffer、StringBuilder分析
- 13、【2.5.2】Object 类的常见方法
- 三 l Java 面向对象
- 四 l Java 核心技术
学习教程: [Java基础知识]
零 l 写在前面
1、对“Java基础知识”中的一些扩展,有意思的文中和文外内容都会记录下来。
一 l JVM
1、【1.1.2.1】java程序运行的一般流程
.java源代码文件 ——JDK中的javac粗编译——》.class字节码文件 ——JVM解释或编译——》二进制机器码
2、【1.1.2.1】JVM一般运行流程
读取.class字节码文件,使用解释器逐行解释执行
3、【1.1.2.1】JIT(just in time 即时编译编译器)
JIT是JVM的一部分,使用编译的方式提高JVM执行代码的速度。
当某行代码或某个函数调用次数过多(已经执行很多次后),JVM会使用JIT将.class中的对应代码块进行编译,下次直接使用编译后的文件。
如果某次循环的时候JIT遍历的循环内的代码块,则下次执行时优先使用编译后的二进制文件,而不会进行逐行解释。
扩展链接:深入浅出 JIT 编译器及其优化策略
JIT优化策略概括来说就是:选择合适的编译模式(client/server)、提高编译后代码的存储空间、调整编译阈值、调整编译线程数
4、堆与栈
简单来说
栈 | 堆 | |
---|---|---|
访问速度 | 快 | 慢 |
空间 | 小 | 大(理论上可以使用全部虚拟内存) |
对应变量(一般情况) | 没有new的变量 | new的变量 |
例子 | 简单类型、数组或对象的引用 | new的一个类、String常量池 |
对于 String数组 ,如果 不new 则数组实体指向 栈的常量池(指向的是个地址,但是这个常量池地址在堆) ;new 的话数组实体在 堆 。
其他类型数组 无论是否new ,数组引用都在栈,数组实体都在堆。
在如下代码中,user是User类实例的一个引用,所以在栈里;user指向的User实例是new出来的,所以在堆里。
User user = new User();
扩展链接[堆栈的详细区别]
二 l Java 基础
1、【1.1.4】Java的类不能多继承,接口可以
2、【1.1.4】Java的字符串有length()方法得到长度,所以不用在最后加上’\0’结束符
3、【1.1.7】java的包和javax的包没有差别
4、【1.2.1】变量空间
基本类型 | 含义 | 大小 | 默认值 | 最小值 | 最大值 | 包装类型 | 包装类型常量池大小 |
---|---|---|---|---|---|---|---|
boolean | 布尔类型 | 数组中1byte / 单个变量4byte | false | false | true | Boolean | true/false |
char | 单个字符 | 2byte / 16bits | ‘u0000’ | 0 | 216-1 | Character | [0, 127] |
byte | 字节整形 | 1byte / 8bits | 0 | -128 | 127 | Byte | [-128, 127] |
short | 短整形 | 2byte / 16bits | 0 | -215 / -32768 | 215-1 / 32767 | Short | [-128, 127] |
int | 整形 | 4byte / 32bits | 0 | -231 / 约-2.1*109 | 231-1 / 约2.1*109 | Integer | [-128, 127] |
long | 长整形 | 8byte / 64bits | 0L | -263 / 约-9.2*1018 | 263-1 / 约9.2*1018 | Long | [-128, 127] |
float | 单精度浮点数 | 4byte / 32bits | 0f | IEEE754 | IEEE754 | Float | 无 |
double | 双精度浮点数 | 8byte / 64bits | 0d | IEEE754 | IEEE754 | Double | 无 |
void | 空 | Void |
boolean的大小:在数组中为了存储方便,一般会转换成byte类型;在单个变量中为了比较方便,一般会转换成int类型
5、【1.2.4】关键字是特殊的标识符,标识符是变量、方法等的名字
常见的关键字(红色字体为容易忽略的内容):
方法: 表示实例方法和静态方法都可以使用
变量、类变量、引用变量: 都是变量,因为java中都是对象,所以变量都是类变量;变量的实质就是引用,所以又可以说是引用变量
成员变量: 定义在类内函数外的变量,默认自动赋默认值(final必须显式赋值)
局部变量: 定义在函数内的变量,不会自动赋值
关键字类型 | 关键字 | 约束范围 | 解释 |
---|---|---|---|
访问控制 | private | 成员变量 / 方法 / 类 | 私有,不允许用在局部变量【允许访问修改范围:同一类内】 |
default | 默认的访问控制权限【允许访问修改范围:同一包内】 | ||
protected | 成员变量 / 方法 / 类 | 保护,不允许用在局部变量【允许访问修改范围:同一包内、不同包的子类】 | |
public | 成员变量 / 方法 / 类 / 接口 | 公开,不允许用在局部变量【允许访问修改范围:所有地方】 | |
类、 方法、 变量修饰符 | class | 类 | 说明正在定义一个类 |
abstract | 类 | 限定这个类或方法是抽象的 [扩展链接:abstract详解] | |
extends | 子类 | 继承一个父类,只能继承一个 | |
interface | 接口 | 说明正在定义一个接口 接口方法默认都是public abstract 接口变量默认都是public static final [扩展链接:abstract与interface的区别] | |
implements | 类 | 说明此类实现了什么接口 | |
final | 变量 / 方法 / 类 | 引用为基本数据类型,该引用为常量无法修改。 引用为引用数据类型,引用指向地址不能更改,地址里的内容可以修改。 修饰方法时,方法可以被继承,不能被重写。 修饰类时,无法被继承。 | |
【不常用】native | 方法 | JNI(Java Native Interface Java本地接口) 与c/c++联合开发时才会使用 | |
new | 变量 | 创建一个已定义的类实例 [扩展链接:new是如何实现的] | |
static | 成员变量 / 静态方法 | 不允许用在局部变量,方便在没有创建对象的情况下来进行调用(方法/变量) static不允许用来修饰局部变量 [扩展链接:Java中的static关键字解析] | |
【不常用】strictfp | 类 / 接口 | (strict float point 精确浮点),严格使用IEEE754定义进行计算 | |
【常考】transient | 变量 | 标识变量不会输入到流中(不能串行化),如网络流、文件流 | |
synchronized | 方法 / 代码块 | 在并行中同步方法或代码块(同一时间只能有一个线程调用) [扩展链接:synchronized实现原理] | |
【常考】volatile | 保证可见性和一定的有序性,不能保证原子性 在JMM(Java内存模型中),如果一个线程先去写一个变量,然后一个线程去进行读取,那么写入操作肯定会先行发生于读操作 [扩展链接:volatile关键字解析] | ||
程序控制 | break | 方法 | 跳出最内层循环 |
continue | 方法 | 省略循环体内的后续步骤,进入下一次循环 | |
return | 方法 | 直接结束方法 | |
if、else | 方法 | 判断语句 if {} else if {} else {} | |
switch、case、default | 方法 | 跳转语句,常用来优化多重if else switch(条件){case ...: break; case default: break;} switch条件可以为byte、short、int、char、enum、String(JDK7) | |
do、while | 方法 | 循环语句,do{干活}while(条件) 或 while(条件){干活} | |
for | 方法 | 循环语句,for( 条件(略) ) {干活} | |
instanceof | 方法 | A instanceof B 判断A是否是B的类实例、子类、接口实现类(A能否转换为B) 是则返回true,否则返回false (大白话就是A是不是跟B有关,且B比A的“大”) | |
错误处理 | try、 catch、 finally | 方法 | 错误处理, try {干活} catch(Exception e) {处理异常} finally {无论是否异常都会执行的代码} |
throw | 方法体 | 程序员主动抛出的异常,示例:throw new NumberFormatException(); | |
throws | 方法头 | 定义方法时预先声明可能会抛出的异常 | |
包相关 | package | .java文件开头 | 声明当前包路径 |
import | .java文件开头 | 导入依赖包 | |
基本类型 | 略 | 略 | boolean、byte、char、short、int、long、float、double、 null、true、false |
变量引用 | super | 类内 | 使用父类的成员属性或者成员方法 (能否使用以属性或方法的访问控制关键字为准) |
this | 类内 | 使用当前实例的属性,示例:this.userName | |
void | 方法 | 说明返回值为空 | |
保留字 | goto、const | 不允许使用 | c/c++中的goto不在java中被允许; c/c++中的const在java中为final关键字 |
默认类加载顺序:
——》父类static方法
——》子类static方法
——》父类参数初始化
——》父类无参构造器初始化
——》子类参数初始化
——》子类无参构造器初始化
扩展:某些关键字的具体使用范围
关键字 | 使用范围 |
---|
6、【1.2.7】泛型与类型擦除
(1)常用通配符:T、E、K、V、?
(2)泛型最大的作用是用来给编译器检查的,编译后(.class文件)中会将泛型替换为最接近的共同父类
(3).java文件中,子类重写父类的泛型方法。
.class文件中,子类重载了父类的方法,同时将父类方法指向子类的方法,使其最终的结果与重写相同。
(4)泛型不能是基本数据类型,泛型最大的父类就是Object,Object不能存储某个数,只能引用某个对象
(5)[扩展链接:Java泛型类型擦除以及类型擦除带来的问题]
7、【1.2.8】==与equals
== | equals | |
---|---|---|
基本数据类型 | 比较值 | 不能比较 |
引用数据类型 | 比较地址 | 没有重写equals方法:比较地址 重写equals方法:根据方法比较 |
注意:
String类型不new的时候是从常量池从取值的,所以地址一样。即a==b为trueString a = "aaa"; String b = "aaa";
8、【1.2.9】equals与hashcode
(1)equals和hashcode都是Object类的方法,所有的类都会带这两个方法
(2)对于复杂类的equals效率可能低,所以可以使用hashcode快速粗筛选
示例:HashSet检查重复就是先使用hashcode再使用equals
(3)hashcode()一般将int类型的散列码后返回,默认是对堆上的对象生成尽量不相同的特殊值,所以重写了equals()也要重写hashcode(),不然内容相同的对象hashcode不同,容易出bug。(比如HashSet里面添加了两个有相同数据的对象)
(4)[扩展链接:hashCode()和equals()的若干问题解答]
9、【1.4.2】形参是传实参的值还是实参的引用
对于基本数据类型和对象,都是是传值,只不过对象传的是对象地址(可以看做是引用,本质上是传的地址值,还是传值)。
所以形参修改对象成员值,实参也会跟着改变,因为指向的都是同一个对象。但是形参改变对象引用不会影响实参所指的对象引用。
10、【1.4.4】浅拷贝、深拷贝
浅拷贝 | 深拷贝 | |
---|---|---|
基本数据类型 | 传递值 | 传递值 |
引用数据类型 | 传递引用 | new一个新对象并拷贝所有内容 |
11、【2.4】接口、抽象类细化比较
接口是特殊的抽象类,所以更加严格
接口 | 抽象类 | |
---|---|---|
继承 | impelement 多继承 | entends 单继承 |
限定符 | public、protected、default | public、protected、default |
变量类型 | static final | 普通变量 |
方法声明 | 抽象方法、 默认方法(JDK8)、静态方法(JDK8)、 私有方法(JDK9)、私有静态方法(JDK9) | 抽象方法、非抽象方法 |
方法实现 | 不允许 | 允许 |
实现约束 | 要实现所有方法 | 不用实现所有方法 |
12、【2.5.1】String、StringBuffer、StringBuilder分析
String | StringBuffer | StringBuilder | |
---|---|---|---|
数据可变性 | 不可变 | 可变 | 可变 |
线程安全性 | 安全 | 安全 | 不安全 |
速度 | 慢,要new个String对象 | 中 | 快,比 StringBuffer 快10~15% |
使用场景 | 少量数据 | 多线程大量数据 | 单线程大量数据 |
String类的源码中,使用private final char value[]来存储数据
JDK9中使用private final byte value[]来存储数据
StringBuffer和StringBuilder都继承自AbstractStringBuilder
13、【2.5.2】Object 类的常见方法
方法类型 | 具体定义 | 解释 |
---|---|---|
常用方法(可重写) | public native int hashCode() | 返回当前对象的hash值 |
public boolean equals(Object obj) | 默认是比较两个对象的地址是否相同,String类重写了此方法用来比较字符串 | |
public String toString() | 默认是返回类的名字@实例的哈希码的16进制字符串(默认返回地址) System.out输出对象时默认调用类的toString方法 | |
protected native Object clone() throws CloneNotSupportedException | 返回当前对象的深拷贝 | |
常用方法(不可重写) | public final native Class<?> getClass() | 返回当前对象的class对象 |
等待方法(不可重写) | public final void wait() throws InterruptedException | 暂停线程执行并释放所占用的锁(sleep不会释放占用的锁),会一直等待直到唤醒 |
public final native void wait(long timeout) throws InterruptedException | 功能同上(timeout是等待时间) | |
public final void wait(long timeout, int nanos) throws InterruptedException | 功能同上(nanos表示更精确的等待时间(纳秒),范围0~999999) | |
唤醒方法(不可重写) | public final native void notify() | 唤醒 一个 在此对象监视器(锁)上等待的线程 |
public final native void notifyAll() | 唤醒 所有 在此对象监视器(锁)上等待的线程 | |
销毁方法(可重写) | protected void finalize() throws Throwable { } | 实例被垃圾回收器回收时自动调用 |
三 l Java 面向对象
Java性能差的主要原因不是因为Java是面向对象的语言,而是因为Java是半编译语言,编译出来的.class文件不是二进制文件,而是要继续编译或解释。
1、【2.2.2】继承tips
子类拥有父类所有的属性和方法(包含私有的内容),但是根据访问控制前缀来判断能否访问。
2、【2.2.3】多态tips
父类的引用指向子类的实例才叫多态,如List<Integer> list = new ArrayList<Integer>();
(此时的引用不能调用子类独有的方法,有重写方法会调用重写方法,类型转换回子类后可以调用子类独有的方法)
四 l Java 核心技术
1、【3.1.1】Collections 工具类和 Arrays 工具类常见方法
要用的时候再查,直接看也记不住。
[扩展链接:Collections工具类和Arrays工具类常见方法]
2、【3.2.1】Java异常类层次结构图
Errors类是 程序无法处理的异常 ,碰到Error的时候虚拟机一般会直接终止线程。
Exceptions类是 程序可以处理的异常
在Exceptions类下的Check Exceptions是编译的时候检查的异常,Uncheck Exceptions是运行时的异常
3、【3.2.3】try-catch-finally
注意:finally是在函数返回之前做的,所以会覆盖try或catch里面的return
建议:使用try-with-resources替代try-catch-finally
try-catch-finally写法:
//读取⽂本⽂件的内容
Scanner scanner = null;
try {
scanner = new Scanner(new File("D://read.txt"));
while (scanner.hasNext()) { System.out.println(scanner.nextLine()); }
} catch (FileNotFoundException e) {
e.printStackTrace();
} finally {
if (scanner != null) {
scanner.close();
}
}
try-with-resources写法 (需要类实现AutoCloseable接口) :
try (Scanner scanner = new Scanner(new File("test.txt"))) {
while (scanner.hasNext()) { System.out.println(scanner.nextLine()); }
} catch (FileNotFoundException e) {
e.printStackTrace();
}
4、【3.3.1】程序、进程、线程
程序:在数据存储设备中的静态代码
进程:程序被操作系统载入到内存中运行
线程:共享进程资源的cpu最小执行单位
线程状态:[图片来源]
5、【3.4】文件与I\O流
流分类:
流的流向 | 操作单元 | 流的角色划分 |
---|---|---|
输入流 | 字节流 | 节点流 |
输出流 | 字符流 | 处理流 |
4个抽象基类:
InputStream:字节输入流
OutputStream:字节输出流
Reader:字符输入流
Writer:字符输出流
传输字符的时候为了防止乱码或转换问题,使用字符流;其他时候使用字节流。
操作对象 | 字节输入流 | 字节输出流 | 字符输入流 | 字符输出流 |
---|---|---|---|---|
文件 | FileInputStream | FileOutputStream | FileReader | FileWriter |
基本数据类型 | DataInputStream | DataOutputStream | ||
数组 | ByteArrayInputStream | ByteArrayOutputStream | CharArrayReader | CharArrayWriter |
对象序列化操作 | ObjectInputStream | ObjectOutputStream | ||
缓冲控制 | BufferedInputStream | BufferedOutputStream | BufferedReader | BufferedWriter |
字节流转字符流 | InputStreamReader | OutputStreamWriter | ||
打印控制 | printStream | printWriter | ||
管道操作 (不推荐在同一个线程中使用,容易死锁) | PipedInputStream | PipedOutputStream | PipedReader | PipedWriter |