java基础总结

2022-12-31 15:43:00 浏览数 (2)

Java基础面试题

一: Java基础

1java是什么类型语言

一、你可以说它是编译型的:因为所有的Java代码都是要编译的,.java不经过编译就什么用都没有。 二、你可以说它是解释型的:因为java代码编译后不能直接运行,它是解释运行在JVM上的,所以它是解释运行的,那也就算是解释的了。 三、但是,现在的JVM为了效率,都有一些JIT优化。它又会把.class的二进制代码编译为本地的代码直接运行,所以,又是编译的。

2简单说说Java中对象如何拷贝?
代码语言:javascript复制
一、浅拷贝clone()

如果对象中的所有数据域都是数值或者基本类型,使用clone()即可满足需求,如:

Person p = new Person();

Person p1 = p.clone();

这样p和p1分别指向不同的对象。

二、深度拷贝

如果在对象中包含子对象的引用,拷贝的结果是使得两个域引用同一个对象,默认的拷贝是浅拷贝,没有拷贝包含在对象中的内部对象。

如果子对象是不可变的,如String,这没有什么问题;如果对象是可变的,必须重新定义clone方法;

三、序列化可克隆(深拷贝)

四、BeanUtils.copyProperties()
3: 什么是Object,有哪些常用的方法,怎么创建对象?
代码语言:javascript复制
1、使用new关键字;
2、使用Class类的newInstance方法,可调用无参的构造函数创建对象;
3、使用Constructor类的newInstance方法;
4、使用clone方法;
5、使用反序列化。
4:什么是内部类,说说你对他的理解以及实战场景
5: 说说 static 和 final 在Java中的意义
代码语言:javascript复制
使用final的意义:

为方法上锁,防止任何继承类改变它的本来含义和实现。设计程序时,若希望一个方法的行为在继承期间保持不变,而且不可被覆盖或改写,就可以采取这种做法。
提高程序执行的效率,将一个方法设成final后,编译器就可以把对那个方法的所有调用都置入嵌入调用里(内嵌机制)。

static总结
static修饰成员函数:该成员函数不能使用this对象
static不能修饰构造函数
static不能修饰函数参数
static不能修饰局部成员变量
static修饰成员字段
当类被虚拟机加载时,首先按照字段声明的先后顺序对static成员字段进行初始化
static修饰语句块
当类被虚拟机加载时,按照声明顺序先后初始化static成员字段和static语句块
static所修饰的方法和字段是只属于类,所有对象共享。
在static所修饰的函数和语句块中不能使用非static成员字段。
在Java不能直接定义全局变量,是通过static来实现的
在Java中没有const,不能直接定义常量,通过static final来实现
6:int和Integer有什么关联?为什么需要Integer?装箱拆箱
7:什么是序列化,反序列化.说说运用场景
代码语言:javascript复制
https://www.cnblogs.com/binarylei/p/10987540.html
8:你知道运算符&和&& ,| 和||的区别吗?
代码语言:javascript复制
& 按位与操作:只有对应的两个二进制数位1时,结果位才为1
    1&1=1;
|按位或操作:有一个为1时,结果位就为1
    1|0=1    
&& ||短路运算符 在逻辑判断中常见
9:用最有效率的方法计算 2*8(位运算)
代码语言:javascript复制
将一个数左移,相当于乘以2的n次方,位运算是cpu直接支持的,所以效率高
2 << 3

hashmap的初始容量 1<< 4 //16
直接二进制操作,表示1左移4,变成10000,转为10进制就是16
10 写个方法传递两个非0的int数值进去,实现变量交换,有几种方式?
代码语言:javascript复制
a= a b;
b=a-b;
a=a-b;
代码语言:javascript复制
a=a^b;
b=b^a;
a=a^b;
11 java基础数据类型分类
代码语言:javascript复制
基础数据类型
byte  short int long
float double char boolean
引用数据类型:其他引用类型
string和enum分别是什么类型:引用类型
12 运算
代码语言:javascript复制
int i=5
return i  ; 5
return   i; 6 
13 == 和equals的区别
代码语言:javascript复制
基本数据类型 比较要用==判断是否相等
引用数据类型 == 比较的就是内存地址是否一样,不同对象的内存地址不一样,equals比较的是具体的内容
14 string stringbuffer与stringbuilder的区别

三者都是final不允许被继承 本质上都是通过char[]数组实现的 string stringbuffer与stringbuilder中,string是不可变对象。另外两个都是可变的

  • stringbuilder
    • 效率快,因为不需要加锁,但是不具备线程安全
  • stringbuffer
    • 有枷锁操作,不具备线程安全。
15 面向对象oop理解

四大特性 - 抽象 - 声明的类叫抽象类,抽象方法(只有一个声明没有方法体) - 封装 - 让代码更容易理解和维护,加强了安全性 - 继承 - 具有公共的方法和属性 - 多态

16 overload与override区别
  • overload
    • 重载
    • 在同一个类中存在多个名称相同的方法,但是这些方法的参数列表不相同,参数个数或者这个类型不同
  • override
    • 重写
    • 表示子类重写父类的方法
17 接口是否可以继承接口,接口是否支持多继承,类是否支持多继承,接口里面是否可以有方法实现
  • 接口里面可以有静态方法和方法体
  • 接口不是被类继承,而是被实现
  • 接口支持多继承,类不支持多继承
  • 一个类只能继承一个类,但是可以实现多个接口
18 jdk8接口的新特性
  • 接口中可以有static方法,但必须有方法实现体,该方法只属于该接口,接口名直接调用该方法。
  • 接口中新增default关键字修饰的方法,default方法只能定义在接口中,可以在子类或者子类接口中被重写default定义的方法必须有方法体。
19 为什么重写equals还要重写hashcode
  • hashcode
    • 底层是c语言写的,根据对象内存地址,转换成整数类型
  • equals
    • 如果说两个对象的hashcode值一样,对象的内容值不一定相等 set集合的底层就是基于hashmap,key,比较hashcode值和equest值

二: 多线程

一 线程
1 线程的创建方式有哪些
  • 继承thread类,本质还是实现了runable接口的实例,代表一个线程的实例,start方法。没有返回值
  • 实现runable接口,实现run方法,没有返回值,无法抛出异常,使用的时候还需要新建thread。
  • 实现callable接口,实现call方法,任务执行后有返回值,可以抛出异常,需要使用futureTask(本质上还是继承了runable)传递进去,还需要创建thread类进行传递。
2 线程的状态?
  1. 新建状态,new start线程
  2. 运行状态(就虚,和运行),调用线程的start方法
    • 就虚调用了start方法,cpu嗨没有分配时间片
    • 运行掉一哦那个了start方法,cpu正在调度
  3. 堵塞状态:当竞争锁的时候,没拿到,线程挂起
  4. 等待状态:join,wait,park方法
  5. 超时等待状态,thread.sleep,wait,join。。。
  6. 死亡状态:run方法结束
3 进程和线程的区别

进程是操作系统的基本单位,是操作系统结构的基础 线程是cpu调度的最小单位。

4 sleep/yied/join wait/notify/notifyAll
  • thread类
    • sleep
      • 让当前线程暂缓执行,等待预计时间之后,在恢复,不会释放锁,会进入阻塞状态,让出cpu执行权
    • yied
      • 让当前线程暂缓执行,执行其他线程,不会释放锁,不会让线程进入阻塞状态,直接变为就绪,只需要重新获得cpu使用权
    • join
      • 优先执行join的线程,不会释放已经持有的对象锁
  • object类
    • wait
      • 释放当前对象的锁,进入等待队列,需要依靠notify/notifyAll/wait(timeout)时间唤醒
    • notify
      • 唤醒在对象监视器等待的单个线程,是任意的一个
    • notifyAll
      • 唤醒在对象监视器等待的全部线程
5 threadlocal到底是什么/实现原理

存储Thread的局部变量, 从而达到各个Thread之间的隔离运行。它被广泛应用于框架之间的用户资源隔离、事务隔离等。

6 threadlocal的内存泄露问题

ThreadLocal操作不当会引发内存泄露,最主要的原因在于它的内部类ThreadLocalMap中的Entry的设计。 Entry继承了WeakReference<ThreadLocal<?>>,即Entry的key是弱引用,所以key’会在垃圾回收的时候被回收掉, 而key对应的value则不会被回收, 这样会导致一种现象:key为null,value有值。 久而久之,value累加就会导致内存泄漏。

  • 每次使用完ThreadLocal都调用它的remove()方法清除数据。因为它的remove方法会主动将当前的key和value(Entry)进行清除。
  • ThreadLocal的设计者也意识到了这一点(内存泄漏), 他们在一些方法中埋了对key=null的value擦除操作,这里拿ThreadLocal提供的get()方法举例,它调用了ThreadLocalMap#getEntry()方法,对key进行了校验和对null key进行擦除。
二 线程池
1 线程池的核心参数

java提供了基于Executor构建线程池的方式 ThreadPoolExecutor 中

代码语言:javascript复制
int corePoolSize, //核心线程数
int maximumPoolSize,//最大线程数
BlockingQueue<Runnable> workQueue,任务队列/阻塞队列(array数组,like链表,优先级,delay延迟)
long keepAliveTime, //生存时间
TimeUnit unit,//生存时间的单位
RejectedExecutionHandler handler//拒绝策略
ThreadFactory threadFactory //线程工厂的名称
2 线程池的执行流程
  • 提交任务到线程池中,让线程池中的线程去执行任务
    • 如果有空闲的核心线程数,直接执行
      • 如果没有空闲的核心线程,尝试创建核心线程,去执行任务。
    • 如果已经达到了核心线程数配置
      • 将任务扔到任务队列排队,等待狠心线程执行完其他任务再来执行
        • 如果任务队列满了放不下,放不下任务,构建最大线程数
          • 如果最大线程数也慢了,执行拒绝策略
4 线程池中的ctl是干什么的。

线程池中一个属性,本质 就是int类型的数值,高三位描述线程池的状态,低29位,描述工作线程的数量,线程池在执行任务时,需要多次判断线程池状态,来确定任务是否需要执行,低29位,表述线程池中现存的工作线程数量。

5 线程池的状态有哪些?(5种)
代码语言:javascript复制
running :线程池正在正常工作,可以处理提交的任务
shutdown:调用线程池的shutdown方法,不接受新的任务,但是会处理现有的任务
stop:调用线程池的shutNow(),不接受新的任务,中断正在处理的任务,不管工作队列
ttdying:过度状态,会从shutdown(工作队列和线程位空)和stop转换成ttdying状态,要停止,但是没停止
terminated:当线程池达到了ttdying状态后,在源码中,会自动调用,进入到了terminated状态。线程池就没了。
6 什么是工作线程

worker对象继承aqs实现runable,线程池执行任务,其实就是调用了worker种的run方法内部的runworker方法 线程池中的工作线程是用worker对象表述的 worker - true 核心线程 - false 最大线程数

7 worker对象为什么继承aqs

其实是为了添加标识来判断当前工作线程,是否可以被打断。

8 工作线程保存在哪里?

保存在线程池的hashset里面。

9 拒绝策略

线程池一共有 4个拒绝策略

  • abort 抛出一个异常
  • discard 扔掉不处理了
  • discardOldest 扔掉排队时间久的
  • callerruns 调用者处理服务
10 如何在线程池执行任务前后做任务处理

befareExecute(前置钩子)-默认空实现

afterExecute(后置钩子)-默认空实现

11 如何合理的分配线程池的大小

在分配线程池容量大小时,必须要根据业务类型来决定。 cpu密集 - 更多的是cpu在计算,一直在工作 - 线程数少一些(推荐:cpu内核数 1) io密集 - 更多的时候线程在等待io - 线程数多一些(推荐:cpu内核数*2)

三 并发编程
1 项目哪里用到了多线程
  • 用户注册
    • 异步renwu用户注册,记录日志
    • 定时任务,定期备份日志,备份数据库
2 线程不安全的数据结构

hashmap,arraylist linkedlist

3 线程安全的数据结构

CopyOnWriteArrayList Vector ConcurrentHashMap

4 如果保证多线程的安全性
  1. 通过volatile 关键字修饰变量,可以实现线程之间的可见性, 避免变量脏读的出现,底层是通过限制jvm指令的重排序来实现的 适用于一个线程修改,多个线程读的场景
  2. 通过synchronized锁(任意对象)来实现线程同步,自动锁的思想, 底层实现原理:当有线程进入同步代码块之后,利用jvm的计数器将 锁的标记置为1,当别的线程再想进入的时候,发现锁的标记为1, 该线程就去锁池等待,当第一个线程出来之后,锁的标记会置为0, 之后cpu会随机分配一个线程再次进入同步代码块.
  3. 通过lock锁的机制,进行手动lock,和unlock,但是这种很容易出现死锁。 注意加锁以及解锁的顺序,就可以避免死锁
  4. 通过线程安全的集合类,可以解决并发问题 ConcurrentHashMap CopyonWriteArrayList
  5. 使用并发包下面的原子类,底层使用的是cas机制(乐观锁),可以解决并发问题 atomicInteger 线程安全的原子整型类
  6. 使用线程池来创建和管理线程,也可以一定程度上解决并发问题
  7. 使用ThreadLocal来修饰变量,可以解决并发问题 ThreadLocal底层是怎么实现的? 多个线程会复制一份threadLocao变量的副本进行操作,互不影响,来保证线程安全的
5 volatile与synchronized有什么区别
  • volatile
    • 是一个轻量级的synchronized,都是保证共享变量的可见行,但是保证不了原子性,避免出现脏毒现象
    • 可以避免指令冲排
      • 包含编译器冲排序和运行时冲排序,jvm在编译的时候,对现有的指令进行重新排序,在不改变程序运行结果的情况下,会进行排序
  • synchronized
    • 保证可见行,也能保证原子性
    • 悲观锁
    • 独占锁
    • 可重入锁
    • 非公平锁
    • 获取不到锁的状态
6 并发编程三要素
  • 原则性
    • 要么全成功 要么全失败 不能中段
  • 有序性
  • 可见行
7 cas(自旋锁)/存在的问题

就是 比较和交换 完了再改。

  • aba问题
    • 追加版本号解决
  • cAS是循环判断如果失败次数过多,占用cpu资源
8 aqs

aqs其实是并发包juc下面的类,实现了一个先进先出的队列,底层就是一个双向链表。使用Node实现队列,利用int类型标识状态。在AQS类中有一个叫做state的成员变量,线程会首先尝试获取锁,如果失败就将当前线程及等待状态等信息包装成一个node节点加入到同步队列sync queue里,接着会不断的循环尝试获取锁。

独占方式。 共享方式。

9 ReentrantLock重入锁

ReentrantLock(重入锁)是lock接口的实现类,,悲观锁,底层是aqs

  • 公平锁
    • 先获取锁资源,拿到当前线程的状态,判断有没有在排队。没有的话给锁
    • 如果已经拿到了,在把锁的次数 1
  • 非公平锁(默认)
    • 不管有没排队,直接拿锁,只要能拿到。
    • 非公平锁的效能高一点
10 ReentrantReadWriteLock重入读写锁
  • 实现了ReadWriteLock接口
    • 读写分离
  • 支持公平和非公平
  • 读锁是共享的,写锁是独占的。

三: 集合框架

一 list
1 arraylist底层如何实现?

arrayli - 数组 arraylist.get()是根据index下标查询的,时间复杂度是(0)1。

2 arraylist add方法/扩容

创建空的数组 给elementData,初始化并且没有容量,采用懒加载的形式, 当使用add方法的时候, 首先判断是否需要扩容 - 如果等于空的时候,直接10的大小的数组 - ArrayList 每次扩容之后容量都会变为原来的 1.5 倍左右。 完了通过index负值。

3 arraylist get方法

会先判段是否越界直接根据下标查询

4 arraylist与Vector

相同点都是基于数组实现的,默认的初始容量都是10

  • arraylist
    • 线程不安全
    • 扩容1.5
  • Vector
    • 线程安全
    • 扩容2倍
4 LinkedList底层原理

底层基于链表【双向】实现的。

5 copyonwritearraylist/synchronizedlist
  • copyonwritearraylist
    • 执行修改的时候,会拷贝一份新的数据,代价昂贵,修改后会将原来的集合指向新的集合。ReentrantLock锁
    • 读的话不需要加锁。
    • 读写分离 最终一致
  • synchronizedlist
    • 每个方法都➕synchronized锁了,
    • 写操作可以,读操作并不如copyonwritearraylist
二 map
1 hashmap key是否允许重复

不允许,比较hashcode值和equest值

2 hashmap如何避免内存泄漏问题

内存泄漏,key一直占用内存,不被回收 如果使用hashmap,自定义key对象,一定要重写hashcode值和equals。

3 hashmap底层如何实现?

使用entry对象存放健值对

3.1 arraylist

不需要考虑hash膨胀,但是查询很慢

3.2 (jdk1.7)数组 链表

初始化大小是16 同一个链表中存放的都是hashCode值可能相同,但是内容值却不同

3.3 (jdk1.8)数组 链表 红黑树/hash如何解决hash冲突

初始化大小是16(1<<4),当数组容量大于等于64()并且链表长度大于8的时候,就会把链表转红黑树存储(红黑树的个数小于6的时候转为链表),hash碰撞以后,链表长度就回很长。hashmap通过长度n-1 余hash,降低hash冲突。如果hashmap中元素超过阈值的时候会通过0.75扩展因子扩容,

3 hashmap如果key为null

存放在0个位置

4 hashmap1.7与1.8的区别
6 hashmap与hashtable区别
  • hashmap
    • 线程不安全
    • 允许存放key为null
  • hashtable
    • 默认11
    • 线程安全(synchronized)
    • 不允许存放key为null
7 hashmap与treeMap区别
  • hashmap
    • 无序
  • treeMap
    • 有序
8 concurrentHashmap
  • 线程安全
    • 分段锁(node cas synchronized)[1.8]
    • 分段锁(ReentrantLock)[1.7]
三 set
1 hashset底层实现原理

对于HashSet而言,它是基于HashMap实现的,HashSet底层使用HashMap来保存所有元素,因此HashSet 的实现比较简单,相关HashSet的操作,基本上都是直接调用底层HashMap的相关方法来完成。

0 人点赞