最近带应届新员工,教然后知不足,发现自己把很多基础知识已经还给了大学老师,因此开贴,温故而知新!
从最基础的Java知识开始由浅入深,在某个知识点中遇到有疑惑的点会额外多写几句或者单独开帖子展开。
Java特性
面相对象(封装、继承、多态),易维护、易扩展、易复用。
跨平台,JVM实现一次编译随处运行。
支持多线程,C++依赖调用操作系统的多线程功能,而java内置多线程。
支持网络编程,Java诞生时就为了简化网络编程而设计,内置方便的网络编程接口。
编译与解释并存。
什么是面向对象与面向过程
面向过程把解决问题的方法拆分成一个个函数,通过依次执行函数来实现功能
面向先将现实事物抽象成包含属性和方法的对象,通过对象调用方法来实现
面向过程性能高、面向对象易维护且更灵活,
因为类调用需要将对象的实例化,消耗性能。
Java和C++异同
都是面向对象预言,支持封装、继承、多态
Java不提供指针来访问内存,内存安全
C++可以直接多继承,Java多继承需要通过接口实现
Java自动管理内存,C++需要手动回收内存
C++可以重载方法和操作符,而Java只能重载方法
JVM JDK JRE三者分别如何
JVM是运行字节码文件的虚拟机,可以将字节码翻译成机器码让CPU执行。JVM并不只有一种,只要满足JVM开发规范,任何人都可以开发自己的Java虚拟机。常见的HotSpot JVM只是其中一种实现,还有J9、Zing等等,他们有不同的编译-解释策略。
JDK是开发java代码必备的基本工具包,其中包含了JRE,还有JavaC编译器、JavaDoc等工具。
JRE是Java运行时环境,包含JVM、Java类库等基础构建,但不能满足开发Java程序。
什么是字节码
是JVM可以理解的代码,即 .class 文件,不面向任何特定的处理器CPU,只面向JVM虚拟机。
由.java文件通过JavaC工具生成,然后通过JVM中的编译器和解释器(例如JIT)转成CPU可以识别的机器码。
什么是JIT
是JVM中的just-in-time 编译器和解释器,正常JVM解释的过程是在运行代码时出现,加载class文件然后由解释器逐行解释再交给CPU运行,编译和执行同时进行,效率较低。JIT是的策略是通过执行次数分析出热点代码,在JIT完成第一次编译出字节码后同时将其机器码保存下来,下次可以直接执行。这也是为什么说Java是编译与解释共存的预言。
HotSpot采用惰性评估方法,根据二八定律,消耗系统大部分资源的只有一小部分代码,这部分代码就是JIT需要编译的,随着程序执行次数增加不断优化,因此执行越多速度越快。
除此之外还有AOT编译策略,JAVA9引入,提前编译模式,提前将所有字节码编译成机器码,节省JIT预热的开销。JDK支持分层编译和AOT协作使用。
为什么不全用AOT
因为JAVA的动态特性,存在动态代理技术,动态代理基于运行时在内存中加载class文件,若全部提前解释成机器码,则无法支持动态代理。
基本数据类型
byte-1字节
boolean-1字节
char-2字节
short-2字节
int-4字节
float-4字节
long-8字节
double-8字节
void
基本类型和包装类型
基本类型有默认值0不是null,包装类型不赋值就是null
基本类型占用空间小,因为包装类型还有成员变量和方法,比如length
Byte Short Integer Long包装类型有缓存机制127以下不用创建新对象,从堆内存直接取
成员变量和局部变量
成员变量属于类或者对象实例,存储在堆内存中,与对象的生命周期一致
局部变量存在与方法或者代码块内部,存储在栈内存中,随着方法调用生成调用完结束
静态代码不能调用非静态成员
静态代码在非静态代码存在之前就已经存在了,因为在JVM加载类的时候就已经为其分配内存,可以直接通过类名访问,而非静态代码需要在类实例化之后才能通过对象名调用
重载和重写
重载发生在同一个类中,相同的方法名和不同的出入参数
重写发生在子类继承父类的时候,方法名、出入参数一致,内部逻辑不同
对象的值相等和引用相等
值相等比较多是内存中存放的内容是否相等,equals
引用相等是比较内存地址是否相同,是同一个对象,全等符==
若类没有重写equlas方法则默认还是==比较地址
重写equals方法也要重写hashcode方法因为对象相等则hashcode也相同,若不重写hashcode则可能出现equals相同但是hashcode不同的情况
深拷贝和浅拷贝
浅拷贝会在堆内存中生成一个拷贝对象,但若靠背对象的成员变量是引用类型的,则引用会指向同一个对象
深拷贝则完全复制这个对象包括其内部成员变量对象
都可以通过重写clone方法实现,只是深拷贝要在复制对象的基础上连着内部对象一起复制
Java常见类-Object
是所有类的父类,主要方法:
Getclass / hashcode / equals / clone / toString
线程相关方法:wait / notify / notifyAll
GC相关方法:finalize
Java常见类-String
String是不可变的,若修改String实际上是声明一个新对象并赋值。因为String内部的字符数字被private final修饰
StringBuilder和StringBuffer都是继承AbstractStringBuilder,内部通过可变字符数组来维护字符串,并提供了修改字符串的方法,区别在于StringBuffer对方法加了同步锁,是线程安全的,在低频操作时性能差异不大,高频操作时性能比StringBuilder差
使用+运算符拼接字符串时 实际上内部还是声明StringBuilder来append,然后toString,但是拼接越多就会创建多个StringBuilder对象,而不是复用单个对象,浪费
字符串常量池
是 JVM 为了提升性能和减少内存消耗针对字符串(String 类)专门开辟的一块区域,主要目的是为了避免字符串的重复创建。存放在堆内存中,声明的字符串若重复声明则指向同一个内存地址,长时间不用则回收
异常Throwable
所有异常都有一个祖先类Throwable,下面分Exception和error,Exception是程序可以处理的异常,有CheckedException受检查异常必须处理和UnCheckedException不受检查异常可以不捕获直接抛出。Error是程序无法处理的错误,通常不能通过捕获处理,JVM遇到error时通常会终止线程,例如内存溢出、类定义错误。
Try-catch-finally的使用
Try-catch,try执行代码,catch用于捕获异常,可以有多个catch若无catch块则必须有finally,正常情况下finally一定会被执行,除非在执行到finally之前虚拟机被终止运行比如出现error导致线程终止。正常情况下Finally中的方法会在try-catch中的return执行之前被执行,不建议在finally中写return,因为try-catch中的return的值会暂存在一个本地临时变量中等待finally执行完,若finally中有return则会覆盖该临时变量并返回
内存泄漏、内存溢出
内存泄漏是JVM中已被占用的堆内存无法被释放,严格来说是Java对象不再被引用但是GC迟迟没有回收他们,只要堆内存空间足够大,少量的内存泄漏无伤大雅,但是内存泄漏发展到一定程度就会导致内存溢出
内存溢出是指JVM辣鸡回收释放内存的速度跟不上内存消耗的速度,原因可能是堆内存空间过小或者程序中集中生成了巨量对象
什么是泛型,有哪些使用方式
泛型是JDK5引入的特性,用于指定传入参数的类型,增强代码可读性和稳定性
例如ArrayList<Persion> persons = new ArrayList<Persion>()
就指明了该 ArrayList
对象只能传入 Persion
对象,如果传入其他类型的对象就会报错。
有泛型类、泛型接口、泛型方法
泛型定义时可以不指定具体类型,但是在实例化或者调用时必须指定
什么是反射
是java的一种特性,一些接口,通过反射可以获取任一个java类都名称、属性、方法
反射被大量应用于各种框架,比如spring的@configuration、@Value注解,都是基于反射
什么是序列化
如果想将java对象写入文件,或者在网络上传输java数据,就需要序列化和反序列化,将java对象转换成二进制字节流或者反向转换回java对象
如果对象中有不想进行序列化的变量属性,则可以用transient修饰,但是不能修饰方法,另外static变量由于不属于任何对象,也不会参与序列化
什么是I/O
I/O是input/output,代表外部数据与计算机之间的输入输出在Java中称作I/O流
InputStream/reader 输入流的基类,前者是字节输入流,后者是字符输入流
OutputStream/writer 输出流的基类,前者是字节输出流,后者是字符输出流
信息的最小存储单元是字节,为什么java中有字节流和字符流呢
字符流是JVM通过字节流转换而来的,比较消耗性能
但若使用者不知道数据来源的编码格式,使用字节流容易出现乱码