2020-9-25日,开始准备校招的面试部分

计划是复习四个方面
1:java基础
2:android基础
3:专业知识基础
4:框架学习

首先是java基础的复习
知识点
因为是用于秋招所以只记录自己记得不是特别清楚的部分

java中的基本数据类型类型和所占大小
byte,boolean 1B
char,short 2B
int float 4B
long double 8B

面向对象三大特征
封装 继承 多态
封装:把对客观事物的描述抽象为一个类
继承:让类去继承类的属性和方法
多态:类或者方法在不同条件下的状态不同,比如重写和重载
(还有泛型)
泛型:

public 对本类、同包下的类、子类、其它包的类或子类可见
private对本类、同包下的类可见其它不可见
protect对其它包的类或者子类不可见

静态变量和实例变量
静态变量属于类,实例变量属于某个对象
因此静态变量在内存中只有一个
1 .静态变量随着类的加载而加载,随着字节码文件的加载而加载
2. 被类的所有对象共享
3. 通过类名.调用

重载和重写
重写必须继承,重载不用

接口和抽象类

相同点
抽象类和接口都无法实例化
如果一个类继承了接口或者抽象类必须实现抽象方法
不同点
1.抽象类可以有构造方法,接口中不能有构造方法。
2.抽象类中可以有普通成员变量,接口中没有普通成员变量
3.抽象类可以包含非抽象方法,接口只能包含抽象方法
4.接口必须是public (不然就没有意义了)抽象类可以可以使用三种修饰符
5.抽象类可以有静态方法,接口不能

LinkList和ArrayList的区别
两者的实现不同一个是基于链表一个是基于数组
然后对于链表和数组他们的查询,插入删除效率不一样,所以这两者的主要区别是不同操作的效率不同

HashMap和HashTable的区别
Map不是线程安全,所以效率高点(因为允许多个线程同时操作)ConcurrentHashMap可以让他安全(锁)
Table是线程安全的但是效率低(不允许多个同时操作,所有线程都在等待一个资源)
HashMap允许空值
HashTable不允许空值
Hm继承了map接口HT继承了map接口和dictionary抽象类
HT的是直接使用key的hashcode对table的长度取模
HM计算hash对key 的hashcode进行了二次hash然后对table的长度取模
HashMap默认长度12

String类被final修饰 不能被继承和值修改

为什么重写equals后要重写hashcode

原则上要求equals结果为true那么hashcode相同
==比较的是两个变量的值,而对于引用类型比较的是他们是否指向同一块内存区域,equals他源码里对于两个对象的比较就是写的==,而hashcode返回的就是对象所在的内存区域的地址,所以 当返回true的时候两个对象hashcode的值一定是相等的。哈希表的存储是根据hashcode的值来判断是否是同一个对象的,如果不重写hashcode那么你在new一个对象就会产生equals返回的值相同但是hashcode不同的情况。
Hashcode是jdk根据对象的地址或者字符串或者数字算出来的int类型的数值

用String类作为key
String重写了equals 假设 他没有重写hashcode
那么根据源码
值相同的两个地址不同的对象返回的equals相同
在使用hashmap.put的时候
因为值相同
假设hashmap.put(key,“1”);
再次使用hashmap.put(key,“2”);
因为他们所在的内存地址不一样
hashmap在判断key是否存在的时候始终会判断key值不存在
这时候就出现问题了
https://www.zhihu.com/tardis/sogou/art/50206657

java文件通过编译器得到class文件,然后类加载器把class文件加载到jvm中

java类加载机制
类加载器是预加载的,不是某个类被首次主动使用的时候加载的
从本地磁盘、网上加载、其它文件生成、数据库、压缩包加载class文件。

从类加载到jvm到gc为止
生命周期有七个阶段
加载、验证、准备、解析、初始化、使用、卸载。
类的加载包括前面五个

加载
加载阶段,虚拟机做了三件事情
1.通过类的全限定名获取该类对应的二进制字节流
2.将字节流代表的静态存储结构转化为方法区的运行时数据结构
3.在堆中生成一个代表该类的对象,作为方法区中这些数据的访问入口.

验证
验证的作用是确保加载的类的正确性
主要完成四个阶段的验证
1.文件格式(字节流是否符合class文件的格式规范)
2.元数据(主要是语义分析)
(主要是对数据的类型等的分析)
3.字节码验证(通过数据流和控制流确保语义的合法性)
(主要是对方法的分析)
4.符号引用验证
发生在jvm把符号引用转换为直接引用的时候
符号引用:比如定义的一些常量
直接引用:直接引用是可以指向目标的指针、相对偏移量或者是一个能直接或间接定位到目标的句柄。和虚拟机实现的内存有关,不同的虚拟机直接引用一般不同。

准备
该阶段主要是为类和变量分配内存和设置初始值
(static变量,,实例变量是随着对象的实例化分配到java堆中的)这里的初始值指的是数据类型的默认初始值。

解析
该阶段jvm把常量池中的符号转换为直接引用
解析动作主要针对类或接口、字段、类方法、接口方法、方法类型、方法句柄和调用点限定符7类符号引用进行

初始化
该阶段主要对静态变量赋值,JVM负责对类进行初始化,主要对类变量赋值
(
提下 免得问到了懵逼
对类变量赋值有两种方式,一种是在定义的时候就赋值 int a =0;
还有一种是在静态代码块中赋值 a=0;
)

类加载器种类
启动类加载器 加载核心库 底层是c++写的
扩展类加载器 加载扩展库javax*
应用程序类加载器 加载程序所在目录
自定义类加载器 加载自定义的class文件

类加载器的双亲委派机制
1.自底向上检查类是否已经集在

2.自顶向下加载类
加载某个类时会先委托父加载器寻找目标类,找不到再委托上层父加载器加载,如果所有父加载器在自己的加载类路径下都找不到目标类,则在自己的类加载路径中查找并载入目标类。

Java中,类的全限定名并不能唯一确定一个类,只有类的全限定名和类加载器一起才会唯一确定一个类。比如有一个com.test类,然后自定义了两个类加载器去加载他,就会产生两个不同的对象,类加载会在类加载的名称空间标识该类名称,用于保证类加载器和类的全限定名指向同一个类。
所以双亲委托机制的存在能够保证安全,
如果没有双亲委派机制,比如说我们自定义了一个java.lang.String类,因为只用一个类加载器去加载,这个类就会被加载到内存,结果可能会导致内存中有两个String类,会产生安全问题,而有双亲委派机制的情况下,会自底向上找类加载器,如果在某个父类加载器中发现已经加载过同名类了,那这个类就不会进行加载,保证了安全性。

JVM初始化步骤
1.若该类未被加载和连接,则先加载和连接
2.若该类直接父类为初始化,先初始化该类父类
3.若类中有初始化语句,则依次执行
只有当类被主动使用时才初始化
类的主动使用包括6种
1.new ,创建实例
2.创建了某类的子类时,父类会被初始化
3.访问类的静态变量
4.使用类的静态方法
5.反射
6.JVM启动时被标注为启动类的类

jdk指的是java开发工具包
包括编译(编译java源码的编译器javac)、运行环境(JVM)及其它一些工具
JRE 运行环境(JVM)、基础类库
JAVA EE、JAVA SE、JAVA ME是三个平台
提供了不同的类库针对不同的环境
JAVA EE 主要针对WEB和服务器
JAVA SE 用于普通的JAVA程序
JAVA ME 用于嵌入式开发

对于字符串的拼接 StringBuild比String + " "快的原因
(StringBuild只创建了一个对象)

JAVA反射
反射就是动态加载类对象,并对对象进行剖析,使得对于任意一个类,
在运行时都能调用的方法。
优点:灵活
缺点:慢(因为本质上是一种解释操作,所以比直接执行要慢)
jdbc中数据库驱动程序是通过反射动态加载的
web服务器中是通过反射调用sevlet服务的
idea 这些开发工具的语法提示也是通过反射实现的

深拷贝和浅拷贝
深拷贝是复制了一个对象并申请了一块新的内存区域来存储这个变量
而浅拷贝只是多了一个指针指向该对象
如果拷贝的对象只有值没有引用那两者没区别,都会对原有对象复制一份,产生一个新对象。但是若拷贝的对象包含引用,那么如果是浅拷贝,对新的对象里的引用的值进行修改,会影响原对象的值。而深拷贝不会。
通过重写clone()方法可以实现深拷贝和浅拷贝
深拷贝还可以通过对象序列化实现拷贝

java IO模型
同步和异步
同步就是无法并发执行
异步可以并发执行

阻塞和非阻塞
阻塞就是指某个任务在执行过程中发出一个请求操作但是该操作执行的条件不足,那么该任务就会一只等待,直到条件满足
非阻塞就是当某个任务在执行过程中法出一个请求操作但是该操作执行的条件不足,会立即返回一个标志信息告知不满足,不会一直等待。

阻塞IO和非阻塞IO
IO包括两个阶段
1.查看是否就绪
2.进行数据拷贝
对于当用户线程发起IO时如果是阻塞IO,条件不满足则会等待,非阻塞IO则会返回一个标志信息。当条件满足后内核会将数据拷贝到用户线程。
java传统io都是阻塞io

同步IO和异步IO
能否并发

五种IO模型
阻塞IO模型
当玉壶线程发出IO请求后,内核会去查看数据是否就绪,若未就绪,用户线程就会处于阻塞,数据就绪后内核会把数据拷贝到用户线程才会接触阻塞
非阻塞IO模型
当用户线程发起一个read操作后,会直接得到一个结果,如果时error表示数据未就绪,会继续发送read操作请求,直到准备好。
多路复用IO模型
该模型中会有一个线程不断去轮询socket的状态,只有当socket真正有读写事件时,才会调用IO操作。因为只用一个线程所以减少了资源消耗。
多路复用IO模型效率比非阻塞高的原因
多路复用是在内核中轮询而非阻塞IO是在用户线程中轮询。

信号驱动IO模型
在信号驱动IO模型中,当用户线程发起IO请求操作时,会在对应的socket里注册一个信号函数,当数据就绪后,内核会发送一个信号给用户线程,用户线程收到后,会在信号函数里调用IO读写操作来进行实际的IO
异步io模型
IO操作的阶段不会阻塞用户线程,当两个阶段(数据就绪、数据拷贝)结束后,会发送信号告知用户线程操作已完成。

静态分派和动态分派
根据参数的静态类型来定位目标方法: 静态分派
编译器在重载时是通过参数的静态类型而不是实际类型作为判定的依据。

根据变量的实际类型来决定调用哪个方法 :动态分派
编译器在重写时是通过参数实际类型作为判定的依据。

通过一张虚拟方法表存放了方法的入口地址,如果没有重写,那么子类方法地址会跟父类一样,如果重写了就会替换为实际的方法入口地址

泛型
目的
编译的时候通过增加强制类型转换的代码,来避免用户编写出可能引发ClassCastException的代码
供了编译期的类型安全,确保你只能把正确类型的对象放入集合中。
泛型只在编译期有效,运行后泛型参数类型都会被清除。
如果 JVM 将泛型类型延续到运行期,那么到运行期时 JVM 就需要进行大量的重构工作了,提高了运行期的效率
使用泛型的时候加上的类型参数,会被编译器在编译的时候去掉。这个过程就称为类型擦除。
类型擦除的基本过程:首先是找到用来替换类型参数的具体类。这个具体类一般是Object。如果指定了类型参数的上界的话,则使用这个上界。把代码中的类型参数都替换成具体的类。同时去掉出现的类型声明,即去掉<>的内容。

泛型支持List不支持ArrayList:Object[]数组是任何数组的父类,任何数组都可以向上转型为他定义时的原始数据类型,所以在编译的时候我们放入不同的类型不会报错,但是在运行的时候,会检查数组中的对象类型,会报错。
而集合类的类型在设计上是不可变的。

通配符与上下界
List<? extends Number>说明List中可能包含的元素类型是Number及其子类
List<? super Number>则说明List中包含的是Number及其父类

HashMap为什么不直接使用hashCode()处理后的哈希值直接作为table的下标:
hashCode()方法返回的是int整数类型,其范围为-(2 ^ 31)~(2 ^ 31 - 1),约有40亿个映射空间,而HashMap的容量范围是在16(初始化默认值)~2 ^ 30,HashMap通常情况下是取不到最大值的,并且设备上也难以提供这么多的存储空间,从而导致通过hashCode()计算出的哈希值可能不在数组大小范围内,进而无法匹配存储位置

为什么数组长度要保证为2的幂次方
只有当数组长度为2的幂次方时,h&(length-1)才等价于h%length,即实现了key的定位,2的幂次方也可以减少冲突次数,提高HashMap的查询效率

那为什么是两次扰动:加大哈希值低位的随机性,使得分布更均匀,从而提高对应数组存储下标位置的随机性&均匀性,最终减少Hash冲突,两次就够了,已经达到了高位低位同时参与运算的目的

为什么HashMap中String、Integer这样的包装类适合作为K?
答:String、Integer等包装类的特性能够保证Hash值的不可更改性和计算准确性,能够有效的减少Hash碰撞的几率

都是final类型,即不可变性,保证key的不可更改性,不会存在获取hash值不同的情况
内部已重写了equals()、hashCode()等方法,不容易出现Hash值计算错误的情况

如果我想要让自己的Object作为K应该怎么办呢?
重写hashCode()和equals()方法

集合和数组的区别
长度 数组固定,集合可变
内容 数组可以是引用和值 集合只能是引用
内容 数组只能存同一种类型,集合可以存不同类型

GC
Java运行时内存区的划分:
1.线程私有区:
程序计数器:记录正在执行的虚拟机字节码地址
虚拟机栈:方法执行的内存区
本地方法栈:虚拟机的Native方法执行的内存区
2.线程共享区
Java堆:对象分配内存的区域,主要在这GC
方法区:存放类信息、常量、静态变量、编译器编译后的代码等数据

JAVA 的GC算法有两种
GC的任务
1.分配内存
2.确保被引用对象的内存不被错误的回收
3.回收不再被引用的对象的内存空间

垃圾回收机制的主要解决问题
1.哪些内存需要回收?
2.什么时候回收?
3.如何回收

https://www.cnblogs.com/igoodful/p/8727241.html 详细
1.引用计数法.每个对象都有一个引用计数器,被引用一次就+1,引用失效就减一,对于计数器值为0的就代表可以被GC(可能会导致内存泄漏)
2.可达性算法:从一些列的GC Root出发,整个连通图中的对象都是活对象,不可达的就可以GC
在Java语言中,可作为GC Roots的对象包含以下几种:
虚拟机栈(栈帧中的本地变量表)中引用的对象。(可以理解为:引用栈帧中的本地变量表的所有对象)
方法区中静态属性引用的对象(可以理解为:引用方法区该静态属性的所有对象)
方法区中常量引用的对象(可以理解为:引用方法区中常量的所有对象)
本地方法栈中(Native方法)引用的对象(可以理解为:引用Native方法的所有对象)
在确定了哪些对象可以GC后
 1会在cpu空闲的时候自动进行GC
 2在堆内存存储满了之后GC
 3主动调用System.gc()后尝试进行GC
回收:
四种回收算法 
1.标记清除算法,对所有需要回收的都进行标记然后统一回收,效率不高,而且会造成内存碎片
2.复制算法,把内存划分为两块相同的大小,每次使用其中的一块,当这一块使用完了,把不需要gc的复制到另外一块内存,然后清理。内存不能得到有效利用
3.标记整理法,在清除法的基础上对清理过后的存活对象进行整理,不会产生内存碎片
4.分代收集法
划分新生代和年老代
对新生代用复制算法
年老代用标记清除/整理法。
在jdk8的时候java废弃了永久代,但是并不意味着我们以上的结论失效,因为java提供了与永久代类似的叫做“元空间”的技术。
废弃永久代的原因:由于永久代内存经常不够用或发生内存泄露,爆出异常java.lang.OutOfMemoryErroy。元空间的本质和永久代类似。不过元空间与永久代之间最大的区别在于:元空间并不在虚拟机中,而是使用本地内存。也就是不局限与jvm可以使用系统的内存

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值