面向过程与面向对象的区别:
面向过程性能比面向对象性能高,但是不容易维护,不容易调用,也不容易扩展
面向对象性能比较低,但是易维护,易复用,也易扩展,有三大特性:多态,继承,封装,所以能够降低耦合
JVM
JVM是运行java字节码的虚拟机,在不同的操作系统都会都各自的虚拟机,所以java之所以这么强大就是因为可以一次编译处处运行,什么是字节码呢,字节码就是计算机能都读懂的代码
程序代码到运行的步骤
java代码通过jdk的javac编译获得字节码文件,通过虚拟机加载字节码文件执行程序
JDK是java开发工具,内含jre可以创建和编辑程序,jre是Java运行环境,jre包含jvmjava类库等,不能创建新程序
java面向对象编程三大特征
封装,继承,多态
封装:把一个类的属性私有化,同时提供外面访问属性的方法
继承:拥有父类所有的属性和方法,但是只能访问父类允许的方法和属性
多态:
String、StringBuilder、StringBuffer
String是不可变字符串,每次操作String都会创建新的string对象,StringBuilder和StringBuffer都继承abstractStringBuilder,每次操作上面两个对象都是在自身基础上操作的,且StringBuffer对方法加了同步锁,线程安全,StringBuilder线程不安全,所以StringBuilder性能会不StringBuffer性能高. String是由final修饰的字符数组来保存字符串的,所以String不可变,StringBuffer和StringBuilder是由字符数组来保存字符串的所以两种对象是可变的
装箱和拆箱
装箱:将基本类型用对应的引用类型包装起来
拆箱:将包装类型转换成基本数据类型
为什么要定义一个无参构造器
在执行子类构造方法之前如果没调用super(来调用父类特定的构造方法),则会调用无参构造方法,所以如果没有定义一个无参构造器的话且没有用super来调用特定的构造方法就会编译错误
接口和抽象类的区别是什么
接口默认是public 所有方法在接口中不能有实现方法,抽象方法可以有其他非抽象方法
一个类可以继承多个接口,但是只能实现一个抽象类
接口默认public修饰,抽象类除了private不能修饰其他都可以修饰
成员变量和局部变量有什么区别
成员变量定义在类中方法外,局部变量是定义在方法中,在内存空间中,成员变量是存在堆中,局部变量是存在栈中.成员变量的生命周期是伴随着对象的创建而存在的,伴随对象销毁而销毁,而局部变量是在方法被调用时而存在的,方法调用完毕而销毁
一个类的构造方法的作用
构造方法是初始化对象,每个类都会有一个默认的无参构造器
构造方法有哪些特征
构造名与类名相同,没有返回值但是不能用void来修饰
hashCode和equals
就比如往hashSet集合中存放对象时,会先调用hashcode方法来计算hash值,如果不相等的就说明不存在可以存,如果相同的话就调用equals方法比较两个对象是否相等,如果相等不存,不相等就存
线程的基本状态
初始状态,线程被创建,但是没有调用start()方法
运行状态
阻塞状态
等待状态
超时等待状态
终止状态
当线程被创建后调用start()方法后就进入就绪状态,等待获取cpu的时间片,然后进入运行状态,调用wait()方法进入等待状态,调用sleep()进入超时等待状态,当超时时间达到后就会重新进入运行状态,当线程调用同步锁后就会进入阻塞状态,只有执行完当前县城后才会重新回到运行状态
final关键字
final修饰的变量必须要初始化后才能使用,且初始化后不能再改变
final修饰的方法不能被重载
final修饰的类不能被继承
IO流常见的流
字符流/字节流
BIO和NIO的区别
BIO是阻塞IO,当一个请求过来,要接收数据,都会先开启Socket,然后开启一个线程,读取数据,返回结果,只有业务完成后线程才能释放线程,当大量的请求进来后都会进去阻塞状态
NIO是非阻塞IO,当每一个请求进来后,都会创建一个通道,一个selector会监听多个通道的方式,当通道的状态为就绪状态时就会开启一个线程,执行多路复用操作
final、finally、finalize
finally是异常处理的一部分,只能用在try/cath语句中,表示最后一句要执行
finalize,对象被回收的时候会被调用,在垃圾收集器删除对象之前会被调用
ArraysList扩容机制
ArraysList的默认长度为10,当调用add方法的时候,会对ArrayList的长度进行改变,当往list存储的元素长度大于默认长度时就会对原素组的长度+1后进行扩容1.5倍,然后在判断新数组的长度是否足够,够了就用这个长度创建一个新的数组,不够将数组长度设置为需要的长度,再将原数组复制到新数组
ArraysList,LinkedList,Vector的区别
ArrayList和Vector都是底层通过数组实现的,可以通过索引查询元素,但是修改数组后需要重新创建新的数组并且将修改后的数组复制到新数组,比较麻烦,Vector底层是通过链表来实现的,提供了双向链表存储功能,所以查询元素是需要遍历,但是插入数据只需要记录前后元素即可,插入数据比较方便
ArraysList和Vector:ArrayList线程不安全,Vector方法加了同步锁所以线程安全,但是性能就会降低,ArrayList
ArraysList的扩容是在原数组长度+1后的1.5倍,Vector是在原数组长度+1后的2倍
深拷贝和浅拷贝的区别
浅拷贝:复制内存地址,被复制的对象发生改变,复制的对象也会跟着改变
深拷贝:在内存中新开辟一个新的空间用于存储拷贝的对象
序列化和反序列化
序列化就是将对象转换成字节序列也就是比较常见的变成文件
反序列化就是将字节序列化转变成对象
怎么实现序列化
实现Serializable接口即可
如果不想字段被序列化怎么处理
使用transient关键字修饰即可
HashMap底层原理
HashMap是基于Map接口实现的,以键值对的形式存储数据,底层是由数组,链表和黑红树数据结构实现的,三种结构结合使用可以取其精华,数组查询效率高,链表增删改效率高,当链表元素超过8个的时候就会用红黑树代替链表,因为红黑树是二叉平衡数,所以查询效率会比链表高,HashMAp的初始容量为16,扩容是原长度的2倍
HashMap,HashTable和ConcurrentHashMap的区别
HashMap的线程不安全,接收一个空的key和空的value,异步执行,快速失败迭代器,在遍历的过程中有其他线程操作增删操作的时候会抛出异常,因为迭代器操作的是集合本身
HashTable线程安全,key和value不能为空,同步执行,在遍历是其他线程进行增删操作的时候不会抛出异常,因为迭代器操作的是原集合的拷贝
HashMap在jdk1.8后当链表长度大于8个是红黑树就会替换链表结构
HashTable和ConcurrentHashMap的区别,ConcurrentHashMap是在HashTable上优化的,优化了链表的查询修改的效率,因为HashTable的是锁住整个Map,而ConcurrentHashMap只锁了一部分Map,所以效率有所提高
为什么HashMap存储数据的容量会采用二进制
因为为了HashMap的存取效率高,要经量减少碰撞,就要经量把数据分配均匀,而取模运算就很好符合这个需求,所以存储容量会采用二进制
HashMap集合中put是如何实现的
调用put方法是首先判断数组是否为空,如果为空的话先进行扩容,然后再根据key计算出hash值得到插入的数组索引,如果索引所在的元素是空的话就直接插入元素然后判断已存的键值对数是否超过最大容量,如果超过就要进行扩容.如果索引所在的元素不为空的话比较数组的元素与key是否相同,如果相同直接覆盖,不相同的话判断元素是否为红黑树,如果数红黑树的话直接插入数据即可,如果不是的话,判断链表长度是否大于8,如果没大于的话直接遍历链表插入,如果遍历过程中发现key已经存在就直接覆盖,如果是红黑数的话直接树中插入键值对