Java基础

重载与重写的区别

**重载:(发生在编译时)**同一个类中,相同名称的多个办法,这些方法的参数类型不用,个数不同,顺序不同,方法返回值和访问修饰符可以不同。
**重写:(发生在运行时)**发生在父子类中,方法名,参数列表必须相同,返回值范围小于等于父类,抛出的异常范围小于等于父类,访问修饰符大于等于父类;如果父类方法访问修饰符为private,则子类不能重写。
(访问修饰符的大小,public protected 缺省 private)
多态:父类的引用指向子类的实例对象,必须在继承关系的状态下产生

Object里面常用的方法

  • hashCode() 对象地址值
  • equals() 比价对象
  • notify() 唤醒线程
  • wait() 线程等待

equals()和 == 的区别

  • ==的作用:
    • 基本类型:比较的就是值是否相等
    • 引用类型:比价的就是地址值是否相同
  • equals()的作用:
    • 引用类型:默认情况下,比较的就是地址值
    • 特殊情况:String Integer Date这些类中的equals()被重写,比较的是内容而不是地址
    • ArrayList的equals()比较的是内容,相同下标内容值一样,两个相等

装箱和拆箱

装箱:将值类型转换成引用类型的过程
用于垃圾回收堆中存储类型,装箱是值关系类型到Object类型或到此类型所实现的任何接口类型的隐式转换。
拆箱:将引用类型转成成值类型的过程
从Object类型到值类型或从接口类型到实现该接口的值类型的显示转换
作用:

  • 为了保证通用性和提高系统的性能

好处:

  • 良好的封装可以减少耦合
  • 类内部的结构可以自由修改
  • 可以对成员进行更精准的控制
  • 隐藏信息,实现细节

String

string不可以被继承,因为是string类是final类

String,StringBuffer, StringBuilder的区别

String: 字符串常量,String中的String类中使用final关键字修复字符数组来保存字符串,String对象是不可变的,也就可以理解为常量,线程安全
**StringBuffer: 字符串变量(线程安全)**对方法加了同步锁或者对调用的方法加了同步锁,所以线程是安全的。
**StringBuilder:字符串变量(线程不安全)**并没有对方法进行加同步锁,所以是非线程安全。
小结:

  • 如果要操作少量的数据用String
  • 多线程下操作字符串缓冲区下操作大量数据用StringBuffer
  • 单线程下操作字符串缓冲区下操作大量数据用StringBuilder

接口和抽象类的区别是什么

  • **实现:**抽象类的子类使用extends来继承,接口必须使用implements来实现接口
  • **构造函数:**抽象类可以有构造函数,接口不能有
  • **main方法:**抽象类中有main方法,并且可以运行,接口不能有main方法
  • **实现数量:**类可以实现很多个接口,但只能继承一个抽象类
  • **访问修饰符:**接口中的方法默认使用public修饰,抽象中的方法可以使任意访问修饰符。
  • 抽象类中的方法不一定需要全部实现,如果A是抽象类,B继承了A,B可以不需要实现A的所有方法,抽象类可以包含抽象方法和非抽象方法,如果B是非抽象类,那么B必须实现A的所有抽象方法,如果B没有实现A的所有抽象方法,那么B可以定义成抽象类。
  • 实现接口的类必须实现接口的所有方法(如果是抽象类,那么可以不用全部实现接口的所有方法)。
接口的实际作用是在项目建模当中起到一个规范作用,
接口相当于生活中的生活规则,比如早上8点起床洗脸然后去上班等等 
它是生活的基石,也是框架的基石。
所以spring在接口层面的设计是所有框架中最优秀的
Spring也有一个叫法叫做面向接口编程
接口的作用更像在人类社会中的法律, 法律可以规范人应该做什么不该做什么
能做什么不能做什么。

抽象类的作用是什么,它既有 接口的优势 也有普通类的优势, 
抽象类的作用就是比如根据法律来理解的话,
法律的最终解释权是由 制定者或者执行者来规范的 普通人只需要遵循它就可以了

接口和抽象类 拿 计算机硬件来举例子是最合适 也是最贴切的
比如接口是usb接口 它只能插usb
他不能插别的 因为接口规范定死了 不能改变
抽象类可以在接口上进行继承来实现接口的具体功能,
抽象类有点类似于转接头 在usb接口上可以增加更多的实用性功能比如提供的typec 
或者提供的网线 或者提供其他的 接口等等
类的实现就相当于插在接口上的键盘鼠标 或者其他有关外设的东西

final,finally,finalize

  • final用于属性,方法,类,分别标识属性不可变,方法不可覆盖,类不可继承
  • finally是异常处理语句接口的一部分,一定会执行
  • finalize是Object类的一个方法,再垃圾收集器执行的时候会调用被回收对象的此方法,可以覆盖方法提供垃圾收集的其他资源回收,比如关闭文件等。

final 修饰的类有什么特点

  • final类不能被继承,没有子类,final类中的方法默认是final的
  • final方法不能被子类的方法覆盖,但可以被继承
  • final成员变量表示常量,只能被赋值一次,赋值后值不能改变
  • final不能用于修饰构造方法

final修饰引用,引用地址不能改变,但是对象内容可以改变

集合

集合和数组的区别

  • 数组长度固定,集合长度可变
  • 数组中存储的是同一种数据类型的元素,可以存储基本数据类型,也可以存储引用数据类型。
  • 集合:存储的都是对象,而且对象的数据类型可以不一致,开发当中一一般使用对象较多。

List, Map, Set的区别

List和Set是存储单列数据的集合,Map是存储键值对这样的双列数据的集合

  • List中存储的数据是有顺序的,并且值允许重复
  • Map中存在的数据是无序的,他的键不允许重复,值可以重复;
  • Set中存储的数据是无顺序的,并且不允许重复,但是元素再集合中的位置是有hashCode决定的,(Set集合是根据hashCode进行数据存储的,所以位置是固定的,但是这个位置是用户可以控制的,所以对于用户来说Set中的元素还是无序的)

List是如何删除元素的

  • 使用Iterator(迭代器)的hasNext方法进行判断并删除
  • 使用Stream流的filter

ArrayList和LinkedList的区别

数据结构

  • ArrayList是动态数组的数据结构(查找o1)
  • LinkedList是链表的数据结构,查找是on

执行效率:

  • 查找数据是,ArrayList效率更高,因为LinkedList需要通过遍历查找,而ArrayList直接用下班查找,
  • 对数据进行增加和删除的操作时,LinkedList的效率更高,因为ArrayList是数组,再其中进行增删操作时,会对操作点之后的所有数据下标索引照成影响,需要进行数据的移动。LinkedList只需要改变指向就好。

ArrayList怎么扩容

起始值为10,最大为Max_Value - 8
每次扩容为原长度的1.5倍,扩容时会创建一个1.5倍的新数组,将原数组数据复制过去。

HashMap和TreeMap的区别

是否有序:

  • HashMap是无序的
  • TreeMap默认的排序为升序,如果要改变其排序可以直接写一个Comparator;

底层结构:

  • HashMap是数组+链表+红黑树
  • TreeMap是红黑树

线程安全:
两者都是线程不安全的

HashMap,HashTable, ConcurrentHashMap区别

HashMap 和 HashTable 区别

  • HashMap是非线程安全的,HashTable是线程安全的。
  • HashMap的键和值都允许有null的值存在,而HashTable则不行
  • 因为线程安全问题,HashMap的效率比HashTable的更高
  • HashTable是同步的,而HashMap不是,因此HashMap更适合于单线程环境,而HashTable适合于多线程环境

现在一般不建议用HashTable

  • HashTable是遗留类,内部实现很多没有优化和冗余
  • 现在使用ConcurrentHashMap

HashTable和ConcurrentHashMap 的区别

  • HashTable使用的是synchronized关键字修饰
  • ConcurrentHashMap是JDK1.7使用了锁分段技术来保证线程安全的
  • JDK1.8 ConcurrentHashMap取消了Segment分段锁,采用CAS和synchronized来保证并发安全。
  • 数据接口跟HashMap的结构类型,数组+链表+红黑树
  • synchronized只锁定当前链表或者红黑树的首节点,这样只要hash不冲突,就不会产生并发,效率又提升

Hash冲突:
再向hash表中存放数据时,首先要用Hash函数计算处该数据要存放的地址,但再这个地址中已经有值的存在,就发生了Hash冲突

HashMap的Put流程

  • 首先根据Key的值计算Hash值,找到该元素再数组中存储的下班;
  • 如果数组是空的,则调用resize进行初始化;
  • 如果没有哈希冲突值直接放在对象的数组下标里;
  • 如果冲突了,且key已经存在了,则覆盖值,
  • 如果冲突后是链表结构,就判断该链表是否大于8,如果大于8并且数组容量小于64,则进行扩容,
    • 如果链表节点数量大于8并且数组的容量大于64,则将这个结构转成红黑树;
    • 否则,链表插入键值对,诺key存在,就覆盖掉value值。
  • 如果冲突后,发现该节点是红黑树,将节点挂在树上。

HashMap的底层原理

  • 数组+链表+红黑树实现,主要的目的是提高查找效率;当链表中的元素大于等于8个以后,会将链表转成红黑树,当红黑树节点小于等于6个时又会退化成链表;
  • 当new HashMap()底层没有创建数组的,首次调用put()方法示时,底层创建长度为16的数组;

HashMap的扩容机制是什么

HashMap在容量超过负载因子所定义的容量之后,就会扩容;
如果数组长度16,负载因子0.75 那么当容量超过12的时候会触发扩容;
因为HashMap用的数组结构,并且数组是无法自己扩容;
所以HashMap会创建一个原来2倍大小的数组,即32,将旧数据移动过去

多线程

线程和进程的区别
线程:是进程是一个实体,是CPU调度和分派的基本单位,是比进程更小的可以独立运行的基本单位。
进程:具有一定独立功能的程序关于某个数据集合上的一次运行活动,是操作系统进行资源分配和调度的一个独立单位。
特点:线程的划分尺度小于进程,这使多线程程序拥有高并发性,进程在运行时各自内存单元相互独立,线程之间内存共享,这使得多线程编程可以拥有更好的性能和用户体验。
线程安全:当多个线程访问一个对象时,如果不考虑这些线程在运行环境下的调用和交替执行,也不需要进行额外的同步,调用这个对象都可以获得正确结果,那么叫线程安全。

创建线程的几种方式

  • 继承Thread类并重写run方法创建线程,实现简单但是不可以继承其他类
  • 实现Runnable接口并重写run方法
  • 实现Callable接口并重写call方法,创建线程,可以获取线程执行结果的返回值,并且可抛出异常
  • 使用线程池创建,线程池使用ThreadPoolExecutor;

image.png

常用线程池,该线程池包含哪些参数

常用线程池为ThreadPoolExecutor.
参数如下:

  • corePoolSize: 核心线程池
  • maximumPoolSize: 最大线程数
  • keepAliveTime: 最大等待时间
  • unit: 最大等待时间单位
  • workQueue: 等待队列
  • threadFactory: 线程工厂
  • handler: 拒绝策略

拒绝/饱和策略有哪些

  • AbortPolicy策略:会直接抛出异常,阻止系统正常工作。
  • CallerRunPolicy策略:只要线程池没有关闭,该策略直接在调用者线程中,运行当前被丢弃的任务。
  • DiscardoldestPolicy策略: 将丢弃即将执行的下一个任务,并尝试再次提交当前任务
  • DiscardPolicy策略: 默默丢弃没发处理的任务,不予处理。
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;

import java.util.concurrent.Executor;
import java.util.concurrent.ThreadPoolExecutor;

/**
 *
 * @author cyh
 * 11:29
 */
@Configuration
@EnableAsync
public class AsyncConfiguration {


    @Bean("doContractExecutor")
    public Executor doSomethingExecutor() {
        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
        // 核心线程数:线程池创建时候初始化的线程数
        executor.setCorePoolSize(10);
        // 最大线程数:线程池最大的线程数,只有在缓冲队列满了之后才会申请超过核心线程数的线程
        executor.setMaxPoolSize(20);
        // 缓冲队列:用来缓冲执行任务的队列
        executor.setQueueCapacity(500);
        // 允许线程的空闲时间60秒:当超过了核心线程之外的线程在空闲时间到达之后会被销毁
        executor.setKeepAliveSeconds(60);
        // 线程池名的前缀:设置好了之后可以方便我们定位处理任务所在的线程池
        executor.setThreadNamePrefix("do-contract-");
        // 缓冲队列满了之后的拒绝策略:由调用线程处理(一般是主线程)
        executor.setRejectedExecutionHandler(new ThreadPoolExecutor.DiscardPolicy());
        executor.initialize();
        return executor;
    }


}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值