八股
2025-12-01 23:17:03 0 举报
AI智能生成
八股
作者其他创作
大纲/内容
MYsql
数据库基本概念
数据库是什么?数据库是一种持久化结构化存储数据的系统
关系型数据库用表来组织数据,表由行和列组成,表与表之间由主键和外键相关联
一个mysql实例中由多个数据库组成,数据库由表构成,表中是数据行
事务:一组要么全部成功,要么全部失败的操作
引擎:MySQL基本都用的Innodb(支持事务,行级锁,外键)
存储层级
实例
数据库
表
行
列
SQL四大类
DDL:数据定义,定义结构,create,alter,drop,rename
DML:数据修改,修改数据,Insert ,update,delete
DQL:数据查询,查询数据,select from where
DCL:权限控制,commit,rollback,grant,revoke
常用数据类型
整数:tinyint int bigint(1 4 8个字节大小)
小数:DECIMAL(精确,钱相关用它),float,double近似科学统计使用
字符串:CHAR定长字符串,VARCHAR不定长字符串,TEXT长文本
日期时间:DATE,DATETIME
布尔:BOOLEAN(0/1)
枚举:enum/set
二进制:BLOB
约束
PRIMARYKEY主键,一行的唯一标识
UNIQUE,唯一
NOTNULL:非空
DEFAUlT:默认值
FOREIGNKEY:外键
聚合函数:
count(*),统计行数,包括null
count(xxx),统计xxx非null的行数
sum/avg/MAX/MIN:忽略null,如果全是null,结果就是null
分组函数
group by规则:凡事非聚合列,都必须出现在GroupBy
job就是非聚合列,count(*)就是聚合列
where VS having
where适用于分组前过滤“行”且性能更好
having适用于分组后过滤“组”
事务
定义:事务就是一系列要么全部成功要么全部失败的数据库操作单元
作用:保证数据一致性,保证数据正确性
四大特性(ACID)
A(automicity):原子性
C(consistency):一致性
I(Isolation):隔离性
D(Durability):持久性
事务隔离级别
定义:事务是控制并发任务的可见性和范围,平衡数据一致性和性能
分类
读已提交:允许读其他事物未提交的数据变更
读未提交:仅读取其他事物已提交的数据变更
可重复读:同一事物多次查询结果一致
串行化:完全顺序执行事务,类似单线程操作
并发问题:
脏读:只有读已提交会发生,读到别的事务还没提交的数据
不可重复读:读未提交级别及以下会发生,同一事务读两次行内数据不一样
幻读:可重复读以下会发生,可重复读用Next-key lock能避免,同一事务读两次数据数量不一样
串行化不会出现以上任何问题。
索引
索引概述:是一种用于快速定位数据的一种特殊数据结构,可以类比于书的目录
作用:
加速查询
快速排序
保证唯一性
加速标的链接
代价:
额外的空间
插入更新等操作时会需要维护,降低写入性能
索引类型
B+树索引:InnoDB默认;有序,适合范围查询,排序,前缀匹配
哈希索引:Memory 引擎支持,等值查找块,不适合范围/排序
全文索引:适用于文本检索
空间索引:地理空间数据
JDBC
sql注入:通过字符串拼接让用户绕过数据库数据直接访问权限
解决方法:做参数化查询,输入校验,把‘’转义,最小化数据库权限,使用安全框架
jdbc缺点:
硬编码
繁琐
性能差
Mybatis怎么解决
用配置文件解决硬编码问题
框架封装简化操作
使用连接池技术避免浪费
Mybatis:
是什么?
持久层框架,简化jdbc的开发
持久层是什么?是数据访问层,用于操作数据库
主键返回机制
就是mybatis如果配置了显式配置 useGeneratedKeys=true + keyProperty,不然不会返回主键
多参数时参数传入机制
单参数时可以使用#{传入参数名称}这种 ,但是如果是多参数,必须使用模板化#{param1},#{param1}或者#{arg1}这种,或者使用@param参数在传入的参数前面加上变量名
使用了连接池技术,介绍一下
如果没有连接池,客户端执行sql的流程是创建对象,执行sql,执行完再把对象关闭来释放资源。每次执行sql都需要创建对象销毁对象导致资源消耗
数据库连接池是什么?
是一种容器用来分配管理数据库连接
数据库连接池做了什么?
在程序启动时,会在数据库连接池中创建一些连接对象,允许程序重复使用连接对象,执行完再送回连接池
释放超过最大空闲时间的对象
好处是什么?
资源重用,不会重复创建销毁对象
提高响应速度,减去了创建对象的时间
避免数据库连接遗漏,释放超过最大空闲时间的对象
lombok
Lombok是一个实用的Java类库,可以通过简单的注解来简化和消除一些必须有但显得很臃肿的Java代码
@data,@Getter/@Setter
@ToString :会给类自动生成易阅读的 toString 方法 |
@EqualsAndHashCode :根据类所拥有的非静态字段自动重写 equals 方法和 hashCode 方法 |
@Data :提供了更综合的生成代码功能(@Getter + @Setter + @ToString + @EqualsAndHashCode) |
@NoArgsConstructor :实体类生成无参的构造器方法
@AllArgsConstructor :为实体类生成除了static修饰的字段之外带有各参数的构造器方法。
@ToString :会给类自动生成易阅读的 toString 方法 |
@EqualsAndHashCode :根据类所拥有的非静态字段自动重写 equals 方法和 hashCode 方法 |
@Data :提供了更综合的生成代码功能(@Getter + @Setter + @ToString + @EqualsAndHashCode) |
@NoArgsConstructor :实体类生成无参的构造器方法
@AllArgsConstructor :为实体类生成除了static修饰的字段之外带有各参数的构造器方法。
Spring
Spring
是什么?
是一款轻量级框架,核心思想包括IOC,AOP,DI
IOC:控制反转,将创建和管理对象生命周期的权限交给框架,使用者无需自行new对象,而是由框架统一管理实现组件间解耦。
AOP:spring通过AOP将横切关注点(例如日志,事务等)与业务相分离,通过动态代理技术实现增强。例如@transitional注解,并未对业务逻辑进行更改却可以使用事务
DI:依赖注入,Spring通过依赖注入,动态将对象注入到目标类中,避免硬编码依赖关系,是代码更灵活,易测试,这是实现IOC的具体手段
IOC
什么是Spring Bean?如何将一个类声明为Spring Bean?
Spring Bean是一种交由Spring IOC管理的对象 。他是Spring框架的基本组成架构,所有被容器管理的组件(Service、Repository、Controller等)都是bean
注解和xml方式,注解比较常用
DI依赖注入的方式有几种?答:三种
一:构造器注入:通过构造器函数传递对象,确保对象初始化时依赖已就绪
二:setter注入:通过setter注入比较灵活但是依赖可能未完全初始化
三:直接@autowired注入,代码简洁但是隐藏依赖关系
bean作用域
单例(singleton):IoC容器中只有唯一的Bean实例。Spring中的Bean默认都是单例的,只有一个,是单例设计模式的应用。
多例(prototype):每次获取都创建一个新的Bean实例。所以说连续getbean两次,得到的是不同的bean实例
Request(仅 Web 应用可用):每次http请求都会创建一个。作用域就是当前http请求
session(仅 Web 应用可用):只在http session中有效
websocket(仅 Web 应用可用):每一次websocket有效
如何判断一个bean是否是线程安全的?
检查bean的作用域:
若为多例(prototype),每次请求都是独立实例,无资源共享,故不存在线程安全问题
若为单例(singleton):需要进一步分析bean的状态
看有无状态:
无状态:线程安全
有状态:需要调整作用域或者使用同步机制确保线程安全
有状态无状态分别是什么?
无状态:不存储任何可变数据(既没有可变的成员变量),每次调用仅依赖参数和局部变量。
特点:线程安全,适合工具类,service层逻辑。
有状态:包含成员变量,方法的执行结果可能会依赖这些变量
特点:非线程安全,需要同步机制同步
适合于需要缓存和状态记录的场景,如计数器和缓存服务
如何保证一个有状态的bean线程安全?
加锁保证线程安全:使用synchronized或者lock锁保证
无锁并发:使用CAS和while自旋的方式保证线程安全。
使用线程隔离:比如使用threadLocal保证各个线程都有自己的副本变量
避免可变成员变量:将有状态的bean设计成无状态的bean
bean的生命周期
指的是bean从创建到销毁的整个生命周期,由IoC容器管理
BeanFactory和FactoryBean
从定义上,beanfactory是bean工厂,是ioc容器中的顶级接口常用语管理bean,最常用的方法时getBean方法。而FactoryBean是一个工厂Bean,实现它的唯一目的是为了创建其他的特殊bean对象,常用的方法有:getObject(),getObjectType(),isSingleton()方法。
简单来说就是BeanFactory负责管理bean,FactoryBean负责生产对象的特殊的Bean。
FactoryBean和普通Bean的区别:
普通Bean:仅包含类本身
FactoryBean:放的不是它本身而是他要生产的对象。
applicationContext和FactoryBean有什么区别?
applicationContext是BeanFactory的一个子接口,在本体之上实现了更强大的功能,比如说国际化,资源加载和事件监听等。applicationContext会在容器创建的时候一次项加载所有Bean对象,而BeanFactory是延迟加载的,就是说只有在getBean的时候才会去创建Bean对象,刷新的时候不会创建。这么一对比就能看出来A比BeanFactory快但是消耗更多内存。
SpringAOP如何理解
定义:AOP是一种编程范式,可以将横切关注点从业务逻辑中删除,通过动态代理技术 实现代码模块化管理 。
好处:可以在不修改源码的情况下进行逻辑增强
关键概念:横切关注点:多个模块公用的功能比如说:日志事务权限校验。
类比:如果把源码比作一本书,那么aop就像是 便利贴, 在没修改书中内容的情况下,在特定页码添加注释
通知类型:
Before:前置通知,目标对象的方法调用之前通知
After:后置通知,目标对象方法调用之后通知
AfterThrowing:失败通知,目标对象运行中抛出/处罚异常。与AfterReturning互斥
AfterReturning:成功返回通知,目标对象返回值之后触发
Around:环绕通知,编程式控制目标对象的方法调用。环绕通知是所有通知类型中可操作范围最大的一种,因为它可以直接拿到目标对象,以及要执行的方法,所以环绕通知可以任意的在目标对象的方法调用前后搞事,甚至不调用目标对象的方法
与AspectJ对比
循环依赖
循环依赖是指 Bean 对象循环引用,是两个或多个 Bean 之间相互持有对方的引用,例如 CircularDependencyA → CircularDependencyB → CircularDependencyA。
解决方法
Spring 创建 Bean A 的实例(此时属性还没注入)。
把 A 的“半成品”放进三级缓存。
创建 Bean B 时发现依赖 A,就从缓存中拿到 A 的半成品注入。
A、B 最终都完成依赖注入。
把 A 的“半成品”放进三级缓存。
创建 Bean B 时发现依赖 A,就从缓存中拿到 A 的半成品注入。
A、B 最终都完成依赖注入。
构造函数注入循环依赖怎么解决,以上方法解决不了
通过@lazy延迟加载
SpringBoot
前端参数传递:分为原始传递和Springboot传递
原始传递:使用HttpServletRequest,获取相关参数
SpringBoot传递:简单参数,参数名与形参变量名相同,定义同名的形参即可接收参数。例如
如果参数不相同,则需要使用@RequestParam注解。例如
特殊参数比如json数据,需要使用@RequestBody注解
路径参数@PathVariable,格式为
响应参数
1xx:信息性状态码
2xx:成功
3xx:重定向。301 Moved Permanently(永久重定向)、302 Found(临时重定向)、304 Not Modified(使用缓存)
4xx:客户端错误。400 Bad Request(请求错误)、401 Unauthorized(未认证)、403 Forbidden(无权限)、404 Not Found(资源不存在)
5xx:服务器错误。500 Internal Server Error(服务器内部错误)、502 Bad Gateway(网关错误)、503 Service Unavailable(服务不可用)
maven知识点
作用
依赖管理
同一项目架构
构建项目
package和install区别:package将项目编译测试打包好,install不仅会干package会干的活还会将打包好的放在本地仓库
http协议:
基于tcp,安全(TCP是一种面向连接的(建立连接之前是需要经过三次握手)、可靠的、基于字节流的传输层通信协议,在数据传输方面更安全)
无状态协议:对于数据没有记忆能力。每次请求-响应都是独立的
tomcat
一种web服务器
服务器软件:
服务器软件本质是一个运行在服务器设备上的应用程序
能够接收客户端请求,并根据请求给客户端响应数据
JAVASE
学前自测
面向对象三大特性?封装继承和多态
多态怎么理解?同一种方法如果参数不同那么对应的处理方法不同
什么是向上转型和向下转型?不会?
java可以多继承么?不可以。为什么只能单继承?为了保证同名方法只有一个直系父类同名方法
重载和重写的区别?重载是在同一类下同名方法但是参数不同的不同处理方法,重写是父类的子类中和父类同名方法的不同处理。
为什么需要内部类?什么是匿名内部类
静态内部类和非静态内部类有什么区别
静态内部类的使用场景是什么
接口和抽象类有什么区别?有印象,记得比较模糊
封装继承和多态
封装
含义:字面上就是包装的意思,专业点就是信息隐藏。是指利用抽象将数据和基于数据的操作包装到一起,使其成为一个不可分离的独立实体。
好处:第一可以隐藏信息,操作的详细实现。第二可以减少耦合。第三可以对类成员进行更精确的控制。第四可以对类内部的结构自由修改。
继承
含义:继承就是子类继承父类的属性和方法,使得子类对象具有父类的属性和方法(或者只继承方法)
为什么需要继承?使用继承不仅可以使得代码大大减少,还可以使代码结构更加清晰。
java虽然本身不支持多继承,但是有许多方法可以实现类似多继承的功能:
内部类
多层继承:子类继承父类,父类A再继承父类B,这叫做多层继承。子类会拥有A和B所有的属性和方法,同名属性方法遵循就近原则。
接口。类和接口相比,类就是一个实体,拥有属性和方法,而接口更倾向于一组方法。
如何实现继承?
extends关键字。class 子类名 extends 父类名{}
继承的特点:继承的主要内容就是子类继承父类,并重写父类方法。使用子类的属性和方法时首先需要通过构造方法去创建一个对象,在构造方法中会调用一些子类和父类的属性和方法,这时候就需要用到this和super关键字。
this关键字:表示当前对象,是指向自己的引用。
super关键字,表示父类对象,指向的是自己的父类。
构造方法是一种特殊的方法,它是一个与自己类同名的方法。继承中有以下几点需要注意:
父类的构造方法不能继承
子类的构造方法必须调用父类的构造方法
如果子类没有显形调用父类构造方法,则系统默认调用父类的无参构造方法
方法重写:指的是与父类一模一样的方法(包括方法名,返回值类型,参数列表),但是方法不同。可以理解为外壳不变,但是核心内容重写。
方法重载:指的是方法名相同参数不同,其他有条件可能不同。
继承和修饰符。
访问修饰符:访问权限:public>protected>private,子类从父类继承的时候访问权限不可以降低,作用域不能比父类小
非访问修饰符:static,final,Abstract
static:使用该修饰符修饰的方法和变量称为静态方法,静态变量,他们可以直接通过类访问,不需要创建对象来访问成员。
final:最终的,最后的含义。
变量:一旦被赋值后,就不能重新被赋值。
方法:父类的final方法可以被子类继承,但是不能被子类改写
类:final类不能被继承
Abstract:主要用来修饰类和方法。称为抽象类和抽象方法
抽象方法:有很多类的方法都是相似的,但具体内容有不太一样,所以只能抽取他们的声明,没有具体的方法体,即抽象方法可以表达概念但是不能具体实现
抽象类:有抽象方法的类一定是抽象类,抽象类就是可以表达概念但是无法构造实体的类
多态
含义:指的是同一类的对象在不同情况下表现得不同行为和状态。
子类可以继承父类的字段和方法,子类对象可以直接使用父类中的方法和字段(私有的不行)。
子类可以重写从父类继承来的方法,使得子类对象调用这个方法时表现出不同的行为。
可以将子类对象赋给父类类型的引用,这样就可以通过父类类型的引用调用子类中重写的方法,实现多态。
子类可以重写从父类继承来的方法,使得子类对象调用这个方法时表现出不同的行为。
可以将子类对象赋给父类类型的引用,这样就可以通过父类类型的引用调用子类中重写的方法,实现多态。
目的:是为了提高代码的灵活性和可拓展性,是的代码更容易维护和扩展
向上转型和向下转型:
向上转型:父类引用指向子类对象,Animal animal =new cat();animal.sing();
因为是父类的引用,所以不能使用子类独特的方法,只能使用父类方法或者子类重写过的父类方法。
向下转型:子类引用指向父类对象,Animal animal = new Cat();Cat cat=(cat)animal(); cat.sing();需要强制类型转换,是不安全的。常用于:恢复子类功能:当需要操作子类特有方法时
多态扩展:结合instanceof实现分支逻辑
处理集合元素:从父类集合中取出特定子类对象
多态扩展:结合instanceof实现分支逻辑
处理集合元素:从父类集合中取出特定子类对象
接口抽象类区别
接口:是抽象方法的集合,是为了实现api的定义和具体实现相分离。接口不能实例化,接口中只有抽象方法和静态方法。
抽象类:抽象类是用Abstract修饰的java类,是为了实现代码复用。抽象类除了不能实例化以外和普通java类基本没区别,他可以包含普通方法和抽象方法,也可以不包含抽象方法。但是普通类不能包含抽象方法,只要包含抽象方法就得改成抽象类。
重载与重写的区别:
重写:指的是子类继承父类的方法,返回类型,参数,名称都相同但是具体实现不同,也就是壳同内不同。目的是为了实现子类实现特有功能。
细节:子类的比父类范围小,static方法不能重写但是能重新声明,final不能重写
重载:指的是同一类下同名方法的参数不相同,返回类型可相同也可以不相同。目的是为了保证对于不同参数有对应的具体实现。
问题:1. 形参和实参的区别是什么?
2. Java是值传递还是引用传递?
3. 值传递和引用传递区别是什么?
4. final作用是什么?
5. final、finally、finalize 有什么不同?
6. static作用是什么?
7. static 和 final 区别是什么?
2. Java是值传递还是引用传递?
3. 值传递和引用传递区别是什么?
4. final作用是什么?
5. final、finally、finalize 有什么不同?
6. static作用是什么?
7. static 和 final 区别是什么?
形参和实参区别?形参作用于方法内部,实参是方法引用时的参数。
一定是值传递,但是不同数据类型传递的值不相同。
一般数据类型(int long等)包装类型(Short Integer)传递的是它本身的值,函数不会对实参造成修改
引用数据类型如String等,传递的是地址,会对实参造成修改。
final作用?修饰方法,类,变量。方法不可重写,类不可继承,变量不能修改。
finally:是java代码保证代码一定执行的一种机制。可以用try-finally或者try-catch-finally来进行类似jdbc关闭以及保证unlock锁等动作。
finalize:是基础类java.long.object的一个方法,设计的目的是保证对象在垃圾收集前完成特定资源的回收。
static,静态,修饰变量方法类,和代码块。可以被直接用类名.静态调用。静态方法只能访问静态变量,非静态方法两种都能使用。、
1. Java基本数据类型有几种?各占多少位?
2. 基础类型和包装类有什么区别?
3. 自动装箱与拆箱了解吗?原理是什么?
4. int 和 Integer 区别是什么?
5. Integer缓存机制了解吗?
1. 怎么避免浮点数精度丢失的问题?(了解)
2. 如何比较两个BigDecimal是否相等?(了解)
2. 基础类型和包装类有什么区别?
3. 自动装箱与拆箱了解吗?原理是什么?
4. int 和 Integer 区别是什么?
5. Integer缓存机制了解吗?
1. 怎么避免浮点数精度丢失的问题?(了解)
2. 如何比较两个BigDecimal是否相等?(了解)
Java基本数据类型有几种?各占多少位?
有八种基本数据类型
boolean 8位
char 16位
byte 8位
short 16位
int 32位
long 64位
float 32位
double 64位
z值类型和引用类型区别:
指向不同:值类型变量名指向具体数值,引用类型变量名指向对象的内存地址
内存:值类型在声明后立马分配内存空间,引用类型在声明时不会分配内存,只存储一个内存地址
使用方面:值类型必须在声明时必须有具体值,直接用==比较是否相同,引用类型不必有一个具体的值,默认为null,必须用equals方法比较
数据转换
自动类型转换细节:
1.由小数据转为大数据。比如int转为long,如果是大数据转为小数据比如long转为int则会丢失精度
2. 转换前后要兼容, boolean 类型只能存放 true 或 false,这与整数或字符是不兼容的,因此不可以做类型转换。
3. 整数类型进行浮点运算后自动转换为浮点类型
强制类型转换
强制转换需要使用括号(),比如float a=0.125;int b=(int) a;
装箱拆箱
包装类:引入的目的就是为了是的基本数据类型和引用数据类型可以相互转换
基本数据类型和引用数据类型之间的相互转换就称为装箱以及拆箱。
装箱:基本数据类型转换为引用数据类型,通过方法valueof实现
拆箱:引用数据类型转为基本数据类型,通过xxxvalue方法实现,如 int manual = num1.intValue();、
自动装箱自动拆箱
jdk 5开始提供,用于简化基本数据类型和包装类互相转化比如
Integer i1 = new Integer(10); // 非自动装箱 Integer i2 = 10; // 自动装箱
注意点:装箱会自动创建对象,造成性能损耗,尽量避免装箱。,
int和integer区别?
个人理解int是原始数据类型integer是包装类型,引用类型
区别的话我认为就是原始数据类型和引用类型之间的区别。
1. 原始数据类型更高效,占用少
2. 原始数据类型在用==比较的时候直接就是在比较本身的值,而引用数据类型是在比较地址是否相同,如果需要比的话得用equals方法
3. 引用数据类型可以为null,声明时不赋初值,基本数据类型不行,必须有初值。
BigDecimal
介绍:可以应对小数点的运算,不会造成精度缺失,通常情况下,bigdecimal适用于有小数点运算以及和钱相关的项目
数组入门
数组本身是一种线性数据结构,他把相同类型的数据存储在连续的内存空间中,位置称为索引,
索引:事实上是数组数据的偏移量,第一个数据的偏移量是0所以索引是从0开始。
数组访问数据十分高效,可以在O(1)时间内随机访问数组中任意元素。
数组中的元素是紧挨着的,如果要插入元素,必须将所有后续元素后移一位,删除则是前移
数组长度不可变,想扩容数组建议将数组每一位都赋值到另一个更大的数组中
数组优点:空间效率高(分配的连续内存块,无需额外开销),数据局部性,访问效率高,支持随机访问
局限:长度不可变,插入删除效率低,空间浪费
数组深入
数组的本质:线性表数据结构,用一组连续的内存存储相同元素
访问时的公式:a[k]的地址=a[0]+k*sizeof(数据类型)
链表入门
特点:可以分开存储不同元素,因为一个可以通过一个节点访问下一个节点,直至访问完成,内存无需连续
因为节点除了自身元素以外还需要存储 下一节点的地址(指针),所以链表占用内存空间比数组更大
通常将头结点做为链表代称
访问节点:访问效率比=数组低,因为需要一个一个遍历,所以时间复杂度是o(n),查找同理
数组vs链表
存储方式:连续 非连续
容量拓展:长度不可变 长度可变
内存效率:占用内存小,但可能浪费 占用大不浪费
访问元素:o1 on
添加元素:on o1
删除元素:on o1
容量拓展:长度不可变 长度可变
内存效率:占用内存小,但可能浪费 占用大不浪费
访问元素:o1 on
添加元素:on o1
删除元素:on o1
链表深入
如何实现lru缓存淘汰算法
每次插入新元素就遍历链表,如果有相同元素就把元素删除并插入到头部,如果没有元素就在分俩种情况讨论,如果缓存满了就删除尾结点再插入头部,如果没满就直接插入到头部
常见类学前自问
1. Object类的常见方法有哪些?equals hashcode
2. ==和equals()的区别是什么?==基本数据类型比较的是数值,引用数据类型比较的是地址是否相同,equals方法默认是object类中的方法,和==相同,但是可以自己重写,常见类比如String等就重写了equals方法
3. 为什么要有hashCode?
4. hashCode和equals区别是什么?hashcode只比较哈希值是否相同,同一元素哈希值一定相同,不同元素哈希值不一定不同,equals方法比较元素是否相同,且必须保证元素相同哈希值一定相同,而元素不同hash值也不一定不同
5. 为什么重写 equals() 时必须重写 hashCode()方法?总结成两点:
1。 因为要保证元素相同的hash值一定相同,但是元素不同的哈希值不一定不同
2。性能方面,如果重写了hashcode方法,那么就会在equals方法执行前先对hash值进行比较,可以过滤掉绝大部分元素不同的情况(这一点主要是因为hash值算法只要o1的时间复杂度,而equals方法一般是on时间复杂度)
6. 浅拷贝和深拷贝有什么区别?
7. 深拷贝有几种实现方式?
8. String、StringBuffer、StringBuilder的区别?忘记
9. Java的 String 类 为什么不可变?
2. ==和equals()的区别是什么?==基本数据类型比较的是数值,引用数据类型比较的是地址是否相同,equals方法默认是object类中的方法,和==相同,但是可以自己重写,常见类比如String等就重写了equals方法
3. 为什么要有hashCode?
4. hashCode和equals区别是什么?hashcode只比较哈希值是否相同,同一元素哈希值一定相同,不同元素哈希值不一定不同,equals方法比较元素是否相同,且必须保证元素相同哈希值一定相同,而元素不同hash值也不一定不同
5. 为什么重写 equals() 时必须重写 hashCode()方法?总结成两点:
1。 因为要保证元素相同的hash值一定相同,但是元素不同的哈希值不一定不同
2。性能方面,如果重写了hashcode方法,那么就会在equals方法执行前先对hash值进行比较,可以过滤掉绝大部分元素不同的情况(这一点主要是因为hash值算法只要o1的时间复杂度,而equals方法一般是on时间复杂度)
6. 浅拷贝和深拷贝有什么区别?
7. 深拷贝有几种实现方式?
8. String、StringBuffer、StringBuilder的区别?忘记
9. Java的 String 类 为什么不可变?
深拷贝和浅拷贝
浅拷贝仅仅是将拷贝对象的地址赋给目标对象,如果对目标对象进行更改,那么原拷贝对象也会被更改,java使用clone方法实现
深拷贝就是创建一个新的对象,将拷贝对象的所有非静态字段都复制到新对象中,不会影响拷贝对象,java通过手动复制所有对象
String类
String类被final修饰,说明String类不能被继承
String类存储在char[]数组中,该数组也被final修饰,表示String对象不能改变,但是String变量可重新赋值,改变的是变量指向的内存地址(指向新对象),而非修改原对象内容。
字符串拼接:优先考虑append和StringBuffer,涉及线程安全的使用StringBuffer,因为他是线程安全,所以会涉及锁竞争,所以它性能会少差一些
String,stringbuilder ,StringBuffer都有什么区别
String是java的一个重要的基础类,因为他是final修饰的,所以他具有不可变性。因为他的不可变性,类似剪切,拼接等操作都会创造新的对象,相关操作会对性能有影响
StringBuffer是特地解决以上大量新对象产生所创建的方法,可以使用append和add方法,将字符串添加到特定位置,线程安全
StringBuilder和StringBuffer几乎相同,只是减去了线程安全部分,保证性能,减少了开销
异常相关
学前自问1. 介绍Java的异常体系?
2. Exception和Error有什么区别?
3. 受检和未受检异常的是什么?
4. 如何自定义异常?
2. Exception和Error有什么区别?
3. 受检和未受检异常的是什么?
4. 如何自定义异常?
异常体系
error:包含有ioerror,threaddeath等等
Exception:又分为RuntimeException和非运行时异常
error(错误)程序无法处理的严重,一般是jvm出现了问题。通常有,虚拟机运行异常,类定义错误,内存不足,栈溢出等等,此类错误出现时,jvm会直接终止程序
Exception:程序出现的可捕获并处理的异常。
RuntimeException:运行时异常及其子类异常,通常包含,下标越界异常,空指针异常,可以捕获处理也可以不处理,这些一般是程序逻辑出现的问题。
非运行时异常:除了运行时异常以外的所有异常,从语法角度来说是必须捕获处理的异常,如果不捕获处理,那么程序就不能编译通过。包含ioException,sqlException,用户自定义异常等。
可查异常与非可查异常
可查异常是除了RuntimeException以外的异常,不处理的话通过不了编译
不可查异常是指RuntimeException以及error。
异常基础
关键字:
try:用于监听代码,如果监听的代码出现异常,那么会被抛出
catch:用于捕获异常,用来捕获try代码块中的异常
finally:总会被执行,主要用于回收try块中的物力资源,有了finally块,那么会等finally块执行完才去执行catch块中的return或者throw,如果fianlly块中有return或者throw则直接执行,然后停止
throw:用于抛出异常
throws:y用在方法签名中,声明可能会出现的异常
异常的声明throws
在方法中声明一个异常,方法头中使用关键字throws,后面接上要声明的异常。若声明多个异常,则使用逗号分割。
异常的抛出throw
如果运行时出现异常,需要创建一个合适的异常实类并抛出他
异常的捕获
异常总结
异常基础总结try、catch和finally都不能单独使用,只能是try-catch、try-finally或者try-catch-finally。try语句块监控代码,出现异常就停止执行下面的代码,然后将异常移交给catch语句块来处理。finally语句块中的代码一定会被执行,常用于回收资源 。throws:声明一个异常,告知方法调用者。throw :抛出一个异常,至于该异常被捕获还是继续抛出都与它无关
常用的异常在Java中提供了一些异常用来描述经常发生的错误,对于这些异常,有的需要程序员进行捕获处理或声明抛出,有的是由Java虚拟机自动进行捕获处理。Java中常见的异常类:RuntimeExceptionjava.lang.ArrayIndexOutOfBoundsException 数组索引越界异常。当对数组的索引值为负数或大于等于数组大小时抛出。java.lang.ArithmeticException 算术条件异常。譬如:整数除零等。java.lang.NullPointerException 空指针异常。当应用试图在要求使用对象的地方使用了null时,抛出该异常。譬如:调用null对象的实例方法、访问null对象的属性、计算null对象的长度、使用throw语句抛出null等等java.lang.ClassNotFoundException 找不到类异常。当应用试图根据字符串形式的类名构造类,而在遍历CLASSPAH之后找不到对应名称的class文件时,抛出该异常。java.lang.NegativeArraySizeException 数组长度为负异常java.lang.ArrayStoreException 数组中包含不兼容的值抛出的异常java.lang.SecurityException 安全性异常java.lang.IllegalArgumentException 非法参数异常IOExceptionIOException:操作输入流和输出流时可能出现的异常。EOFException 文件已结束异常FileNotFoundException 文件未找到异常其他ClassCastException 类型转换异常类ArrayStoreException 数组中包含不兼容的值抛出的异常SQLException 操作数据库异常类NoSuchFieldException 字段未找到异常NoSuchMethodException 方法未找到抛出的异常NumberFormatException 字符串转换为数字抛出的异常StringIndexOutOfBoundsException 字符串索引超出范围抛出的异常IllegalAccessException 不允许访问某类异常InstantiationException 当应用程序试图使用Class类中的newInstance()方法创建一个类的实例,而指定的类对象无法被实例化时,抛出该异常# 异常实践
泛型:参数化类型的机制,可以使得我们在定义类,方法时使用占位符,在使用方法时再具体声明
泛型优点:
编译时强类型检查
避免了类型转换
实现通用算法
上界通配符:用于缩小参数类型范围语法形式为:<? extends Number>
下界通配符:用于将参数限制为该类型的特定类型或超类类型:<? super Number>
无界通配符:可以使用 Object 类中提供的功能来实现的方法。使用不依赖于类型参数的泛型类中的方法。
作用:类型安全,消除强制类型转换,代码复用
反射
何为反射?
它赋予了我们在运行时分析类以及执行类中方法的能力
通过反射可以获取一个类中所有的属性和方法,还可以自由调用
反射应用场景
动态代理
注解
反射优缺点
代码更灵活,为各种框架提供了开箱即用的便利
安全问题,性能问题
反射实战
获取class对象
1. 知道类具体名字 Class alunbarClass = TargetObject.class;
2.不知道类具体名字
使用forname获取:Class alunbarClass1 = Class.forName("cn.javaguide.TargetObject");
使用对象实例 instance.getClass():TargetObject o = new TargetObject();
Class alunbarClass2 = o.getClass();
Class alunbarClass2 = o.getClass();
通过类加载器xxxClassLoader.loadClass()获取:ClassLoader.getSystemClassLoader().loadClass("cn.javaguide.TargetObject");
反射基本操作
创建反射类
使用反射操作类的方法以及属性
动态代理
是一种运行时生成代理对象的技术,在不修改原对象的前提下,为对象添加额外的功能,比如日志记录,事务权限控制等。
两种实现
JDK动态代理:基于接口
无需额外依赖,反射存在性能开销,java8以后较好
CGLIB动态代理:基于继承,字节码增强
需额外依赖,执行效率高,避免使用反射
无法代理final方法
注解
什么是注解
是一种特殊的注释,如果没有解析的话和普通注释没两样,
解析注解有两种形式
一是编译时直接扫描,只适用于jdk内置的注解类
二是运行时通过反射,适合自定义注解
元注解
用来修饰其他注解的注解
比如target:用于定义注解的作用范围
序列化
原理:
反序列化:将二进制数据转换为对象,利用反射动态创建对象实例,并填充字段
序列化定义:将对象转化为2进制数据,JVM通过反射获取对象类结构,字段值等
用途:
将对象的字节持久化,保存到文件中
在网络上传送对象的字节序列
rmi(远程调用)
被序列化的类必须属于Enum,array或者Serializable,否则将抛出NoSerializableException异常
jdk序列化
jdk内置一种序列化方式
ObjectInputStream通过writeObject实现反序列化
ObjectOutputStream通过readObject实现序列化
如何实现序列化反序列化识别
serialVersionUID,反序列化时会和程序做比对,确保是兼容的对象
jdk序列化要点
父类是Serializable,子类都能被序列化
子类是而父类不是,子类可序列化,父类不行,会数据丢失(父类的属性)
对象是Serializable才可以被序列化
如果serialVersionUID被修改,则不能反序列化
如果序列化后的的二进制文件被修改,能正常反序列化但是会丢失属性,不会报错
IO
所谓io就是计算机内存与外部设备之间拷贝数据的过程
因为cpu访问内存的速度远高于外部设备,所以cpu会把外部设备的数据读取到内存中,在进行处理
IO模型
unix系统下io有五种模型
同步阻塞:用户线程发起read以后就阻塞了,让出cpu。只有read返回以后才释放
同步非阻塞:用户发起read以后等待内核准备数据并回应,不成功回应的话就一直发起read请求
io多路复用:和同步非阻塞区别是先发的是select筛选数据状态 ,等到数据准备好以后再发起read然后阻塞
信号驱动io:
异步io:read以后直接read返回,但是不阻塞,等待数据准备好以后再回调
如何区分
第一种维度:区分同步异步。简单来说,同步是一种可靠的有序运行机制,当我们进行同步操作的时候,后续步骤必须等待我们当前任务返回才能进行,异步相反,通常依靠时间回调等机制
第二种维度:区分阻塞和非阻塞,阻塞就是做这个任务的时候其他任务全都不能执行,非阻塞相反
当用户发起io操作时,网络读取数据经理两个步骤
用户线程等待内核将网络数据从网卡拷贝到内核空间
内核将数据从内核空间拷贝到用户空间
javaIO
BIO:阻塞IO,比如file抽象,输入输出流。交互方式是同步阻塞
BIO优点是简单直观,
缺点是性能效率拓展性不高,不适合高并发
NIO:非阻塞IO
解决了BIO的性能问题
优点:
利用缓冲区优化读写流
与传统BIO的区别是nio的基本单位是块,他以快为基本单位处理数据。其中最重要的是buffer(缓冲区)以及Channel(管道)。
buffer是一个连续的内存块,是NIO读写数据的缓冲。Buffer可以一次性将数据都读取到缓冲区当中,而传统方式是边读边处理数据。Channel表示缓冲=区数据的源头或者目的地,是访问缓存的接口
使用 DirectBuffer 减少内存复制
传统情况下Buffer是由JVM分配的堆内存,而directBuffer是直接分配的物理内存,减少了数据复制的开销
优化IO,避免阻塞
传统io会在用户空间和内核空间来回复制,内核空间的数据由操作系统io从磁盘或者接口读入
NIO的Channel有自己的处理器,可以完成内核空间和磁盘之间的io操作。
AIO异步非阻塞io
传统IO流
字节流:主要操作字节数据和二进制对象
输入字节流InputStream
输出字节流OutputStream
字符流:主要操作字符,一个字符等于两个字节
输入字符流:Reader
输出字符流:Writer
字节流vs字符流
相同点:操作基本相同,都有read,write,close等方法
不同点:
操作对象不同,核心类不同
字节流不操作缓存,字符流操作缓存
结论:除了纯文本文件用字符流,其他都用字节流
设计模式
谈谈你知道的设计模式
创建型模式
单例模式,工厂模式,构建器模式
结构性模式
桥接,装饰,代理,适配器模式
行为性模式
策略,解释器,命令,观察者
stream流,函数式编程
优点:
接近自然语言
易于并发编程
简洁,方便开发
lambda
语法糖,对匿名内部类进行写法简化
stream流
使用步骤
1. 创建流:
如果是单列集合的话:集合对象.stream()
数组:Arrays.stream();
双列集合:转换成单列集合在创建
2. 中间操作
filter过滤
map计算
特点:
惰性求值,只要没有终结操作,中间操作不会执行
流只要进行了终结操作,流不能再被使用
不会影响原来的数据
Optional
类似于包装类,把需要处理的对象包装给Optional,,再用Optional封装好的方法区访问封装好的数据,可以优雅地避免空指针异常
函数式接口
只有一个抽象方法的接口都被称之为函数式接口
方法引用:
在运用lambda表达式时,如果,方法体中只有一个方法调用,那么就可以进一步简化
0 条评论
下一页