Java高频面试题详解 助你成为Java大牛
Java代理的几种实现方式
第一种:静态代理,只能静态的代理某些类或者某些方法,不推荐使用,功能比较弱,但是编码简单
第二种:动态代理,包含Proxy代理和CGLIB动态代理
Proxy代理是JDK内置的动态代理
特点:面向接口的,不需要导入三方依赖的动态代理,可以对多个不同的接口进行增强,通过反射读取注解时,只能读取到接口上的注解
原理:面向接口,只能对实现类在实现接口中定义的方法进行增强
定义接口和实现
package com.proxy; public interface UserService { public String getName(int id); public Integer getAge(int id); }
package com.proxy; public class UserServiceImpl implements UserService { @Override public String getName(int id) { System.out.println("------getName------"); return "riemann"; } @Override public Integer getAge(int id) { System.out.println("------getAge------"); return 26; } }
package com.proxy; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; public class MyInvocationHandler implements InvocationHandler { public Object target; MyInvocationHandler() { super(); } MyInvocationHandler(Object target) { super(); this.target = target; } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { if ("getName".equals(method.getName())) { System.out.println("++++++before " + method.getName() + "++++++"); Object result = method.invoke(target, args); System.out.println("++++++after " + method.getName() + "++++++"); return result; } else { Object result = method.invoke(target, args); return result; } } }
package com.proxy; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Proxy; public class Main1 { public static void main(String[] args) { UserService userService = new UserServiceImpl(); InvocationHandler invocationHandler = new MyInvocationHandler(userService); UserService userServiceProxy = (UserService) Proxy.newProxyInstance(userService.getClass().getClassLoader(), userService.getClass().getInterfaces(),invocationHandler); System.out.println(userServiceProxy.getName(1)); System.out.println(userServiceProxy.getAge(1)); } }
CGLIB动态代理
特点:面向父类的动态代理,需要导入第三方依赖
原理:面向父类,底层通过子类继承父类并重写方法的形式实现增强
Proxy和CGLIB是非常重要的代理模式,是springAOP底层实现的主要两种方式
CGLIB的核心类: net.sf.cglib.proxy.Enhancer – 主要的增强类 net.sf.cglib.proxy.MethodInterceptor – 主要的方法拦截类,它是Callback接口的子接口,需要用户实现 net.sf.cglib.proxy.MethodProxy – JDK的java.lang.reflect.Method类的代理类,可以方便的实现对源对象方法的调用,如使用: Object o = methodProxy.invokeSuper(proxy, args);//虽然第一个参数是被代理对象,也不会出现死循环的问题。
net.sf.cglib.proxy.MethodInterceptor接口是最通用的回调(callback)类型,它经常被基于代理的AOP用来实现拦截(intercept)方法的调用。这个接口只定义了一个方法 public Object intercept(Object object, java.lang.reflect.Method method, Object[] args, MethodProxy proxy) throws Throwable;
第一个参数是代理对像,第二和第三个参数分别是拦截的方法和方法的参数。原来的方法可能通过使用java.lang.reflect.Method对象的一般反射调用,或者使用 net.sf.cglib.proxy.MethodProxy对象调用。net.sf.cglib.proxy.MethodProxy通常被首选使用,因为它更快。
package com.proxy.cglib; import net.sf.cglib.proxy.MethodInterceptor; import net.sf.cglib.proxy.MethodProxy; import java.lang.reflect.Method; public class CglibProxy implements MethodInterceptor { @Override public Object intercept(Object o, Method method, Object[] args, MethodProxy methodProxy) throws Throwable { System.out.println("++++++before " + methodProxy.getSuperName() + "++++++"); System.out.println(method.getName()); Object o1 = methodProxy.invokeSuper(o, args); System.out.println("++++++before " + methodProxy.getSuperName() + "++++++"); return o1; } }
package com.proxy.cglib; import com.test3.service.UserService; import com.test3.service.impl.UserServiceImpl; import net.sf.cglib.proxy.Enhancer; public class Main2 { public static void main(String[] args) { CglibProxy cglibProxy = new CglibProxy(); Enhancer enhancer = new Enhancer(); enhancer.setSuperclass(UserServiceImpl.class); enhancer.setCallback(cglibProxy); UserService o = (UserService)enhancer.create(); o.getName(1); o.getAge(1); } }
hashcode和equals如何使用
equals()源自于java.lang.Object,该方法用来简单验证两个对象的相等性。Object类中定义的默认实现只检查两个对象的对象引用,以验证它们的相等性。 通过重写该方法,可以自定义验证对象相等新的规则,如果你使用ORM处理一些对象的话,你要确保在hashCode()和equals()对象中使用getter和setter而不是直接引用成员变量
hashCode()源自于java.lang.Object ,该方法用于获取给定对象的唯一的整数(散列码)。当这个对象需要存储在哈希表这样的数据结构时,这个整数用于确定桶的位置。默认情况下,对象的hashCode()方法返回对象所在内存地址的整数表示。hashCode()是HashTable、HashMap和HashSet使用的。默认的,Object类的hashCode()方法返回这个对象存储的内存地址的编号。
hash散列算法,使得在hash表中查找一个记录速度变O(1). 每个记录都有自己的hashcode,散列算法按照hashcode把记录放置在合适的位置. 在查找一个记录,首先先通过hashcode快速定位记录的位置.然后再通过equals来比较是否相等。如果hashcode没找到,则不equal,元素不存在于哈希表中;即使找到了,也只需执行hashcode相同的几个元素的equal,如果不equal,还是不存在哈希表中。
HashMap和HashTable的区别及底层实现
HashMap和HashTable对比
-
HashTable线程同步,HashMap非线程同步。
-
HashTable不允许<键,值>有空值,HashMap允许<键,值>有空值。
-
HashTable使用Enumeration,HashMap使用Iterator。
-
HashTable中hash数组的默认大小是11,增加方式的old*2+1,HashMap中hash数组的默认大小是16,增长方式是2的指数倍。
5.HashMap jdk1.8之前list + 链表 jdk1.8之后list + 链表,当链表长度到8时,转化为红黑树
6.HashMap链表插入节点的方式 在Java1.7中,插入链表节点使用头插法。Java1.8中变成了尾插法
7.Java1.8的hash()中,将hash值高位(前16位)参与到取模的运算中,使得计算结果的不确定性增强,降低发生哈希碰撞的概率
#面试##java后端面试##后端面试##阿里面试##面试题目#每天用半小时的时间来看几个面试经常问的面试题,日积月累你也会成为编程大牛!