1、对“Java基础知识”中的一些扩展,有意思的文中和文外内容都会记录下来。
.java源代码文件 ——JDK中的javac粗编译——》.class字节码文件 ——JVM解释或编译——》二进制机器码
读取.class字节码文件,使用解释器逐行解释执行
JIT是JVM的一部分,使用编译的方式提高JVM执行代码的速度。 当某行代码或某个函数调用次数过多(已经执行很多次后),JVM会使用JIT将.class中的对应代码块进行编译,下次直接使用编译后的文件。
如果某次循环的时候JIT遍历的循环内的代码块,则下次执行时优先使用编译后的二进制文件,而不会进行逐行解释。 扩展链接:深入浅出 JIT 编译器及其优化策略 JIT优化策略概括来说就是:选择合适的编译模式(client/server)、提高编译后代码的存储空间、调整编译阈值、调整编译线程数
简单来说
栈堆访问速度快慢空间小大(理论上可以使用全部虚拟内存)对应变量(一般情况)没有new的变量new的变量例子简单类型、数组或对象的引用new的一个类对于 String数组 ,如果 不new 则数组实体指向 栈的常量池(指向的是个地址) ;new 的话数组实体在 堆 。 其他类型数组 无论是否new ,数组引用都在栈,数组实体都在堆。
在如下代码中,user是User类实例的一个引用,所以在栈里;user指向的User实例是new出来的,所以在堆里。
User user = new User();扩展链接[堆栈的详细区别]
boolean的大小:在数组中为了存储方便,一般会转换成byte类型;在单个变量中为了比较方便,一般会转换成int类型
常见的关键字(红色字体为容易忽略的内容):
方法: 表示实例方法和静态方法都可以使用 变量、类变量、引用变量: 都是变量,因为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 elseswitch(条件){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.userNamevoid方法说明返回值为空保留字goto、const不允许使用c/c++中的goto不在java中被允许;c/c++中的const在java中为final关键字默认类加载顺序: ——》父类static方法 ——》子类static方法 ——》父类参数初始化 ——》父类无参构造器初始化 ——》子类参数初始化 ——》子类无参构造器初始化
扩展:某些关键字的具体使用范围
关键字使用范围
(1)常用通配符:T、E、K、V、?
(2)泛型最大的作用是用来给编译器检查的,编译后(.class文件)中会将泛型替换为最接近的共同父类
(3).java文件中,子类重写父类的泛型方法。 .class文件中,子类重载了父类的方法,同时将父类方法指向子类的方法,使其最终的结果与重写相同。
(4)泛型不能是基本数据类型,泛型最大的父类就是Object,Object不能存储某个数,只能引用某个对象
(5)[扩展链接:Java泛型类型擦除以及类型擦除带来的问题]
注意: String类型不new的时候是从常量池从取值的,所以地址一样。即a==b为true
String a = "aaa"; String b = "aaa";
(1)equals和hashcode都是Object类的方法,所有的类都会带这两个方法
(2)对于复杂类的equals效率可能低,所以可以使用hashcode快速粗筛选 示例:HashSet检查重复就是先使用hashcode再使用equals
(3)hashcode()一般将int类型的散列码后返回,默认是对堆上的对象生成尽量不相同的特殊值,所以重写了equals()也要重写hashcode(),不然内容相同的对象hashcode不同,容易出bug。(比如HashSet里面添加了两个有相同数据的对象)
(4)[扩展链接:hashCode()和equals()的若干问题解答]
对于基本数据类型和对象,都是是传值,只不过对象传的是对象地址(可以看做是引用,本质上是传的地址值,还是传值)。 所以形参修改对象成员值,实参也会跟着改变,因为指向的都是同一个对象。但是形参改变对象引用不会影响实参所指的对象引用。
接口是特殊的抽象类,所以更加严格
接口抽象类继承impelement 多继承entends 单继承限定符public、protected、defaultpublic、protected、default变量类型static final普通变量方法声明抽象方法、默认方法(JDK8)、静态方法(JDK8)、私有方法(JDK9)、私有静态方法(JDK9)抽象方法、非抽象方法方法实现不允许允许实现约束要实现所有方法不用实现所有方法
String类的源码中,使用private final char value[]来存储数据
JDK9中使用private final byte value[]来存储数据 StringBuffer和StringBuilder都继承自AbstractStringBuilder
Java性能差的主要原因不是因为Java是面向对象的语言,而是因为Java是半编译语言,编译出来的.class文件不是二进制文件,而是要继续编译或解释。
子类拥有父类所有的属性和方法(包含私有的内容),但是根据访问控制前缀来判断能否访问。
父类的引用指向子类的实例才叫多态,如List<Integer> list = new ArrayList<Integer>(); (此时的引用不能调用子类独有的方法,有重写方法会调用重写方法,类型转换回子类后可以调用子类独有的方法)
要用的时候再查,直接看也记不住。 [扩展链接:Collections工具类和Arrays工具类常见方法]
Errors类是 程序无法处理的异常 ,碰到Error的时候虚拟机一般会直接终止线程。
Exceptions类是 程序可以处理的异常
在Exceptions类下的Check Exceptions是编译的时候检查的异常,Uncheck Exceptions是运行时的异常
注意: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(); }
程序:在数据存储设备中的静态代码
进程:程序被操作系统载入到内存中运行
线程:共享进程资源的cpu最小执行单位 线程状态:[图片来源]
流分类:
流的流向操作单元流的角色划分输入流字节流节点流输出流字符流处理流4个抽象基类:
InputStream:字节输入流 OutputStream:字节输出流 Reader:字符输入流 Writer:字符输出流
传输字符的时候为了防止乱码或转换问题,使用字符流;其他时候使用字节流。
操作对象字节输入流字节输出流字符输入流字符输出流文件FileInputStreamFileOutputStreamFileReaderFileWriter基本数据类型DataInputStreamDataOutputStream数组ByteArrayInputStreamByteArrayOutputStreamCharArrayReaderCharArrayWriter对象序列化操作ObjectInputStreamObjectOutputStream缓冲控制BufferedInputStreamBufferedOutputStreamBufferedReaderBufferedWriter字节流转字符流InputStreamReaderOutputStreamWriter打印控制printStreamprintWriter管道操作(不推荐在同一个线程中使用,容易死锁)PipedInputStreamPipedOutputStreamPipedReaderPipedWriter