技术面"/>
某大厂第一技术面
1、java容器有哪些?哪些是同步容器,哪些是并发容器?
一、基本概念
Java容器类类库的用途是“持有对象”,并将其划分为两个不同的概念:
Collection是一个独立元素的序列,这些元素都服从一条或多条规则。List必须按照插入的顺序保存元素,而Set不能有重复的元素。Queue按照排队规则来确定对象产生的顺序(通常与它们被插入的顺序相同)。
Map:一组成对的“键值对”对象,允许你使用键来查找值。
注:
1、java.util.Collection是一个集合接口。
它提供了对集合对象进行基本操作的通用接口方法。Collection接口在java类库中有很多具体的表现。
Collection接口的意义是为了各种具体的集合提供了最大化的统一操作方式。
2、java.util.Collections是一个包装类。
它包含有各种有关集合操作的静态多态方法。此类不能实例化,就像是一个工具类,服务于Java的Collection框架。
容器集
|Collection
| |-List
| |-Linkedist
| |-ArrayList
| |-Verctor
| |-Stack
| |-Set
| |-HashSet
| |-TreeSet
| |-LinkedSet
|
|Map
|-HashTable
|-HashMap
|-WeakHashMap
同步容器
- Vector
- Stack
- HashTable
- Collections.synchronized方法生成
并发容器
- ConcurrentHashMap:线程安全的HashMap实现
- CopyOnWriteArrayList:线程安全且在读操作时无锁的ArrayList
- CopyOnWriteArraySet:基于CopyOnWriteArrayList,不添加重复元素
- ArrayBolckingQueue:基于数组、先进先出、线程安全,可实现指定时间的阻塞读写,并且容量可以限制
- LinkedBlockingQuee:基于链表实现,读写各用一把锁,在高并发读写操作都多的情况下,性能优于ArrayBolckingQueue
二、Collection集合接口
Collection是最基本的集合接口,一个Collection代表一组Object,即Collection的元素(Elements)。一些Collection允许相同的元素而另一些不行。一些能排序而另一些不行。JavaSDK不提供直接继承自Collection的类,JavaSDK提供的类都是继承自Collection的子接口,如“List”和“Set”。
主要方法:
- boolean add(Object o)添加对象到集合
- boolean remove(Object o)删除指定的对象
- int size()返回当前集合中元素的数量
- boolean contains(Object o)查找集合中是否有指定的对象
- boolean isEmpty()判断集合是否为空
- Iterator iterator()返回一个迭代器
- boolean containsAll(Collection c)查找集合中是否有集合C中的元素
- boolean addAll(Collection c)将集合C中的所有元素添加给该集合
- void clear()删除集合中的所有元素
- void removeAll(Collection c)从集合中删除 c集合中也有的元素
- void retainAll(Collection c)从集合中删除集合才中不包含的元素
List接口
List是有序的Collectin,使用此接口能有效的控制每个元素插入的位置。用户能够使用索引(元素在List中的位置,也就是下下标)来访问List中的元素,这类似于java的数组。
实现List的常用类有LinkedList、ArrayList、Vector、Stack。
LinkedList类
LinkedList实现了List接口,允许null元素。此外LinkedList提供额外的get,remove,insert的方法在LinkedList的首部或者是尾部。这些操作使LinkedList可被用作堆栈(Stack)、对列(queue)或双向队列(deque)。
LinkedList是非同步的,如果多个线程访问一个List,则必须自己实现访问同步。一种解决方法是在创建List时构造一个同步的List:
List list = Collections.synchronizedList(new LinkedList(…));
ArrayList类
ArrayList实现了可变大小的数组。它允许所有元素,包括null。ArrayList没有同步。size,isEmpty,get,set方法运行时间为常数,但是add方法开销为分摊的常数,添加n个元素需要O(n)的时间,其他方法运行时间为线性,每个ArrayList都有一个实例(Capactiy),即用于存储元素的数组的大小。这个容量可随着不断添加新元素而自动增加,但是增长算法并没有定义。当需要插入大量元素时,在插入前可以调用ensureCapacity方法来增加ArrayList的容量以提高插入效率。
和LinkedList一样,ArrayList也是非同步的(unsynchronized)。一般情况下使用这两个就可以了,因为非同步,所以效率比较高。
如果涉及到堆栈,队列等操作,应考虑用List,对于需要快速插入,删除元素,应该使用LinkedList,如果需要随机快速访问元素,应该使用ArrayList。
Vector类
Vector非常类似于ArrayList,但是Vector是同步的,由Vector创建的Iterator,虽然和ArrayList创建的Iterator是同一接口,但是,因为Vector是同步的,当一个Iterator被创建而且正在被使用,另一个线程改变了Vector的状态(例如,添加或者删除了一些元素),这时调用Iterator的方法时将抛出ConcurrentModificationExceptin,因此必须捕获该异常。
Vector的使用主要有如下两种场景:
- Vector所谓的多线程安全,只是针对单纯的调用某个方法,它是有同步机制的。如Add,多个线程都在对同一个容器Add元素,Vector能够保证最后总数是正确的,而ArrayList没有同步机制,就无法保证。
- Vector的多线程安全,在组合操作时不是线程安全的。比如一个线程先调用Vector的size方法得到有10个元素,在调用get(9)方法获取最后一个元素,而另一个线程调用remove(9)方法正好删除了这个元素,那第一个线程就会抛出越界异常。
总结:
- 在对容器进行组合操作时,vector不适用(需要自己将synchronized将组合操作进行同步);
- 仅在上述第一种场景时,才需要使用Verctor。
public class TestMultiThread {private static Vector vec = new Vector();private static List lst = new ArrayList();public void f() {TestThread testThread1 = new TestThread();TestThread testThread2 = new TestThread();Thread thread1 = new Thread(testThread1);Thread thread2 = new Thread(testThread2);thread1.start();thread2.start();} public static void main(String[] args) {TestMultiThread testMultiThread = new TestMultiThread();testMultiThread.f();try {Thread.sleep(5000);} catch (InterruptedException e) {e.printStackTrace();}System.out.println("vec size is " + vec.size());System.out.println("lst size is " + lst.size());}private class TestThread implements Runnable {@Overridepublic void run() {for (int i = 0; i < 1000; ++i) {vec.add(i);lst.add(i);} } }private static Vector vec = new Vector();private static List lst = new ArrayList();public void f() {TestThread testThread1 = new TestThread();TestThread testThread2 = new TestThread();Thread thread1 = new Thread(testThread1);Thread thread2 = new Thread(testThread2);thread1.start();thread2.start();}
}
执行程序结果为:
vec size is 2000
lst size is 1999
Stack类
Stack类继承自Vector,实现一个后进先出的堆栈,Stack提供5个额外的方法使得Vector得以被当作堆栈使用,基本的push和pop方法,还有peek方法得到栈顶的元素,empty方法测试堆栈是否为空,search方法检测一个元素是在堆栈中存在的位置,Stack刚创建后是空栈。
Set接口
Set是一种不包含重复元素的Collection,即任意的两个元素e1和e2都有e1.equals(e2)=false,Set最多有一个null元素。Set的构造函数有一个约束条件,传入的Collection参数不能包含重复的元素。
Set容器类主要有HashSet和TreeSet等。
HashSet类
Java.util.HashSet类实现了Java.util.Set接口。
- 它不允许出现重复的元素;
- 不保证集合中元素的顺序;
- 允许包含值为null的元素,但是最多只能有一个null元素。
public class TestHashSet
{public static void main(String [] args){HashSet h=new HashSet();h.add("1st");h.add("2nd");h.add(new Integer(3));h.add(new Double(4.0));h.add("2nd"); //重复元素,未被添加h.add(new Integer(3)); //重复元素,未被添加h.add(new Date());System.out.println("开始:size="+h.size());Iterator it=h.iterator();while(it.hasNext()){Object o=it.next();System.out.println(o);}h.remove("2nd");System.out.println("移除元素后:size="+h.size());System.out.println(h);}
}
TreeSet类
TreeSet描述的是Set的一种变体,可以实现排序等功能的集合, 它在讲对象元素添加到集合中时会自动按照某种比较规则将其插入到有序的对象序列中,并保证该集合元素组成的读优先序列时刻按照“升序”排列。
public class TestTreeSet
{public static void main(String [] args){TreeSet ts=new TreeSet();ts.add("orange");ts.add("apple");ts.add("banana");ts.add("grape");Iterator it=ts.iterator();while(it.hasNext()){String fruit=(String)it.next();System.out.println(fruit);}}
}
三、Map集合接口
Map没有继承Collection接口,Map提供key到value的映射,一个Map中不能包含相同的key,每个key只能映射一个value。Map提供了3中集合的视图,Map的内容可以被当作一个中key集合,一组value集合,或者一组key-value的映射。
主要方法:
boolean equals(Object o)比较对象
boolean remove(Object o)删除一个对象
put(Object key,Object value)添加key和value键值对
HashTable类
Hashtable继承Map接口,实现一个key-value映射的哈希表。任何非空(non-null)的对象都可作为key或者value。添加数据使用put(key,value),取出数据使用get(key),这两个基本操作的时间开销为常数。Hashtable通过initialcapacity和load factor两个参数调整性能。通常缺省的load factor0.75较好地实现了时间和空间的均衡。增大loadfactor可以节省空间但相应的查找时间将增大,这会影响像get和put这样的操作。
由于作为key的对象将通过计算其散列函数来确定与之对应的value的位置,因此任何作为key的对象都必须实现hashCode和equals方法。hashCode和equals方法继承自根类Object,如果你用自定义的类当作key的话,要相当小心,按照散列函数的定义,如果两个对象相同,即obj1.equals(obj2)=true,则它们的hashCode必须相同,但如果两个对象不同,则它们的hashCode不一定不同,如果两个不同对象的hashCode相同,这种现象称为冲突,冲突会导致操作哈希表的时间开销增大,所以尽量定义好的hashCode()方法,能加快哈希表的操作。
如果相同的对象有不同的hashCode,对哈希表的操作会出现意想不到的结果(期待的get方法返回null),要避免这种问题,只需要牢记一条:要同时复写equals方法和hashCode方法,而不要只写其中一个。
HashMap类
HashMap和Hashtable类似,不同之处在于HashMap是非同步的,并且允许null,即null value和null key,但是将HashMap视为Collection时(values()方法可返回Collection),其迭代子操作时间开销和HashMap的容量成比例。因此,如果迭代操作的性能相当重要的话,不要将HashMap的初始化容量设得过高,或者loadfactor过低。
JDK1.0引入了第一个关联的集合类HashTable,它是线程安全的。 HashTable的所有方法都是同步的。
JDK2.0引入了HashMap,它提供了一个不同步的基类和一个同步的包装器synchronizedMap。synchronizedMap被称为有条件的线程安全类。
JDK5.0util.concurrent包中引入对Map线程安全的实现ConcurrentHashMap,比起synchronizedMap,它提供了更高的灵活性。同时进行的读和写操作都可以并发地。
HashTable类HashMap类的区别
第一、继承不同。
public class Hashtable extends Dictionary implements Map
public class HashMap extends AbstractMap implements Map
第二、Hashtable 中的方法是同步的,而HashMap中的方法在缺省情况下是非同步的。在多线程并发的环境下,可以直接使用Hashtable,但是要使用HashMap的话就要自己增加同步处理了。
第三、Hashtable中,key和value都不允许出现null值。在HashMap中,null可以作为键,这样的键只有一个;可以有一个或多个键所对应的值为null。当get()方法返回null值时,即可以表示 HashMap中没有该键,也可以表示该键所对应的值为null。因此,在HashMap中不能由get()方法来判断HashMap中是否存在某个键, 而应该用containsKey()方法来判断。
第四、两个遍历方式的内部实现上不同。Hashtable、HashMap都使用了 Iterator。而由于历史原因,Hashtable还使用了Enumeration的方式 。
第五、哈希值的使用不同,HashTable直接使用对象的hashCode。而HashMap重新计算hash值。
第六、Hashtable和HashMap它们两个内部实现方式的数组的初始大小和扩容的方式。HashTable中hash数组默认大小是11,增加的方式是 old*2+1。HashMap中hash数组的默认大小是16,而且一定是2的指数。
WeakHashMap类
WeakHashMap是一种改进的HashMap,它对key实行“弱引用”,如果一个key不再被外部所引用,那么该key可以被GC回收。
2、ArrayList和LinkedList的插入和访问的时间复杂度?
3、java的反射原理,注解原理?
Java注解:
java5.0以后引入了注解的概念,注解只是在源码阶段保留,编译的时候就会忽略,不会影响程序内部的东西,决定运行级别,是一个标识定义一个注解就可以在其他类里边运行。
在学习反射和注解前,首先要练习一个ORM。
练习ORM
- 了解什么是ORM:Object RelationShip Mapping—>对象关系映射。
从上图可知:类和表结构对应;属性和字段对应;对象和记录对应
要求:利用注解和反射完成类和表结构的映射关系。
package test;import java.lang.annotation.Annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.lang.reflect.Field;//通过反射操作注解
public class Test{public static void main(String[] args) {Class c1 = Student.class;//利用反射获取注解Annotation[] annotations = c1.getAnnotations();for(Annotation annotation : annotations) {System.out.println(annotation);}//获取注解的value的值TableHgqin tableHgqin = (TableHgqin) c1.getAnnotation(TableHgqin.class);String value = tableHgqin.value();System.out.println(value);//获得类指定的注解Field[] fields = c1.getDeclaredFields();for(Field field : fields) {System.out.println("#字段为: "+field);annotations = field.getAnnotations();for(Annotation annotation : annotations) {System.out.println(annotation);}//获取注解对应的值FieldHgqin fieldHgqin = (FieldHgqin)field.getAnnotation(FieldHgqin.class);System.out.println(fieldHgqin.columnName());System.out.println(fieldHgqin.type());System.out.println(fieldHgqin.length());}}
}@TableHgqin("db_student")
class Student{@FieldHgqin(columnName="db_id",type="int",length=10)private int id;@FieldHgqin(columnName="db_name",type="int",length=10)private String name;@FieldHgqin(columnName="db_age",type="varchar",length=3)private int age;public Student() {super();// TODO Auto-generated constructor stub}public Student(int id, String name, int age) {super();this.id = id;this.name = name;this.age = age;}public int getId() {return id;}public void setId(int id) {this.id = id;}public String getName() {return name;}public void setName(String name) {this.name = name;}public int getAge() {return age;}public void setAge(int age) {this.age = age;}@Overridepublic String toString() {return "student [id=" + id + ", name=" + name + ", age=" + age + "]";}}//类名的注解
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@interface TableHgqin{String value();
}//属性的注解
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@interface FieldHgqin{String columnName();String type();int length();
}
运行结果为:
java的反射到底有什么用处?怎么用?
java反射意味着在运行状态下,对于任何类,我们都可以知道该类有哪些方法和属性。我们可以调用它的任何属性和方法,我们将此函数定义为动态获取对象信息和调用对象方法的反射机制。
你可以在不使用new关键字的情况下获取对象,并在类对象中使用成员变量、方法、修饰符等。下面是一个获得类的示例:
第一:使用Class.forName类(string classpath)classpath:写需要放映的类名,通常在包含中类名。例如:clazz=Class.forName类(“com.entity.Example示例”)。
第二:直接使用class CLZ=类名。当我们知道有这个类时,通常会用到这种情况。例如:CLZ类=示例.class
CLZ类=对象。GetClass()
如果对象已实例化。例如:
example ex=new example()
class CLZ=例如getClass()
这三种方法各有优点。一般来说,我们多用第一种方法,最好根据自己的实际需要来使用。反射有什么用?有哪些应用场景?
- spring框架的IOC是基于java反射机制的。
- JDBC数据库连接注册驱动程序,访问连接也是基于java的反射。
- 冬眠和越冬已被应用于反射。
java中的注解到底是如何工作的?
什么是注解:
用一个词就可以描述注解,那就是元数据,即一种描述数据的数据。所以,可以说注解就是源代码的元数据。
@Override
public String toString() {return "This is String Representation of current object.";
}
上面的代码中,我重写了toString()方法并使用了@Override注解。但是,即使我不使用@Override注解标记代码,程序也能够正常执行。那么,该注解表示什么?这么写有什么好处吗?事实上,@Override告诉编译器这个方法是一个重写方法(描述方法的元数据),如果父类中不存在该方法,编译器便会报错,提示该方法没有重写父类中的方法。如果我不小心拼写错误,例如将toString()写成了toStrring(){double r},而且我也没有使用@Override注解,那程序依然能编译运行。但运行结果会和我期望的大不相同。现在我们了解了什么是注解,并且使用注解有助于阅读程序。
Annotation是一种应用于类、方法、参数、变量、构造器及包声明中的特殊修饰符。它是一种由JSR-175标准选择用来描述元数据的一种工具。
为什么要引入注解:
使用Annotation之前(甚至在使用之后),XML被广泛的应用于描述元数据。不知何时开始一些应用开发人员和架构师发现XML的维护越来越糟糕了。他们希望使用一些和代码紧耦合的东西,而不是像XML那样和代码是松耦合的(在某些情况下甚至是完全分离的)代码描述。如果你在Google中搜索“XML vs. annotations”,会看到许多关于这个问题的辩论。最有趣的是XML配置其实就是为了分离代码和配置而引入的。上述两种观点可能会让你很疑惑,两者观点似乎构成了一种循环,但各有利弊。下面我们通过一个例子来理解这两者的区别。
假如你想为应用设置很多的常量或参数,这种情况下,XML是一个很好的选择,因为它不会同特定的代码相连。如果你想把某个方法声明为服务,那么使用Annotation会更好一些,因为这种情况下需要注解和方法紧密耦合起来,开发人员也必须认识到这点。
另一个很重要的因素是Annotation定义了一种标准的描述元数据的方式。在这之前,开发人员通常使用他们自己的方式定义元数据。例如,使用标记interfaces,注释,transient关键字等等。每个程序员按照自己的方式定义元数据,而不像Annotation这种标准的方式。
目前,许多框架将XML和Annotation两种方式结合使用,平衡两者之间的利弊。
Annotation是如何工作的?怎么编写自定义的Annotation?:
在讲述这部分之前,建议你首先下载Annotation的示例代码AnnotationsSample.zip 。下载之后放在你习惯使用的IDE中,这些代码会帮助你更好的理解Annotation机制。
编写Annotation非常简单,可以将Annotation的定义同接口的定义进行比较。我们来看两个例子:一个是标准的注解@Override,另一个是用户自定义注解@Todo。
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.SOURCE)
public @interface Override {
}
4、说说一致性Hash原理
5、新生代分为几个区?使用什么算法进行垃圾回收?为什么使用这个算法?
6、HashMap在什么情况下会扩容,或者有哪些操作会导致扩容?
7、HashMap push方法的执行过程?
8、HashMap检测到hash冲突后,将元素插入在链表的末尾还是开头?
9、1.8采用了红黑树,讲讲红黑树的特性,为什么人家一定要用红黑树而不是AVL、B树之类的?
10、https和http区别,有没有用过其他安全传输手段?
11、线程池的工作原理,几个重要参数,然后给了具体几个参数分析线程池会怎么做,最后问阻塞队列的作用是什么?
12、linux怎么查看系统负载情况?
13、请详细描述springmvc处理请求全过程?
14、spring一个bean装配的过程?
15、项目用spring比较多,有没有了解spirng的原理?AOP和IOC的原理?
- 首先为什么要使用spring?
spring能够很好的和各大框架整合,spring通过IOC容器管理了对象的创建和销毁,工厂模式,在使用hibernate和mybatis的时候,不用每次都编写提交事务,可以使用Spring的AOP来进行管理事务。 - AOP
AOP(面向切面)是一种编程范式,提供从另一个角度来考虑程序结构以完善面向对象编程(OOP)。
AOP为开发者提供了一种描述横切关注点的机制,并能够自动横切关注点织入到面向对象的软件系统中,从而实现了横切关注点的模块化。
AOP能够将那些与业务无关,却为业务模块所共同调用的逻辑或责任,例如事务处理、日志管理、权限控制等,封装起来,便于减少系统的重复代码,降低模块间的耦合度,并有利于未来的可操作性和可维护性。
使用AOP的好处
1>降低模块的耦合度
2>使系统容易扩展
3>提高代码复用性
AOP的基本概念
1> 连接点(JoinPoint):需要在程序中插入横切关注点的点,连接点可能是在类初始化、方法调用、字段调用或处理异常等等。Spring中只支持方法执行连接点。
2>切入点(Pointcut):一组相关连接点的集合。
3>通知(Advice):在连接点上执行的行为,增强提供了在AOP中需要在切入点所选择的连接点处进行扩展现有行为的手段。包括前置增强(before advice)、后置增强 (after advice)、环绕增强 (around advice)。
4>切面(Aspect):通知和切入点的结合。
5>织入(Weaving):织入是一个过程,是将切面应用到目标对象从而创建出AOP代理对象的过程。
6>代理(Proxy):通过代理方式来对目标对象应用切面。AOP代理可以用JDK动态代理或CGLIB代理实现。
7>目标对象(Target):需要被织入关注点的对象。即被代理的对象。
实现AOP的主要设计模式就是动态代理。
Spring的动态代理有两种:一是JDK的动态代理;另一个是cglib动态代理。
JDK动态代理模拟
JDK动态代理的两个核心接口(类)分别是InvocationHandler和Proxy。注意:只能代理接口。
public class TimeHandler implements InvocationHandler { // 目标对象 private Object targetObject; public TimeHandler(Object targetObject){this.targetObject = targetObject;}@Override //关联的这个实现类的方法被调用时将被执行 /*InvocationHandler接口的方法,proxy表示代理,method表示原对象被调用的方法, args表示方法的参数*/ public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { Object ret=null; try{ System.out.println("方法之前:"+System.currentTimeMillis()); //调用目标方法 ret=method.invoke(targetObject, args); System.out.println("方法之后:"+System.currentTimeMillis()); }catch(Exception e){ e.printStackTrace(); System.out.println("error"); throw e; } return ret; } }
TimeHandler 类实现了InvocationHandler接口。实现核心方法invoke,共有3个参数。第一个参数 生成的代理类实例,第二个参数 目标对象的方法,第三个参数 方法的参数值数组。
public class ProxyUtil {@SuppressWarnings("unchecked")public static <T> T proxyOne(ClassLoader loader,Class<?>[] clz,InvocationHandler handler){return (T)Proxy.newProxyInstance(loader, clz, handler);}
}
ProxyUtil 类简单封装了一下Proxy.newProxyInstance()方法。该方法也有3个参数。第一个参数产生代理对象的类加载器,第二个参数目标对象的接口数组,第三个参数就是实现InvocationHandler接口的类实例。
public interface UserManager {public void addUser(String userId, String userName);
}
public class UserManagerImpl implements UserManager {@Overridepublic void addUser(String userId, String userName) {System.out.println("addUser(id:"+userId+",name:"+userName+")");}}public static void main(String[] args) {UserManager um=new UserManagerImpl(); LogHandler log =new LogHandler(um); um=ProxyUtil.proxyOne(um.getClass().getClassLoader(), um.getClass().getInterfaces(), log);TimeHandler time = new TimeHandler(um);um=ProxyUtil.proxyOne(um.getClass().getClassLoader(), um.getClass().getInterfaces(), time);um.addUser("1111", "张三");
}
为了演示需要,这边又增加了一个LogHandler,跟TimeHandler代码一样。
CGLIB动态代理模拟
CGLIB动态代理的两个核心接口(类)分别是MethodInterceptor和Enhancer。是不是跟JDK动态代理很相似,用法也差不多。但CGLIB可以代理类和接口。注意:不能代理final类。
public class TimeInterceptor implements MethodInterceptor {private Object target; public TimeInterceptor(Object target) {this.target = target;}@Overridepublic Object intercept(Object proxy, Method method, Object[] args, MethodProxy invocation) throws Throwable {System.out.println("方法之前:"+System.currentTimeMillis());Object ret = invocation.invoke(target, args); System.out.println("方法之后:"+System.currentTimeMillis());return ret;}
}
intercept方法4个参数。1.生成的代理类实例。2.被代理对象的方法引用。3.方法参数值数组。4.代理类对方法的代理引用。
public class ProxyUtil {@SuppressWarnings("unchecked")public static <T> T proxyOne(Class<?> clz,MethodInterceptor interceptor){return (T)Enhancer.create(clz, interceptor);}}
Enhancer类是CGLib中的字节码增强器。
public class UserManage {public void addUser(String userId, String userName) {System.out.println("addUser(id:"+userId+",name:"+userName+")");}
}public static void main(String[] args) {UserManage um = new UserManage();TimeInterceptor time = new TimeInterceptor(um);um = ProxyUtil.proxyOne(um.getClass(), time);um.addUser("111", "老王");
}
- IOC
IOC(控制反转)就是依赖倒置原则的一种代码设计思路。就是把原先在代码里面需要实现的对象创建、对象之间的依赖,反转给容器来帮忙实现。
Spring IOC容器通过xml,注解等其它方式配置类及类之间的依赖关系,完成了对象的创建和依赖的管理注入。实现IOC的主要设计模式是工厂模式。
使用IOC的好处
1>集中管理,实现类的可配置和易管理。
2>降低了类与类之间的耦合度。
简单模拟IOC
public interface BeanFactory {Object getBean(String id);
}public class ClassPathXmlApplicationContext implements BeanFactory {//容器,用来存放注入的Bean private Map<String, Object> container = new HashMap<String, Object>(); //解析xml文件,通过反射将配置的bean放到container中 public ClassPathXmlApplicationContext(String fileName) throws Exception{ SAXBuilder sb = new SAXBuilder(); Document doc =sb.build(ClassPathXmlApplicationContext.class.getResource("/"+fileName));Element root = doc.getRootElement(); List<Element> list = XPath.selectNodes(root, "/beans/bean"); for (int i = 0; i < list.size(); i++) { Element bean = list.get(i); String id = bean.getAttributeValue("id"); String clazz = bean.getAttributeValue("class"); Object o = Class.forName(clazz).newInstance(); container.put(id, o); } }@Override public Object getBean(String id) { return container.get(id); } }
需要导入 jdom.jar包。
<?xml version="1.0" encoding="UTF-8"?>
<beans> <bean id="people" class="com.ioc.People" /> <bean id="chicken" class="com.ioc.Chicken" /> <bean id="dog" class="com.ioc.Dog" />
</beans>
public interface Animal {void say();
}public class Dog implements Animal {@Overridepublic void say() {System.out.println("汪汪"); }
}
public class Chicken implements Animal {@Overridepublic void say() {System.out.println("鸡你很美"); }
}
public class People {public void info(){System.out.println("小明-23岁");}
}
public static void main(String[] args) throws Exception { //加载配置文件 BeanFactory f = new ClassPathXmlApplicationContext("applicationContext.xml"); Object os = f.getBean("dog"); Animal dog = (Animal)os; dog.say(); Object op = f.getBean("chicken"); Animal chicken = (Animal)op; chicken.say(); Object p = f.getBean("people"); People people= (Animal)p; people.info(); }
更多推荐
某大厂第一技术面
发布评论