1、如何将字符串反转?
将对象封装到stringBuilder中,调用reverse方法反转。
2、java 中操作字符串都有哪些类?它们之间有什么区别?
① String
String是不可变对象,每次对String类型的改变时都会生成一个新的对象。
② StringBuilder
线程不安全,效率高,多用于单线程。
③ StringBuffer
线程安全,由于加锁的原因,效率不如StringBuilder,多用于多线程。
不频繁的字符串操作使用String,操作频繁的情况不建议使用String。
④ StringBuilder > StringBuffer > String。
3、在 Java 中,为什么不允许从静态方法中访问非静态变量?
① 静态变量属于类本身,在类加载的时候就会分配内存,可以通过类名直接访问;
② 非静态变量属于类的对象,只有在类的对象产生时,才会分配内存,通过类的实例去访问;
③ 静态方法也属于类本身,但是此时没有类的实例,内存中没有非静态变量,所以无法调用。
4、在 Java 中,什么时候用重载,什么时候用重写?
① 重载是多态的集中体现,在类中,要以统一的方式处理不同类型数据的时候,可以用重载。
② 重写的使用是建立在继承关系上的,子类在继承父类的基础上,增加新的功能,可以用重写。
③ 简单总结:
1) 重载是多样性,重写是增强剂;
2) 目的是提高程序的多样性和健壮性,以适配不同场景使用时,使用重载进行扩展;
3) 目的是在不修改原方法及源代码的基础上对方法进行扩展或增强时,使用重写;
设计模式:cglib实现动态代理,核心原理用的就是方法的重写;
Java的重载(overload) 最重要的应用场景就是构造器的重载,构造器重载后,提供多种形参形式的构造器,可以应对不同的业务需求,加强程序的健壮性和可扩展性,比如我们最近学习的Spring源码中的ClassPathXmlApplicationContext,它的构造函数使用重载一共提供了10个构造函数,这样就为业务的选择提供了多选择性。在应用到方法中时,主要是为了增强方法的健壮性和可扩展性,比如我们在开发中常用的各种工具类,比如我目前工作中的短信工具类SMSUtil, 发短信的方法就会使用重载,针对不同业务场景下的不同形参,提供短信发送方法,这样提高了工具类的扩展性和健壮性。
总结:重载必须要修改方法(构造器)的形参列表,可以修改方法的返回值类型,也可以修改方法的异常信息即访问权限;使用范围是在同一个类中,目的是对方法(构造器)进行功能扩展,以应对多业务场景的不同使用需求。提高程序的健壮性和扩展性。
java的重写(override) 只要用于子类对父类方法的扩展或修改,但是在我们开发中,为了避免程序混乱,重写一般都是为了方法的扩展,比如在cglib方式实现的动态代理中,代理类就是继承了目标类,对目标类的方法进行重写,同时在方法前后进行切面织入。
总结:方法重写时,参数列表,返回值得类型是一定不能修改的,异常可以减少或者删除,但是不能抛出新的异常或者更广的异常,方法的访问权限可以降低限制,但是不能做更严格的限制。
5、Spring核心概念IOC和AOP
? IOC
这就是依赖倒置原则——把原本的高层建筑依赖底层建筑“倒置”过来,变成底层建筑依赖高层建筑。高层建筑决定需要什么,底层去实现这样的需求,但是高层并不用管底层是怎么实现的。
控制反转就是依赖倒置原则的一种代码设计的思路。具体采用的方法就是所谓的依赖注入。这几种概念的关系大概如下:
所谓依赖注入,就是把底层类作为参数传入上层类,实现上层类对下层类的“控制”。
采用的构造函数、Setter传递和接口传递传入的方式进行的依赖注入,实现控制反转。
IOC Container优点:
1、因为采用了依赖注入,在初始化的过程中就不可避免的会写大量的new。这里IoC容器就解决了这个问题。这个容器可以自动对你的代码进行初始化,你只需要维护一个Configuration(可以是xml,也可以是一段代码),而不用每次初始化一辆车都要亲手去写那一大段初始化的代码。
2、我们在创建实例的时候不需要了解其中的细节。
Spring的IOC实现原理:
Spring实现IOC容器的是通过:工厂 + 反射,实现的。
通过一张图来给大家讲解SpirngIOC的实现原理(基于XML配置文件)
如果是基于全注解形式的话,只是将读取配置文件的步骤改成了读取配置类,然后通过配置类获取需要创建实现的Bean,并通过反射将其创建。其整体实现思路和使用XML配置文件是一样的。
? AOP
Spring AOP全称为Spring Aspect-Oriented Programming,即面向切面编程,是运行时织入的,那么运行时织入到底是怎么实现的呢?答案就是代理对象。代理对象又可以分为静态代理和动态代理。
? 静态代理:由程序员创建或特定工具自动生成源代码,再对其编译。在程序运行前,代理类的.class文件就已经存在了。
? 动态代理:在程序运行时,运用反射机制动态创建而成。
Spring AOP使用动态代理技术在运行期间织入增强的代码,主要有两种代理机制:基于JDK的动态代理;基于cglib的动态代理。JDK本身只提供接口的代理,而不支持类的代理。
6、JDK1.8 新特性
① Lambda表达式
lambda表达式本质上是一段匿名内部类,也可以是一段可以传递的代码
② 函数式接口
函数式接口的提出是为了给Lambda表达式的使用提供更好的支持。
函数式接口定义了一个抽象方法的接口(Object类的public方法除外),就是函数式接口,并且还提供了注解:@FunctionalInterface
常见的四大函数式接口:
1) Consumer《T》:消费型接口,有参无返回值
2) Supplier《T》:供给型接口,无参有返回值
3)Function 《T,R》::函数式接口,有参有返回值
4)Predicate《T》: 断言型接口,有参有返回值,返回值是boolean类型
在四大核心函数式接口基础上,还提供了诸如BiFunction、BinaryOperation、toIntFunction等扩展的函数式接口,都是在这四种函数式接口上扩展而来的,不做赘述。
总结:函数式接口的提出是为了让我们更加方便的使用lambda表达式,不需要自己再手动创建一个函数式接口,直接拿来用就好了。
③ 方法引用
若lambda体中的内容有方法已经实现了,那么可以使用“方法引用”
也可以理解为方法引用是lambda表达式的另外一种表现形式并且其语法比lambda表达式更加简单
1)方法引用
? 对象::实例方法名
? 类::静态方法名
? 类::实例方法名 (lambda参数列表中第一个参数是实例方法的调用 者,第二个参数是实例方法的参数时可用)
2)构造器引用
格式:ClassName::new
3)数组引用
格式:Type[]::new
④ Stream API
Stream操作的三个步骤
(1)创建stream
(2)中间操作(过滤、map)
(3)终止操作
stream的创建:
Stream的中间操作:
Stream的终止操作:
还有功能比较强大的两个终止操作 reduce和collect
reduce操作: reduce:(T identity,BinaryOperator)/reduce(BinaryOperator)-可以将流中元素反复结合起来,得到一个值
collect操作:Collect-将流转换为其他形式,接收一个Collection接口的实现,用于给Stream中元素做汇总的方法
⑤ 并行流和串行流
在jdk1.8新的stream包中针对集合的操作也提供了并行操作流和串行操作流。并行流就是把内容切割成多个数据块,并且使用多个线程分别处理每个数据块的内容。Stream api中声明可以通过parallel()与sequential()方法在并行流和串行流之间进行切换。
jdk1.8并行流使用的是fork/join框架进行并行操作
⑥ ForkJoin框架
Fork/Join 框架:就是在必要的情况下,将一个大任务,进行拆分(fork)成若干个小任务(拆到不可再拆时),再将一个个的小任务运算的结果进行 join 汇总。
关键字:递归分合、分而治之。
采用 “工作窃取”模式(work-stealing):
当执行新的任务时它可以将其拆分分成更小的任务执行,并将小任务加到线
程队列中,然后再从一个随机线程的队列中偷一个并把它放在自己的队列中
相对于一般的线程池实现,fork/join框架的优势体现在对其中包含的任务的
处理方式上.在一般的线程池中,如果一个线程正在执行的任务由于某些原因
无法继续运行,那么该线程会处于等待状态.而在fork/join框架实现中,如果
某个子问题由于等待另外一个子问题的完成而无法继续运行.那么处理该子
问题的线程会主动寻找其他尚未运行的子问题来执行.这种方式减少了线程
的等待时间,提高了性能。
展示多线程的效果:
Optional容器:使用Optional容器可以快速的定位NPE,并且在一定程度上可以减少对参数非空检验的代码量。
⑦ 新的日期API LocalDate | LocalTime | LocalDateTime
新的日期API都是不可变的,更使用于多线程的使用环境中
7、静态变量会被序列化吗?
不会。因为序列化是针对对象而言的, 而静态变量优先于对象存在, 随着类的加载而加载, 所以不会被序列化.
看到这个结论, 是不是有人会问, serialVersionUID也被static修饰, 为什么serialVersionUID会被序列化? 其实serialVersionUID属性并没有被序列化, JVM在序列化对象时会自动生成一个serialVersionUID, 然后将我们显示指定的serialVersionUID属性值赋给自动生成的serialVersionUID。
8、Java 序列化中如果有些字段不想进行序列化,怎么办?
对于不想进行序列化的变量,使用 transient 关键字修饰。
transient 关键字的作用是控制变量的序列化,在变量声明前加上该关键字,可以阻止该变量被序列化到文件中,在被反序列化后,transient 变量的值被设为初始值,如 int 型的是 0,对象型的是 null。transient 只能修饰变量,不能修饰类和方法。
9、什么是serialVersionUID
serialVersionUID 用来表明类的不同版本间的兼容性
Java的序列化机制是通过在运行时判断类的serialVersionUID来验证版本一致性的。在进行反序列化时,JVM会把传来的字节流中的serialVersionUID与本地相应实体(类)的serialVersionUID进行比较,如果相同就认为是一致的,可以进行反序列化,否则就会出现序列化版本不一致的异常。
10、反射机制的原理是什么?
? 反射获取类实例 Class.forName(),并没有将实现留给了java,而是交给了jvm去加载!主要是先获取 ClassLoader, 然后调用 native 方法,获取信息,加载类则是回调 java.lang.ClassLoader。最后,jvm又会回调 ClassLoader 进类加载!
? newInstance() 主要做了三件事:
1) 权限检测,如果不通过直接抛出异常;
2) 查找无参构造器,并将其缓存起来;
3) 调用具体方法的无参构造方法,生成实例并返回。