自己整理的一些关于java的知识点
String&StringBuilder&StringBuffer的区别
- String final 不能被继承 private final char value[ ] 值不可以变
- StringBuilder 和 StringBuffer一样 char value[ ] 值可以改变
- StringBuilder 不可以保证线程安全、适合单线程
- StringBuffer 可以保证线程安全、适合多线程同时访问同一个资源
- 运行速度:StringBuilder > StringBuffer > String
cookie与session的区别与联系
- cookie数据存放在浏览器上,session数据存放在服务器上
- cookie的存储是有限制的,一个站点最多保存20个cookie,单个cookie保存的数据不能超过4k,而session的存储是没有限制的
- cookie并不是很安全,session相对安全
- 由于session是存放在服务器上的,当访问量增多,会占用服务器的性能,考虑到减轻服务器性能方面,应当把不是用户敏感的信息保存到cookie上
- 一般情况下,session生成的sessionid都是保存在cookie中
- cookie:客户端 不安全,只保存字符串,有大小限制
- session:服务端 安全,保存对象,数据大小没有限制
java中反射机制
动态获取一个类或者一个对象的属性或者方法
Java 反射机制的作用是:
- 在运行时判断任意一个对象所属的类。
- 在运行时构造任意一个类的对象。
- 在运行时判断任意一个类所具有的成员变量和方法。
- 在运行时调用任意一个对象的方法。
简述List、Set、Map的区别
List:有序、可重复。通过索引查找快,增删速度慢 (操作时后续的数据需要移动)
Set:无序、不可重复的集合
Map:Map 集合中存储的是键值对,键不能重复,值可以重复。根据键得到值,对 map 集合遍历时先得到键的 set 集合,对 set 集合进行遍历,得到相应的值。
@Resource和@Autowired的区别
@Resource是J2EE提供的注解,默认按照byName名称自动注入,有两个重要的属性,name和type,spring将@Resource注解的name属性解析为bean的名字,而type属性解析为bean的类型
@Autowired是spring提供的注解,默认是按照byType类型装配依赖对象,一般情况下要求依赖对象必须存在,如果允许null值,可以设置它的required属性为false。
说说对Spring中AOP技术的理解
AOP表示面向切面编程,是通过预编译方式和运行期间动态代理实现程序功能的一种统一维护的技术,使用AOP技术可以对业务逻辑做统一的处理,比如所有的请求都需要记录日志,做权限校验等等,可以降低业务逻辑之间的耦合度,提升程序的可用性,提高开发效率。AOP技术核心原理是使用动态代理的设计模式执行方法,执行方法前后,或出现异常后,加入相应的逻辑,常规用法有:
- 事务处理,执行方法前开启事务,方法执行完成后提交事务,出现异常后回滚事务
- 权限判断,执行方法钱,判断是否具有执行权限
- 记录日志,请求执行完后,进行日志记录
说说你对Spring中IOC的理解
- IOC表示控制反转,是指创建对象时控制权的转移,未使用spring的IOC容器时,创建对象的主动权和时机是由程序自己把控的,而现在这种创建对象的控制权力转移到spring容器中,并由容器根据配置去创建实例和管理各个实例之间的依赖关系,对象与对象之间松散耦合,也利于功能的复用。
- 最简单的理解就是IOC让对象的创建不再去new,可以由spring自动生产,使用java的反射机制,根据配置文件再运行时动态的去创建对象以及管理对象
- spring中IOC的注入方法有三种:构造方法注入、setter方法注入、注解注入
- 实现原理是反射机制+工厂模式
- IOC是一种设计思想,是将原本在程序中手动创建对象的控制权,交给spring框架来统一管理,IOC容器是spring用来实现IOC设计思想的载体,IOC容器实际上就是个Map(key,value),map中存放的是各种对象
- 将对象之间的相互依赖关系交给IOC容器来进行管理,并由IOC容器来实现对象的注入,把应用从复杂的依赖关系中解脱出来。IOC容器就像是一个工厂一样,当我们需要创建一个对象的时候,只需要配置好配置文件或者注解,完全不用考虑对象是如何被创建的,然后在需要使用的地方引用对象即可,大大增加了项目的可维护性且降低了开发难度
说说你对Spring中DI的理解
- DI表示依赖注入,和控制反转是同一个概念的不同角度的描述,即应用程序在运行时依赖IOC容器来动态注入对象需要的外部资源,比如spring在创建对象的过程中,会将该对象以来的属性(如简单的值,集合,其他对象等等)通过IOC容器自动设置给该对象
- DI是一种设计模式,其作用是去除Java类之间的依赖关系,实现松耦合,以便于开发测试
- DI相对IOC而言,明确描述了“被注入对象依赖IOC容器配置依赖对象”
- DI注入的三种方式:setter注入,构造器注入,接口注入
请解释Spring中bean的生命周期分为哪几个阶段
- bean对象被实例化
- 封装bean对象的属性
- 如果bean实现BeanNameAware接口,则执行setBeanName方法
- 调用对象中自定义的初始化init-method方法
- 执行业务处理
- 调用对象中自定义的destroy-method销毁方法
Spring框架的核心模块有哪些
- Spring Core:核心类库,提供IOC服务
- Spring Context:提供框架式的Bean访问方式,以及企业级功能(如定时任务)
- Spring AOP:提供AOP面向切面的服务
- Spring DAO:对JDBC的抽象封装,简化了数据访问的操作
- Spring ORM:对现有的ORM框架的支持
- Spring Web:提供了基本的面向Web的综合特性,比如多文件上传
- Spring MVC:提供面向Web应用的Model-View-Controller的实现
- Spring Test:提供了对Junit和TestNG测试的支持
- Spring Aspects:该模块与Aspectj的继承提供支持
Spring中bean的作用域有哪些
singleton(单例模式):唯一bean实例,spring中IOC的容器只会创建该bean的唯一实例,spring中的bean默认都是单例的
prototype(原型模式):每次使用该实例时,都会创建一个新的bean实例,项目开发中有时候使用多线程处理任务时会用到
request:每一次HTTP请求都会产生一个新的bean,需要注意该作用域仅在基于Web的Spring ApplicationContext情形下有效
session:每次会话创建一个实例,一次会话可以包含多次HTTP请求,比如从登录系统,进行业务操作,最后退出系统表示是一次会话,都只会创建一个新的bean,该bean仅在当前httpSession内有效
global-session:全局session作用域:只会返回该bean的同一个实例,该实例在项目的整个运行期间都有效
拓展:singleton和global-session作用域返回的都是同一个实例,区别是作用范围不一样。global-session是整个项目,singleton作用域是局部的
将一个类声明为Spring中bean实例的注解有哪些
- @Component:通用注解,可以标注任意类
- @Repository:对应项目中的持久层,也就是dao数据操作层,主要用于数据库的相关操作
- @Service:对应服务层,主要处理一些复杂的业务逻辑,需要用到dao层
- @Controlelr、@RestController:对应控制层,主要用于接受用户请求并调用service层处理业务,并将数据返回给前端页面
- @Configuration:将类声明为配置类
- @Bean
@Component和@Bean注解的区别是什么
- 用途不同:@Component用于标识普通的类;@Bean是在配置类中声明喝配置Bean对象
- 使用方式不同:@Component是一个类级别的注解,Spring通过@ComponentScan注解扫描并注册为Bean;@Bean是方法级别的注解,在配置类中手动声明和配置Bean
- 控制权不同:@Component注解修饰的类是由Spring框架来创建和初始化的;@Bean注解允许开发人员手动控制Bean的创建和配置过程
- 拓展:真实开发中@Component应用很广泛,比如定时任务类就会使用这个注解,将定时任务类统一交给Spring管理,或者是某些工具类进行统一处理一些逻辑的时候,并且工具类会引用其他类,这时候可以使用该注解。@Bean应用也非常多,比如在初始化Redis数据库的时候,会设置连接地址、账号、密码等基本信息,这时候使用该注解就非常方便,可以返回自定义的Bean对象
对SpringMVC的理解
首先,SpringMVC是属于Spring Framework生态里面的一个模块,它是在servlet的基础上进行构建并且使用了MVC模式设计的一个Web框架,它的主要目的是为了简化传统的servlet+jsp的web开发方式;其次,SpringMVC整个架构设计是对JavaWeb里面的MVC框架模式做了一些增强和扩展,主要体现在以下几个方面:1.把传统的MVC框架里面的Controller控制器做了拆分,分成了前端控制器DispatchServlet和后端控制器Controller;2.把Model模型拆分成业务成Service和数据访问层Repository;3.在视图层,可以支持不同的视图,比如Freemark,velocity,JSP等。因此,在开发MVC应用的时候,会更加的方便灵活。
SpringMVC的整体工作流程
浏览器的请求首先会经过SpringMVC里面的前端控制器DispatchServlet,主要是把请求分发到对应的Controller里面,Controller处理完业务逻辑之后,会返回一个ModelAndView,然后DispatchServlet会去寻找ViewResolver的视图解析器,找到ModelAndView指定的视图,并且把数据展示到浏览器。
SpringBoot自动装配的原理
自动装配简单来说就是:自动去把第三方组件的Bean装载到IOC容器中,不需要开发人员再去写Bean相关的一个配置,在SpringBoot应用里面呢,只需要在启动类上加上@SpringBootApplication注解,就可以实现自动装配。@SpringBootApplication是一个复合注解,它包括以下三个注解,@SpringBootConfiguration、@EnableAutoConfiguration、@ComponentScan。真正实现自动装配的注解是@EnableAutoConfiguration,自动装配的实现主要依靠三个核心的关键技术:1.引入Starter,启动依赖组件的时候,组件必须包含一个@Configuration配置类,并且需要通过@Bean这个注解去声明需要装配到IOC容器里面的Bean对象;2.这个配置类是放在第三方的jar包里面,然后通过SpringBoot中约定优于配置这样的一个理念,去把配置类的全路径放在classPath:/META-INF/spring.factories文件里面,这样的话,SpringBoot就可以知道第三方jar包里面这个配置类的位置,这个步骤主要使用到了Spring当中的SpringFactoriesLoader来完成的;3.通过Spring提供的ImportSelector的接口来实现对这些配置类的动态加载,从而完成自动装配的一个动作。
Spring框架中单例Bean是线程安全的吗
Spring框架并没有对单例Bean进行任何多线程的封装处理,关于单例bean的线程安全和并发问题需
要开发者自行去解决。
- 实际项目中Spring管理的大部分bean并没有状态变化(比如Service业务相关的类和Dao 数据库操作相关的类),所以在某种程度上说Spring的单例bean是线程安全的。
- 如果你的bean有多种状态的话,则需要开发者自行保证线程安全,例如使用某一个类来统计请求总数,多个线程都在操作同一个类进行递增请求总数
Spring中用到了哪些设计模式
- 工厂设计模式:Spring IOC核心的设计模式思想体现就是工厂模式,用来创建各种被Spring IOC容器管理的bean对象
- 代理设计模式:Spring AOP功能的实现就用到了代理模式,JDK动态代理(对实现了接口的类进行代理)
- 单例设计模式:Spring中的bean默认作用域是singleton单例的
- 模板方法设计模式:Spring中的jdbcTemplate、redisTemplate等以Template结尾的类,使用的都是模板模式
- 包装器设计模式:项目中需要连接多个数据库时,并且不同的客户端在每次访问时会根据需要去访问不同的数据库,Spring中的这种设计模式让我们可以根据客户的需求能够动态切换不同的数据源
- 观察者模式:Spring事件驱动模式就是观察者模式中很经典的一个应用
- 适配器模式:Spring AOP的增强通知使用到了适配器模式,SpringMVC也用到了适配器模式来适配Controller控制器
Spring框架有哪些优点
- Spring属于非侵入式设计,代码污染极低,它可以使应用程序代码对框架的依赖最小化,代码污染是指增加需求后,要实现新需求,会破坏原有代码的封装性以及稳定性
- 方便解耦,简化开发。Spring的IOC容器用来统一控制对象的创建;Spring的DI机制将业务对象之间的依赖关系交由框架处理,降低了组件之间的耦合性。可以将Spring理解为一个大工厂,这个工厂将所有的对象的创建和依赖关系的维护工作都交给Spring容器处理
- Spring提供的AOP技术,支持将一些通用任务如安全、事务、日志、权限等进行集中式管理,从而提供更好的代码复用
- Spring对于主流的应用框架如mybatis、SpringMVC、redis等等框架提供友好的集成支持
- 支持声明式事务处理,使用Spring框架,可以从单调的事务管理代码中解脱出来,通过声明式事务灵活地进行事务管理,提高开发效率和质量
- 降低JavaEE API的使用难度,Spring对很多难用的JavaEE API(如JDBC,远程调用等)提供了一个薄薄的封装层,通过Spring的简易封装
- 方便程序测试,Spring提供对JUnit的支持,可以通过注解方便的测试用Spring框架开发的程序
constructor构造方法注入和set方法注入有什么区别
- set支持大部分的依赖注入,对于基本类型如果我们没有注入的话,可以为基本类型设置默认值
- 构造方法注入,不支持大部分的依赖注入,调用构造方法时必须传入正确的构造参数才能注入成功
- set注入不会重写构造方法的值,如果对同一个变量同时使用了构造方法注入,又使用了set方法注入的话,set方法注入的值有效,因为构造方法在对象创建时调用,set注入是在调用构造方法之后在进行调用
- 使用set注入时有可能还不能保证某个值是否注入成功,比如set一个对象,可以设置为一个null值,对象的依赖关系有可能是不完整的,构造器注入则不会出现这种情况,被注入对象必须存在才能够注入成功
- set注入时如果对象A依赖对象B,当B对象为创建时,创建对象A时Spring会抛出
ObjectCurrentlylnCreationException异常,因为在B对象被创建之前A对象是不能被创建的
Spring中自动装配的方式有哪些
- no:不进行自动装配,手动设置Bean的依赖关系
- byName:根据Bean的名字进行自动装配
- byType:根据Bean的类型进行自动装配,当有多个符合条件的Bean时,会抛异常
- constructor:类似于byType,不过是应用于构造器的参数,如果正好有一个Bean与构造器的参数类型相同则可以自动装配,否则会抛出异常
- autodetect:如果有默认的构造器,则通过constructor的方式进行自动装配,否则使用byType的方式进行自动装配(从Spring3.0开始,该方式废弃)
请写出5种常见到的runtime exception
- NullPointerException
- IndexOutOfBoundsException
- SQLException
- IOException
- ClassCastException
final、 finally、finalize的区别
- Final 是修饰符,也是关键字,被final 修饰的类无法被继承,对于一个final变量,如果是一个基本数据类型,则其数值在被初始化后就无法改变,如果是一个引用类型的变量,则其初始化后就不能指向其他对象,但是它指向的对象的内容是可以改变的。
- Finally 是关键字,用在异常处理异常时提供finally块来执行任何清除操作,不管有没有抛出异常或者捕获异常,finally块都会执行,通常用于释放资源
- Finalize 是方法,子类可以覆盖该方法以实现资源清理的工作,GC在回收对象之前都会调用该方法
简述TCP和UDP的区别及其应用场景
区别:
- 连接
- TCP时面向连接的传输层协议,传输数据之前要先建立连接
- UDP不需要连接,即刻传输数据
- 服务对象
- TCP 是一对一的两点服务,即一条连接只有两个端点
- UDP 支持一对一、一对多、多对多的交互通信
- 可靠性
- TCP是可靠交付数据的
- UDP不保证可靠交付数据
- 拥塞控制、流量控制
- TCP有拥堵机制和流量控制机制,保证数据传输的安全性
- UDP则没有,即使网络非常拥堵了,也不会影响UDP的发送效率
应用场景:
- 由于TCP是面向连接,能保证数据的可靠性交付,因此经常用于
- FTP文件传输
- HTTP/HTTPS
- 由于UDP是面向无连接,它可以随时发送数据,再加上UDP本身的处理既简单又高效,因此经常用于
- 包总量较少的通信,如DNS,SNMP
- 视频、音频德国等多媒体通信
- 广播通信
什么是垃圾回收?什么时候触发垃圾回收?如何降低垃圾回收的触发频率?它能保证程序有足够的可用内存吗?
什么是垃圾回收?
- 垃圾回收是对内存堆中已经死亡的或者长时间没有使用的对象进行清除和回收
什么时候触发垃圾回收?
- 对象没有引用
- 作用域发生未捕获异常
- 程序在作用域正常执行完毕
- 程序执行了System.exit()
- 程序发生意外终止(被杀进程等)
如何降低垃圾回收的触发频率?
- 对象不用的时候显示地设为Null
- 尽量少用System.gc()
- 少用静态变量,静态变量属于全局变量,不会被GC回收,会一直占用内存
- 尽量使用StringBuffer而不是用String累加字符串
- 分散对象创建或者删除的时间
- 尽量少用 finalize 函数
- 能用基本类型入int就不用对象Integer
它能保证程序有足够的可用内存吗?
- 不能保证有足够的内存
谈谈对ClassLoader的理解
类加载器ClassLoader它是一个抽象类,ClassLoader的具体实例负责把java字节码读取到JVM当中,ClassLoader还可以定制以满足不同字节码流的加载方式,比如从网络加载、从文件加载
什么是Java序列化,如何实现Java序列化?
- 序列化就是一种用来处理对象流的机制,所谓对象流也就是将对象的内容进行流化,可以对流化后的对象进行读写操作,也可以将流化后的对象传输于网络之间,序列化是为了解决在对对象流进行读写操作时所引发的问题
- 将需要被序列化的类实现Serializable接口,该接口没有需要实现的方法,implement Serializable只是为了标注该对象是可被序列化的,然后使用一个输出流来构造一个ObjectOutputStream(对象流)对象,接着,使用ObjectOutputStream对象的writeObject(Object obj)方法就可以将参数为obj的对象写出(即保存其状态),要恢复的话则用输入流。
死锁的场景?如何解决?
场景:例如同一时间,账户A给账户B转账,账户B也在给账户A转账,都第一时间获取到了锁定转出账本的锁,在获取转入账本的锁时发现锁以及被抢占,就会出现先死锁
解决方法:一般没有特别好的方法,只能重启应用,最好的方法是规避死锁
出现死锁的四个条件:
- 互斥,共享资源X和Y只能被一个线程占用
- 占用且等待,线程T1已经获得共享资源X,在等待共享资源Y的时候,不释放共享资源X
- 不可抢占,其他现场不能强行抢占T1占有的资源
- 循环等待,线程T1等待线程T占有的资源,线程T2等待线程T1占有的资源,就是循环等待
只要我们破坏其中一个,就能成功避免死锁
HashMap与HashTable、TreeMap的区别?
HashMap:
- 底层实现是hash表
- key-value都可以为null
- 线程不安全
- 实现自Map接口
- 初始容量16,负载因子0.75,扩容后为之前容量的2倍
HashTable:
- 底层实现是hash表
- key-value不可以为null
- 线程是安全的
- 继承自Dictionary类
- 初始容量11,负载因子0.75,扩容后为之前容量的2倍+1
TreeMap
- 底层由二叉树支出
- key不可以为null,value可以为null
- 线程不安全
- 自然顺序排序
- 实现自Map接口
进程和线程分别该怎么理解
进程:我们运行的程序通常会对应一个或多个进程,进程是操作系统分配内存的基本单位。
线程:一个进程通常会包含一个或多个线程,线程是操作系统分配CPU的基本单位。
举个例子,比如一间教室,教室里面有学生,教室就是一个进程,而里面的学生就是线程
在我的网盘项目中,有个客户端,一个客户端就是一个进程,客户端一启动,主线程就开启了,然后我还有设定一个“心跳线程”,可以用来判断客户端是否在线,还有个接收消息的线程,用来实时接收客户端收到的消息
同步和异步有何异同,在什么情况下分别使用他们?请举例说明
同步:发送一个请求,等待返回,然后再发送下一个请求
异步:发送一个请求,不需要等待返回,随时可以再发送下一个请求
同步的例子:打电话。发起者需要等待接收者接通电话后,才能开始通信,就是需要等待接收者的返回消息
异步的例子:发短信。发起者不需要关心接收者的状态,就可直接对接收者发送消息
try catch finally执行顺序
如果finally存在,任何执行try 或者catch中的return语句之前,都会先执行finally语句,如果finally中有return语句,那么程序就return了,所以finally中的return是一定会被return的。而且,编译器会把finally中的return实现为一个warning。
什么是单例设计模式,有什么优点和缺点,应用场景在哪里?
单例模式就是保证系统中一个类只有一个实例。也就是说只能自己new自己并且实例唯一并对外提供。例如Spring的单例模式,Spring中IOC的容器只会创建该bean的唯一实例,spring中的bean默认都是单例的
优点:
- 减少内存开销,尤其是频繁的创建和销毁实例
- 避免对资源过多的占用
缺点:
- 没有抽象层,不能继承扩展很难
- 不适用于变化对象
- 如果实例化的对象长时间不被利用,系统会认为是垃圾而被回收,这样将导致对象状态丢失
应用场景:统计当前在线人数(网站计数器):用一个全局对象来记录
Array 和 ArrayList 的区别
- 存储类型不同
- array可存储基本数据类型和对象
- arraylist只能存储对象
- 大小不同
- array大小是固定的
- arraylist是可变长度的
- 对象所包含的方法不同
- array所包含的方法没有arraylist多
- arraylist有很多操作方法:addAll,removeAll,iteration等
JVM的内存结构
- 程序计数器
- 是一块较小的内存空间,可以看作是当前线程所执行的字节码的型号指示器,在多线程环境下,每个线程都有自己独立的程序计数器,互不影响。它的作用是指向当前线程正在执行的字节码指令的地址,以实现线程切换和恢复执行。
- 虚拟机栈
- 每个线程在创建时会被分配一个私有的栈空间,栈中存放着栈帧(Stack Frame),包括局部变量表、操作数栈、动态链接、方法出口等信息。在方法调用和返回时,对应的栈帧会入栈和出栈。
- 本地方法栈
- 与Java虚拟机栈类似,不同的是本地方法栈为Native方法服务。它用于支持在Java代码中调用Native方法(如C语言编写的方法)的情况。
- 堆
- 是Java虚拟机管理的最大一块内存,用于存放对象实例。堆是线程共享的区域,所有的对象实例以及数组都在堆上分配内存。堆被设计成可以动态扩展和收缩的,可以通过参数来控制其最大和最小值。
- 方法区
- 用于存储已被虚拟机加载的类信息、常量、静态变量、即时编译器编译后的代码等数据。在JDK 8及之前,方法区是一个逻辑上的概念,物理上可以是堆的一部分(永久代)。而在JDK 8之后,使用元空间(Metaspace)代替了永久代,方法区的实现发生了较大变化。
- 运行时常量池
- 方法区的一部分,用于存放编译期生成的各种字面量和符号引用。
- 方法区的一部分,用于存放编译期生成的各种字面量和符号引用。