基础部分
面向对象和面向过程的差别
面向过程:面向过程的性能比面向对象的性能高(参考C与Java的区别)。
面向对象:代码可读性高,容易维护,容易扩展。
Java语言的特点
- 多态
- 封装
- 面向对象
- 线程安全
- 网络编程比较方便(面向业务端)
- 可靠性
- 安全性
- 编译与解释共存
JDK、JRE、JVM
在我的视角JVM就是帮助实现跨平台的,Java的字节码虚拟机
JDK是开发者工具,拥有JRE拥有的一切
JRE是Java的运行环境
Java和C++的区别
- 面向对象
- Java没有指针,C++有指针,相对来说说Java安全性更高(带来的性能也就低了一些)
- Java单继承,C++是多继承的,Java是通过接口这个东西变相实现了多继承。
- Java不需要程序员手动释放内存
- 字符串结尾有区别
字符型常量和字符串常量的区别
- 字符常量代表一个字符,而字符串常量代表多个字符
- 占据内存大小不一样
- 一个是数值类型的,一个是表明一个地址
构造器是否可以被重写
构造器不能被重写
重载和重写的区别
重写是发生在继承上面的,是子类重写父类同名,同变量的方法,返回类型也要相同,而且抛出异常的范围要小于父类
重载发生同一个类上,同名不同变量的方法
Java的三大特性封装,多态,继承
封装,典型的就是Private,限定能够访问的等级,防止乱修改
继承,在原有类的基础上增加或者改变一些功能或者方法,核心目的还是为了复用以前的代码
- 子类拥有父类对象的所有属性和方法,但是私有属性是无法访问的
- 子类可以用自己的方法实现父类的方法
- 子类可以拥有自己的属性和方法
多态,在程序运行的时候才会确定调用哪个类比如 Animal = Cat() Animal.eat(),可以通过继承或者接口来实现这个功能。
String StringBuffer和StringBuilder区别是什么,String为什么是不可变的
- String类中使用final关键字修饰字符串数组来保存字符串,所以String是不可变的,他是被final修饰的。
- StringBuffer和StringBuilder都是继承AbstractStringBuilder,所以这两个是可以改变的,并没有被final关键字修饰
- StringBuilder是StringBuffer的县城不安全版本,可以提高性能,但是不值得。
装箱和拆箱
- 装箱:将基本类型用它们对应的引用类型包装起来
- 拆箱:将引用类型转换成基本类型
在静态方法中为什么不能调用非静态成员
因为这个成员有可能还没有被初始化。
Java中定义一个不做事且没有参数的构造方法
加上这个方法主要防止子类因为没有使用super()关键字重写父类的构造方法而造成的编译错误
接口和抽象类的区别是什么
- 接口中除了static final不能有其他变量
- 接口抽象的是行为,而抽象类抽象的是对象
- 接口的方法默认是public,方法不能在接口中实现
- 对象可以实现多个接口只能继承一个抽象类
成员变量和局部变量
- 生存周期不同
- 局部变量不能被static修饰
- 成员变量会被附上初始值,而局部变量不会
- 存储方式不同。如果局部不变量是基本类型放在栈中,如果是引用类型放在堆中
方法的返回值
返回方法执行的后果,他的通就是接受结果,使得这个方法的结果可以被其他方法使用
类的构造方法
用于初始化这个对象,没有写构造方法会自动带一个无参的构造方法。
构造方法特性
1.没有返回值
2.与类同名
3.生成对象的时候自动调用。
4.调用子类构造方法之前会调用父类构造方法,帮助类完成初始化。
静态方法是实例方法的不同
1.静态方法可以通过类名调用实例需要初始化对象之后才能调用
2.静态方法不能访问成员变量
对象的相等和只想他们的引用相等,两者的不同
对象相等是内存中存放的内容是否相等, 而引用相等,是地址是否相等。简单来说对象相等,就是两个存放的东西一不一样,引用相等就是地址是否相等。
== 和 equals的不同
- == ,对于基本类型比较的是值,对于引用类型表示的是地址是否相等
- equal表示的是对象是否相等,如果没有重写euqals方法,就是等价于==,如果重写了euqal方法一般来说会根据对象中的值是否相等来返回True or False
HashCode与equals
在使用HashSet来装载对象的时候,首先会拿HashCode来进行比较,如果没有相同的HashCode就认为这个对象并没有在集合中出现过。如果出现了相同的HashCode就会拿equals来比较,如果equals结果为False会散列到其他位置,equals相等,就拥有相同的HashCode,如果equals不等,则HashCode不等。简单来说HashCode是euqals相等的必要不充分条件,糟糕的散列算法会使得HashCode碰撞概率提升。
线程 程序 和进程
线程是比进程更小的单位,进程可以产生多个线程。同一个进程产生的线程共享内存cpu等资源。在线程级别的切换代价小很多。
程序是一个完整的指令,包含数据和指令,存储在设备程序。
进程是程序的一次执行过程。
线程的一些基本状态
- 创建状态
- 运行状态
- 阻塞状态
- 等待状态
- 终止状态
final须知
- final修饰变量,则这个变量不能更改。如果修饰的是一个引用变量,则这个变量之后就不能指向其他对象。
- 当final修饰一个雷, ,表示这个类不能被继承,
- final修饰方法,子类不能重写这个方法
java中异常处理
Java IO
java中有字节流和字符流,字符流是java虚拟机将字节转换得到的,比较耗时且会出现乱码的问题,所以提供一个字符流,直接操作字符。但是对于一些音频,图片视频等文件,直接用字节流比较好。
深拷贝和浅拷贝
深拷贝,相当于创建一个对象,然后赋予相同的值
浅拷贝,复制一个地址
集合部分
List,Set,Map的区别
List普通集合,可以由重复元素
Set,不可以有重复元素
Map 键值对的形式存在,一对一映射
ArrayList和LinkedList区别
- ArrayList底层用的Object数组,而LinkedList用的双向列表。
- 两者都是不同步的,也就是说不保证线程安全
- LinkedList指定插入,添加删除就是O(n),指定位置i插入和删除元素都是O(n)
- arraylist的话,插入会默认在末尾插入,就是O(1),指定插入删除就是O(n)
- Arraylist支持随机访问LinkedList不支持
- ArrayList浪费的空间在于,在最后会有一部分空间,而LinkedList每一个元素的存储会比较小号空间
ArrayList和Vector的区别
Vector是线程安全的,但是比较慢
ArrayList的扩容机制
HashMap和Hashtable的区别
- HashMap 非线程安全,HashTable线程安全(其中的方法都经过Synchronized修饰)
- HashMap效率更高(非线程安全的数据结构相对来说都会有更高的效率)
- HashMap底层的数据结构是红黑树
HashSet、LinkedHashSest和TreeSet的异同
TreeSet底层用的是红黑树,按照元素顺序进行遍历
LinkedHashSet是Hashset的子类
HashSet是Set的实现类,HashSet底层是HashMap,线程不安全
集合框架总结
Collection下面的集合
-
List
- ArrayList
- Vector
- LinkedList
-
Set
- HashSet
- LinkedHashSet
- TreeSet
Map
- HashMap
- LinkedHashMap
- Hashtable
- TreeMap
多线程部分
什么是进程,什么是线程
进程是程序的一次执行过程
线程与进程相似,是一个比进程更小的执行单位。进程执行的过程中产生很多线程,系统在线程中切换的代价,比在进程级别切换的代价要小。
同一个进程的线程共享进程的堆和方法去,但是每一个线程都有自己的程序计数器,虚拟机栈和本地方法栈。
程序计数器可以发出指令,计数器是这么读取指令的嘛,所以程序计数器一定是需要独立的。
为什么要使用多线程
- 线程是比进程更小的执行单位,切换成本较小,减少了线程上下文的开销
- 高并发
- 切换线程,就属于上下文切换
如何避免线程死锁
- 互斥条件
- 请求与保持条件
- 不剥夺
- 循环等待
上面是条件
Sleep()和Wai()t的区别
- 两者最主要的区别是Sleep()没有释放锁,而Wait()方法释放了锁
- 两者都可以暂停线程的执行
- wait()用于线程通信,需要等待被唤醒,而sleep()会自动苏醒
直接使用run()和先使用start()再使用run()的区别
前者相当于在main()方法下调用一个方法,不是多线程。
先start()再run相当于让线程准备好了,等待cpu分配时间片,分配到时间片后开始运行。
Synchronized关键字
Synchronized的存在是为了解决多个线程访问资源的同步性。
被Synchronized方法修饰的代码块和方法可以保证在某一个时刻,只有一个线程在调用它
volatile是和Synchronize互补的存在,是轻量级的Sychronized,可以帮助变量可见
使用线程池的好处
- 降低资源消耗
- 提高响应速度
- 提高线程的可管理性
Runnable和Callable的区别
Runnable不返回结果和抛出异常,而Callable可以
JVM部分
线程私有的和共有的
- 程序计数器
- 虚拟机栈
- 本地方法栈
共有的
- 堆
- 方法去
- 直接内存
程序计数器
程序计数器是一个较小的内存空间,当做指令的指示器即可
java虚拟机栈
java虚拟机栈也是线程私有的,它的生命周期和线程相同,描述的是java方法执行的内存模型
本地方法栈
Java对象的创建过程
- 类加载检查,查询这个类是否被加载过,如果没有加载需要加载到内存中
- 为新生对象分配内存
- 内存分配完成后,虚拟机将分配到的内存空间初始化为0值
- 设置对象头,放一些对象的基本信息在其中
- 执行init方法
JVM的内存分配和回收
Java堆是垃圾收集器的主要区域,被称为GC堆,从垃圾回收的角度,Java的堆可以分为新生代老生带,相当于对象在新生代创建,经过几次垃圾回收,增加它的年龄,到老生代,再过几次垃圾回收机制就被销毁。一些大对象直接进入老年代
废弃常量和无用类
没有引用指向这个对象说明是一个废弃常量
判断无用类
- 所有实例都被回收
- 加载该类的ClassLoader已经被回收
- java.lang.Class没有被引用
Synchronized
- volatile:这个关键字能展示数据是共享的。如何让变量做到线程可见,他是每一次都去主存中读取数据的。与此同时,volatile也不保证变量的原子性,如果要可见也很简单,加锁。
来到正餐。
首先说一下,Synchronized的使用方法。- 使用在代码块上。看括号内的内容,决定拿什么锁。
- 使用在实例方法上。拿的是对象锁
- 使用在静态方法上。拿的类锁
- 任何一个对象本身内置了一个monitor对象,而Synchronized的底层是通过两条指令来完成的,分别是monitorenter、monitorexsit。这两个指令,初始的时候monitor对象的初始值为1,而后,如果有线程进入,monitor对象变成1,之后,再有线程来拿资源就拿不到了。
- synchronized 和 ReentrantLock
- 两者都是可重入锁,什么是可重入锁:当一个线程获取了一个对象的锁,如果再次申请这个对象的锁,如果是不可冲入锁,就会陷入思索。如果是可重入锁,如同Synchronized,就会有一个计数器急记录,可以再次进入
- 两者的差别:ReentrantLock实现了一些高级功能,比如可以中断等待去干其他事,比如他是可以指定公平锁还是非公平锁。(Synchronized只能设定为非公平锁,非公平锁就是,你拿了线程你有优先权。)且Synchronized是在JVM层面实现的,而ReentrantLock是在代码层面实现的。
Synchronized的四种状态
无锁:操作通过CAS
偏向锁:不用CAS了
轻量锁:竞争线程自旋而不是阻塞
重量锁:竞争线程是阻塞的
HashCode 和 equals
HashCode这个函数的作用,是取得一个对象的哈希值。
equals它的作用也是判断两个对象是否相等,如果对象重写了equals()方法,比较两个对象的内容是否相等;如果没有重写,比较两个对象的地址是否相同,价于“==”。同样的,equals()定义在JDK的Object.java中,这就意味着Java中的任何类都包含有equals()函数。
首先声明。HashCode()和equals发生关联仅仅在用到散列表的时候。
一个散列表如HashMap和HashSet
判断有没有重复元素的时候,
实现判断HashCode(),如果HashCode()不相同,默认为两个元素,如果相同,就再通过equals(),如果equals()判定相同,则认为两者相同。于是就有了equals()相同,HashCode()相同的说法了
关于GC
- 首先GC分为
- Minor GC:发生在Eden和Survior
- Marjor GC:发生在老年代
- Full GC:发生在整个堆
- 老年代的空间不足,就会发生Full GC为老年代腾出较大空间
- 永久空间被占满
- CMS GC,导致Surivo区的对象转入老年代