java基础
Q:java的异常体系讲一下
java异常体系
Error和Exception的区别:
- Error: 程序无法处理的系统错误,编译器不做检查,内存不足,方法调用栈溢出(OutOfMemoryError)
- Exception:程序可以处理的异常,捕获后可能恢复RuntimeException(空指针异常),IO异常,SQL异常
- 总结:Error是无法处理,Exception是可以处理的,它们的子类,可以看后缀,比如OutOfMemoryError就是Error异常,NullPointException就是Exception异常
java异常处理机制:抛出异常,和捕获异常
项目中如何使用呢:新增一个AppException 继承RuntimeException
java集合框架
Q:java集合包含拿一些?hashmap和concurrentHashmap的实现原理
Collection --list 和set
list:有序列表,继承Collection的接口,重要的子类,ArrayList,LinkedList和Vector。
ArrayList
底层是由数组实现的,ArrayList是一个可改变大小的数组,而且是有顺序的。
List<String> list1 = Arrays.asList("11","22");
list1.add("33") //会报错,抛出异常
因为,Arrays.aslist得到的list是一个固定长度,不允许删除和新增。它只是Arrays的一个静态方法而已
LinkedList
底层是双向链表。增删比较快,但是查询和修改比较慢,LikedList实现了Queue的接口,提供了offer(),peek(),poll()方法。
Vector
底层是数组实现的,但是因为它的很多方法是通过synchronized来处理保证线程安全的,所以相比Arraylist,它的效率比较慢,通常是用Arraylist
Set:无序集合,去重
子类有HashSet和TreeSet
HashSet和TreeSet
HashSet底层是hashmap,是一个不允许有重复元素的集合,它存放一个对象,会调用該对象的hashcode方法,根据hashcode值来决定对象在hashset中的位置,如果相等,不能插入,如果不等,才会调用equals()方法,如果结果为true,则不能插入,false,可以插入
TreeSet底层实现实红黑树的数据结构,有序的,但是采取了自然排序,元素自身是必须要实现Comparable接口,并覆盖其compareTo方法。
Map:键值对的集合
map的key是由set组成的,不允许重复,value是由collection组成
所以Map中的集合不能包含重复的键,值可以重复;每个键只能对应一个值
Map<String,Object> map = new HashMap<>();
map.put("1","value");
map.put("1","value1");
System.out.println(map);
输出 {1=value1}
Q:HashMap,HashTable和ConcurrentHashMap区别
1.HashMap实现原理:
jdk1.7版本实现原理
put原理
-
首先hashmap是一个数组+链表构成的,默认容量是16,如果传9,那么它的容量一定是大于9的2的平方数,也就是16。
-
Map<String,Object> map = new HashMap<>(3); 这里容量设置3,那么它的容量一定是2的2次方=4
-
当put(null,Ojbect),key为null时,默认插入数组下标为0的位置
-
当key不为null,首先会对key进行hashcode运算,然后值 &(与操作)(table.length -1)(key的hash值 & 数组长度-1,效率高),如果此时的数组下标index没有值,就把值放入到此处的位置中。
-
如果此时的table[index] 有值,就会生成一个单向链表(头插法)。
-
如果table数组元素个数大于16*0.75=12时,那么它就会扩容两倍32,链表也会重新找位置。
get原理
- 如果key =null,那就从第一个table[0]查询
- 首先也是对key进行hash运算(hashcode & (table.length -1)),根据hash值找到对应table中的索引,在改索引对应的单向链表查找是否有键值对于的key如目标key相等,有的话返回value,没有返回null
jdk1.8原理
数组+链表+红黑树
get方法:
- 前面跟1.7一样,不一样的地方在于,table[index]此时的对象不为空,且有first.next 不为空,且first不属于红黑树,那就对链表查询,如果属于红黑树,那么就对红黑树查找。
put方法:
- 如果key为空,默认设置table[0]
- 首先对key进行hash运算(hashcode & table.length -1)=index,如果table[index] 没有对象,就放在此位置
- 如果table[index] 已有值了,且该元素是红黑树,则插入到红黑树中,如果该元素是链表,则遍历链表,找到则替换,否则插入到尾部,如果链表长度大于8,先扩容,且table.length>64则转换成红黑树
- 如果table[index] 是红黑树,且长度小于等于6,那么就转换成链表
HashTable:线程安全的,里面有同步方法修饰符,里面也是Map的实现方式,且不允许键或值为null
,不建议使用hashtable
ConcurrentHashMap
- jdk1.7:核心是Segment分段锁+HashEntity(默认16个分段锁) 默认是16个分段锁,每个锁锁一段数据(数据又可以理解为数组+链表)对于一个key要进行三次hash运算,最终确认在哪里,且value不能为空
- jdk1.8:数组+链表+红黑树+CAS+Synchronized,且锁是每个数组元素加锁(Node),其他逻辑就是HashMap
- put逻辑
- 判断Node[]数组是否被初始化,没有则进行初始化操作
- 通过Hash运算定位数组的索引坐标,是否有node节点,如果没有则进行CAS进行添加(链表的头部),如果失败则进入下次循环
- 扩容
- 如果hash运算值查出的值不为空,则使用synchronized锁住
- 如果是链表,则进行链表添加操作
- 如果是红黑树,则进行红黑树操作
- 判断链表长度是否大于8,如果大于8,且数组长度<64,先扩容,等数组长度>64就转换成红黑树
java最基础篇
几个重要的关键字:static abstract interface final
-
static:直接通过类名进行访问。
- 被static修饰的代码块,只会在类被加载的时候执行一次(只有一次)
- static变量:静态变量被所有对象共享,在内存中只有一个副本【存放在方法区】。
-
abstract 和 interface 的区别
- 两者都是抽象类,不能为new
- abstract一个类只能继承一个抽象类,interface接口可以被多个子类实现(implements)
-
final:被final修饰过的变量,不能被修改。线程是安全的,如果一个类被定为final,那么它的变量是可以变的
java的数据类型(一个字节占8位)
- 八种基础类型:byte(8位), short(16位) , int(32位) ,long(64位) ,flout(32位,用于小数点), double(64位,双精度), boolean(true,false), char(16位字符)
- 八个基础类型包装类:Byte,Short ,Integer , Long ,Flout, Double , Character,Boolean
- 自动装箱和自动拆箱: 装箱就是将int 转换成Integer,拆箱就是Integer 转换成int
string和stringbuffer和stringbuilder的区别
-
都是final类修饰,不能被继承,String不能被改变
-
StringBuffer:线程安全,被synchronized修饰了,速度慢,且可以被改变
-
StringBuilder:线程不安全,速度快
-
a == b 和a.equals(b)有什么区别
-
如果a,b都是对象,那么比a==b比较的是对象的内存地址,a.equals(b)是内容进行比较
IO机制04(IO即input和Output的缩写)
NIO和BIO和AIO区别
-
BIO=block-IO(阻塞IO):字符流:reader,writer,字节流:InputStream,OutputStream
纯文本数据:读写使用Reader系,写使用Writer系
非纯文本:读使用InputStream系,写使用OutPutStream系(文本非文本都可以用它)
-
NIO=nonBlock-IO(非阻塞IO)Channels(通道),Buffers(缓冲区),Selectors
读:从通道Channel开始读取,创建一个缓冲区,然后请求通道读取数据
写:从通道进行数据写入,创建一个缓冲区,填充数据,然后要求通道写入数据。
-
AIO=异步非阻塞IO
异步IO是基于事件和回调机制实现的,也就是操作之后直接返回,不会阻塞在哪里,后台处理完成后,才会通知相应的线程进行后续的操作。
实例:高并发下,订单支付,直接返回结果,由redis进行订单轮询更改订单状态,这也算一种AIO
反射:
Q:java反射的作用是什么?
**定义:**在运行状态中,对于任意一个类,都能知道这个类的所有属性和方法;对于任意一个对象,都能调用它的属性和方法(private也可以),这种动态获取信息以及动态调用的功能叫反射。
用途:如果一个类定义了私有化,可以通过反射机制来获取
与反射相关的类:
Class类,Field类(属性),Method类(方法),Constructor类(构造器)
Class类(ReflectTarget)
-
用最多的,最方便的就是Class.forName(String className);--根据类名返回类的对象 Class clasz = Class.forName("demo.pattern.reflect.ReflectTarget");
-
获取类的实例 (ReflectTarget)clasz.getConstructor().newInstance();
Constructor
1.批量获取构造方法:
public Constructor[] getConstructors():所有"公有的"构造方法
public Constructor[] getDeclaredConstructors():获取所有的构造方法(包括私有、受保护、默认、公有)
2.获取单个方法:
public Constructor getConstructor(Class... parameterTypes):获取单个的"公有的"构造方法:
public Constructor getDeclaredConstructor(Class... parameterTypes):获取"某个构造方法"可以是私有的,或受保护、默认、公有;
Filed
1.批量获取属性
Field[] getFields():获取所有的"公有字段",包含继承字段
Field[] getDeclaredFields():获取所有字段,包括:私有、受保护、默认、公有;不包含继承字段
2.获取单个的
public Field getField(String fieldName):获取某个"公有的"字段;包含继承字段
public Field getDeclaredField(String fieldName):获取某个字段(可以是私有的),不包含继承的字段
3.设置值
* 设置字段的值:
* Field --> public void set(Object obj,Object value):
* 参数说明:
* 1.obj:要设置的字段所在的对象;
* 2.value:要为字段设置的值;
*
Method
1.批量的
public Method[] getMethods():获取所有"公有方法";(包含了父类的方法也包含Object类)
public Method[] getDeclaredMethods():获取所有的成员方法,包括私有的(不包括继承的)
2.单个的
public Method getMethod(String name,Class<?>... parameterTypes):
* 参数:
* name : 方法名;
* Class ... : 形参的Class类型对象
public Method getDeclaredMethod(String name,Class<?>... parameterTypes)
3.调用
* 调用方法:
* Method --> public Object invoke(Object obj,Object... args):
* 参数说明:
* obj : 要调用方法的对象;
* args:调用方式时所传递的实参;
6.注解
原注解:
@Target:注解的作用目标
注解使用范围ElementType里面有type(类)Field(属性),method(方法)。。。
@Retention:注解的生命周期
RetentionPolicy有三个Source(只存在源文件中,不会在clas文件中)。
class(不仅出现在源文件中,也在编译后的class文件中)
RUNTIME(运行时有效)
@Documented:注解是否应当被包含在javaDoc中
也可以通过返回获取注解的各种属性
通过反射获取注解的方法:@Retention(RetentionPolicy.RUNTIME)这里必须是运行状态,其他的就不能获取
//获取类上面
Annotation[] annotations = clazz.getAnnotations();
//获取属性上注解的内容
Field[] declaredFields = clazz.getDeclaredFields();
//获取方法上注解的内容
Method[] declaredMethods = clazz.getDeclaredMethods();
代码案例:
public class ReflectTarget {
public String name;
protected int index;
private boolean n;
String age;
}
package demo.pattern.reflect;
import java.lang.reflect.Field;
import lombok.SneakyThrows;
public class FieldDemo {
@SneakyThrows
public static void main(String[] args) {
Class clasz = Class.forName("demo.pattern.reflect.ReflectTarget");
//1.获取所有公有的字段
System.out.println("************获取所有公有的字段********************");
Field[] fields = clasz.getFields();
for (Field f: fields){
System.out.println(f);
}
//2.获取所有的字段
System.out.println("************获取所有的字段(包括私有、受保护、默认的)********************");
Field[] fields1 = clasz.getDeclaredFields();
for (Field f: fields1){
System.out.println(f);
}
//3.获取单个特定公有的field
System.out.println("*************获取公有字段并调用***********************************");
Field name = clasz.getField("name");
System.out.println(name);
//4.给获取到的field赋值
ReflectTarget objects = (ReflectTarget)clasz.getConstructor().newInstance();
name.set(objects,"反射field-Name");
//5.验证对应的值name
System.out.println("验证name : " + objects.name);
//6.获取单个私有的Field
System.out.println("**************获取私有字段targetInfo并调用********************************");
Field n = clasz.getDeclaredField("n");
n.setAccessible(true);
n.set(objects,true);
System.out.println("验证n : " +n);
}
}
public class MethodDemo {
@SneakyThrows
public static void main(String[] args) {
Class clasz = Class.forName("demo.pattern.reflect.ReflectTarget");
//2、获取所有公有方法
System.out.println("***************获取所有的public方法,包括父类和Object*******************");
Method[] methods = clasz.getMethods();
for (Method method: methods){
System.out.println(method);
}
//3、获取该类的所有方法
System.out.println("***************获取所有的方法,包括私有的*******************");
Method[] methods1 = clasz.getDeclaredMethods();
for (Method method: methods1){
System.out.println(method);
}
//4、获取单个公有方法
System.out.println("***************获取公有的show1()方法*******************");
Method s = clasz.getMethod("show1", String.class);
System.out.println(s);
//5、调用show1并执行
ReflectTarget objects = (ReflectTarget)clasz.getConstructor().newInstance();
objects.show1("1");
//6、获取一个私有的成员方法
System.out.println("***************获取私有的show4()方法******************");
Method show3 = clasz.getDeclaredMethod("show3", int.class);
show3.setAccessible(true);
System.out.println(show3);
show3.invoke(objects,1);
}
}