java基础知识

1. 数据结构

java的数据结构有哪些
线性结构数组、链表、哈希表队列、栈
非线性结构有堆、树(二叉树、B树、B+树、红黑树)

常用的集合类有List集合,Set集合,Map集合,其中List集合与Set集合继承了Collection接口,
在这里插入图片描述
List有序可重复的集合接口继承自Collection接口表示元素按照插入顺序排列。
Set无序不重复的集合接口继承自Collection接口表示元素唯一性。
Map键值对映射的集合接口表示具有唯一键和对应值的集合。
集合的遍历可以使用迭代器Iterator来遍历集合中的元素也可以使用增强型for循环foreach来简化遍历操作。迭代器提供了对集合中元素的统一访问方式。
1.List、Set 和 Map 之间有什么区别它们的常用实现类有哪些

List、Set和Map是Java集合框架中的三个核心接口它们之间的区别如下

List
list是有序可重复的集合接口可以按照顺序或者指定的顺序访问和操作元素还可以通过索引访问元素
常用实现类ArrayList、LinkedList
Set
set是无序不重复的集合接口不能通过索引访问元素。
常用实现类HashSet、TreeSet
Map
map是键值对映射的集合接口每个键只能对应一个值。键值均可以为空
常用实现类HashMap、ConcurrentHashMap

3.ArrayList 和 LinkedList 的区别是什么它们适用于不同的场景吗

ArrayList底层是数组查询快增删慢可以通过索引随机访问时间复杂度是O(1),
LinkedList: 底层是链表查询慢增删块不能使用索引访问需要从头结点或者尾结点开始遍历时间复杂度为O(n)

ArrayList适用于需要快速随机访问元素的场景例如需要频繁地根据索引读取或修改元素的情况。
LinkedList适用于需要频繁进行插入和删除操作的场景特别是在中间或开头进行插入和删除操作比较多的情况。
对于大部分常见的情况ArrayList的性能要优于LinkedList因为数组的访问速度更快。
但在某些特定的场景下LinkedList可能会更适合例如需要频繁进行插入和删除操作并且对于随机访问的性能要求较低的情况。

4.HashSet 和 TreeSet 的区别是什么它们如何保证元素的唯一性
HashSet的底层是hash表且是无序不重复的集合接口
TreeSet的底层是红黑树是有序不重复的集合接口

为了保证元素的唯一性HashSet和TreeSet在判断元素是否重复时依赖于元素的equals方法和哈希码

在选择HashSet和TreeSet时需要根据具体的需求进行选择
如果只关心元素的唯一性而不关心元素的顺序可以选择HashSet它的插入、删除和查找操作的性能较好。
如果需要对元素进行排序可以选择TreeSet它会根据元素的顺序进行存储但由于需要维护红黑树的平衡性插入、删除和查找操作的性能稍低于HashSet。
总结HashSet和TreeSet都可以保证元素的唯一性HashSet适用于无序需求而TreeSet适用于有序需求。

5.HashMap 和 HashTable 的区别是什么它们的线程安全性如何
HashMap是非线程安全的存储的键和值都可以为null
而HashTable是线程安全的存储的键和值都不可以为null
HashMap的性能通常比HashTable更好。

6.ConcurrentHashMap 是如何实现线程安全的它与 HashMap 的区别是什么
实现线程安全的几个关键点
ConcurrentHashMap内部使用了分段式的锁将整个数据结构分成一些独立的部分并称为“段”并对不同的段进行了锁的力度的控制。还使用了volatile关键字来确保可见性确保当一个线程修改了某个段的内容后其他线程可以立即看到修改的结果。还使用并发安全的数据结构hashEntry数组和链表与线程安全的迭代器

与HashMap相比ConcurrentHashMap的区别如下
ConcurrentHashMap是线程安全的可以在多线程环境下进行并发操作而HashMap是非线程安全的。在并发环境下ConcurrentHashMap的性能通常比HashMap好,因为它通过分段锁的机制允许多个线程同时进行读操作提高了并发性能。

7.如何遍历集合框架中的元素有哪些遍历方式

1.使用迭代器Iterator通过调用集合的iterator()方法获取迭代器对象然后使用while循环和next()方法逐个访问元素直到遍历完所有元素。


List<String> list = new ArrayList<>();
// 添加元素到列表中
Iterator<String> iterator = list.iterator();
while (iterator.hasNext()) {
    String element = iterator.next();
    // 处理元素
}

2.使用增强型for循环foreach适用于数组和实现了Iterable接口的集合类可以直接通过for循环遍历元素不需要显式使用迭代器。


List<String> list = new ArrayList<>();
// 添加元素到列表中
for (String element : list) {
    // 处理元素
}

3.**使用Lambda表达式和Stream API**从Java 8开始引入了Lambda表达式和Stream API可以通过Stream的forEach()方法对集合进行遍历并结合Lambda表达式进行元素处理。

List<String> list = new ArrayList<>();
// 添加元素到列表中
list.stream().forEach(element -> {
    // 处理元素
});

4.使用普通的for循环适用于数组和实现了RandomAccess接口的集合类通过下标访问元素进行遍历。


List<String> list = new ArrayList<>();
// 添加元素到列表中
for (int i = 0; i < list.size(); i++) {
    String element = list.get(i);
    // 处理元素
}

8.collection与collections的区别

Collection 是一个集合接口
Collection提供了对集合对象进行基本操作的通用接口方法所有集合都是它的子类比如 List、Set 等。
Collections 是一个包装类
Collections是一个工具类它包含了很多静态方法不能被实例化比如排序方法 Collections. sort(list)等。

9.如何实现数组和 List 之间的转换

数组转 List使用 Arrays. asList(array) 进行转换。
List 转数组使用 List 自带的 toArray() 方法。

10.HashMap的实现原理

HashMap 基于 Hash 算法实现的我们通过 put(key,value)存储get(key)来获取。当传入 key 时HashMap 会根据 key. hashCode() 计算出 hash 值根据 hash 值将 value 保存在 bucket 里。当计算出的 hash 值相同时我们称之为 hash 冲突HashMap 的做法是用链表和红黑树存储相同 hash 值的 value。当 hash 冲突的个数比较少时使用链表否则使用红黑树

2.流

IO流根据流向分为 输入流和输出流
根据类型分为字节流与字符流字节流用于处理二进制数据以字节为单位进行读写操作字符流用于处理文本数据以字符为单位进行读写操作。
字节流InputStream、OutputStream、FileInputStream、FileOutputStream等。
字符流Reader、Writer、FileReader、FileWriter等。
还有缓冲流他是基于字节流或字符流的装饰器通过在内存中设置缓冲区来提高读写的效率。缓冲流可以减少对底层流的频繁读写操作从而提高性能。常用的缓冲流类有
字节缓冲流BufferedInputStream、BufferedOutputStream。
字符缓冲流BufferedReader、BufferedWriter。

3.线程池 多线程

3.1线程

1.创建线程有三种方式

1.继承Thread重写run方法
2.实现Runable接口重写run方法
3.实现Callable接口重写call方法

2.Runnable与callable区别

Runnable规定的方法是run()Callable规定的是call()
Runnable 的任务执行后无返回值Callable的任务执行后有返回值
call()方法可以抛出异常,run()方法不可以因为run()方法本身没有抛出异常所以自定义的线程类在重写run方法的时候也无法抛出异常
运行Callable 任务可以拿到一个Future对象表示异步计算的结果。

3.线程的状态
五态1.新建2.就绪3.运行4.阻塞5.终止
七态1.新建2.就绪3.运行4.阻塞5.等待6.超时等待7.终止

线程生命周期,主要有五种状态:

当线程创建后进入新建状态调用start()方法进入就绪状态cup分配时间片拿到分配的时间片后进入运行状态。

新建状态(New) : 当线程对象创建后就进入了新建状态.如:Thread t = new MyThread();
就绪状态(Runnable):当调用线程对象的start()方法,线程即为进入就绪状态.
运行状态(Running):当CPU调度了处于就绪状态的线程时,此线程才是真正的执行,即进入到运行状态
阻塞状态(Blocked):处于运状态中的线程由于某种原因,暂时放弃对CPU的使用权,停止执行,此时进入阻塞状态,直到其进入就绪状态才有机会被CPU选中再次执行.
根据阻塞状态产生的原因不同,阻塞状态又可以细分成三种:
等待阻塞:运行状态中的线程执行wait()方法,本线程进入到等待阻塞状态
同步阻塞:线程在获取synchronized同步锁失败(因为锁被其他线程占用),它会进入同步阻塞状态
其他阻塞:调用线程的sleep()或者join()或发出了I/O请求时,线程会进入到阻塞状态.当sleep()状态超时.join()等待线程终止或者超时或者I/O处理完毕时线程重新转入就绪状态
死亡状态(Dead):线程执行完了或者因异常退出了run()方法,该线程结束生命周期

3.2 线程池

线程池的创建有七个参数分别是核心线程数最大线程数工作队列线程工厂存活时间存活时间单位拒绝策略

1. 线程池的工作流程

接收到任务首先判断一下核心线程是否已满如果未满则去创建一个新的线程执行任务如果核心线程数已满工作对列未满将线程存储到工作对列中等待核心线程获取执行如果工作对列已满且线程数小于最大线程数则创建一个新的线程线程处理任务需要获取全局锁如果线程数超过了最大线程数按照四种拒绝策略处理任务四种拒绝策略有1.提交任务的线程自己去执行该任务 2.默认拒绝策略会抛出异常 3.直接丢弃任务没有任何异常抛出4.丢弃最老的任务其实就是把最早进入工作队列丢弃然后把新任务加入到工作对列

2. 三种常见的线程池

创建线程池的方式有多种我了解到的有1.固定大小的线程池可控制并发的线程数超出的线程会在工作队列中等待。2.带有缓存的线程池。3可以执行延迟任务的线程池

2.为什么使用线程池

线程池可以降低线程生命周期的系统开销问题加快响应速度;统筹内存和CPU的使用避免资源使用不当;可以统一管理资源

4.锁

1. 锁的类型
锁分为乐观锁、悲观锁、synchronized
乐观锁是每次去拿数据的时候都认为别人不会修改所以不会上锁但是在提交更新的时候会判断一下在此期间别人有没有去更新这个数据。
悲观锁每次去拿数据的时候都认为别人会修改所以每次在拿数据的时候都会上锁这样别人想拿这个数据就会阻止直到这个锁被释放
2…lock和synchronize的区别synchronized
都是解决线程安全的工具synchronize是java中的同步关键字而lock是J.U.C包中提供的接口
synchronized 可以给类、方法、代码块加锁而 lock 只能给代码块加锁。
synchronized 不需要手动获取锁和释放锁使用简单发生异常会自动释放锁不会造成死锁而 lock 需要自己加锁和释放锁如果使用不当没有 unLock()去释放锁就会造成死锁。

2.lock和synchronize的区别

这个问题我从四个方面来回答
第一个从功能角度来看lock和synchronize都是java中用来解决线程安全问题的一个工具
第二个从特性来看首先synchronize是java中的同步关键字而lock是J.U.C包中提供的接口而这个接口它有很多的实现类其中就包括reentrantLock这样一个重入锁的实现其次synchronize可以通过两种方式去控制锁的力度一种是把synchronize关键字修饰在方法层面另一张种是修饰在代码块上并且我们可以通过synchronize加锁对象的生命周期来控制锁的作用范围比如锁对象是静态对象或者类对象那么这个锁就属于全局锁如果锁对象是普通实例对象那么这个锁的范围取决于这个实例的生命周期。lock中锁的力度是通过它里面提供的lock( )方法和unlock( )方法来决定的包裹在两个方法之间的代码是可以保证线程安全的而锁的作用域取决于lock实例的生命周期。
lock比synchronize的灵活性更高lock可以自主的去决定什么时候加锁什么时候释放锁只需要调用lock和unlock方法就可以了。同时lock还提供了非阻塞的竞争锁的方法叫trylock()这个方法可以通过返回true/false来告诉当前线程是否已经有其他线程正在使用锁而synchronize由于是关键字所以他无法去实现非阻塞竞争锁的方法另外synchronize锁的释放是被动的就是当synchronize同步代码块执行结束以后或者代码出现异常的时候才会被释放。
最后lock提供了公平锁和非公平锁的机制公平锁是指线程竞争锁资源时候如果已经有其他线程正在排队或者等待锁释放那么当前竞争锁的线程是无法插队的而非公平锁就是不管是否有线程在排队等待锁他都会去尝试竞争一次锁synchronize只提供了一种非公平锁的实现。
第三个从性能方面来看synchronize和lock在性能方面相差不大。在实现上会有一定的区别synchronize引入了偏向锁轻量级锁重量级锁以及锁升级的机制去实现锁的优化而lock中用到了自旋锁的方式去实现性能优化以上就是我对这个问题的理解。

3.说一下死锁
当线程 A 持有独占锁a并尝试去获取独占锁 b 的同时线程 B 持有独占锁 b并尝试获取独占锁 a 的情况下就会发生 AB 两个线程由于互相持有对方需要的锁而发生的阻塞现象我们称为死锁。
产生死锁的原因1 因为系统资源不足。2 进程运行推进的顺序不合适。3 资源分配不当等。
怎样防止死锁
1、尽量使用try lock( )防范设置超时时间超时则关闭防止死锁
2、使用安全类concurrent 代替自己手写锁
3、减少锁的使用粒度避免几个功能共用一把锁
4、减少同步代码块

5.面向对象

5.2 封装、继承、多态

1.谈谈对面向对象的理解

面向对象 ( Object Oriented ) 是将现实问题构建关系然后抽象成 类 ( class )给类定义属性和方法后再将类实例化成 实例 ( instance ) 通过访问实例的属性和调用方法来进行使用。面向对象有三大特性封装、继承和多态。封装隐藏了类的内部实现机制可以在不影响使用功能的情况下改变类的内部结构同时也保护了数据。隐藏内部细节只暴露给外界访问方法。继承子类继承父类表明子类是特殊的父类并且拥有父类不具有的一些属性或方法Java通过extends关键字实现继承父类中通过private 定义的变量和方法不会被继承子类不可以直接操作父类私有的private变量和方法多态指的是类和类的关系两个类由继承关系存在有方法的重写因此可以调用父类引用指向子类对象。多态必备三要素继承重写父类引用指向子类对象

2.继承

通过extends关键字来构成继承关系
子类无法继承父类的私有方法无法继承构造方法
子类在创建对象时默认会先调用父类的构造方法
支持单继承具有传递性耦合性非常强
子类可以修改父类的功能也可以拓展自己的功能

3.封装
把相关的数据封装成一个类组件仅对外提供操作数据的方法

4.多态

多态的前提是继承重写、父类引用指向子类对象
多态对象使用的成员变量是父类的
若使用的方法被重写了则使用的是子类的方法
静态资源 调用谁的就是谁的
不用关心某个对象是什么类型的就可以直接使用其方法

5.2抽象、接口

1.抽象

Java中被abstract关键字修饰的方法叫抽象方法只有声明没有方法体。
被abstract关键字修饰的类叫抽象类可以不包含抽象方法不可被实例化
子类继承抽象类之后a.继续抽象b.重写父类的所有抽象方法
abstract不能与private连用子类无法重写
abstract不能与static连用存在加载顺序的问题
abstract不能与final连用无法重写

2.接口

被interface修饰的类叫接口理解为是一种特色的类由全局常量和公共的抽象方法所组成。通过implements来实现接口接口与类之间可以多实现接口与接口之间可以实现多继承降低了耦合性

3.接口与抽象类的区别

实现抽象类的子类使用extends来继承接口必须使用implements来实现接口
构造函数抽象类可以有构造函数接口没有
实现数量类可以实现很多个接口但是只能继承一个抽象类
访问修饰符接口中的方法默认使用public抽象类中的方法可以是任意访问修饰符
何时使用
需要为一些类提供公共的实现代码时应优先考虑抽象类
当注重代码的扩展性跟可维护性时应当优先采用接口

5.3重写 、 重载

1.重写
重写也可以看做覆盖子类重新定义父类中具有相同名称和参数的虚函数函数特征相同但函数的具体实现不同它主要在继承关系中出现

1.继承以后子类就拥有了父类的功能。
2.在子类中可以添加子类特有的功能也可以修改父类原有的功能。
3.子类中方法的签名与父类完全一致时会发生覆盖/复写的现象
4.父类的私有方法不能被重写
5.重写的要求是两同两小一大

两同方法名 参数列表完全一致
两小

子类返回值类型小于等于父类的返回值类型
子类抛出异常小于等于父类方法抛出异常

一大子类方法的修饰符权限要大于等于父类被重写方法的修饰符权限

2.重载
重载是函数名相同参数列表不同重载只是在类的内部存在但是不能返回类型来判断。

1.重载overload与重写override的区别

重载是一个类中的现象同一个类中存在方法名相同参数列表不同的方法
重写是指建立了继承关系之后子类对父类的方法不满意可以重写遵循两同两小一大的原则
重载的意义是为了外界调用方法时方便不管传入什么样的参数都可以匹配到对应的同名方法
重写的意义在不修改源码的情况下进行功能的修改与拓展OCP原则面向修改关闭面向拓展开放

5.4final

1.final 的作用

被final修饰的类是最终类不能被继承
被final修饰的方法是最终实现不可被重写
被final修饰的变量是常量必须初始化且不可被改变

2.被final修饰的属性是否可以被修改为什么
不可以

6.设计模式

1.设计模式有

单例模式Singleton Pattern确保一个类只有一个实例并提供全局访问点。
工厂模式Factory Pattern通过工厂类创建对象隐藏对象的具体实现。
抽象工厂模式Abstract Factory Pattern提供一个创建一系列相关或相互依赖对象的接口而无需指定其具体类。
建造者模式Builder Pattern将一个复杂对象的构建过程与其表示分离使同样的构建过程可以创建不同的表示。
原型模式Prototype Pattern通过复制已有对象来创建新对象避免了创建过程中的复杂性。
适配器模式Adapter Pattern将一个类的接口转换为客户端所期望的另一个接口。
装饰器模式Decorator Pattern动态地给对象添加额外的职责避免使用子类扩展功能。
代理模式Proxy Pattern通过代理对象控制对实际对象的访问并提供额外的功能。
观察者模式Observer Pattern定义了对象之间的一对多依赖关系当一个对象改变状态时所有依赖它的对象都会得到通知。
策略模式Strategy Pattern定义一系列算法将每个算法都封装起来并且使它们可以互换。
模板方法模式Template Method Pattern定义一个算法的骨架将一些步骤延迟到子类中实现。
迭代器模式Iterator Pattern提供一种访问容器对象中各个元素的方法而不需要暴露其内部结构。

2.设计模式的使用场景

单例模式Singleton Pattern适用于需要确保只有一个实例存在的情况比如数据库连接池、日志记录器等。
工厂模式Factory Pattern适用于需要根据参数创建不同对象的情况比如创建不同类型的数据库连接。
抽象工厂模式Abstract Factory Pattern适用于需要创建一系列相关对象的情况比如创建不同操作系统下的窗口、按钮等界面组件。
建造者模式Builder Pattern适用于创建复杂对象的情况通过将对象的构建步骤进行组合和组装使得创建过程更灵活、可扩展。
原型模式Prototype Pattern适用于需要创建大量相似对象的情况通过复制已有对象来提高创建效率。
适配器模式Adapter Pattern适用于需要将一个类的接口转换为另一个类的情况解决接口不兼容的问题。
装饰器模式Decorator Pattern适用于需要动态地为对象添加额外功能的情况比如为文件流添加缓冲区、加密等操作。
代理模式Proxy Pattern适用于需要通过代理对象控制对实际对象的访问的情况可以实现延迟加载、权限控制等功能。
观察者模式Observer Pattern适用于对象间存在一对多的依赖关系当一个对象状态发生改变时通知所有依赖它的对象。
策略模式Strategy Pattern适用于需要在运行时动态选择算法的情况通过定义一系列算法并封装起来使得可以灵活切换算法实现。
模板方法模式Template Method Pattern适用于定义一个算法的骨架将一些步骤延迟到子类实现的情况。
迭代器模式Iterator Pattern适用于需要遍历集合对象的情况通过提供一种统一的访问方式可以遍历不同类型的集合对象。

7.反射

1.反射是什么

Java 程序开发语言的特征之一可以在运行时获取一个类的所有信息可以获取到任何定义的信息包括成员变量成员方法构造器等并且可以操纵类的字段、方法、构造器等部分。

2.获取class对象的三种方式

		对象.getclass().class
		Class.forname("类全路径")

3.反射的运行原理

磁盘中的.java源码通过javac编译成.class字节码文件classLoad类加载器将.class字节码加载到虚拟机中文件中的成员变量封装为成员类方法被封装为成员方法类构造方法被封装为构造类运行时则进入运行阶段加载到内存中变为class类

8.异常

1.常见异常
1、算术异常类ArithmeticException
2、空指针异常类NullpointerException
3、类型强制转换异常ClassCastException
4、数组下标越界异常ArrayIndexOutOfBoundsException
5、文件未找到异常FileNotFoundException
6、操作数据库异常SQLException
7、I/O 异常的根类IOException
2.异常处理
处理异常有两种方式

1.在方法声明的位置上使用throws关键字抛出谁调用就抛给谁
2.使用try…catch语句对异常进行捕捉。这个异常不会上报自己把异常事件处理了。异常抛到此处位置为止不在上抛了。

3.异常的分类

1.error
Error是非程序异常即程序不能捕获的异常一般是编译或者系统性的错误如OutOfMemorry内存溢出异常等
2.exception
Exception是程序异常类由程序内部产生。Exception又分为运行时异常非运行时异常类。

9.常用类

9.1 String

1.String常用的方法有

indexOf()返回指定字符的索引。
charAt()返回指定索引处的字符。
replace()字符串替换。
trim()去除字符串两端空白。
split()分割字符串返回一个分割后的字符串数组。
getBytes()返回字符串的 byte 类型数组。
length()返回字符串长度。
toLowerCase()将字符串转成小写字母。
toUpperCase()将字符串转成大写字符。
substring()截取字符串。
equals()字符串比较。

2.Stringbuilder / StringBuffer的区别
1.从线程安全方面来说:StringBuffer是线程安全的。StringBuilder是线程不安全的
2. 从执行效率上来说StringBuilder > StringBuffer > String
3.源码体现本质上都是在调用父类抽象类AbstractStringBuilder来干活只不过Buffer把代码加了同步关键字使得程序可以保证线程安全

9.2 Object

常用方法 toString( ), equles( ) getClass(), hashCode( )

9.3 数组

1.数组常用方法

push : 尾部追加
sort: : 排序
join : 分隔
reverse : 反转
splice 删除元素或者添加元素

2.数组扩容原理
利用数组复制方法可以变通的实现数组扩容。
System.arraycopy()可以复制数组。
Arrays.copyOf()可以简便的创建数组副本。
创建数组副本的同时将数组长度增加就变通的实现了数组的扩容。

10 其他

Linux基础

Linux常用命令大全

常用指令
解压 tar
查看进程 ps
关闭进程 kill /kill -9
查看日志 grep tail

cookie / session的区别

存储位置不同session 存储在服务器端cookie 存储在浏览器端。
安全性不同cookie 安全性一般在浏览器存储可以被伪造和修改。
容量和个数限制cookie 有容量限制每个站点下的 cookie 也有个数限制。
存储的多样性session 可以存储在 Redis 中、数据库中、应用程序中而 cookie 只能存储在浏览器中。

转发 、 重定向的区别

地址栏 url 显示foward url 不会发生改变redirect url 会发生改变
数据共享forward 可以共享 request 里的数据redirect 不能共享
效率forward 比 redirect 效率高。

http与https 的区别

HTTP协议以明文方式发送内容不提供任何方式的数据加密。HTTP协议不适合传输一些敏感信息
https则是具有安全性的ssl加密传输协议
http和https使用的是完全不同的连接方式用的端口也不一样前者是80后者是443。
https协议需要到ca申请证书。HTTPS协议是由SSL+HTTP协议构建的可进行加密传输、身份认证的网络协议要比http协议安全。

阿里云国内75折 回扣 微信号:monov8
阿里云国际,腾讯云国际,低至75折。AWS 93折 免费开户实名账号 代冲值 优惠多多 微信号:monov8 飞机:@monov6
标签: Java