【1.java面试-基础篇】

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逻辑
  1. 判断Node[]数组是否被初始化,没有则进行初始化操作
  2. 通过Hash运算定位数组的索引坐标,是否有node节点,如果没有则进行CAS进行添加(链表的头部),如果失败则进入下次循环
  3. 扩容
  4. 如果hash运算值查出的值不为空,则使用synchronized锁住
    1. 如果是链表,则进行链表添加操作
    2. 如果是红黑树,则进行红黑树操作
  5. 判断链表长度是否大于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);
   }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值