Java语言基础
程序就是能完成某一项功能的语句
Java运行基础
JVM | 虚拟机 | 屏蔽不同操作系统之间的差异 |
---|---|---|
JRE | 运行环境 | 为Java提供基本的运行环境 |
JDK | Java的开发工具包 |
Java的代码都要写在一个类中,一个类可以有一个主函数作为程序的入口
public class与class之间的区别
public class是公开类,类名必须与源文件名一致,在同一个程序中只能有一个公开类
class 是公共类,在同一个程序中可以存在一个或多个
Java的语言特点
面向对象,简单,跨平台
计算机的运行机制
编译执行,解释执行,先编译再解释
编码规范
注释:单行注释,多行注释,javadoc
什么是字节码?好处是什么?
字节码:Java源代码经过虚拟机编译器编译后产生的文件(.class文件),他不面向任何特定的处理器,只面向虚拟机
基础语法
Java语言是强类型语言,对于每一种数据都定义了明确的具体的数据类型,在内存中分配了不同大小的内存空间。
基本数据类型
整数
类型 | 字节 | 取值范围 |
---|---|---|
byte | 1 | -128~127 |
short | 2 | -32768~32767 |
int | 4 | -2147483648~2147483647 |
long | 8 | int为整数默认的数据类型,如需为long类型赋值需要在值的后面加“L” |
小数
类型 | 字节 |
---|---|
float | 4 |
double | 8 |
字符:char 2字节 万国码 A 65 0 48 a 97
布尔: Boolean 1 取值 false/true 仅可描述真或者假
引用数据类型
字符串 string
数组
对象
容器(集合)
在这里插入图片描述
collection与collections的区别?
- java.util.Collection 是一个集合接口(集合类的一个顶级接口)。它提供了对集合对象进行基本操作的通用接口方法。Collection接口在Java 类库中有很多具体的实现。Collection接口的意义是为各种具体的集合提供了最大化的统一操作方式,其直接继承接口有List与Set。
- Collections则是集合类的一个工具类/帮助类,其中提供了一系列静态方法,用于对集合中元素进行排序、搜索以及线程安全等各种操作。
list
特点:有序,元素可重复
实现类
ArrayList :底层数组
Vector:底层数组,线程安全
LinkdeList:底层链表
set
特点:无序,不可重复
实现类
hashset:不保证元素顺序恒久不变,依靠hashcode和eques
treeset:排序,不重复底层依靠红黑自平衡二叉树
linkedset:有序,不重复
map
jdk8.0中hashmap采用的是数组加链表加红黑树的结构,当链表节点数大于等于8且数组长度不小于64时转换为红黑树结构存储,如果转为红黑树存储后检测到节点数小于6时则解散红黑树转为链表结构
put方法实现的原理如下:
(1) 调用 hash方法 获取 key 对应 的 哈希码值
(2) 如果 table 为空,则先新建一个table数组
(3) 如果根据key的哈希码值和table数组长度获取的存储下标位置上没有任何
数据,则直接存储即可
(4) 如果需要存储的table对应下标位置上已有元素,则需要判断是否有冲突key:
a. 先判断数组对应位置上,第一个节点是否存在冲突,冲突直接获取此节点
b. 再判断是否采用的红黑树存储,是-则利用红黑树中的方法存储键值对
c. 采用链式存储,则需要遍历的判断链表中是否有冲突的 key
I. 如果到最后一个节点,还没有发现有冲突的key,则新建一个节点插入到尾部
同时在这个步骤中判断链表是否需要转换为红黑树进行存储,依据为链表长度
是否大于等于8
II. 如果循环过程中,发现有冲突的key,则获取该节点,同时结束循环
d. 如果存在有冲突的 key,则新值替换旧值
get方法实现原理如下:
(1) 如果 table不为空,或是对应key对应的存储下标元素不为null
a. 先判断第一个节点 是否和要查找的key映射的节点,相同则直接返回此节点
b. 如果第一个节点 不是要查找的key映射的节点,则需要判断后面的节点
I. 如果为红黑树形式存储,则需要利用 红黑树进行操作
II. 如果为传统链表形式存储,则遍历所有节点,找到则返回对应的Node
(2) 如果table为空,或是key没有映射的结果,则返回null
特点:存储键值对,键不可重复,值可重复
实现类
hashmap,treemap,hashtable
覆盖hashCode方法的原则:
a. 必须保证内容相同的元素返回相同的哈希码值
b. 为了提高效率,尽可能做到内容不同的元素返回不同的哈希码值
hashmap与hashtable的区别?
hashmap可以允许null作为键或者值,线程不安全,效率高
hashtable不允许null作为键或值,线程安全,效率低。
异常
异常分类
error | exception | |
---|---|---|
继承 | Throw able | Throw able |
子类 | runtime Exception和非runtime Exception |
throw和throws的区别?
throws是用来声明一个方法可能抛出的所有异常信息,throws是将异常声明但是不处理,而是将异常往上传,谁调用我就交给谁处理。而throw则是指抛出的一个具体的异常类型。
final、finally、finalize 有什么区别?
- final可以修饰类、变量、方法,修饰类表示该类不能被继承、修饰方法表示该方法不能被重写、修饰变量表示该变量是一个常量不能被重新赋值。
- finally一般作用在try-catch代码块中,在处理异常的时候,通常我们将一定要执行的代码方法finally代码块中,表示不管是否出现异常,该代码块都会执行,一般用来存放一些关闭资源的代码。
- finalize是一个方法,属于Object类的一个方法,而Object类是所有类的父类,该方法一般由垃圾回收器来调用,当我们调用System的gc()方法的时候,由垃圾回收器调用finalize(),回收垃圾。
常见的异常类有哪些?
-
NullPointerException:当应用程序试图访问空对象时,则抛出该异常。
-
SQLException:提供关于数据库访问错误或其他错误信息的异常。
-
IndexOutOfBoundsException:指示某排序索引(例如对数组、字符串或向量的排序)超出范围时抛出。
-
NumberFormatException:当应用程序试图将字符串转换成一种数值类型,但该字符串不能转换为适当格式时,抛出该异常。
-
FileNotFoundException:当试图打开指定路径名表示的文件失败时,抛出此异常。
-
IOException:当发生某种I/O异常时,抛出此异常。此类是失败或中断的I/O操作生成的异常的通用类。
-
ClassCastException:当试图将对象强制转换为不是实例的子类时,抛出该异常。
-
ArrayStoreException:试图将错误类型的对象存储到一个对象数组时抛出的异常。
-
IllegalArgumentException:抛出的异常表明向方法传递了一个不合法或不正确的参数。
-
ArithmeticException:当出现异常的运算条件时,抛出此异常。例如,一个整数“除以零”时,抛出此类的一个实例。
-
NegativeArraySizeException:如果应用程序试图创建大小为负的数组,则抛出该异常。
-
NoSuchMethodException:无法找到某一特定方法时,抛出该异常。
-
SecurityException:由安全管理器抛出的异常,指示存在安全侵犯。
-
UnsupportedOperationException:当不支持请求的操作时,抛出该异常。
-
RuntimeExceptionRuntimeException:是那些可能在Java虚拟机正常运行期间抛出的异常的超类。
try-catch-finally 中哪个部分可以省略?
答:catch 可以省略
原因:
更为严格的说法其实是:try只适合处理运行时异常,try+catch适合处理运行时异常+普通异常。也就是说,如果你只用try去处理普通异常却不加以catch处理,编译是通不过的,因为编译器硬性规定,普通异常如果选择捕获,则必须用catch显示声明以便进一步处理。而运行时异常在编译时没有如此规定,所以catch可以省略,你加上catch编译器也觉得无可厚非。
理论上,编译器看任何代码都不顺眼,都觉得可能有潜在的问题,所以你即使对所有代码加上try,代码在运行期时也只不过是在正常运行的基础上加一层皮。但是你一旦对一段代码加上try,就等于显示地承诺编译器,对这段代码可能抛出的异常进行捕获而非向上抛出处理。如果是普通异常,编译器要求必须用catch捕获以便进一步处理;如果运行时异常,捕获然后丢弃并且+finally扫尾处理,或者加上catch捕获以便进一步处理。
至于加上finally,则是在不管有没捕获异常,都要进行的“扫尾”处理。
运行时异常和编译时异常的区别?
运行时异常可以处理也可以不处理
编译时异常必须处理,否则编译不通过程序运行报错
try-catch-finally 中,如果 catch 中 return 了,finally 还会执行吗?
答:会执行,在 return 前执行。
异常注意事项
子类覆盖父类方法,父类方法如果抛出异常,子类只能抛出一样的或者不抛异常(父类坏了子类不能更坏)父类如果没有抛出异常,子类方法不能抛出异常,如果子类方法中出现了异常只能try…catch…解决
多线程
线程和进程的区别?
进程是程序运行和资源分配的基本单位,一个程序最少有一个进程,一个进程至少有一个线程,是cpu调度和分派的基本单元。同一个进程的多个线程可以并发执行。
并行和并发的区别?
并行是指两个或多个事件在同一时刻发生,而并发是指两个或多个事件在同一时间间隔发生。
什么是死锁?
死锁是指两个或者两个以上的进程在执行过程中由于竞争资源或者彼此通信而造成的一种阻塞的现象,若无外力作用,它们都将无法推进。
线程的创建方式?
继承Thread类
实现Runnable接口
使用线程池,Callable接口,Future实现具有返回值的多线程。
线程池
线程的状态都有哪些?
初始状态:线程对象被创建。
就绪状态:调用start()之后,进入就绪状态,等待OS选中,并分配时间片。
运行状态:获得时间片之后进入运行状态,如果时间片到期则回到就绪状态。
终止状态:主线程main()结束或者独立线程run()结束,进入终止状态,并释放所持有的时间片。
阻塞状态:调用sleep()休眠若干毫秒,join()无限期的等待,yield()放弃cpu时间片,回到就绪状态。
当线程共同访问同一个对象(临界资源)如果破坏了不可分割的操作(原子性操作),就可能发生数据不一致。
反射
通过类对象能够使用该类中的任意一个属性和方法,构造方法
获取类对象的方式
1.class.forname(“类的全路径”)
2.数据类型.Class
3.Object中的一个方法getClass()
锁
JVM
什么是JVM?
JVM java虚拟机,是JRE(java运行环境)的一部分,java最重要的特点就是跨平台运行,使用JVM就是为了支持与操作系统无关,实现跨平台,JVM的内存区域模型大致可以分为三部分,类加载器子系统,运行时数据区,和执行引擎,JVM的运行时数据区由方法区,堆,栈本地方法区,程序计数器构成。
1.JVM类加载机制
虚拟机将描述类的数据从.class文件加载到内存中,并对数据进行校验,准备,解析,初始化,最终形成可以被虚拟机直接使用的java类型。
类加载的过程
加载–>验证–>准备–>解析–>初始化–>使用–>卸载
加载:将class文件加载到内存中,并将这些内容转换成运行时数据区的数据结构,在内存中生成一个代表这个类的java.lang.Class对象,最为方法区的访问入口。
验证:确保class文件中的内容符合JVM的规范,并且不会危害JVM自身的安全。
准备:正式为类变量(静态变量)分配内存空间,并为静态变量初始化(赋默认值)静态变量的内存在方法去中分配。
解析:虚拟机常量池内的符号引用替换为直接引用的过程。
初始化:根据程序员通过程序指定的主观计划完成静态变量的初始化。在这个过程中完成静态变量的赋值和静态代码块中的语句。
类加载器
用于实现类加载过程中的加载阶段,负责将class文件中的字节码内容加载到内存中。
类加载器的分类
虚拟机自带类加载器:
启动类加载器:C++语言实现,负责加载jre/lib/rt.jar中的内容
扩展类加载器:java语言实现,负责加载jre/lib/*.jar中的内容
应用程序类加载器:又称为系统类加载器,如果应用程序中没有定义过类加载器,一般情况下默认使用系统类加载器。
自定义类加载器:用户可以自定义类加载器,继承java.long.ClassLoader。
双亲委派模型
如果一个类加载器收到了加载请求,他首先不会尝试加载这个类,而是将这个请求委派给父类加载器去完成,每一个层次的类加载器都是如此,因此所有的加载请求最总都应该会被传送给最顶层的类加载器中,只有父类反馈自己无法完成这个加载请求的时候,子类才会尝试自己加载。
2.JVM的内存结构
方法区
也成为永久代,非堆,用于存储虚拟机加载的类信息,常量,静态变量,是各个线程共享的内存区域。运行时常量池也是方法区的一部分。
虚拟机栈
描述的是Java方法执行的内存模型,每个方法被执行的时候,都会创建一个“栈帧”,用于存储局部变量,操作栈,方法出口等信息。
本地方法栈
与虚拟机栈类似,为Native方法(本地方法)服务
堆
也叫GC堆,java堆,是JVM中所管理的最大的一块内存区域,是线程共享的,在JVM启动时创建,存放了对象的实例及数组(所有new的对象)
简述:新生区是对象的创建、应用、消亡的区域,一个对象在这里产生、应用、 终被垃圾回收器收集,消亡。新生区又分为两部分:伊甸区和幸存者区。所有新创建的 对象(new) 都是在伊甸区; 幸存者分为两个:幸存者0区和1区,当伊甸区的空间用完 时,程序 需要创建新的对象 ,JVM对象伊甸区开始进行垃圾回收,应用的是YGC,将 伊甸区不再使用的对象进行销毁,然后将伊甸区剩余的对象移到幸存者0区,0区 满 了,对0区进行垃圾销毁,存活的对象移到幸存者1区,如果1区也满了,则再将1区的 移动到养老区;如果养老区也 满了,此时将JVM将开启 FullGC(简称:FGC),进行 养老 区的内存清理。但是 如果执行Full GC之后 依然无法保存 新的对象,则产生OOM异 常:堆内存溢出
3.GC(GC算法,垃圾回收器)
常见的GC算法:
标记-清除
它是最基础的垃圾回收算法,其他算法都是基于这种思想。标记-清除算法分为“标记”, “清除”两个阶段:首先标记出需要回收的对象,标记完成后统一清除对象
缺点:1:标记和清除的效率不高
2:标记之后会产生大量不连续的内存碎片
复制
它将可用内存分为两块,每次只用其中的一块,当这块内存用完以后,将还存活的对象 复制到另一块上面,然后再把已经使用的内存空间一次清理掉
优点1:不会产生内存碎片
2:只要移动堆顶的指针,按顺序分配内存即可,实现简单,运行高效
缺点:内存缩小为原来的一半
标记-压缩
标记操作和”标记-清除“算法一样,后续操作变成不直接清理对象,而是在清理无用 对象的时候完成让所有存活的对象都像一端移动,并更新对象的指针
优点:不会产生内存碎片
缺点:在“标记-清除”基础上还要进行对象的移动,成本相对较
分代收集
是目前大部分 JVM 的垃圾收集器采用的算法。它的核心思想是根据对象存活的生命 周期将内存划分为若干个不同的区域。一般情况下将堆区划分为老年代(Tenured Generation)和新生代(Young Generation),老年代的特点是每次垃圾收集时只有少量对 象需要被回收,而新生代的特点是每次垃圾回收时都有大量的对象需要被回收,那么就可以 根据不同代的特点采取最适合的收集算法
目前大部分垃圾收集器对于新生代都采取 Copying 算法,因为新生代中每次垃圾回收都 要回收大部分对象,也就是说需要复制的操作次数较少,但是实际中并不是按照 1:1 的比 例来划分新生代的空间的,一般来说是将新生代划分为一块较大的 Eden 空间和两块较小的 Survivor 空间,每次使用 Eden 空间和其中的一块 Survivor 空间,当进行回收时,将 Eden 和 Survivor 中还存活的对象复制到另一块 Survivor 空间中,然后清理掉 Eden 和刚才使用 过的 Survivor 空间
而由于老年代的特点是每次回收都只回收少量对象,一般使用的是 Mark-Compact 算法
注意,在堆区之外还有一个代就是永久代(Permanet Generation),它用来存储 class 类、常量、方法描述等。对永久代的回收主要回收两部分内容:废弃常量和无用的类
常见的GC收集器
Serial收集器,ParNew收集器,Paralle收集器CMS收集器,G1收集器
JDK1.7和1.8JVM的区别?
永久代被元空间取代