阿里巴巴做网站联系人,网站升级需要什么,网站模板制作与安装教程视频教程,高端网站建设专家来源一 大彬八股文
来源二 2023 20W字八股文
2024秋招八股文
1.Java创建对象有几种方式#xff1f;
Java创建对象有以下几种方式#xff1a;
用new语句创建对象。使用反射#xff0c;使用Class.newInstance()创建对象。调用对象的clone()方法。运用反序列化手段#x…来源一 大彬八股文
来源二 2023 20W字八股文
2024秋招八股文
1.Java创建对象有几种方式
Java创建对象有以下几种方式
用new语句创建对象。使用反射使用Class.newInstance()创建对象。调用对象的clone()方法。运用反序列化手段调用java.io.ObjectInputStream对象的readObject()方法2.说说类实例化的顺序
Java中类实例化顺序 静态属性 静态代码块。普通属性 普通代码块。构造方法。public class LifeCycle {// 静态属性private static String staticField getStaticField();// 静态代码块static {System.out.println(staticField);System.out.println(静态代码块初始化);}// 普通属性private String field getField();// 普通代码块{System.out.println(field);System.out.println(普通代码块初始化);}// 构造方法public LifeCycle() {System.out.println(构造方法初始化);}// 静态方法public static String getStaticField() {String statiFiled 静态属性初始化;return statiFiled;}// 普通方法public String getField() {String filed 普通属性初始化;return filed;}public static void main(String[] argc) {new LifeCycle();}/*** 静态属性初始化* 静态代码块初始化* 普通属性初始化* 普通代码块初始化* 构造方法初始化*/
}3.final, finally, finalize 的区别
final 用于修饰属性、方法和类, 分别表示属性不能被重新赋值方法不可被覆盖类不可被继承。
finally 是异常处理语句结构的一部分一般以try-catch-finally出现finally代码块表示总是被执行。
finalize 是Object类的一个方法该方法一般由垃圾回收器来调用 当我们调用System.gc()方法的时候由垃圾回收器调用finalize()方法回收垃圾JVM并不保证此方法总被调用。
4.什么是反射
反射是通过获取类的class对象然后动态的获取到这个类的内部结构动态的去操作类的属性和方法。 应用场景有要操作权限不够的类属性和方法时、实现自定义注解时、动态加载第三方jar包时、按需加载类节省编译和初始化时间 获取class对象的方法有class.forName(类路径)类.class()对象的getClass
反射有哪些应用场景呢
JDBC连接数据库时使用Class.forName()通过反射加载数据库的驱动程序Eclispe、IDEA等开发工具利用反射动态解析对象的类型与结构动态提示对象的属性和方法Web服务器中利用反射调用了Sevlet的service方法JDK动态代理底层依赖反射实现5.BIO/NIO/AIO区别的区别
同步阻塞IO : 用户进程发起一个IO操作以后必须等待IO操作的真正完成后才能继续运行。
同步非阻塞IO: 客户端与服务器通过Channel连接采用多路复用器轮询注册的Channel。提高吞吐量和可靠性。用户进程发起一个IO操作以后可做其它事情但用户进程需要轮询IO操作是否完成这样造成不必要的CPU资源浪费。
异步非阻塞IO: 非阻塞异步通信模式NIO的升级版采用异步通道实现异步通信其read和write方法均是异步方法。用户进程发起一个IO操作然后立即返回等IO操作真正的完成以后应用程序会得到IO操作完成的通知。类似Future模式。 通过故事讲清楚NIO 下面通过一个例子来讲解下。
假设某银行只有10个职员。该银行的业务流程分为以下4个步骤
1 顾客填申请表5分钟
2 职员审核1分钟
3 职员叫保安去金库取钱3分钟
4 职员打印票据并将钱和票据返回给顾客1分钟。
下面我们看看银行不同的工作方式对其工作效率到底有何影响。
首先是BIO方式。
每来一个顾客马上由一位职员来接待处理并且这个职员需要负责以上4个完整流程。当超过10个顾客时剩余的顾客需要排队等候。
一个职员处理一个顾客需要10分钟5131时间。一个小时60分钟能处理6个顾客一共10个职员那就是只能处理60个顾客。
可以看到银行职员的工作状态并不饱和比如在第1步其实是处于等待中。
这种工作其实就是BIO每次来一个请求顾客就分配到线程池中由一个线程职员处理如果超出了线程池的最大上限10个就扔到队列等待 。
那么如何提高银行的吞吐量呢
思路就是分而治之将任务拆分开来由专门的人负责专门的任务。
具体来讲银行专门指派一名职员AA的工作就是每当有顾客到银行他就递上表格让顾客填写。每当有顾客填好表后A就将其随机指派给剩余的9名职员完成后续步骤。
这种方式下假设顾客非常多职员A的工作处于饱和中他不断的将填好表的顾客带到柜台处理。
柜台一个职员5分钟能处理完一个顾客一个小时9名职员能处理9*60/5108。
可见工作方式的转变能带来效率的极大提升。
这种工作方式其实就NIO的思路。
下图是非常经典的NIO说明图mainReactor线程负责监听server socket接收新连接并将建立的socket分派给subReactor
**subReactor可以是一个线程也可以是线程池负责多路分离已连接的socket读写网络数据。**这里的读写网络数据可类比顾客填表这一耗时动作对具体的业务处理功能其扔给worker线程池完成
可以看到典型NIO有三类线程分别是mainReactor线程、subReactor线程、work线程。
不同的线程干专业的事情最终每个线程都没空着系统的吞吐量自然就上去了。
那这个流程还有没有什么可以提高的地方呢
可以看到在这个业务流程里边第3个步骤职员叫保安去金库取钱3分钟。这3分钟柜台职员是在等待中度过的可以把这3分钟利用起来。
还是分而治之的思路指派1个职员B来专门负责第3步骤。
每当柜台员工完成第2步时就通知职员B来负责与保安沟通取钱。这时候柜台员工可以继续处理下一个顾客。
当职员B拿到钱之后通知顾客钱已经到柜台了让顾客重新排队处理当柜台职员再次服务该顾客时发现该顾客前3步已经完成直接执行第4步即可。
在当今web服务中经常需要通过RPC或者Http等方式调用第三方服务这里对应的就是第3步如果这步耗时较长通过异步方式将能极大降低资源使用率。
NIO异步的方式能让少量的线程做大量的事情。这适用于很多应用场景比如代理服务、api服务、长连接服务等等。这些应用如果用同步方式将耗费大量机器资源。
不过虽然NIO异步能提高系统吞吐量但其并不能让一个请求的等待时间下降相反可能会增加等待时间。
最后NIO基本思想总结起来就是分而治之将任务拆分开来由专门的人负责专门的任务
6.sleep和wait区别
sleep方法
属于Thread类中的方法释放cpu给其它线程 不释放锁资源sleep(1000) 等待超过1s被唤醒wait方法
属于Object类中的方法释放cpu给其它线程同时释放锁资源wait(1000) 等待超过1s被唤醒wait() 一直等待需要通过notify或者notifyAll进行唤醒wait 方法必须配合 synchronized 一起使用
不然在运行时就会抛出IllegalMonitorStateException异常锁释放时机代码演示
public static void main(String[] args) {Object o new Object();Thread thread new Thread(() - {synchronized (o) {System.out.println(新线程获取锁时间 LocalDateTime.now() 新线程名称 Thread.currentThread().getName());try {//wait 释放cpu同时释放锁o.wait(2000);//sleep 释放cpu不释放锁//Thread.sleep(2000);System.out.println(新线程获取释放锁锁时间 LocalDateTime.now() 新线程名称 Thread.currentThread().getName());} catch (InterruptedException e) {throw new RuntimeException(e);}}});thread.start();try {Thread.sleep(100);} catch (InterruptedException e) {throw new RuntimeException(e);}System.out.println(主线程获取锁时间 LocalDateTime.now() 主线程名称 Thread.currentThread().getName());synchronized (o){System.out.println(主线程获取释放锁锁时间 LocalDateTime.now() 主线程名称 Thread.currentThread().getName());}
}7.守护线程是什么
守护线程是运行在后台的一种特殊进程。它独立于控制终端并且周期性地执行某种任务或等待处理某些发生的事件。在 Java 中垃圾回收线程就是特殊的守护线程。8.Java支持多继承吗
java中类不支持多继承。接口才支持多继承。接口的作用是拓展对象功能。当一个子接口继承了多个父接口时说明子接口拓展了多个功能。当一个类实现该接口时就拓展了多个的功能。
Java不支持多继承的原因
1.出于安全性的考虑如果子类继承的多个父类里面有相同的方法或者属性子类将不知道具体要继承哪个。 2.Java提供了接口和内部类以达到实现多继承功能弥补单继承的缺陷。
9.阻塞和非阻塞的区别
阻塞和非阻塞关注的是线程的状态。
阻塞调用是指调用结果返回之前当前线程会被挂起。调用线程只有在得到结果之后才会恢复运行。
非阻塞调用指在不能立刻得到结果之前该调用不会阻塞当前线程。
10.Java8的新特性有哪些
Lambda 表达式Lambda允许把函数作为一个方法的参数 Stream API 新添加的Stream APIjava.util.stream 把真正的函数式编程风格引入到Java中 默认方法默认方法就是一个在接口里面有了一个实现的方法。 Optional 类 Optional 类已经成为 Java 8 类库的一部分用来解决空指针异常。 Date Time API 加强对日期与时间的处理。
11.序列化和反序列化
序列化把对象转换为字节序列的过程称为对象的序列化. 反序列化把字节序列恢复为对象的过程称为对象的反序列化.
什么时候需要用到序列化和反序列化呢?
当我们只在本地 JVM 里运行下 Java 实例这个时候是不需要什么序列化和反序列化的但当我们需要将内存中的对象持久化到磁盘数据库中时当我们需要与浏览器进行交互时当我们需要实现 RPC 时这个时候就需要序列化和反序列化了.
前两个需要用到序列化和反序列化的场景是不是让我们有一个很大的疑问? 我们在与浏览器交互时还有将内存中的对象持久化到数据库中时好像都没有去进行序列化和反序列化因为我们都没有实现 Serializable 接口但一直正常运行.
下面先给出结论:
只要我们对内存中的对象进行持久化或网络传输这个时候都需要序列化和反序列化.
理由:
服务器与浏览器交互时真的没有用到 Serializable 接口吗?
JSON 格式实际上就是将一个对象转化为字符串所以服务器与浏览器交互时的数据格式其实是字符串我们来看来 String 类型的源码:
public final class Stringimplements java.io.SerializableComparableStringCharSequence {/\*\* The value is used for character storage. \*/private final char value\[\];/\*\* Cache the hash code for the string \*/private int hash; // Default to 0/\*\* use serialVersionUID from JDK 1.0.2 for interoperability \*/private static final long serialVersionUID -6849794470754667710L;......
}String 类型实现了 Serializable 接口并显示指定 serialVersionUID 的值.
然后我们再来看对象持久化到数据库中时的情况Mybatis 数据库映射文件里的 insert 代码:
insert idinsertUser parameterTypeorg.tyshawn.bean.UserINSERT INTO t\_user(nameage) VALUES (#{name}#{age})
/insert实际上我们并不是将整个对象持久化到数据库中而是将对象中的属性持久化到数据库中而这些属性如Date/String都实现了 Serializable 接口。
12.实现序列化和反序列化为什么要实现 Serializable 接口?
在 Java 中实现了 Serializable 接口后 JVM 在类加载的时候就会发现我们实现了这个接口然后在初始化实例对象的时候就会在底层帮我们实现序列化和反序列化。
如果被写对象类型不是String、数组、Enum并且没有实现Serializable接口那么在进行序列化的时候将抛出NotSerializableException。
源码如下
// remaining cases
if (obj instanceof String) {writeString((String) obj, unshared);
} else if (cl.isArray()) {writeArray(obj, desc, unshared);
} else if (obj instanceof Enum) {writeEnum((Enum?) obj, desc, unshared);
} else if (obj instanceof Serializable) {writeOrdinaryObject(obj, desc, unshared);
} else {if (extendedDebugInfo) {throw new NotSerializableException(cl.getName() \n debugInfoStack.toString());} else {throw new NotSerializableException(cl.getName());}
}13.实现 Serializable 接口之后为什么还要显示指定 serialVersionUID 的值?
如果不显示指定 serialVersionUIDJVM 在序列化时会根据属性自动生成一个 serialVersionUID然后与属性一起序列化再进行持久化或网络传输. 在反序列化时JVM 会再根据属性自动生成一个新版 serialVersionUID然后将这个新版 serialVersionUID 与序列化时生成的旧版 serialVersionUID 进行比较如果相同则反序列化成功否则报错.
如果显示指定了 serialVersionUIDJVM 在序列化和反序列化时仍然都会生成一个 serialVersionUID但值为我们显示指定的值这样在反序列化时新旧版本的 serialVersionUID 就一致了.
如果我们的类写完后不再修改那么不指定serialVersionUID不会有问题但这在实际开发中是不可能的我们的类会不断迭代一旦类被修改了那旧对象反序列化就会报错。 所以在实际开发中我们都会显示指定一个 serialVersionUID。
14.static 属性为什么不会被序列化?
因为序列化是针对对象而言的而 static 属性优先于对象存在随着类的加载而加载所以不会被序列化.
看到这个结论是不是有人会问 serialVersionUID 也被 static 修饰为什么 serialVersionUID 会被序列化?
其实 serialVersionUID 属性并没有被序列化JVM 在序列化对象时会自动生成一个 serialVersionUID然后将我们显示指定的 serialVersionUID 属性值赋给自动生成的 serialVersionUID.
15.讲讲什么是泛型
Java泛型是JDK 5中引⼊的⼀个新特性 允许在定义类和接口的时候使⽤类型参数。声明的类型参数在使⽤时⽤具体的类型来替换。
泛型最⼤的好处是可以提⾼代码的复⽤性。以List接口为例我们可以将String、 Integer等类型放⼊List中 如不⽤泛型 存放String类型要写⼀个List接口 存放Integer要写另外⼀个List接口 泛型可以很好的解决这个问题。
16.动态代理
16.1什么是动态代理?
动态代理就是在程序运行期创建目标对象的代理对象并对目标对象中的方法进行功能性增强的一种技术。在生成代理对象的过程中目标对象不变代理对象中的方法是目标对象方法的增强方法。可以理解为运行期间对象中方法的动态拦截在拦截方法的前后执行功能操作。
代理类在程序运行期间创建的代理对象称之为动态代理对象。这种情况下创建的代理对象并不是事先在Java代码中定义好的。而是在运行期间根据我们在动态代理对象中的“指示”动态生成的。也就是说你想获取哪个对象的代理动态代理就会为你动态的生成这个对象的代理对象。
动态代理可以对被代理对象的方法进行功能增强。有了动态代理的技术那么就可以在不修改方法源码的情况下增强被代理对象的方法的功能在方法执行前后做任何你想做的事情。
创建代理对象的两个方法 //JDK动态代理
Proxy.newProxyInstance(三个参数);
//CGLib动态代理
Enhancer.create(两个参数);
两种常用的动态代理方式
基于接口的动态代理
提供者JDK
使用JDK官方的Proxy类创建代理对象
注意代理的目标对象必须实现接口
基于类的动态代理提供者第三方 CGLib
使用CGLib的Enhancer类创建代理对象
注意如果报 asmxxxx 异常需要导入 asm.jar包
以下是两种代理方式的实例代码public class LogProxy {/*** 生成对象的代理对象对被代理对象进行所有方法日志增强* 参数原始对象* 返回值被代理的对象* JDK 动态代理* 基于接口的动态代理* 被代理类必须实现接口* JDK提供的*/public static Object getObject(final Object obj){/*** 创建对象的代理对象* 参数一类加载器* 参数二对象的接口* 参数三调用处理器代理对象中的方法被调用都会在执行方法。对所有被代理对象的方法进行拦截*/Object proxyInstance Proxy.newProxyInstance(obj.getClass().getClassLoader(), obj.getClass().getInterfaces(), new InvocationHandler() {public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {//方法执行前long startTime System.currentTimeMillis();Object result method.invoke(obj, args);//执行方法的调用//方法执行后long endTime System.currentTimeMillis();SimpleDateFormat sdf new SimpleDateFormat();System.out.printf(String.format(%s方法执行结束时间%%s 方法执行耗时%%d%%n, method.getName()), sdf.format(endTime), endTime - startTime);return result;}});return proxyInstance;}/*** 使用CGLib创建动态代理对象* 第三方提供的的创建代理对象的方式CGLib* 被代理对象不能用final修饰* 使用的是Enhancer类创建代理对象*/public static Object getObjectByCGLib(final Object obj){/*** 使用CGLib的Enhancer创建代理对象* 参数一对象的字节码文件* 参数二方法的拦截器*/Object proxyObj Enhancer.create(obj.getClass(), new MethodInterceptor() {public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {//方法执行前long startTime System.currentTimeMillis();Object invokeObject method.invoke(obj, objects);//执行方法的调用//方法执行后long endTime System.currentTimeMillis();SimpleDateFormat sdf new SimpleDateFormat();System.out.printf(String.format(%s方法执行结束时间%%s 方法执行耗时%%d%%n, method.getName()), sdf.format(endTime), endTime - startTime);return invokeObject;}});return proxyObj;}
}CGLib 底层原理
通过查看 Enhancer 类源码最终也是生成动态代理类的字节码动态代理类继承要被代理的类然后实现其方法。 和 JDK Proxy 的实现代码比较类似都是通过实现代理器的接口再调用某一个方法完成动态代理的唯一不同的是CGLib 在初始化被代理类时是通过 Enhancer 对象把代理对象设置为被代理类的子类来实现动态代理的。
CGLib 实现步骤
创建一个实现接口 MethodInterceptor 的代理类重写 intercept 方法 创建获取被代理类的方法 getInstance(Object target); 获取代理类通过代理调用方法。
两者区别 JDK Proxy 和 CGLib 的区别主要体现在以下方面
JDK Proxy 是 Java 语言自带的功能无需通过加载第三方类实现Java 对 JDK Proxy 提供了稳定的支持并且会持续的升级和更新
Java 8 版本中的 JDK Proxy 性能相比于之前版本提升了很多JDK Proxy 是通过拦截器加反射的方式实现的JDK Proxy 只能代理实现接口的类JDK Proxy 实现和调用起来比较简单CGLib 是第三方提供的工具基于 ASM 实现的性能比较高CGLib 无需通过接口来实现它是针对类实现代理
主要是对指定的类生成一个子类它是通过实现子类的方式来完成调用的。17.聊聊 Spring AOP原理
AOP 思想
基于动态代理思想对原来目标对象创建代理对象在不修改原对象代码情况下通过代理对象调用增强功能的代码从而对原有业务方法进行增强。
AOP 作用
在不修改源代码的情况下可以增加额外的功能实现在原有功能基础上的增强。
AOP 使用场景
记录日志(调用方法后记录日志) 监控性能(统计方法运行时间) 权限控制(调用方法前校验是否有权限) 事务管理(调用方法前开启事务调用方法后提交关闭事务 ) 缓存优化(第一次调用查询数据库将查询结果放入内存对象 第二次调用直接从内存对象返回不需要查询数据库 ) AOP 实现原理
Spring AOP 的有两种实现方式JDK proxy 和 CGLib 动态代理
当 Bean 实现接口时Spring 使用 JDK proxy实现。当 Bean 没有实现接口时Spring 使用 CGlib 代理实现。
通过配置可以强制使用 CGlib 代理(在 spring 配置中加入 aop:aspectj-autoproxy proxy-target-class“true”)。