面向过程注意函数内部细节的细节,每一步的步骤和顺序、每一步逻辑是具体怎么实现的。
面向对象是一种相对于面向过程,更注重参与者(对象),各自需要做什么 的设计方式。特点是封装,继承和多态。
面向过程更加直接高效,面向对象更加易于维护,复用和拓展。
封装的特点:内部细节对外部调用隐身,外部调用不需要关心内部逻辑怎么实现的。
比如JavaBean的属性私有,提供get set方法对外访问。属性的获取和修改只能由JavaBean本身决定,不能由外部修改
再比如ORM框架,操作数据库,我们不需要关心sql是如何执行的,只需要引入Mybatits,调用方法既可、
,不需要知道框架具体做了什么。
继承的特点: 继承父类的方法和属性,做出自己的改变或拓展,不需要再定义父类的方法或属性。一个类只能直接继承一个父类。
多态的特点:父类引用指向子类对象,外部对同一个方法的调用,基于对象所属类的不同,实际执行的逻辑也不同。多态定义一个对象时,使用变量名.方法名()调用子类的方法(即调用等号右边真实类型,new什么就是什么类的方法),无法调用子类特有方法。成员变量不具备多态性,系统总是试图访问子类编译类型定义的成员变量(即等号左边)。
内部类是什么?内部类是Java中类的一种定义方式,内部类有四种:分别是成员内部类,静态内部类,匿名内部类和局部内部类
多线程是什么?
多线程是指在同一程序中有多个顺序流在执行
1.多线程的三种创建方式继承Thread并且重写run方法,实现Runnable接口,实现Callable接口
Thread1 a=new Thread1();
a.start();
MyRunnable myRunnabble=new MyRunnable();
Thread a=new Thread(myRunnabble);
a.start();
MyCallable 实现callble接口,创建FutureTask,参数放入mycallable,futuretask有俩方法isdone()和get();
前者不会阻塞,用来判断是否结束;后者会阻塞 ,获得返回结果
Callable接口支持返回执行结果,此时需要调用FutureTask.get()方法实现,此方法会阻塞主线程直到获取‘将来’结果;当不调用此方法时,主线程不会阻塞
说说==和queals的区别 ==:比较的是变量(栈)内存中存放对象的(堆)内存地址,用来判断两个对象地址是否相同,即是否是同一个对象
equals:Object类中默认使用 ==,可以重写。
String类中被重写为 比较两个对象中字符串的内容是否相等
hashCode()作用是获取哈希码,也称散列码,(字符串key通过一个函数(可能是补码集)变成数字累加跟数组size取余)根据对象的内存地址返回一个Int整数。作用是确定该对象在hash表中的索引位置。
HashSet如何检查重复来说明HashCode作用Java的集合有两类,一类是List,还有一类是Set。前者有序可重复(可以放任意Null),后者无序不可重复(一个Null),前者可以get,itertor,后者不可,只能itertor.
以上是附加分:
当一个对象加入HashSet时,会调用这个对象的Hashcode()得到一个值,通过这个值找到一个物理地址,这个物理地址上如果有其他元素,则进行equals比对,相同就不存了,不同就散列到其他地址,如果没有,直接存储在这个物理地址位置上。
前者有序可重复(可以放任意Null),后者无序不可重复(一个Null),前者可以get,itertor,后者不可,只能itertor.
什么是散列表根据key值进行直接访问的数据结构。
instanceof关键字作用用来检测一个对象是否是一个类的实例,用法为:
boolean result=obj instanceof Class
obj为一个对象,Class为一个类
我们编写的.java文件,经过编译后,生成.class字节码文件,虚拟机将字节码解释执行。好处是字节码具有与平台无关性,字节码不针对某种机器而针对虚拟机,Java程序无须重新编译便可在多种不同计算机上运行
重载和重写重载发生在一个类中,只需要方法名相同,参数类型,个数,顺序,方法返回值,修饰符都可以不同
重写发生在两个类中,子类继承父类,方法名和参数列表必须相同,返回值范围必须小于等于 父类,抛出异常范围小于等于父类,访问修饰符范围大于等于父类;如果父类方法使用private修饰,则子类不能重写该方法
JAVA反射是在运行状态下,对于任意一个类,都能够获取这个类的所有属性和方法;对于任意一个对象,都能够调用它任意一个属性和方法;这种动态获取信息和动态调用对象方法的能力被称为反射机制。
反射的作用1 可以调用 以参数作为方法名 的方法。
2 当开发环境和运行环境不同时,使用反射不会编译报错。
代理就是通过代理对象去访问实际的目标对象,比如我们在生活中购物,可以直接找商家,也可以通过淘宝网购,通过网购平台的这种方式就是代理。在java中这种网购平台就被叫做代理类,代理类不仅能实现目标对象,还能增加一些额外的功能。(据我所知)java中的代理方式有静态代理和动态代理
什么是动态代理,什么是静态代理,他们有什么区别静态代理就是在代码运行之前,这个代理类就已经存在了。举个例子 首先创建一个购物接口,然后创建一个代理类,一个被代理类。代理类可以在不改变被代理对象的情况下增加功能
静态代理那么强大,那他有什么缺点吗?(接口 :田径 冰雪 水上,假如100个接口 ,代理类要实现100个)
由于静态代理在代码运行之前就已经编译完成,因此对于每一个被代理对象都需要建一个代理类去代理,当有100个需要代理的对象,就需要创建100个代理类,严重降低程序的可维护性,重用性。用动态代理就可以解决这个问题。
动态代理是指代理类不是写在代码中,而是在运行过程中产生的,java提供了两种实现动态代理的方式,分别是基于Jdk的动态代理和基于Cglib的动态代理。
什么是基于JDK的动态代理实现Jdk的动态代理需要实现InvocationHandler接口,然后实现其中的invoke方法。如果代理的方法被调用,那么代理便会通知和转发给内部的 InvocationHandler 实现类invoke,由它实现处理内容。
主方法中执行动态代理
核心是Proxy.newProxyInstance,目的是运行期间生成代理类,最后通过代理类执行被代理的方法
参数是 接口 ,类加载器 ,实现了 Invo接口的类
之前我讲静态代理的时候说静态代理的缺点在于对于每一个被代理的对象,都需要建一个代理类。因为静态代理是在项目运行前就写好的。但是动态代理就不是这样,由于动态代理在运行时才创建代理类,因此只需要写一个动态代理类就好
代理类(非接口)该怎么办呢?jdk动态代理只能代理接口,JDK动态代理是基于接口的方式,换句话来说就是代理类和目标类都实现同一个接口。如果想要代理类的话可以使用CGLib,CGLib动态代理是代理类去继承目标类,然后实现目标类的方法
动态代理平常工作中有使用到吗?但是我工作中使用的Spring系列框架中的AOP和Mybatits框架,用到了动态代理,以AOP为例,AOP通过动态代理对目标对象进行了增强,比如我们最常用的前置通知、后置通知等。
说说你对注解的理解,注解又解决了哪些问题?Java语言中的类、方法、变量、参数和包都可以用注解标记,程序运行过程中我们可以获取到相应的注解以及注解中定义的内容,比如说 Spring 中如果检测到说你的类被 @Component注解标记的话,Spring 容器在启动的时候就会把这个类归为自己管理,这样你就可以通过 @Autowired注解注入这个对象了。
如何自己去定义注解吗?自定义注解主要有以下四步:
第一步通过@interface声明注解:
public @interface Myannotation {
String key() default “”;
}
第二步通过四种元注解修饰注解:(面试的时候说出这四种注解就可以了)
元注解的作用就是负责其他注解,java中一共有四个元注解,分别是@Target,@Retention,@Documented,@Inherited,下面先介绍以下四种注解的作用:
@Target:Target说明了注解所修饰的对象范围,取值(ElementType)有:
用于描述构造器
用于描述属性
用于描述局部变量
用于描述方法
用于描述包
用于描述参数
用于描述类、接口(包括注解类型)或者enum声明
@Retention:Retention定义了注解的保留范围,取值(RetentionPoicy)有:
在源文件中有效(即源文件保留)
在class文件中有效(即class保留)
在运行时有效(即运行时保留)
@Documented:Documented用于描述其它类型的annotation应该被作为被标注的程序成员的公共API,因此可以被例如javadoc此类的工具文档化。Documented是一个标记注解,没有成员。
@Inherited:Inherited 元注解是一个标记注解,@Inherited阐述了某个被标注的类型是被继承的。如果一个使用了@Inherited修饰的annotation类型被用于一个class,则这个annotation将被用于该class的子类。
@Target({ElementType.METHOD,ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
public @interface Myannotation {
String key() default “”;
}
第三步使用注解,因为定义Target时定义了MEHTOD和FIELD,因此可以在属性和方法中使用这个注解:
public class MyannotationTest {
@Myannotation(key = “javayz”)
private String username;
}
第四步利用反射解析注解,并进行一些操作
public static void main(String[] args) {
Class myclass=MyannotationTest.class;
Field[] fields = myclass.getDeclaredFields();
for (Field field :fields){
if (field.isAnnotationPresent(Myannotation.class)){
System.out.println(“配置了自定义注解”);
Myannotation annotation = field.getAnnotation(Myannotation.class);
System.out.println(“属性:”+field.getName()+“上的注解key为”+annotation.key());
}
}
}
输出结果:
配置了自定义注解
属性:username上的注解key为javayz
面试官:我看你上面第四步提到了反射是吧?那你给我讲讲什么是反射,它有啥特点:
(我晕,我就说了反射两个字啊,还好有准备)JAVA 反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性;这种动态获取的信息以及动态调用对象的方法的功能称为 java 的反射机制。
在上面第四步利用反射解析注解中,我通过MyannotationTest.class获取到了MyannotationTest的类对象,又用myclass.getDeclaredFields();获取到了所有的属性。这就是反射。
MVC工作流程1 用户向服务器发送请求被DispatcherServelt前端控制器拦截
2 DispatcherServlet 收到请求后,调用HanderMapping处理器映射器
3 处理器映射器通过URL找到具体的处理器,获得处理器对象及处理器拦截器对象,返回执行链给DispatcherServlet。
4DispatcherServelt发送执行链给 HandlerAdapter
5适配器找到合适的Handler,执行 返回一个ModelAndView对象
6 DispatcherServlet根据 传回的ModelAndView对象 选择一个合适的ViewResolver
7 ViewResolver解析后返回一个View进行渲染视图
8 Dispatcher将渲染效果返回给客户端
DispatcherServlet:前端控制器,也称为中央控制器,它是整个请求响应的控制中心,组件的调用由它统一调度。
HandlerMapping:处理器映射器,它根据用户访问的 URL 映射到对应的后端处理器 Handler。也就是说它知道处理用户请求的后端处理器,但是它并不执行后端处理器,而是将处理器告诉给中央处理器。
HandlerAdapter:处理器适配器,它调用后端处理器中的方法,返回逻辑视图 ModelAndView 对象。
ViewResolver:视图解析器,将 ModelAndView 逻辑视图解析为具体的视图(如 JSP)。
Handler:后端处理器,对用户具体请求进行处理,也就是我们编写的 Controller 类。
第一次握手,服务器知道客户端有发送能力
第二次握手 客户端知道服务器有接收和发送能力
第三次握手 服务器知道客户端有接收能力
客户端向服务器发送连接请求 SYN=1,seq=x(SYN代表同步位,seq代表序列号),并进入SYN_SENT(同步发送)状态,等待服务器的确认
服务器监听到客户端连接请求,向客户端发送同意建立新连接SYN和确认报文ACK,即SYN=1,ACK=1,seq=y,ack=x+1,服务器告诉客户端报文段序号是y(seq=y),表示服务器已经收到客户端序号为x的报文段,准备接受客户端序列号为x+1的报文段,并进入SYN_RCVD(同步收到)状态。
客户端收到SYN+ACK报文后,确认序号字段(ACK=1),对连接进行确认,客户此次的报文段的序列号是x+1(seq=x+1),客户期望接受服务器序列号为y+1的报文段(ack_seq=y+1),向服务器发送确认报文段ACK,即ACK=1,seq=x+1,ack=y+1,并进入ESTABLISHED状态,开始读写数据,当服务器端一旦收到客户端的确认报文,就进入ESTABLISHED状态,就可以读写数据了,第三次握手可以携带数据。
客户端向服务器发送释放连接报文段FIN=1,seq=u,进入FIN_WAIT1状态.
服务器端收到后确认ACK=1,seq=v,ack=u+1.进入CLOSE_WAIT状态,此时如果服务端有数据要发送的话,客户端依然需要接收.客户端收到服务器对结束报文段的确认,就会进入到FIN_WAIT2状态,开始等待服务器的结束报文段
服务器接收完后 ,发送Fin=1,ACK=1,seq=w,ack=u+1.服务器端数据发送完毕后,当服务器真正调用close关闭连接时,会向客户端发送结束报文段FIN包,此时服务器进入LAST_ACK状态,等待最后一个ACK的带来
客户端收到后 发送,ACK=1,seq=u+1,ack=w+1.客户端收到服务器发来的结束报文段, 进入TIME_WAIT, 并发出送确认报文段ACK;服务器收到了对结束报文段确认的ACK,进入CLOSED状态,断开连接。而客户端要等待2MSL的时间,才会进入到CLOSED状态
两次不安全,四次没必要
为什么握手是三次,而挥手时需要四次呢?答:其实在TCP握手的时候,接收端将SYN包和ACK确认包合并到一个包中发送的,所以减少了一次包的发送。对于四次挥手,主动关闭方发送FIN请求不代表完全断开连接,只能表示主动关闭方不再发送数据了。而接收方可能还要发送数据,就不能立即关闭服务器端到客户端的数据通道,所以就不能将服务端的FIN包和对客户端的ACK包合并发送,只能先确认ACK,等服务器无需发送数据时在发送FIN包,所以四次挥手时需要四次数据包的交互
HashMap底层实现JDK1.7及以前用的数组+链表,1.8改用数组+链表+红黑树,这是为了提升发生哈希冲突时的查找性能,链表的查找时间复杂度为O(n),红黑树的时间复杂度为O(log2 n)。节点(链表中的数据量)超过8,并且数组长度达到64,链表转为红黑树,(红黑树中的数据量)长度低于6则转回链表
HashMap和HashTable区别HashMap没有synchronized修饰,线程非安全,HashTable线程安全
HashMap允许key和value为null,而HashTable不允许
囧辉:如果我们设置节点多于8个转红黑树,少于8个就马上转链表,当节点个数在8徘徊时,就会频繁进行红黑树和链表的转换,造成性能的损耗。
String,StringBuilder,StringBuffer的区别String 使用了final关键词修饰,不可以修改,每次操作都会产生新的String对象。
StringBuffer和StringBuilder 都是在原对象上进行操作。前者线程安全,有synchronized修饰,后者非线程安全
线程安全是指在多线程环境下,程序可以始终执行正确的行为,符合预期的逻辑。
原子性 接口和抽象类的区别抽象类是对类本质的抽象,接口是对行为的抽象。
抽象类定义的是一个继承体系共性的内容,接口定义的是额外的功能。
抽象类可以有成员变量,可以有常量,接口只能由静态常量
一个类只能继承一个类,但可以实现多个接口。
一个类只要有抽象方法,那么就是抽象类,接口的所有方法都是抽象方法。
基于动态数组,底层是Array,适合下标访问(随机访问),随机访问时间复杂度为O(1),查找为O(n).扩容机制:因为数组长度固定,超出长度存放数据时需要新建数组,然后将老数组的数据拷贝到新数组。
LinkedList:基于双链表,可以分散储存在内存中,适合做增删操作(遍历情况下 时间复杂度为O(n),知道前驱节点 为O(1)),
异常体系全部来源于顶级父类 Throwable
下有两子类 ERROR 和 Exception
线程状态 :创建 就绪 运行 阻塞 死亡
阻塞三种:
等待阻塞:运行线程执行wait方法
同步阻塞:运行线程正在获取对象的同步锁,该同步锁被别的线程占用。
其他阻塞:运行的线程执行sleep或join
sleep方法
会让当前线程进入阻塞状态,但是不会释放锁.
(注意: 会丢弃当前剩余的时间片,立马进入阻塞状态)
yield方法
会让当前线程暂停执行,进入就绪状态
(注意: 会丢弃当前剩余的时间片,立马回到调度中心)
join方法
会让产生线程进入阻塞状态,直到相应线程执行完毕,释放锁。(内部实现了wait())
(注意: 会丢弃当前剩余的时间片,立马进入阻塞状态)
wait和notify/notifyAll方法
一个线程必须在拿到一个对象锁的情况下调用这个对象的wait()方法进入阻塞的状态并且释放锁,当另一个线程也拿到这个锁时可以调用这个对象的notify/notifyAll让一个或者全部休眠线程回到就绪状态(调度中心),然后如果休眠的线程拿到锁和时间片将会从上次休眠的代码处继续执行。
并发就是两个任务彼此干扰,一个时间点只有一个任务运行,交替执行。
并行就是两个任务 在同一时间点 同时运行,彼此没有干扰。
串行是两个任务在时间上不能发生重叠,一个任务没有完成,另一个任务不能开始。
原子性
可见性
有序性
1,避免了创建与销毁额外开销,降低资源消耗
2,即来即用,提高了响应速度
3,方便管理,使用线程池可以统一分配调优监控
corePoolSize : 线程池维护线程的最小数量。(注意线程池刚创建完,线程数是0,来任务才会创建线程)
maximumPoolSize :线程池维护线程的最大数量 。
keepAliveTime : 线程池维护线程所允许的空闲时间。一个线程如果处于空闲状态,并且当前的线程数量大于corePoolSize,那么在指定时间后 ,这个空闲线程会被销毁(补充:但是如果调用了线程池的allowCoreThreadTimeOut(true)方法,在线程池中的线程数不大于corePoolSize时,keepAliveTime参数也会起作用,直到线程池中的线程数为0)
unit : 线程池维护线程所允许的空闲时间的单位,keepAliveTime的计量单位
workQueue : 线程池所使用的缓冲(阻塞)队列,缓冲队列的长度决定了能够缓冲的最大数量。
ThreadFactory:创建一个新线程时使用的工厂,可以用来设定线程名、是否为daemon线程
handler : 线程池对拒绝任务的处理策略。
一般队列只能保证作为一个有限长度缓冲区,如果超过缓冲长度,就无法保存当前任务,阻塞队列可以通过阻塞 保留住当前想要继续入队的任务
阻塞队列可以保证任务队列中没有任务时阻塞获取任务的线程,使得线程(当没有任务时,阻塞核心线程)进入wait状态,释放cpu资源。
阻塞队列自带阻塞和唤醒的功能,不需要额外处理,无任务执行时,线程池利用阻塞队列的take方法挂
起,从而维持核心线程的存活、不至于一直占用cpu资源。
在创建新线程的时候,是要获取全局锁的,这个时候其它的就得阻塞,影响了整体效率。
就好比一个企业里面有10个(core)正式工的名额,最多招10个正式工,要是任务超过正式工人数
(task > core)的情况下,工厂领导(线程池)不是首先扩招工人,还是这10人,但是任务可以稍微积
压一下,即先放到队列去(代价低)。10个正式工慢慢干,迟早会干完的,要是任务还在继续增加,超
过正式工的加班忍耐极限了(队列满了),就的招外包帮忙了(注意是临时工)要是正式工加上外包还
是不能完成任务,那新来的任务就会被领导拒绝了(线程池的拒绝策略)。
1 配置 文件 配置包扫描路径
2递归包扫描获取.class文件
3反射,确定需要交给IOC的类
4对需要注入的类实现依赖注入
依赖注入是实现IOC的方式,控制反转之后,获取对象的过程由自身管理变成了由IOC容器管理,IOC容器在运行期间,动态的将 依赖关系注入到对象之中
Spring常用注解@Controller 访问层(等学了springmvc后再详细介绍)
@Service 业务逻辑层
@Repository 持久层
@Component 除上述三种注解的其它情况
//前置通知
//@Before注解表示作为前置通知
@Before(value = " execution(* cn.yiliedu.aop.User.add(…))")
public void before() {
System.out.println(“before…”);
}
//后置通知(返回通知)
@AfterReturning(value = "execution(* cn.yiliedu.aop.User.add(..))")
public void afterReturning() {
System.out.println("afterReturning.........");
}
//最终通知
@After(value = "execution(* cn.yiliedu.aop.User.add(..))")
public void after() {
System.out.println("after.........");
}
//异常通知
@AfterThrowing(value = "execution(* cn.yiliedu.aop.User.add(..))")
public void afterThrowing() {
System.out.println("afterThrowing.........");
}
//环绕通知
@Around(value = "execution(* cn.yiliedu.aop.User.add(..))")
public void around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
System.out.println("环绕之前.........");
//被增强的方法执行
proceedingJoinPoint.proceed();
System.out.println("环绕之后.........");
}
}
SpringMVC中常用注解@RequestMapping 用于处理 请求url映射的注解,可用于类或方法上
@RequestBody 接收HTTP请求的JSON数据,将JSON转换成Java对象
@ResponseBody 将controller方法返回的对象转换成json格式
Spring是一种轻量级的JavaEE开源框架,以 IOC 和 AOP 为内核,可以单独使用 ,也可以跟SpringMVC,Mybatits等其他框架整合使用。
Spring 有两个核心部分:IOC 和AOP
IOC:控制反转,把创建对象过程交给 Spring 进行管理
Aop:面向切面,不修改源代码进行功能增强
解决了Java开发领域对象的创建,管理的问题
传统开发⽅式:⽐如类A依赖于类B,往往会在类A中new⼀个B的对象,并手动set一些必要的属性
IoC思想下开发⽅式:我们不⽤⾃⼰去new对象了,⽽是由IoC容器(Spring框架)去帮助我们实例化对象并且管理它,我们需要使⽤哪个对象,去问IoC容器要即可。
我们丧失了⼀个权利(创建、管理对象的权利),得到了⼀个福利(不⽤考虑对象的创建、管理等⼀系列 事情)
控制:指的是对象创建(实例化、管理)的权利
反转:控制权交给外部环境了(spring IoC容器)
IoC解决了对象之间的耦合问题。(耦合度即模块与模块之间的依赖程度)
对AOP理解传统情况下,我们想对某个方法进行增强,我们都需要修改源代码,这种方式比较low,会导致代码臃肿。
AOP应运而生,可以在不修改源代码的基础上,将增强的逻辑以一种无侵入的方式插入到特定业务方法的执行前、抛异常或执行后等时刻,对源代码功能进行增强,这种将原业务方法分成不同切面的的编程方式又叫面向切面编程
(1)BeanFactory:IOC 容器基本实现,是 Spring 内部的使用接口,不提供开发人员进行使用
- 加载配置文件时候不会创建对象,在获取对象(使用)才去创建对象
(2)ApplicationContext:BeanFactory 接口的子接口,提供更多更强大的功能,一般由开发人员进行使用。
ApplicationContext加载配置文件时候就只会把在配置文件中单例且非懒加载的对象进行创建。
实例化 属性注入 初始化 销毁
举个例子来说明 Bean的一生就是人的一生
Bean容器在配置文件中找到Person Bean的定义,这个可以说是妈妈怀上了。
Bean的实例化就相当于人的出生,属性赋值就相当于起名字,检查Aware相关接口就是选学校(调用BeanNameAware,BeanFactoryAware),BeanPostProccssor前置处理就像是报名,实现InitailizainBean接口就像是第一天报道,自定义init method 就像是 认真学习 BeanPostProccsor后置处理就像是毕业了 注册Destruction相关接口就是工作前商量社保相关 ,使用就像开始工作,实现DisPOSableBean接口就像是退休, 自定义destory-method就像是死亡,Bean的生命周期就结束了
singleton:如果bean的作用域的属性被声明为singleton,那么Spring Ioc容器只会创建一个共享的bean实例。对于所有的bean请求,只要id与该bean定义的相匹配,那么Spring在每次需要时都返回同一个bean实例。
prototype:为每一个bean请求提供一个实例,在每次注入都会创建一个新的对象。
request:在每个HTTP请求中创建一个单例对象,也就是说每个请求都会复用这一个单例对象。
session:与request相似,确保每个session中有一个bean实例,在session过期后,bean会随之失效。
application:bean被定义为在ServletContext的生命周期中复用一个单例对象。
数据+指针就是一个node,node是组成链表的元素



