多态
方法继承
利用extends关键字一个方法继承另一个方法,而且只能直接继承一个类。
当Sub类和Base类在同一个包时Sub类继承Base类中的public/protected/默认级别的变量个方法,在不同包时继承public/protected级别的变量和方法。
构造函数:用父类的构造函数。如果存在无参构造函数(或者不存在任何构造函数),子类中不显式调用父类的构造函数,则默认调用父类的无参数构造函数(使用super()修改调用父类
当子类继承一个父类时,构造子类时需要调用父类的构造函数,在父类不存在无参构造函数的情况下,子类的构造函数中必须显式调的构造函数)。
方法重载
如果有两个方法的方法名相同,但参数不一致,哪么可以说一个方法是另一个方法的重载。 @Overload
1) 方法名相同
2) 方法的参数类型,个数顺序至少有一项不同
3) 方法的返回类型可以不相同
4) 方法的修饰符可以不相同
5) main方法也可以被重载
方法重写
如果在子类中定义一个方法,其名称、返回类型及参数签名正好与父类中某个方法的名称、返回类型及参数签名相匹配,那么可以说,子类的方法覆盖了父类的方法。
1) 子类的方法名称返回类型及参数签名 必须与父类的一致
2) 子类方法不能缩小父类方法的访问权限,只能把方法的作用域放大(不能把public修改为private)。但是父类的私有方法不能被子类覆盖
3) 子类方法不能抛出比父类方法更多的异常
4) 方法覆盖只存在于子类和父类之间,同一个类中只能重载
5) 父类的静态方法不能被子类覆盖为非静态方法,父类的非静态方法不能被子类覆盖为静态方法
6) 子类可以定义与父类的静态方法同名的静态方法,以便在子类中隐藏父类的静态方法(满足覆盖约束),而且Java虚拟机把静态方法和所属的类绑定,而把实例方法和所属的实例绑定。
7) 父类的抽象方法可以被子类通过两种途径覆盖(即实现和覆盖),父类的非抽象方法可以被覆盖为抽象方法 @Override
成员覆盖
当子类覆盖父类的成员变量时,父类方法使用的是父类的成员变量,子类方法使用的是子类的成员变量
Super关键字:
super和this关键字都可以用来覆盖Java语言的默认作用域,使被屏蔽的方法或变量变为可见。
1) 父类的成员变量和方法为private使用super访问编译出错
2) 在子类的构造方法种,通过super语句调用这个类的父类的构造方法
3) 在子类种访问父类被屏蔽的方法和属性
4) 只能在构造方法或实例方法内使用super关键字,而在静态方法和静态代码块内不能使用super
继承的利弊和使用原则:
优点:
(1)定义了下层子类都用有的相同属性和方法,尽可能默认实现,从而提高重用性
(2)代表系统的接口,描述系统所能提供的服务
缺点:
继承关系最大的弱点:打破封装(封装 (encapsulation)
1.事物的内部实现细节隐藏起来
2.对外提供一致的公共的接口――间接访问隐藏数据
3.可维护性
被继承类的使用原则:
(1)对这些类必须提供良好的文档说明
(2)尽可能的封装父类的实现细节,把代表时间细节的属性和方法定义为private
(3)如果某些实现细节必须被子类访问,定义为protected类型
(4)把不允许子类覆盖的方法定义为final类型
(5)父类的构造方法不允许调用可被子类覆盖的方法
(6)如果某些类不是专门为了继承而设计,那么随意继承它是不安全的
请看列子:综合评价:★★★
class A { public String show(D obj){ return ("A and D"); } public String show(A obj){ return ("A and A"); } } class B extends A{ public String show(B obj){ return ("B and B"); } public String show(A obj){ return ("B and A"); } } class C extends B{ public String show(B obj){ return ("C and B"); } public String show(A obj){ return ("C and A"); } } class D extends B{ public String show(B obj){ return ("D and B"); } public String show(A obj){ return ("D and A"); } }
public class Test { public static void main(String[] args) { //test polymorphic A a1 = new A(); A a2 = new B(); B b = new B(); C c = new C(); D d = new D();
System.out.println(a1.show(b)); System.out.println(a1.show(c)); System.out.println(a1.show(d));
//********************'' System.out.println(a2.show(b)); System.out.println(a2.show(c)); System.out.println(a2.show(d)); System.out.println(b.show(b)); System.out.println(b.show(c)); System.out.println(b.show(d)); // test extends /* SubClass sc1 = new SubClass(); SubClass sc2 = new SubClass(400);*/ } }
|
问题:写出9个输出语句的结果,并且解释原因。
答案: 1)A and A\ 2) A and A\ 3) A and D\ 4)B and A\ 5)B and A\ 6) A and D\
7)B and B\ 8)B and B\ 9)A and D
分析: 运行时多态性是面向对象程序设计代码重用的一个最强大机制,动态性的概念也可以被说成“一个接口,多个方法”。Java实现运行时多态性的基础是动态方法调度,它是一种在运行时而不是在编译期调用重载方法的机制。
实际上这里涉及方法调用的优先问题 ,优先级由高到低依次为:this.show(O)、super.show(O)、this.show((super)O)、super.show((super)O)。其中super查找会首先查找直接父级,如果直接父级存在则调用父级对象,否则再进行父父级查找。直到找到顶级父级对象。
比如④,a2.show(b),a2是一个引用变量,类型为A,则this为a2,b是B的一个实例,于是它到类A里面找show(Bobj)方法,没有找到,于是到A的super(超类)找,而A没有超类,因此转到第三优先级this.show((super)O),this仍然是a2,这里O为B,(super)O即(super)B即A,因此它到类A里面找show(Aobj)的方法,类A有这个方法,但是由于a2引用的是类B的一个对象,B覆盖了A的show(Aobj)方法,因此最终锁定到类B的show(A obj),输出为"B and A”。
再比如⑧,b.show(c),b是一个引用变量,类型为B,则this为b,c是C的一个实例,于是它到类B找show(C obj)方法,没有找到,转而到B的超类A里面找,A里面也没有,因此也转到第三优先级this.show((super)O),this为b,O为C,(super)O即(super)C即B,因此它到B里面找show(B obj)方法,找到了,由于b引用的是类B的一个对象,因此直接锁定到类B的show(B obj),输出为"B and B”。
Java集合
综合评价:★★★
Java容器类类库的用途是“保存对象”,并将其划分为两个不同的概念:
1)Collection 一组对立的元素,通常这些元素都服从某种规则。如,List必须保持元素特定的顺序,而Set 不能有重复元素。
2)Map 一组 成对的“键值对”对象。
Collection和Map的区别在于容器中每个位置保存的元素个数。Collection 每个位置只能保存一个元素(对象)
1) Collection –对象之间没有指定的顺序,允许重复元素。
2) Set – 对象之间没有指定的顺序,不允许重复元素
3) List– 对象之间有指定的顺序,允许重复元素,并引入位置下标。
4) Map – 接口用于保存关键字(Key)和数值(Value)的集合,集合中的每个对象加入时都提供数值和关键字。Map 接口既不继承 Set 也不继承 Collection。
5) List、Set、Map共同的实现基础是Object数组
集合框架分解图:
Collection
Collection是最基本的集合接口,一个Collection代表一组Object,即Collection的元素。(Elements)。所有实现Collection接口的类都必须提供两个标准的构造函数:无参数的构造函数用于创建一个空的Collection,有一个Collection参数的构造函数用于创建一个新的Collection,这个新的Collection与传入的Collection有相同的元素。后 一个构造函数允许用户复制一个Collection。注意:集合必须只有对象,集合中的元素不能是基本数据类型。
Collection的实现基础是数组,所以有转换为Object数组的方法:
Object[] toArray() ; Object[]toArray(Object[] a)
迭代器
Collection不提供get()方法。如果要遍历Collectin中的元素,就必须用Iterator。迭代器(Iterator)本身就是一个对象,它的工作就是遍历并选择集合序列中的对象,而客户端的程序员不必知道或关心该序列底层的结构。Iterator选取容器中的元素,它将容器转换成一个序列。Collection 接口的 iterator() 方法返回一个 Iterator对象。
Collection的Iterator 用法:
1)使用方法 iterator() 要求容器返回一个Iterator.第一次调用Iterator 的next() 方法时,它返回集合序列的第一个元素。
2)使用next() 获得集合序列的中的下一个元素。
3)使用hasNext()检查序列中是否元素。
4)使用remove()将迭代器新返回的元素删除。
需要注意的是:方法删除由next方法返回的最后一个元素,在每次调用next时,remove方法只能被调用一次。
List
List的最大的特点就是能够自动的根据插入的数据量来动态改变容器的大小。集合框架”中有两种常规的 List 实现:ArrayList 和 LinkedList。与 Set 不同的是 List 允许重复。
| 简述 | 实现 | 操作特性 | 成员要求 |
List | 提供基于索引的对成员的随机访问 | ArrayList | 1、提供快速的基于索引的成员访问,对尾部成员的增加和删除支持较好 2、允许null元素。 | 成员可为任意Object子类的对象 |
LinkedList | 1、对列表中任何位置的成员的增加和删除支持较好,但对基于索引的成员访问支持性能较差 。 2、允许null元素。 3、可被用作堆栈(stack),队列(queue)或双向队列(deque)。
| 成员可为任意Object子类的对象 |
除了具有Collection接口必备的iterator()方法外,List还提供一个listIterator()方法,返回一个 ListIterator接口,和标准的Iterator接口相比,ListIterator多了一些add()之类的方法,允许添加,删除,设定元素,还能向前或向后遍历。
List 接口不但以位置友好的方式遍历整个列表,还能处理集合的子集:
1) ListIterator listIterator() :返回一个ListIterator 迭代器,默认开始位置为0
2) ListIterator listIterator(intstartIndex) :返回一个ListIterator 迭代器,开始位置为startIndex
3) List subList(int fromIndex, inttoIndex) :返回一个子列表List ,元素存放为从fromIndex 到toIndex之前的一个元素。
ListIterator接口
ListIterator 接口继承 Iterator 接口以支持添加或更改底层集合中的元素,还支持双向访问,逆向访问注意ListIterator
最初位于列表尾之后(list.size()
)。
Vector类
Vector非常类似ArrayList,但是Vector是同步的。由Vector创建的Iterator,虽然和 ArrayList创建的Iterator是同一接口,但是,因为Vector是同步的,当一个Iterator被创建而且正在被使用,另一个线程改变了 Vector的状态(例如,添加或删除了一些元素),这时调用Iterator的方法时将抛出ConcurrentModificationException,因此必须捕获该异常。
Stack类
Stack继承自Vector,实现一个后进先出的堆栈。Stack提供5个额外的方法使得Vector得以被当作堆栈使用。基本的push和pop 方法,还有peek方法得到栈顶的元素,empty方法测试堆栈是否为空,search方法检测一个元素在堆栈中的位置。Stack刚创建后是空栈。
Vector和ArrayList区别
1) vector是线程同步的,也是线程安全的,而arraylist是线程异步的,是不安全的。
2) 如果集合中的元素的数目大于目前集合数组的长度时,vector增长率为目前数组长度的100%,而arraylist增长率为目前数组长度的50%.如过在集合中使用数据量比较大的数据,用vector有一定的优势。
3) 如果查找一个指定位置的数据,vector和arraylist使用的时间是相同的,都是0(1),这个时候使用vector和arraylist都可以。如果移动一个指定位置的数据可以考虑使用LinkedList
arraylist和linkedlist的区别
1) ArrayList是实现了基于动态数组的数据结构,LinkedList基于链表的数据结构。
2) 对于随机访问get和set,ArrayList觉得优于LinkedList,因为LinkedList要移动指针。
3) 对于新增和删除操作add和remove,LinedList比较占优势,因为ArrayList要移动数据。
Set
Set 接口继承 Collection 接口,而且它不允许集合中存在重复项。Set的常用实现类的描述:
| 简述 | 实现 | 操作特性 | 成员要求 |
Set | 成员不能重复 | HashSet | 外部无序地遍历成员。 | 成员可为任意Object子类的对象,但如果覆盖了equals方法,同时注意修改hashCode方法。 |
TreeSet | 外部有序地遍历成员; 附加实现了SortedSet, 支持子集等要求顺序的操作 | 成员要求实现Comparable接口,或者使用Comparator构造TreeSet。成员一般为同一类型。 | ||
LinkedHashSet | 外部按成员的插入顺序遍历成员 | 成员与HashSet成员类似 |
Set的实现原理
HashSet的实现,建立一个“键值对”,“键”就是我们要存入的对象,“值”则是一个常量。
用“集合框架”设计软件时,记住该框架四个基本接口的下列层次结构关系会有用处:
1) Collection 接口是一组允许重复的对象。
2) Set 接口继承 Collection,但不允许重复。
3) List 接口继承 Collection,允许重复,并引入位置下标。
4) Map 接口既不继承 Set 也不继承 Collection,存取的是键值对
集合的实现类之间的区别:
接口 | 成员重复性 | 元素存放顺序(Ordered/Sorted) | 元素中被调用的方法 | 基于那中数据结构来实现的 | |
Set | Unique elements | No order | equals() hashCode() | Hash 表 | |
LinkedHashSet | Set | Unique elements | Insertion order | equals() hashCode() | Hash 表和双向链表 |
SortedSet | Unique elements | Sorted | equals() compareTo() | 平衡树(Balanced tree) | |
ArrayList | List | Allowed | Insertion order | equals() | 数组 |
LinkedList | List | Allowed | Insertion order | equals() | 链表 |
Vector | List | Allowed | Insertion order | equals() | 数组 |
HashMap | Map | Unique keys | No order | equals() hashCode() | Hash 表 |
LinkedHashMap | Map | Unique keys | Key insertion order/Access order of entries | equals() hashCode() | Hash 表和双向链表 |
Hashtable | Map | Unique keys | No order | equals() hashCode() | Hash 表 |
TreeMap | SortedMap | Unique keys | Sorted in key order | equals() compareTo() | 平衡树(Balanced tree) |
Map
Map Map没有继承Collection接口,Map提供key到value的映射。一个Map中不能包含相同的key,每个key只能映射一个 value。Map接口提供3种集合的视图,Map的内容可以被当作一组key集合,一组value集合,或者一组key-value映射。
public Set keySet() :返回所有的键(key),并使用Set容器存放
1) public Collection values() :返回所有的值(Value),并使用Collection存放
2) public Set entrySet() :返回一个实现Map.Entry 接口的元素 Set
Map的常用实现类的比较:
| 简述 | 实现 | 操作特性 | 成员要求 |
Map | 保存键值对成员,基于键找值操作,使用compareTo或compare方法对键进行排序 | HashMap | 能满足用户对Map的通用需求 | 键成员可为任意Object子类的对象,但如果覆盖了equals方法,同时注意修改hashCode方法。 |
TreeMap | 支持对键有序地遍历,使用时建议先用HashMap增加和删除成员,最后从HashMap生成TreeMap; 附加实现了SortedMap接口,支持子Map等要求顺序的操作 | 键成员要求实现Comparable接口,或者使用Comparator构造TreeMap键成员一般为同一类型。 | ||
LinkedHashMap | 保留键的插入顺序,用equals 方法检查键和值的相等性 | 成员可为任意Object子类的对象,但如果覆盖了equals方法,同时注意修改hashCode方法。 |
Map中的entrySet()方法,返回一个实现 Map.Entry 接口的对象集合。集合中每个对象都是底层 Map 中一个特定的键-值对。Map.Entry 接口是Map 接口中的一个内部接口,该内部接口的实现类存放的是键值对。
HashMap的存入顺序和输出顺序无关。而LinkedHashMap 则保留了键值对的存入顺序。TreeMap则是对Map中的元素进行排序。实际使用中:使用HashMap或者LinkedHashMap 来存放元素,如果需要排序的Map就再使用TreeMap来重构原来的Map对象,提高存储效率。注意:TreeMap中是根据键(Key)进行排序的,Key 中存放的对象必须实现Comparable 接口。
Comparable接口,Comparable 接口适用于一个类有自然顺序的时候。在1.2 中有十四个类实现 Comparable 接口,如:
类 | 排序 |
BigDecimal, BigInteger, Byte, Double, Float, Integer, Long, Short | 按数字大小排序 |
Character | 按 Unicode 值的数字大小排序 |
CollationKey | 按语言环境敏感的字符串排序 |
Date | 按年代排序 |
File | 按系统特定的路径名的全限定字符的 Unicode 值排序 |
ObjectStreamField | 按名字中字符的 Unicode 值排序 |
String | 按字符串中字符 Unicode 值排序 |
Map的实现原理
HashMap使用的Hash算法。Hash翻译“散列、哈希” 把任意长度的输入,通过散列算法,变换成固定长度的输出。HashCode最重要因素是,无论何时,对同一个对象调用hashCode()都应该生成同样的值。Object的hashCode()方法,返回的是当前对象的内存地址。
Hashtable类
Hashtable继承Map接口,实现一个key-value映射的哈希表。任何非空(non-null)的对象都可作为key或者value。并且通过initial capacity和load factor两个参数调整性能。
由于作为key的对象将通过计算其散列函数来确定与之对应的value的位置,因此任何作为key的对象都必须实现hashCode和equals方法。hashCode和equals方法继承自根类Object,如果两个对象相同,即obj1.equals(obj2)=true,则它们的hashCode必须相同,一定要同时复写equals方法和hashCode方法。Hashtable是同步的。
HashMap类
HashMap和Hashtable类似,不同之处在于HashMap是非同步的,并且允许null,即null value和null key。
WeakHashMap类
WeakHashMap是一种改进的HashMap,它对key实行“弱引用”,如果一个key不再被外部所引用,那么该key可以被GC回收。
HashMap与TreeMap区别
1、HashMap通过hashcode对其内容进行快速查找,而TreeMap中所有的元素都保持着某种固定的顺序,如果你需要得到一个有序的结果你就应该使用TreeMap(HashMap中元素的排列顺序是不固定的)。集合框架”提供两种常规的Map实现:HashMap和TreeMap (TreeMap实现SortedMap接口)。
2、在Map 中插入、删除和定位元素,HashMap 是最好的选择。但如果您要按自然顺序或自定义顺序遍历键,那么TreeMap会更好。使用HashMap要求添加的键类明确定义了hashCode()和 equals()的实现。
hashtable与hashmap区别
1) 历史原因:Hashtable是基于陈旧的Dictionary类的,HashMap是Java 1.2引进的Map接口的一个实现
2) 同步性:Hashtable是线程安全的,也就是说是同步的,而HashMap是线程序不安全的,不是同步的
3) 值:只有HashMap可以让你将空值作为一个表的条目的key或value
Java 枚举
综合评价:★★
普通类实现枚举,定义一个Weekday的类来模拟枚举功能。
1) 私有的构造方法,避免被实例化
2) 每个元素分别用一个公有的静态成员变量表示
3) 可以有若干公有方法或抽象方法。采用抽象方法定义nextDay就将大量的if.else语句转移成了一个个独立的类。(充分利用匿名内部类)
public abstract class WeekDay1 { private WeekDay1(){} public final static WeekDay1 SUN = new WeekDay1(){ @Override public WeekDay1 nextDay() { return MON; } }; public final static WeekDay1 MON = new WeekDay1(){ @Override public WeekDay1 nextDay() { return SUN; } }; public abstract WeekDay1 nextDay();
/* public WeekDay nextDay(){ if(this == SUN){ return MON; }else{ return SUN; } } */ public String toString(){ return this==SUN?"SUN":"MON"; }} |
枚举特点
1. 枚举就相当于一个类,其中也可以定义构造方法、成员变量、普通方法和抽象方法。
2. 枚举元素必须位于枚举体中的最开始部分,枚举元素列表的后要有分号与其他成员分隔。
public enum WeekDay{ SUN(1),MON(),TUE,WED,THI,FRI,SAT; private WeekDay(){System.out.println("first");} private WeekDay(int day){System.out.println("second");} } public enum TrafficLamp{ RED(30){ public TrafficLamp nextLamp(){ return GREEN; } }, GREEN(45){ public TrafficLamp nextLamp(){ return YELLOW; } }, YELLOW(5){ public TrafficLamp nextLamp(){ returnRED; } }; public abstract TrafficLamp nextLamp(); private int time; private TrafficLamp(int time){this.time = time;} } |
枚举只有一个成员时,就可以作为一种单例的实现方式。
Java反射
一个类被类加载器加载到内存中,占用一片存储空间,这个空间里面的内容就是类的字节码,不同的类的字节码是不同的,所以它们在内存中的内容是不同的,这一个个的空间可分别用一个个的对象来表示。反射就是把Java类中的各种成分映射成相应的Java类。
1、通过以下方式得到各个字节码对应的实例对象( Class类型)
1) 类名.class,例如,System.class
2) 对象.getClass(),例如,newDate().getClass()
3) Class.forName("类名"),例如,Class.forName("java.util.Date");
2、 九个预定义Class实例对象:
a) 参看Class.isPrimitive方法的帮助
b) Int.class == Integer.TYPE
c) 数组类型的Class实例对象,Class.isArray()
注意:
1) 字节码,一个类在虚拟机中通常只有一份字节码
2) 静态代码块不是在类加载时被调用的,而是第一个实例对象被创建时才执行的。
3) 一个类中的每个成员都可以用相应的反射API类的一个实例对象来表示,通过调用Class类的方法可以得到这些实例对象
Constructor类
1. Constructor类代表某个类中的一个构造方法
2. 得到某个类所有的构造方法:
Constructor [] constructors=Class.forName("java.lang.String").getConstructors();
得到某一个构造方法://获得方法时要用到类型
Constructor constructor =Class.forName(“java.lang.String”).getConstructor(StringBuffer.class);
3. 实例对象:
通常方式:String str = new String(new StringBuffer("abc"));
反射方式:String str = (String)constructor.newInstance(newStringBuffer("abc"));
//调用获得的方法时要用到上面相同类型的实例对象
4. Class.newInstance()方法:
例子:String obj = (String)Class.forName("java.lang.String").newInstance();
该方法内部先得到默认的构造方法,然后用该构造方法创建实例对象。
field类
- Field类代表某个类中的一个成员变量
- 字段fieldX 代表的是x的定义,而不是具体的x变量。
- 示例代码:
ReflectPointpoint = new ReflectPoint(1,7);
Fieldy =Class.forName("cn.John.corejava.ReflectPoint").getField("y");
System.out.println(y.get(point));
//Fieldx =Class.forName("cn.John.corejava.ReflectPoint").getField("x");
Fieldx = Class.forName("cn.John.corejava.ReflectPoint").getDeclaredField("x");
x.setAccessible(true);
System.out.println(x.get(point));
中断
method类
Method类代表某个类中的一个成员方法
得到类中的某一个方法:
例子:Method charAt =Class.forName("java.lang.String").getMethod("charAt",int.class);
调用方法:
通常方式:System.out.println(str.charAt(1));
反射方式: System.out.println(charAt.invoke(str, 1));
如果传递给Method对象的invoke()方法的第一个参数为null,这有着什么样的意义呢?说明该Method对象对应的是一个静态方法!
jdk1.4和jdk1.5的invoke方法的区别:
Jdk1.5:public Object invoke(Object obj,Object... args)
Jdk1.4:public Object invoke(Object obj,Object[] args),即按jdk1.4的语法,需要将一个数组作为参数传递给invoke方法时,数组中的每个元素分别对应被调用方法中的一个参数,所以,调用charAt方法的代码也可以用Jdk1.4改写为 charAt.invoke(“str”, new Object[]{1})形式。
问题:
启动Java程序的main方法的参数是一个字符串数组,即public static void main(String[] args),通过反射方式来调用这个main方法时,如何为invoke方法传递参数呢?按jdk1.5的语法,整个数组是一个参数,而按jdk1.4的语法,数组中的每个元素对应一个参数,当把一个字符串数组作为参数传递给invoke方法时,Javac会到底按照哪种语法进行处理呢?jdk1.5肯定要兼容jdk1.4的语法,会按jdk1.4的语法进行处理,即把数组打散成为若干个单独的参数。所以,在给main方法传递参数时,不能使用代码mainMethod.invoke(null,newString[]{“xxx”}),Javac只把它当作jdk1.4的语法进行理解,而不把它当作jdk1.5的语法解释,因此会出现参数类型不对的问题。
解决办法:
mainMethod.invoke(null,new Object[]{newString[]{"xxx"}});
mainMethod.invoke(null,(Object)newString[]{"xxx"}); ,编译器会作特殊处理,编译时不把参数当作数组看待,也就不会数组打散成若干个参数了
数组反射
具有相同维数和元素类型的数组属于同一个类型,即具有相同的Class实例对象。
代表数组的Class实例对象的getSuperClass()方法返回的父类为Object类对应的Class。
基本类型的一维数组可以被当作Object类型使用,不能当作Object[]类型使用;非基本类型的一维数组,既可以当做Object类型使用,又可以当做Object[]类型使用。
Arrays.asList()方法处理int[]和String[]时的差异。String[] 通过Arrays.asList()方法可以打印数组元素,int[]则不行。
Array工具类用于完成对数组的反射操作。
Object[] 与String[]没有父子关系,Object与String有父子关系,所以new Object[]{“aaa”,”bb”}不能强制转换成new String[]{“aaa”,”bb”};,Object x = “abc”能强制转换成String x = “abc”。
main.invoke(null, (Object)(new Object[]{“aaa”,“xxx”}));不能调用public static void main(String [] args)
properties 在hashmap上扩展的功能,能够将内存中的文件写入文件。也能读取文件中的信息,放入键值对。
使用反射调用配置文件中的信息,并且实例化对象
//InputStream ips = newFileInputStream("config.properties");
//InputStream ips = ReflectTest2.class.getClassLoader().getResourceAsStream("cn/John/day1/config.properties");
//InputStream ips =ReflectTest2.class.getResourceAsStream("resources/config.properties");
InputStream ips = ReflectTest2.class.getResourceAsStream("/cn/John/day1/resources/config.properties");
Properties props = new Properties();
props.load(ips);
ips.close();
String className =props.getProperty("className");
Collection collections =(Collection)Class.forName(className).newInstance();。
//开发环境使用绝对路径,getRealPath();//金山词霸/内部
一定要记住用完整的路径,但完整的路径不是硬编码,而是运算出来的
配置文件的加载,通常使用类加载器。
HashCode作用
为了方便在一个集合中大量元素查找需要元素时,有人发明了一种哈希算法来提高从集合中查找元素的效率,这种方式将集合分成若干个存储区域,每个对象可以计算出一个哈希码,可以将哈希码分组,每组分别对应某个存储区域,根据一个对象的就可以确定该对象应该存储在哪个区域。
HashSet就是采用哈希算法存取对象的集合,他内部采用对某个数字n进行取余的方式对哈希码进行分组和划分对象的存储区域。Object类中定义了一个HashCode()方法来返回每个Java对象的哈希码,当从HashSet集合中查找某个对象时,Java系统首先调用对象的hashCode()方法获得该对象的哈希码,然后根据哈希码找到相应的存储区域,最后去除该存储区域内的每个元素与该对象进行equals方法比较,这样不用便利集合中国的所有元素就可以得到结论。HashSet集合具有很多好的对象检索性能,但是HashSet集合存储对象的效率相对要低些,因为向HashSet集合中添加一个对象时,要先计算出对象的哈希码和根据这个哈希码确定对象在集合中存放的位置。
为了保证一类的实例对象能在HashSet正常存储,要求这个类的两个实例对象用equals()方法比较的结果相等时,他们的哈希码也必须相等。也就是说,如果obj1.equals(obj2)的结果为true,那么一下表达式的结果也要为true:
Obj1.hashCode()=obj2.hashCode()
只有类的实例对象要被采用哈希算法进行存储和检索时,这个类才需要按要求覆盖hashCode方法。即使程序可能暂时不会用到当前类的hashCode方法大,但是为他提供一个hashCode方法也不会有什么不好,没准以后什么时候有用到这个方法了,所以,通常要求hashCode方法和equals方法一并被覆盖。
提示:
· 通常来说,一个类的两个实例对象用equal()方法比较的结果相等时,他们的哈希码也必须相等,但反之则不成立,即equals方法比较结果不相等的对象可以有相同的哈希码,或者说哈希码相同的对象的equals方法比较的结果可以不等,例如,字符串”BB”和字符串”Aa”的equals方法比较结果肯定不相等,但他们的hashCode方法返回值却相等。
· 当一个对象被存储进HashSet集合中以后,就不能修改这个对象中的那些参与计算哈希值的字段了,否则,对象修改后的哈市细致与最初村粗进HashSet集合中时的哈希值就不同了,在这种情况下,继续在contains方法是用该对象的当前作为参数去HashSet集合中检索对象,也将返回找不到对象的结婚过,这也会导致无法从HashSet集合中单独删除当前对象,从而造成内存泄露
Method?
Date dt = new Date();
Class clazz = Date.class ;//字节码
Dt.getClass();
Class.forName(“java.lang.String”);
得到字节码
1、 虚拟机已经加载
2、 虚拟机未加载
8个基本类型,都有对应的class对象。字节码和类对象有关
Int.class ==Integer.type
Int[].class.isArray();
Void.class
Constructor代表某个类中的一个构造方法
得到某个类所有的构造方法
Construtor[] contructors = Class.forName(“java.lang.String”).getContructors();
得到某一个构造方法
Constructor constructor = Class.forName(“java.lang.String”).getConstructor(StringBuffer.class);
//获得方法时要用到类型
创建实例对象
通常,String str = newString(new StringBuffer(“abc”));
反射,String str =(String)constructor.newInstance(new StringBuffer(“abc”));
//调用获得方法时要用到上面相同类型的实例对象
Arrays.asList()方法处理int[]和String[]时的差异。
内省
1) 内省(Introspector)是Java 语言对Bean类属性、事件的一种缺省处理方法。例如类 A 中有属性 name, 那我们可以通过 getName,setName 来得到其值或者设置新的值。通过getName/setName 来访问 name 属性,这就是默认的规则。
Java 中提供了一套 API 用来访问某个属性的 getter/setter 方法,通过这些 API 可以使你不需要了解这个规则(但你最好还是要搞清楚),这些 API 存放于包 java.beans 中。
2) 直接通过属性的描述器java.beans.PropertyDescriptor类,来访问属性的getter/setter 方法;
3) 通过类 Introspector来获取某个对象的 BeanInfo 信息,然后通过 BeanInfo 来获取属性的描述器(PropertyDescriptor ),通过这个属性描述器就可以获取某个属性对应的 getter/setter 方法,然后我们就可以通过反射机制来调用这些方。
4) 我们又通常把Javabean的实例对象称之为值对象(Value Object),因为这些bean中通常只有一些信息字段和存储方法,没有功能性方法。一个JavaBean类可以不当JavaBean用,而当成普通类用。JavaBean实际就是一种规范,当一个类满足这个规范,这个类就能被其它特定的类调用。一个类被当作JavaBean使用时,JavaBean的属性是根据方法名推断出来的,它根本看不到Java类内部的成员变量(Javabean的成员变量通常都是私有private的)。
5) 除了反射用到的类需要引入外,内省需要引入的类如下所示,它们都属于java.beans包中的类,自己写程序的时候也不能忘了引入相应的包或者类。
import java.beans.BeanInfo;
import java.beans.IntrospectionException;
import java.beans.Introspector;
import java.beans.PropertyDescriptor;
6) 下面讲解一些开源的工具类Beanutils,需要额外下载的,commons-beanutils.jar,要使用它还必须导入commons-logging.jar包,不然会出异常;
//获取Javabean对象的某个属性的值
PropertiesDescriptor pd2 = new PropertiesDescriptor(propertiesName,pt1.getClass());
Method methodGetX= pd2.getReadMethod();
需要了解beanutil等包的学习,属性连功能。
Java 注解
什么是Java注解
注解,顾名思义,注解,就是对某一事物进行添加注释说明,会存放一些信息,这些信息可能对以后某个时段来说是很有用处的。
Java注解又叫Java标注,Java提供了一套机制,使得我们可以对方法、类、参数、包、域以及变量等添加标准(即附上某些信息)。且在以后某个时段通过反射将标注的信息提取出来以供使用。
自定义注解
Java从1.5版本以后默认内置三个标注:
1) @Override:只能用在方法之上的,用来告诉别人这一个方法是改写父类的。
2) @Deprecated:建议别人不要使用旧的API的时候用的,编译的时候会用产生警告信息,可以设定在程序里的所有的元素上.
3) @SuppressWarnings:这一个类型可以来暂时把一些警告信息消息关闭.
1. 如何自定义注解
自定义步骤大致分为两步:
1) 通过@interface关键字(注意,不是interface,是@interface)声明注解名称,以及注解的成员属性或者叫做注解的参数。
2)使用Java内置的四个元注解对这个自定义标注的功能和范围进行一些限制
2. 什么是元注解
元注解,就是定义注解的注解,也就是说这些元注解是的作用就是专门用来约束其它注解的注解。请区别上面那三个注解,他们也是通过元注解定义而来的。
元注解有哪些呢,主要有四个@Target,@Retention,@Documented,@Inherited
数组类型的属性
int [] arrayAttr() default{1,2,3};
@MyAnnotation(arrayAttr={2,3,4})
如果数组属性中只有一个元素,这时候属性值部分可以省略大括
枚举类型的属性
EnumTest.TrafficLamplamp() ;
@MyAnnotation(lamp=EnumTest.TrafficLamp.GREEN)
注解类型的属性
MetaAnnotationannotationAttr() default @MetaAnnotation("xxxx");
@MyAnnotation(annotationAttr=@MetaAnnotation(“yyy”))
可以认为上面这个@MyAnnotation是MyAnnotaion类的一个实例对象,同样的道理,可以认为上面这个@MetaAnnotation是MetaAnnotation类的一个实例对象,调用代码如下:MetaAnnotation ma = myAnnotation.annotationAttr();
System.out.println(ma.value));
注解的详细语法可以通(过看Java语言规范了解,即看Java language specification。
注解与反射
if(AnnotationTest.class.isAnnotationPresent(MyAnnotation.class)){
MyAnnotation myAnnotation = AnnotationTest.class.getAnnotation(MyAnnotation.class);
}
创建自定义注解
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD,ElementType.TYPE})
public @interface JohnAnnotation {
Stringcolor() default "blue";
Stringvalue();
int[] arrayAttr() default{3,4,4};
EnumTest.TrafficLamplamp() default EnumTest.TrafficLamp.RED;
MetaAnnotation annotationAttr() default @MetaAnnotation("lhm");
}
通过Eclipse自动创建注解,类似于创建接口一样。Eclipse包含注解创建选项
注解的注解叫元注解
@Retention元注解的讲解,其三种取值:RetetionPolicy.SOURCE、RetetionPolicy.CLASS、RetetionPolicy.RUNTIME;分别对应:Java源文件-->class文件-->内存中的字节码。Class文件里面不是字节码,只有加载到内存中才是字节码。
@Override、@SuppressWarnings和@Deprecated这三个注解的属性值分别是RetetionPolicy.SOURCE、RetetionPolicy.SOURCE、RetetionPolicy. RUNTIME
@Target描述我们的注解用在什么元素上。注解生命周期默认值可在class文件
添加属性和设置属性
l 注解的属性:@MyAnnotation(color="red")
l 定义基本类型的属性和应用属性:
在注解类中增加 String color();@MyAnnotation(color="red")
l 用反射方式获得注解对应的实例对象后,再通过该对象调用属性对应的方法
MyAnnotation a =(MyAnnotation)AnnotationTest.class
.getAnnotation(MyAnnotation.class);
System.out.println(a.color());
//可以认为上面这个@MyAnnotation是MyAnnotaion类的一个实例对象
l 为属性指定缺省值:String color() default "yellow";
l value属性:String value()default "zxx";
如果注解中有一个名称为value的属性,且你只想设置value属性(即其他属性都采用默认值或者你只有一个value属性),那么可以省略value=部分,例如:@MyAnnotation("lhm")。
l 数组类型的属性:int [] arrayAttr() default {1,2,3};int[]和String[]区别
@MyAnnotation(arrayAttr={2,3,4})//如果数组属性中只有一个元素,这时候属性值部分可以省略大括
l 枚举类型的属性:
EnumTest.TrafficLamplamp() ;
@MyAnnotation(lamp=EnumTest.TrafficLamp.GREEN)
l 注解类型的属性:MetaAnnotation annotationAttr() default@MetaAnnotation("xxxx");
@MyAnnotation(annotationAttr=@MetaAnnotation(“yyy”) )
注意:可以认为上面这个@MyAnnotation是MyAnnotaion类的一个实例对象,同样的道理,可以认为上面这个@MetaAnnotation是MetaAnnotation类的一个实例对象,调用代码如下:
MetaAnnotationma = myAnnotation.annotationAttr();
System.out.println(ma.value());
注解的详细语法可以通过看Java语言规范了解,即看Java language specification。
注解与枚举、注解与注解、注解与反射
Java 泛型
Java 泛型基本知识
泛型(generic)是提供给Javac编译器使用的,可以限定集合中的输入类型,让编译器挡住源程序中的非法输入,编译器编译带类型说明的集合时会去除掉“类型”信息,使程序运行效率不受影响,对于参数化的泛型类型,getClass()方法的返回值和原始类型完全一样。由于编译生成的字节码会去掉泛型的类型信息,只要能跳过编译器,就可以往某个泛型集合中加入其它类型的数据,例如,用反射得到集合,再调用其add方法即可。
在JDK 1.5中,你还可以按原来的方式将各种不同类型的数据装到一个集合中,但编译器会报告unchecked警告,泛型是表现给编译器的。
l ArrayList<E>类定义和ArrayList<Integer>类引用中涉及如下术语:
1. 整个称为ArrayList<E>泛型类型
2. ArrayList<E>中的E称为类型变量或类型参数
3. 整个ArrayList<Integer>称为参数化的类型
4. ArrayList<Integer>中的Integer称为类型参数的实例或实际类型参数
5. ArrayList<Integer>中的<>念着typeof
6. ArrayList称为原始类型
l 参数化类型与原始类型的兼容性:
1. 参数化类型可以引用一个原始类型的对象,编译报告警告,例如,
Collection<String> c = new Vector();
2. 原始类型可以引用一个参数化类型的对象,编译报告警告,例如,
Collection c = new Vector<String>();
l 参数化类型不考虑类型参数的继承关系:
1. Vector<String> v = newVector<Object>(); //错误!///不写<Object>没错,写了就是明知故犯
2. Vector<Object> v = newVector<String>(); //也错误!
l 编译器不允许创建泛型变量的数组。即在创建数组实例时,数组的元素不能使用参数化的类型,例如,下面语句有错误:
Vector<Integer>vectorList[] = new Vector<Integer>[10];
l 思考题:下面的代码会报错误吗?
Vector v1 = new Vector<String>();
Vector<Object> v = v1; 编译期检查不考虑运行阶段,不会报错
<E>和<T>区别
遇到<A>,<B>,<K,V>等,是用到了Java中的泛型。
一般使用<T>来声明类型持有者名称,自定义泛型类时,类持有者名称可以使用T(Type)
如果是容器的元素可以使用E(Element),若键值匹配可以用K(Key)和V(Value)等,
若是<?>,则是默认是允许Object及其下的子类,也就是Java的所有对象了。
通常来说,如果Foo是Bar的子类型,G是一种带泛型的类型,则G<Foo>不是G<Bar>的子类型。这也许是泛型学习里面最让人容易混淆的一点。
l 问题:定义一个方法,该方法用于打印出任意参数化类型的集合中的所有数据,该方法如何定义呢?
public static void printCollection(Collection<?>cols) {
for(Objectobj:cols) {
System.out.println(obj);
}
//cols.add("string");//错误,因为它不知自己未来匹配就一定是String
cols.size();//没错,此方法与类型参数没有关系
cols = new HashSet<Date>();
}
l 总结:使用?通配符可以引用其他各种参数化的类型,?通配符定义的变量主要用作引用,可以调用与参数化无关的方法,不能调用与参数化有关的方法。
l 限定通配符的上边界:
正确:Vector<? extends Number> x = new Vector<Integer>();
错误:Vector<? extends Number> x = new Vector<String>();
l 限定通配符的下边界:
正确:Vector<? super Integer> x = new Vector<Number>();
错误:Vector<? super Integer> x = new Vector<Byte>();
C++的模板函数
l 如下函数的结构很相似,仅类型不同:
int add(int x,int y) {
return x+y;
}
float add(float x,float y) {
return x+y;
}
double add(double x,double y) {
return x+y;
}
l C++用模板函数解决,只写一个通用的方法,它可以适应各种类型,如下:
template<classT>
T add(T x,T y) {
return (T) (x+y);
}
定义泛型方法
l Java的泛型方法没有C++模板函数功能强大,Java中的如下代码无法通过编译:
<T> T add(T x,T y) {
return (T) (x+y);
//return null;
}
注意:用于放置泛型的类型参数的尖括号应出现在方法的其他所有修饰符之后和在方法的返回类型之前,也就是紧邻返回值之前。按照惯例,类型参数通常用单个大写字母表示。
l 交换数组中的两个元素的位置的泛型方法语法定义如下:
static <E> void swap(E[] a, int i, int j) {
E t = a[i];
a[i] = a[j];
a[j] = t;
}//或用一个面试题讲:把一个数组中的元素的顺序颠倒一下
l 只有引用类型才能作为泛型方法的实际参数,swap(new int[3],3,5);语句会报告编译错误。
l 除了在应用泛型时可以使用extends限定符,在定义泛型时也可以使用extends限定符,例如,Class.getAnnotation()方法的定义。并且可以用&来指定多个边界,如<V extends Serializable & cloneable> void method(){}
l 普通方法、构造方法和静态方法中都可以使用泛型。
l 也可以用类型变量表示异常,称为参数化的异常,可以用于方法的throws列表中,但是不能用于catch子句中。
l 在泛型中可以同时有多个类型参数,在定义它们的尖括号中用逗号分,例如:
public static <K,V> V getValue(K key) { returnmap.get(key);}
Java中的泛型类型(或者泛型)类似于 C++ 中的模板。但是这种相似性仅限于表面,Java 语言中的泛型基本上完全是在编译器中实现,用于编译器执行类型检查和类型推断,然后生成普通的非泛型的字节码,这种实现技术称为擦除(erasure)(编译器使用泛型类型信息保证类型安全,然后在生成字节码之前将其清除)。这是因为扩展虚拟机指令集来支持泛型被认为是无法接受的,这会为 Java 厂商升级其 JVM 造成难以逾越的障碍。所以,Java的泛型采用了可以完全在编译器中实现的擦除方法。
例如,下面这两个方法,编译器会报告错误,它不认为是两个不同的参数类型,而认为是同一种参数类型。
private static voidapplyGeneric(Vector<String> v){}
案例:交换数组中两个元素的位置。
Private static <T> void swap(T[]a,int i, int j){
Ttmp = a[i];
a[i]= a[j];
a[j]=tmp;
}
泛型的类型只能是引用类型,不能是基本类型。
除了在应用泛型时可以使用extends限定符,在定义泛型时也可以使用extends限定符,例如,Class.getAnnotation()方法的定义。并且可以用&来指定多个边界,如<V extendsSerializable & cloneable> void method(){}
泛型方法练习,object转换为string
Private static <T> TautoConvert(Object obj){
Return(T)obj;
}
类型推断传播
类型参数的类型推断
l 编译器判断范型方法的实际类型参数的过程称为类型推断,类型推断是相对于知觉推断的,其实现方法是一种非常复杂的过程。
l 根据调用泛型方法时实际传递的参数类型或返回值的类型来推断,具体规则如下:
1、当某个类型变量只在整个参数列表中的所有参数和返回值中的一处被应用了,那么根据调用方法时该处的实际应用类型来确定,这很容易凭着感觉推断出来,即直接根据调用方法时传递的参数类型或返回值来决定泛型参数的类型,例如:
swap(new String[3],3,4) � static <E> void swap(E[] a, int i,int j)
2、当某个类型变量在整个参数列表中的所有参数和返回值中的多处被应用了,如果调用方法时这多处的实际应用类型都对应同一种类型来确定,这很容易凭着感觉推断出来,例如: add(3,5) �static <T> T add(T a, T b)
3、当某个类型变量在整个参数列表中的所有参数和返回值中的多处被应用了,如果调用方法时这多处的实际应用类型对应到了不同的类型,且没有使用返回值,这时候取多个参数中的最大交集类型,例如,下面语句实际对应的类型就是Number了,编译没问题,只是运行时出问题:
fill(new Integer[3],3.5f) � static <T> void fill(T[] a,T v)
4、当某个类型变量在整个参数列表中的所有参数和返回值中的多处被应用了,如果调用方法时这多处的实际应用类型对应到了不同的类型,并且使用返回值,这时候优先考虑返回值的类型,例如,下面语句实际对应的类型就是Integer了,编译将报告错误,将变量x的类型改为float,对比eclipse报告的错误提示,接着再将变量x类型改为Number,则没有了错误:
intx =(3,3.5f) � static <T> T add(Ta, T b)
5、参数类型的类型推断具有传递性,下面第一种情况推断实际参数类型为Object,编译没有问题,而第二种情况则根据参数化的Vector类实例将类型变量直接确定为String类型,编译将出现问题:
copy(new Integer[5],new String[5]) �static <T> void copy(T[] a,T[] b);
copy(new Vector<String>(), newInteger[5]) �
static<T> void copy(Collection<T> a , T[] b);
定义泛型类型
l 如果类的实例对象中的多处都要用到同一个泛型参数,即这些地方引用的泛型类型要保持同一个实际类型时,这时候就要采用泛型类型的方式进行定义,也就是类级别的泛型,语法格式如下(定义通用的dao方法):
public classGenericDao<T> {
private Tfield1;
public voidsave(T obj){}
public TgetById(int id){}
}
l 类级别的泛型是根据引用该类名时指定的类型信息来参数化类型变量的,例如,如下两种方式都可以:
GenericDao<String> dao = null;
new genericDao<String>();
l 注意:
1. 在对泛型类型进行参数化时,类型参数的实例必须是引用类型,不能是基本类型。
2. 当一个变量被声明为泛型时,只能被实例变量、方法和内部类调用,而不能被静态变量和静态方法调用。因为静态成员是被所有参数化的类所共享的,所以静态成员不应该有类级别的类型参数。
反射获得泛型的参数化类型
如:
ClassGenericalReflection {
private Vector<Date> dates = newVector<Date>();
public void setDates(Vector<Date>dates) {
this.dates = dates;
}
public static void main(String[] args) {
Method methodApply = GenericalReflection.class.getDeclaredMethod("applyGeneric",Vector.class);
ParameterizedType pType =
(ParameterizedType)(methodApply .getGenericParameterTypes())[0];
System.out.println("setDates("+ ((Class)pType.getRawType()).getName() + "<"+ ((Class)(pType.getActualTypeArguments()[0])).getName()+ ">)" );
}
}
泛型DAO的应用:
public abstract class DaoBaseImpl<T>implements DaoBase<T> {
protectedClass<T> clazz;
publicDaoBaseImpl() {
Typetype = this.getClass().getGenericSuperclass();
ParameterizedTypept = (ParameterizedType) type;
this.clazz= (Class) pt.getActualTypeArguments()[0];
System.out.println("clazz= " + this.clazz);
}
}
public class ArticleDaoImpl extendsDaoBaseImpl<Article> implements ArticleDao {}
Java类加载以及委托机制
Java类加载器
l Java虚拟机中可以安装多个类加载器,系统默认三个主要类加载器,每个类负责加载特定位置的类:BootStrap,ExtClassLoader,AppClassLoader
l 类加载器也是Java类,因为其他是Java类的类加载器本身也要被类加载器加载,显然必须有第一个类加载器不是不是Java类,这正是BootStrap。
l Java虚拟机中的所有类装载器采用具有父子关系的树形结构进行组织,在实例化每个类装载器对象时,需要为其指定一个父级类装载器对象或者默认采用系统类装载器为其父级类加载。
类加载器的委托机制
l 当Java虚拟机要加载一个类时,到底派出哪个类加载器去加载呢?
ü 首先当前线程的类加载器去加载线程中的第一个类。
ü 如果类A中引用了类B,Java虚拟机将使用加载类A的类装载器来加载类B。
ü 还可以直接调用ClassLoader.loadClass()方法来指定某个类加载器去加载某个类。
l 每个类加载器加载类时,又先委托给其上级类加载器。
ü 当所有祖宗类加载器没有加载到类,回到发起者类加载器,还加载不了,则抛ClassNotFoundException,不是再去找发起者类加载器的儿子,因为没有getChild方法,即使有,那有多个儿子,找哪一个呢?
ü 对着类加载器的层次结构图和委托加载原理,解释先前将ClassLoaderTest输出成jre/lib/ext目录下的John.jar包中后,运行结果为ExtClassLoader的原因。
l 有一道面试,能不能自己写个类叫java.lang.System,为了不让我们写System类,类加载采用委托机制,这样可以保证爸爸们优先,也就是总是使用爸爸们能找到的类,这样总是使用Java系统提供的System。
l 把先前编写的类加入到jdk的rt.jar中,会有怎样的效果呢?不行!!!看来是不能随意将自己的class文件加入进rt.jar文件中的。删除CLASSPATH环境下的类文件,再执行上一步操作就没问题了。
自定义类加载器
l 知识点:
ü 自定义的类加载器的必须继承ClassLoader
ü loadClass方法与findClass方法
ü defineClass方法
l 编程步骤:
ü 编写一个对文件内容进行简单加密的程序。
ü 编写了一个自己的类装载器,可实现对加密过的类进行装载和解密。
ü 编写一个程序调用类加载器加载类,在源程序中不能用该类名定义引用变量,因为编译器无法识别这个类。程序中可以除了使用ClassLoader.load方法之外,还可以使用设置线程的上下文类加载器或者系统类加载器,然后再使用Class.forName。
l 实验步骤:
ü 对不带包名的class文件进行加密,加密结果存放到另外一个目录,例如:Java MyClassLoader MyTest.class F:\itcast
ü 运行加载类的程序,结果能够被正常加载,但打印出来的类装载器名称为AppClassLoader:JavaMyClassLoader MyTest F:\itcast
ü 用加密后的类文件替换CLASSPATH环境下的类文件,再执行上一步操作就出问题了,错误说明是AppClassLoader类装载器装载失败。
实例代码
注:自定义类加载器,异或加密,加密和解密类加载器
import java.io.*;
import java.lang.reflect.*;
public class MyClassLoader extends ClassLoader{
private String path = null;
public MyClassLoader(String path) throws Exception { //检查文件是否存在
File f = new File(path);
if(!f.isDirectory()) {
throw newRuntimeException(path + " is not adirectory");
}
this.path = path;
}
public Class findClass(String name) { //throws Exception //为什么不能抛出
try{
Filef = new File(path,name.substring(name.lastIndexOf('.')+1) + ".class");
FileInputStream fis = new FileInputStream(f);
ByteArrayOutputStreambos = new ByteArrayOutputStream();
cypher(fis,bos);
byte [] buf = bos.toByteArray();
fis.close();
bos.close();
return defineClass(name,buf,0,buf.length);
}catch(Exception e) {
throw newClassNotFoundException(name + " is notfound!");
}
return null;
}
public staticvoid cypher(InputStreamistream,OutputStream ostream) throwsException
{
//下面这段代码可能遇到255的字节,当成byte就成了-1
/*byte b = 0;
while((b =(byte)istream.read()) != -1) {
ostream.write(b ^0xff);
}*/
int b = 0;
while((b = istream.read()) != -1) {
ostream.write(((byte)b) ^ 0xff);
}
}
public staticvoid main(String [] args) throws Exception {
//下面省略了错误检查
if(!args[0].endsWith("class")){
ClassLoaderloader = new MyClassLoader(args[1]);
Classcls = loader.loadClass(args[0]);
/*
让自定义类继承Date类
System.out.println(cls.getClassLoader().getClass().getName());
java.util.Dated = (java.util.Date)cls.newInstance();
System.out.println(d.toString());
*/
//Method m = cls.getMethod("test",null);//在jdk1.5中报警告,为什么?
Methodm = cls.getMethod("test");
//m.invoke(cls.newInstance(),null);
m.invoke(cls.newInstance());
//((Test)cls.newInstance()).test();
return;
}else {
FileInputStreamfis = new FileInputStream(args[0]);
Filef = new File(args[1], new File(args[0]).getName());
//不用检查目录最后是否有目录分割符
FileOutputStreamfos = new FileOutputStream(f);
cypher(fis,fos);
fis.close();
fos.close();
}
}
}
//类加载器不能加载这种非public的类
/*
Exception in thread"main" java.lang.IllegalAccessException: Class MyClassLoader
can not access a member of class MyTest withmodifiers ""
*/
/*
class MyTest{
public void test() {
System.out.println("hello,www.it315.org");
}
}
*/
Java 网络编程
Socket
Java多线程与并发
1、 synchronized
2、 volatile
3、 java.util.concurrent
Java 多线程
多线程创建、start() 和run()、后台线程和前台线程
Start:创建线程以及调用run方法
为什么要覆盖run方法?
Thread类用于描述线程。该类定义了一个功能,用于存储线程要运行的代码。该存储功能就是run方法。也就是说thread类中的run方法用于存储线程运行的代码。
单独调用run方法:单线程。封装自定义运行代码
联合线程、join方法线程合并、子类继承thread和实现runnable接口不同方式
<重点>
售票,某日某次列车 100张票。一个thread是一个线程对象。
使用runnable接口创建线程,适用于多个相同程序代码的线程去处理同一资源的情况。
定义类实现runnable接口,覆盖runnable中的run方法,将线程中要运行的代码放入run方法中,通过Thread类建立线程对象
将runnable接口的实现类作为实际参数传递给thread类的构造函数,调用Thread类的start()方法,并调用runnable接口实现类的run方法。
解决java单继承。
创建线程对象,明确要运行的线程代码。
多线程应用。。
分析单线程和多线程的利弊。即时通讯工具(feiq、qq等)
线程同步问题,程序原子性、线程池、锁旗标
线程本身不能控制cpu的释放。同步线程是牺牲程序性能为代价的。
Cpu的切换通过操作系统控制。
线程同步,类变量?多个线程同步,必须使用同一个监视器。
同步代码块(对象、监视器),同步函数。
线程同步原理:检查某一对象的标志位,实现线程同步。
同步函数,使用的同步对象是this对象。
线程的执行内部机制
演示同步代码块和同步方法,并验证同步对象。
线程同步死锁问题,死锁特征,代码停止不前。多个线程共享同一数据,发生线程安全问题。
线程间通信问题。有问题
Wait,notify,notify all 1.40
线程的生命周期
Cpu在同一时间只执行一个进程,任务管理器中显示的是cpu在不断切换。
P2p下载,占用的只是带宽。线程是进程中的内容,线程是进程中的控制单元,或者叫执行路径。
进程:是一个正在执行中的程序,每一个进程执行都有一个执行顺序,该顺序是一个执行路径,或者叫一个控制单元。
线程:就是进程中的一个独立的控制单元。线程在控制着进程中的执行。一个进程至少有一个线程。
多线程现象:
Windows本身是多任务操作系统,cpu在程序之间快速切换线程。多核电脑,内存是瓶颈。
多线程每次运行顺序都不相同,多线程都有获取cpu执行权,但在某一刻只能有一个程序在运行(多核除外)。
多线程的特性:1)随机性
线程的状态?sleep与wait。
线程的结束,stop() 和程序运行结束。阻塞状态,线程start之后程序并不一定运行,在等待cpu的执行权。具备运行资格,没有执行权。
线程都有自己默认的名称,Thread-编号(0、1、2...)
使用static?和runnable,提示static生命周期过长
创建线程实现runnable接口和继承Thread类有什么不同?
继承Thread类,就不能再继承其他父类,实现runnable接口还可以实现其他接口(实现方式避免了单继承的局限性)
线程安全问题?问题原因:当多条语句在操作同一个线程共享数据时,一个线程对多条语句只执行了一部分,还没有执行完。另一个线程参与进来执行。导致了共享数据的错误。解决办法:对多条操作共享数据的语句,只能让一个线程都执行完成。在执行过程中其他线程不能参与。(sleep())
Java对于多线程的安全问题,提供专业的解决方式,就是同步代码块。Synchronized(object){}那些代码需要同步,哪些代码操作共享数据。
同步函数锁,this。函数需要被对象调用,那么函数都有一个所属对象,就是this
主线程启动后,再创建线程。。资格和执行权
函数可以使用static修饰,如果同步函数被static修饰,通过验证发现不是this,静态方法中不能定义this,锁是该方法所在类的字节码文件对象,类名.class。静态进内存时,内存中没有本类对象,但是一定有该类对应的字节码文件对象。类名.class,该对象的类型是class。
单例模式:饿汉式和懒汉式区别
懒汉式特点延迟加载,问题。多线程访问时出现安全问题。通过添加同步解决问题,添加同步方式,使用双重判断可以提高效率。加同步时锁是,该类所属的字节码文件对象。
同步死锁问题?静态同步和非静态同步。单例涉及到同步知识点是重点。现象,程序不动。同步中嵌套锁,但是锁不同。同步函数中包含同步代码块。编写死锁。。
线程在较低的层次上扩展了多任务的概念:一个程序同时执行多个任务。通常每个任务称为一个线程(thread),他是线程控制的简称。可以同时运行一个以上线程的程序称为多线程程序(multithreaded)。
多线程和多进程的区别?本质的区别每个进程拥有自己的一整套变量,而线程则共享数据。共享变量使线程之间的通信比进程直接更有效、更容易。
线程分为六种状态:1、New(新生)2、Runnable(可运行)3、Blocked(被阻塞)4、Waiting(等待)5、Timed waiting(计时等待)6、Terminated(被终止)
新生状态:当用new操作符创建一个线程时,该线程还没有运行。
可运行状态:一旦一个线程开始运行,它不必始终保持运行。事实上,运行中的线程被中断,目的是为了让其他线程获得运行机会。线程调度的细节依赖于操作系统提供的服务。抢占式调度系统给每一个可运行线程一个时间片来执行任务。在任何给定时刻,一个可运行的程序可能正在运行也可能没有运行。
被终止的线程:1)因为run方法正常退出而自然死亡。2)因为一个没有捕获异常终止了run方法而意外死亡。
被阻塞线程和等待线程:它不运行任何代码且消耗资源最小。当一个一个线程试图获取一个内部对象锁而该锁被其他对象所持有,则该线程进入阻塞状态。当所有其他线程释放该锁,并且线程调度器允许本线程持有它的时候,该线程变成非阻塞状态。
线程优先级:每当线程调度器有机会选择新线程时,它首先会选择具有较高优先级的线程。但是,线程优先级是高度依赖系统的。Windows 有7个优先级。Sun为linux提供的java虚拟机,线程优先级被忽略——所有线程具有相同的优先级。如果有几个高优先级的线程没有进入非活动状态,低优先级的线程可能永远也不能执行。
守护线程:t.setDaemon(true); 将线程转换为守护线程(daemonthread)。守护线程唯一的作用是为其他线程提供服务。守护线程应该永远不去访问固有资源,如文件、数据库。因为他会在任何时候甚至在一个操作中间发生中断。当只剩下守护线程时,虚拟机就应该退出了。
未捕获的异常处理器:线程run方法不能抛出任何被检测的异常,但是,不被检测的异常会导致线程终止。在这种情况下线程就死亡了。
线程组
同步:当多个线程操作共享数据,如果每次共享数据操作不是原子性操作就会硬气线程安全问题。
锁对象:有两种机制防止代码块受并发访问的干扰。Synchronized 在java 1.5引入了ReentranLock类。Synchonized 关键字自动提供一个锁以及相关的“条件”,对于大多数显示锁的情况,很便利。Java.util.concurrent 框架为这些基础机制提供独立类(ReentrantLock)。注意,每个Bank对象都有自己的ReetrantLock对象。如果两个线程试图访问同一个Bank对象,那么锁以串行方式提供服务。但是,如果两个线程访问不同Bank对象,每一个线程得到不同的锁对象,两个线程都不会发生阻塞。
锁是可重入的,因为线程可以重复获得已持有的锁。锁保持一个持有计数(hold count)来跟踪对lock方法的嵌套调用。线程在每一次调用lock都要用unlock来释放锁。由于这一特性,被一个锁保护的代码可以调用另一个使用相同的锁的方法。通常,可以想要保护需要若干个操作来更新或检查共享对象的代码块。要确保这些操作完成后,另一个线程才能使用相同对象。
条件对象,历史原因也叫条件变量(conditional variable)。通常线程进入临界区,却发现在某一条件满足之后它才能执行。要使用一个条件对象来管理那些已经或缺的一个锁但是却不能做有用工作的线程。在转账业务中,如果发现账户中没有足够的余额,应该做什么?等待直到另一个线程向账户中注入了资金。但是,这一线程刚刚获得了对bankLock的排他性访问,因此别的线程没有进行存款操作的机会。这就是为什么我们需要条件对象的原因。一个锁对象可以有一个或多个相关的条件对象。你可以用newCondition方法获得一个条件对象。习惯上给每一个条件对象命名为可以反映他锁表达的条件的名字。New condition(); awaita();singalAll();为了避免死锁,在什么时候调用singalAll()方法?经验上来说,在对象的状态有利于等待线程的方向改变时调用singalAll()
说明:
条件变量的出现是为了更精细控制线程等待与唤醒,在Java5之前,线程的等待与唤醒依靠的是Object对象的wait()和notify()/notifyAll()方法,这样的处理不够精细。
而在Java5中,一个锁可以有多个条件,每个条件上可以有多个线程等待,通过调用await()方法,可以让线程在该条件下等待。当调用signalAll()方法,又可以唤醒该条件下的等待的线程。有关Condition接口的API可以具体参考JavaAPI文档。
关于线程的使用,最好既不要使用Lock/condition也不要使用synchronized关键字。如果synchronized关键字适合你的程序,那么请尽量使用它。这样可以减少编写的代码数量,减少出错的几率。如果特别需要使用Lock/condition结构提供的独有特性时,才使用Lock/condition
Synchronized关键字
Void notify(),解除那些在该对象上调用wait的线程的阻塞方法。该方法只能在同步方法或者同步块内部调用。如果当前线程不是对象锁的持有者,该方法就抛出一个IllegalMonitorStateException异常。
Void notify(),随机选择一个在该对象上调用wait方法的线程,解除其阻塞状态。该方法只能在一个同步方法或同步块中调用。如果当前线程不是对象锁的持有者,该方法抛出一个IllegalmonitorException异常
Void wait()、void wait(long millis),导致线程进入线程等待状态直到他被通知。该方法只能在一个同步方法中调用。
不用考虑如何枷锁的情况下,保证多线程的安全,最成功的解决方案之一是监视器(monitor)监视器的特征:
ü 监视器是只包含私有域的类。
ü 每个监视器类的对象有一个相关的锁
ü 使用该锁对所有的方法进行加锁。
ü 该锁可以有任意多个相关条件
Java设计者以不是很精确的方式采用了监视器概念,Java中的每一个对象有一个内部的锁和内部的条件。如果一个方法用synchronized关键字声明,那么,他表现的就像是一个监视器方法。通过调用wait/notifyAll/notify来访问条件变量。然而在下述的3个方面Java对象不同于监视器,从而使的线程的安全性下降:
ü 域不要求必须是private
ü 方法不要求必须是sychronized
ü 内部锁对客户是可用的
Volatile域
有时,仅仅为了读写一个或两个实例就使用同步,显得开销过大。便可以使用volatile关键字。
Brian Goetz给出了下述“同步格言”:“如果一个变量写入值,而这个变量接下来可能会被另一个线程读取,或者,从一个变量读值,而这个变量可能是之前被另一个线程写入的,此时必须使用同步”。
Volatile关键字为实例域的同步访问体用一种免锁机制。如果声明一个域为volatile,那么编译器和虚拟机就知道该域是可能被另一线程并发更新的。例如,假设一个对象有一个布尔标记done,它的值被一个线程设置却被另一个线程查询,如同我们讨论过的那样,你可以使用锁:
Public synchronized boolean isDone(){returndone;}
Public synchronized voidsetDone(){done=true;}
Private boolean done;
或许使用内部所不是个好主意。如果另一个线程已经对该线程加锁,isDone和setDone方法可能阻塞。如果注意到这个方面,一个线程可以为这一变量使用独立的Lock。但是,这也会带来许多麻烦。这种情况下,将域声明为volatile是合理的:
Public boolean isDone(){return done;}
Public void setDone(){done=true;}
Private volatile boolean done;
注意:volatile变量不能提供原子性。例如,方法
Public void flipDone(){done !=done;}// notatomic
不能确保改变域中的值。
死锁:
锁和条件不能解决线程中的所有问题,如:
账户1:$200
账户2:$300
线程1:从账户1转移$300到线程2
线程2:从账户2中转移$400到账户1
结果 :因账户1、账户2中的余额都不足以进行转账,都无法执行下去所以线程1和线程2都被阻塞了。
定时器
什么是线程同步,什么是线程互斥?
外部类静态方法不能创建内部类实体变量。
所对象必须同一个对象。
Static修饰内部类
While(自旋锁)与if
Java多线程面试问题集锦
问题:进程和线程的区别
解答:一个进程对应一个程序的执行,而一个线程则是进程执行过程中的一个单独的执行序列,一个进程可以包含多个线程。线程有时候也被称为轻量级进程.
一个Java虚拟机的实例运行在一个单独的进程中,不同的线程共享Java虚拟机进程所属的堆内存。这也是为什么不同的线程可以访问同一个对象。线程彼此共享堆内存并保有他们自己独自的栈空间。这也是为什么当一个线程调用一个方法时,他的局部变量可以保证线程安全。但堆内存并不是线程安全的,必须通过显示的声明同步来确保线程安全。
java.util.concurrent并发包的应用(5.0)
(ConcurrentHashMap核心源代码分析、ThreadPoolExecutor、FutureTask、Semaphore、Condition、ReentrantReadWriteLock等接口与类的深度分析)
Condition newCondition();//返回一个与锁相关的条件对象。
Void await();// 将该线程放到条件的等待集合中
Void singalAll();// 解除该条件的等待集合中的所有线程的阻塞状态
Void singal();// 从该条件的等待集合中随机地选择一个线程,解除其阻塞状态
锁和条件的关键之处:
4、 锁用来保护代码片段,任何时刻只能有一个线程执行被保护的代码。
5、 锁可以管理试图进入被保护代码段的线程。
6、 锁可以拥有一个活多个相关的条件对象
7、 每个条件对象管理那些已经进入被保护的代码段单还不能运行的线程
Lock和Condition接口被添加到Java SE5.0中,这也向程序设计人员提供了高度的封锁控制。然而,大多数情况下,并不需要那样的控制,并且可以使用一种嵌入到Java语言内部的机制。从1.0版本开始,Java中的每一个对象都一个内部锁。如果一个方法用synchronized关键字声明,那么兑现过得锁将保护整个方法。也就是说,要调用该方法,线程必须获得内部的对象锁。换句话说:
Publicsychronized void method()
{
Method body
}
等价于
Public voidmethod()
{
This.intrinsicLock.lock();
Try
{
Method body
}
Finally
{
This.intrinsicLock.unlock();
}
}
锁测试与超时
线程在调用lock方法获得另一个线程所持有的锁的时候,很可能发生阻塞。应该更加谨慎的申请锁。tryLock方法试图申请一个锁,在成功获得锁后返回true,否则返回false,而且线程可以立即离开去做其他事情。
BooleantryLock()。尝试获得锁而没有发生阻塞;如果返回为真,这个方法可以抢夺可用的锁,即使该锁有公平加锁策略,即便其他线程已经等待很久也是如此。
BooleantryLock(long time,TiemUnit unit);
尝试获得锁,阻塞时间不会超过给定的值,如果成功返回true。
VoidlockInterruptibly();
获得锁,但是不确定地发生阻塞。如果线程被中断,抛出一个InterruptedException异常。
读、写锁
Java.util.concurrent.locks定义了两个锁类,ReentrantLock类和ReentrantReadWriteLock类。如果很多个线程从一个数据结构读取数据,而很少有线程修改其中的数据。这种情况下允许读线程共享访问是可以的,但是写线程必须互斥访问的。
Stop/suspend方法。这两个方法被弃用,其主要原因是
1、 stop方法终止所有方法,包含run方法,会导致对象处于不一致的状态。如,在转账业务中,只发生了转出操作,并未发生转入操作等等
2、 suspend,与stop不同,suspend不会破坏对象,用suspend挂起一个持有一个锁的线程,那么,该锁在线程恢复之前是不可用的。如果调用suspend方法的线程试图获得同一个锁,那么程序死锁:被挂起的线程等着被恢复,而将其挂起的线程等待获得锁。
阻塞队列
对于实际编程来说,应该尽可能的远离底层结构。使用由并发处理的专业人士实现的较高层次的结构要方便的多、安全的多。
对于许多线程问题,可以通过使用一个活多个队列以优雅且安全的方式将其形式化。生产者线程向队列插入元素,消费者线程则取出他们。使用队列,可以安全地从一个线程向另一个线程传递数据。
当试图向队列添加元素而队列已满,或是想从队列移出元素而队列为空的时候,阻塞队列(blocking queue)导致线程阻塞。在协调多个线程之间的合作时,阻塞队列是一个有用的工具。工作者线程可以周期性地将中间结果存储在阻塞队列张。其他的工作者线程移出中间结果并进一步加以修改。队列会自动地平衡负载。如果第一个线程及运行得比第二个慢,第二个线程集在等待结果时会阻塞。如果第一个线程集运行得快,它将等待第二个队列集赶上来。
阻塞队列方法分为以下3类,这取决于当队列满或空时他们的响应方式。如果将队列当作线程管理工具来使用,将要用到put和take方法。当试图向满的队列中添加或从空的队列中移出元素师,add、remove和element操作抛出异常。当然在一个多线程程序中,队列会在任何时候空或满,因此,一定要使用offer、poll和peek方法作为替代。这些方法如果不能完成任务,只是给出一个错误提示而不会抛出异常。
注释:poll和peek方法返回空来指示失败,因此,想这些队列中插入null值是非法的。
Java I/O编程
文件上传下载。。。文件读写
涉及到“装饰模式”
<重点>
Java NIO详解
Java new i/o
UML
1) UML 的概念
2) Use Case Diagram 详解
3) Sequence Diagram 详解
4) Class Diagram 详解
5) 如何读懂现有系统的 UML 设计图
6) 如何设计系统的 UML 设计图
7) 使用 UML 描述常见设计模式
设计模式
MVC模式
M V C包括三类对象。模型M o d el是应用对象,视图Vi e w是它在屏幕上的表示,控制器C o n t r o l l e r定义用户界面对用户输入的响应方式。不使用M V C,用户界面设计往往将这些对象混在一起,而M VC则将它们分离以提高灵活性和复用性。
M V C通过建立一个“订购/通知”协议来分离视图和模型。视图必须保证它的显示正确地反映了模型的状态。一旦模型的数据发生变化,模型将通知有关的视图,每个视图相应地得到刷新自己的机会。这种方法可以让你为一个模型提供不同的多个视图表现形式,也能够为一个模型创建新的视图而无须重写模型。然而这个设计还可用于解决更一般的问题:将对象分离,使得一个对象的改变能够影响另一些对象,而这个对象并不需要知道那些被影响的对象的细节。
单例模式
http://blog.csdn.net/zhengzhb/article/details/7331369
设计模式关系参照:
http://blog.csdn.net/zhengzhb/article/details/7187278
枚举?单例模式?枚举只有一个成员时,就可以作为一种单例的实现方式
定义:确保一个类只有一个实例,而且自行实例化并向整个系统提供这个实例。
类型:创建类模式
单例模式应该是23种设计模式中最简单的一种模式了。它有以下几个要素:
- 私有的构造方法
- 指向自己实例的私有静态引用
- 以自己实例为返回值的静态的公有的方法
单例模式根据实例化对象时机的不同分为两种:一种是饿汉式单例,一种是懒汉式单例。饿汉式单例在单例类被加载时候,就实例化一个对象交给自己的引用;而懒汉式在调用取得实例方法的时候才会实例化对象。代码如下:
饿汉式单例
public class Singleton {
privatestatic Singleton singleton = new Singleton();
privateSingleton(){}
publicstatic Singleton getInstance(){
returnsingleton;
}
}
懒汉式单例
public class Singleton {
privatestatic Singleton singleton;
privateSingleton(){}
public static Singleton getInstance(){
if(singleton==null){//思考为什么
synchronized(this){
if(singleton==null){
singleton = newSingleton();
}
}
}
returnsingleton;
}
}
单例模式的优点:
- 在内存中只有一个对象,节省内存空间。
- 避免频繁的创建销毁对象,可以提高性能。
- 避免对共享资源的多重占用。
- 可以全局访问。
适用场景:由于单例模式的以上优点,所以是编程中用的比较多的一种设计模式。我总结了一下我所知道的适合使用单例模式的场景:
- 需要频繁实例化然后销毁的对象。
- 创建对象时耗时过多或者耗资源过多,但又经常用到的对象。
- 有状态的工具类对象。
- 频繁访问数据库或文件的对象。
- 以及其他我没用过的所有要求只有一个对象的场景。
单例模式注意事项:
- 只能使用单例类提供的方法得到单例对象,不要使用反射,否则将会实例化一个新对象。
- 不要做断开单例类对象与类中静态引用的危险操作。
- 多线程使用单例使用共享资源时,注意线程安全问题。
关于Java中单例模式的一些争议:
单例模式的对象长时间不用会被jvm垃圾收集器收集吗
看到不少资料中说:如果一个单例对象在内存中长久不用,会被jvm认为是一个垃圾,在执行垃圾收集的时候会被清理掉。对此这个说法,笔者持怀疑态度,笔者本人的观点是:在hotspot虚拟机1.6版本中,除非人为地断开单例中静态引用到单例对象的联接,否则jvm垃圾收集器是不会回收单例对象的。
在一个jvm中会出现多个单例吗
在分布式系统、多个类加载器、以及序列化的的情况下,会产生多个单例,这一点是无庸置疑的。那么在同一个jvm中,会不会产生单例呢?使用单例提供的getInstance()方法只能得到同一个单例,除非是使用反射方式,将会得到新的单例。代码如下
Class c = Class.forName(Singleton.class.getName());
Constructor ct = c.getDeclaredConstructor();
ct.setAccessible(true);
Singleton singleton = (Singleton)ct.newInstance();
这样,每次运行都会产生新的单例对象。所以运用单例模式时,一定注意不要使用反射产生新的单例对象。
单例模式只有饿汉式和懒汉式两种吗
饿汉式单例和懒汉式单例只是两种比较主流和常用的单例模式方法,从理论上讲,任何可以实现一个类只有一个实例的设计模式,都可以称为单例模式。
单例类可以被继承吗
饿汉式单例和懒汉式单例由于构造方法是private的,所以他们都是不可继承的,但是其他很多单例模式是可以继承的,例如登记式单例。
饿汉式单例好还是懒汉式单例好
在Java中,饿汉式单例要优于懒汉式单例。C++中则一般使用懒汉式单例。
单例模式比较简单,在此就不举例代码演示了。
工厂模式
http://blog.csdn.net/zhengzhb/article/details/7348707
《设计模式》《设计模式_可复用面向对象软件的基础》《大话设计模式》《Head First 设计模式》spring的工厂方法解析?
模板方法模式
代理模式
代理是一种常用的设计模式,其目的就是为其他对象提供一个代理以控制对某个对象的访问。代理类负责为委托类预处理消息,过滤并转发消息,以及进行消息被委托类执行后的后续处理。
图 1. 代理模式
subject(接口)
为了保持行为的一致性,代理类和委托类通常会实现相同的接口,所以在访问者看来两者没有丝毫的区别。通过代理类这中间一层,能有效控制对委托类对象的直接访问,也可以很好地隐藏和保护委托类对象,同时也为实施不同控制策略预留了空间,从而在设计上获得了更大的灵活性。Java 动态代理机制以巧妙的方式近乎完美地实践了代理模式的设计理念。
远程代理、动态代理???代理设计模式,远程代理和web项目?面向接口编程
策略模式
观察者模式
命令模式
享元模式
装饰模式
Java递归应用
Jvm高级应用
1) Java代码的执行机制
2) 源代码编译机制
3) 类加载与执行机制
4) JVM 内存管理(空间、分配回收)内存管理(空间、分配回收) 内存管理(空间、分配回收)
5) 使用 JConsole、JVisualVM及JMap等查看内存的使用状等查看内存的使用状况
6) 分析程序执行的过程
7) JVM 线程资源同步及交互机制线程资源同步及交互机制
8) 线程交互机制与状态分析线
JDBC
1) JDBC 详解
2) 使用 JDBC 的步骤
3) 使用 JDBC 连接数据库
4) 使用 JDBC 操纵数据库
5) JDBC 操纵数据库的过程与分析
6) 数据库连接池(详解 Apache DBCP 数据库连接池)
7) 深度剖析 DAO 模式
Java动态代理
Aop ->代理->目标?横切性关注点
使用 CGlib 创建代理对象(代理对象没有接口)
查看方法的运行时间,system.currentTimeMillis();--获取开始时间
没有源码,如何解决?使用继承?聚合?
继承代理类越来越大
Jre 默认不带编译器,jdk包含
动态代理实现原理?
Eclipse 导航视图,直观展示硬盘的文件
尚学堂视频缺乏,思维导图。不够清晰
JBPM(工作流)
EJB
OSGI
Java新特性以及重难点
Java7新特性
Map map = {name:”xx”,age:18}
Java5特性
枚举、注解、type、自动装箱、泛型、静态导入
可变参数:
System.out.println(countScore(2,3,5));
System.out.println(countScore(1,2,3,5));
1) 只能出现在参数列表的最后;
2) ...位于变量类型和变量名之间,前后有无空格都可以;
3) 调用可变参数的方法时,编译器为该可变参数隐含创建一个数组,在方法体中以数组的形式访问可变参数。
增强for循环
for (type 变量名:集合变量名 ) { … }
注意事项:
迭代变量必须在( )中定义!
集合变量可以是数组或实现了Iterable接口的集合类
基本数据类型的自动拆箱与装箱
自动装箱:Integer num1 = 12;
自动拆箱:System.out.println(num1+ 12);
基本数据类型的对象缓存:
Integer num1 = 12;
Integer num2 = 12; 这块相等,<=127都是真的
System.out.println(num1 == num2);
Integer num3 = 129; 这块不相等,因为是对象
Integer num4 = 129;
System.out.println(num3 == num4);
Integer num5 = Integer.valueOf(12);
Integer num6 = Integer.valueOf(12) ; 这块的道理同上
System.out.println(num5 == num6);
重难点知识
匿名内部类
匿名内部类就是不能有名称的类,因此是没有办法去引用他们的。必须在创建的时候作为new语句的一部分对其进行声明。这个就必须采用另一种形式的new语句,形式如下:
new <类或者接口> {类的主体},它声明的就是一个匿名内部类,对给定的类进行扩展或者实现一个给定的接口,它创建了那个类的新实例,并将之作为结果返回。如果一个匿名类对另一个类进行扩展,则可以访问变量,覆盖其方法,如果匿名类实现了某一个接口,则它的主体就必须实现接口中方法。
Collection 和 Collections的区别
1、Collection是个java.util下的接口,它是各种集合结构的父接口。继承与他的接口主要有Set 和List。
2、Collections是个java.util下的专用静态类,它包含有各种有关集合操作的静态方法。提供一系列静态方法实现对各种集合的搜索、排序、线程安全化等操作。
1、数组类Array,是Java中最基本的一个存储结构。提供了动态创建和访问 Java 数组的方法。其中的元素的类型必须相同。效率高,但容量固定且无法动态改变。 它无法判断其中实际存有多少元素,length只是告诉我们array的容量。
2、Arrays类,此静态类专门用来操作array ,提供搜索、排序、复制等静态方法。equals():比较两个array是否相等。array拥有相同元素个数,且所有对应元素两两相等。 sort():用来对array进行排序。 binarySearch():在排好序的array中寻找元素。
时间日期等复杂的数据如何处理?
方法签名:方法的名字和参数列表一起组成方法的签名(signature)。签名和修饰符(比如,public和static等)以及抛出异常列表一起组成方法头(header)。
对象的域被称为实例变量(instance variable),因为域在类的每个对象(实例)中都有唯一拷贝。
通常我们需要某个对象的域和其他同类实例化而成的对象同名域不同,但是有时候需要同类的所有对象共享一个域。这种共享的变量叫做类变量(class variable),他特定于类而不是类的变量。在java中,可以通过把域声明为static获得特定于类的域,也叫静态域
Spring
基本概念
1.什么是Spring?
Spring是一个开源的Java EE开发框架。Spring框架的核心功能可以应用在任何Java应用程序中,但对Java EE平台上的Web应用程序有更好的扩展性。Spring框架的目标是使得Java EE应用程序的开发更加简捷,通过使用POJO为基础的编程模型促进良好的编程风格。Spring教程。
2.Spring有哪些优点?
· 轻量级:Spring在大小和透明性方面绝对属于轻量级的,基础版本的Spring框架大约只有2MB。
· 控制反转(IOC):Spring使用控制反转技术实现了松耦合。依赖被注入到对象,而不是创建或寻找依赖对象。
· 面向切面编程(AOP): Spring支持面向切面编程,同时把应用的业务逻辑与系统的服务分离开来。
· 容器:Spring包含并管理应用程序对象的配置及生命周期。
· MVC框架:Spring的web框架是一个设计优良的web MVC框架,很好的取代了一些web框架。
· 事务管理:Spring对下至本地业务上至全局业务(JAT)提供了统一的事务管理接口。
· 异常处理:Spring提供一个方便的API将特定技术的异常(由JDBC, Hibernate, 或JDO抛出)转化为一致的、Unchecked异常。
3.Spring框架有哪些模块?
Spring框架的基本模块如下所示:
- Core module
- Bean module
- Context module
- Expression Language module
- JDBC module
- Web module
- Web-Servlet module
- Web-Struts module
-----------------------------------------
- ORM module
- OXM module
- Java Messaging Service(JMS) module
- Transaction module
- Web-Portlet module
4.解释核心容器(应用上下文)模块
Spring的基本模块,它提供了Spring框架的基本功能。BeanFactory 是所有Spring应用的核心。Spring框架是建立在这个模块之上的,这也使得Spring成为一个容器。
5.BeanFactory– BeanFactory 实例
BeanFactory是工厂模式的一种实现,它使用控制反转将应用的配置和依赖与实际的应用代码分离开来。最常用的BeanFactory实现是XmlBeanFactory类。
6.XmlBeanFactory
最常用的就是org.springframework.beans.factory.xml.XmlBeanFactory,它根据XML文件中定义的内容加载beans。该容器从XML文件中读取配置元数据,并用它来创建一个完备的系统或应用。
7.解释AOP模块
AOP模块用来开发Spring应用程序中具有切面性质的部分。该模块的大部分服务由AOP Aliance提供,这就保证了Spring框架和其他AOP框架之间的互操作性。另外,该模块将元数据编程引入到了Spring。
8.解释抽象JDBC和DAO模块
通过使用抽象JDBC和DAO模块保证了与数据库连接代码的整洁与简单,同时避免了由于未能关闭数据库资源引起的问题。它在多种数据库服务器的错误信息之上提供了一个很重要的异常层。它还利用Spring的AOP模块为Spring应用程序中的对象提供事务管理服务。
9.解释对象/关系映射集成模块
Spring通过提供ORM模块在JDBC的基础上支持对象关系映射工具。这样的支持使得Spring可以集成主流的ORM框架,包括Hibernate, JDO, 及iBATIS SQL Maps。Spring的事务管理可以同时支持以上某种框架和JDBC。
10.解释web模块
Spring的web模块建立在应用上下文(application context)模块之上,提供了一个适合基于web应用程序的上下文环境。该模块还支持了几个面向web的任务,如透明的处理多文件上传请求及将请求参数同业务对象绑定起来。
11.解释Spring MVC模块
Spring提供MVC框架构建web应用程序。Spring可以很轻松的同其他MVC框架结合,但Spring的MVC是个更好的选择,因为它通过控制反转将控制逻辑和业务对象完全分离开来。
12.Spring的配置文件
Spring的配置文件是一个XML文件,文件包含了类信息并描述了这些类是如何配置和互相调用的。
13.SpringIoC容器是什么?
Spring IOC负责创建对象、管理对象(通过依赖注入)、整合对象、配置对象以及管理这些对象的生命周期。
14.IOC有什么优点?
IOC或依赖注入减少了应用程序的代码量。它使得应用程序的测试很简单,因为在单元测试中不再需要单例或JNDI查找机制。简单的实现以及较少的干扰机制使得松耦合得以实现。IOC容器支持勤性单例及延迟加载服务。
15.应用上下文是如何实现的?
FileSystemXmlApplicationContext 容器加载XML文件中beans的定义。XML Bean配置文件的完整路径必须传递给构造器。
FileSystemXmlApplicationContext 容器也加载XML文件中beans的定义。注意,你需要正确的设置CLASSPATH,因为该容器会在CLASSPATH中查看bean的XML配置文件。
WebXmlApplicationContext:该容器加载xml文件,这些文件定义了web应用中所有的beans。
16.BeanFactory和ApplicationContext有什么区别?
ApplicationContext提供了一种解决文档信息的方法,一种加载文件资源的方式(如图片),他们可以向监听他们的beans发送消息。另外,容器或者容器中beans的操作,这些必须以bean工厂的编程方式处理的操作可以在应用上下文中以声明的方式处理。应用上下文实现了MessageSource,该接口用于获取本地消息,实际的实现是可选的。
17.Spring应用程序看起来像什么?
- 一个定义功能的接口
- 实现包括属性,setter和getter方法,功能等
- Spring AOP
- Spring的XML配置文件
- 使用该功能的客户端编程
依赖注入
Spring中的依赖注入是什么?
依赖注入作为控制反转(IOC)的一个层面。不用创建对象而只需要描述如何创建,不必通过代码直接将组件和服务关联在一起。
依赖注入通常有如下两种:
构造器依赖注入:构造器依赖注入在容器触发构造器的时候完成,该构造器有一系列的参数,每个参数代表注入的对象。
Setter方法依赖注入:首先容器会触发一个无参构造函数或无参静态工厂方法实例化对象,之后容器调用bean中的setter方法完成Setter方法依赖注入。
使用依赖注入:使用构造器参数实现强制依赖注入,使用setter方法实现可选的依赖关系。
使用情况:使用构造器参数实现强制依赖注入,使用setter方法实现可选的依赖关系。
一、设值注入
设值注入是指IoC容器使用属性的setter方法来注入被依赖的实例。
传统的依赖方式:
public interface Person { //定义使用斧子的方法 public void useAxe(); } public interface Axe { //Axe接口里面有个砍的方法 public String chop(); } public class Chinese implements Person { private Axe axe; private String name; // 设值注入所需的setter方法 public void setAxe(Axe axe) { this.axe = axe; } public void setName(String name) { this.name = name; } // 实现Person接口的userAxe方法 public void useAxe() { // 调用axe的chop方法,表明Person对象依赖于Axe对象 System.out.println("我是"+name+"用"+axe.chop()); } } |
Spring容器的作用就是以松耦合的方式来管理这种调用关系。在上面的Chinese类中,Chinese类并不知道它要调用的axe实例在哪里,也不知道axe实例是如何实现的,它只是需要调用一个axe实例,这个Axe实例将由Spring容器负责注入。
Axe的实现类:StoneAxe类
public class StoneAxe implements Axe{ public String chop() { return "石斧砍柴好慢啊!!!"; } } |
程序不用知道Chinese类和Axe实例的耦合,Spring容器也不知道。需要通过XML文件来指定实例之间的依赖。
对于本应用的XML配置文件如下:
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://www.springframework.org/schema/beans" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd"> <!-- 配置Chinese实例,其实现类是Chinese --> <bean id="chinese" class="com.spring.service.impl.Chinese"> <!-- 将StoneAxe注入给axe属性 --> <property name="axe" ref="stoneAxe" /> <property name="name" value="孙悟空"/> </bean> <!-- 配置stoneAxe实例 --> <bean id="stoneAxe" class="com.spring.service.impl.StoneAxe" /> </beans> |
XML配置文件中Bean的属性:
id:指定该Bean的唯一标识,程序会通过id属性值来访问该Bean实例。
class:指定该Bean的实现类,Spring容器会使用XML解析器读取该属性值,并利用反射来创建该实现类的实例。
测试程序:
public class BeanTest { public static void main(String[] args) { //创建Spring容器 ApplicationContext ctx = new ClassPathXmlApplicationContext("bean.xml"); //获取Chinese实例 Person person = ctx.getBean("chinese",Person.class); person.useAxe(); } } |
从上面的实例我们可以看出,依赖注入以配置文件管理Bean实例之间的耦合,从而实现松耦合。
Spring IoC容器有如下3个基本要点:
1、 应用程序的各个组件面向接口编程。面向接口编程可以将各个组件的耦合提升到接口层次,从而有利于项目后期的扩展。
2、 应用程序的各组件不再由程序主动产生,由Spring容器来负责产生并初始化。
3、 Spring采用配置文件、或者Annotation来管理Bean的实现类、依赖关系,Spring容器则根据配置文件,利用反射机制来创建实现类,并为之注入依赖关系。
二、构造注入
构造注入就是利用构造器来设置依赖关系的方式。
public class Japanese implements Person{ private Axe axe; //默认构造器 public Japanese(){ } //构造注入所需的带参数构造器 public Japanese(Axe axe){ this.axe = axe; } public void useAxe() { System.out.println(axe.chop()); } |
配置文件如下:
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://www.springframework.org/schema/beans" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd"> <!-- 配置Japanese实例 --> <bean id="japanese" class="com.spring.service.impl.Japanese"> <!-- 使用构造注入 ,为Japanese实例注入SteelAxe实例--> <constructor-arg ref="stoneAxe"/> </bean> <!-- 配置stoneAxe实例 --> <bean id="stoneAxe" class="com.spring.service.impl.StoneAxe" /> </beans> |
spring构造注入多个参数
<bean name="springAction" class="com.bless.springdemo.action.SpringAction"> <constructor-arg index="0" ref="springDao"></constructor-arg> <constructor-arg index="1" ref="user"></constructor-arg> </bean> <bean name="springDao" class="com.bless.springdemo.dao.impl.SpringDaoImpl"></bean> <bean name="user" class="com.bless.springdemo.vo.User"></bean>
|
Spring Beans
21.什么是Spring Beans?
Spring Beans是构成Spring应用核心的Java对象。这些对象由SpringIOC容器实例化、组装、管理。这些对象通过容器中配置的元数据创建,例如,使用XML文件中定义的创建。在Spring中创建的beans都是单例的beans。在bean标签中有一个属性为”singleton”,如果设为true,该bean是单例的
22.SpringBean中定义了什么内容?
Spring Bean中定义了所有的配置元数据,这些配置信息告知容器如何创建它,它的生命周期是什么以及它的依赖关系。
23.如何向Spring 容器提供配置元数据?
有三种方式向Spring 容器提供元数据:
XML配置文件
基于注解配置
24.你如何定义bean的作用域?
在Spring中创建一个bean的时候,我们可以声明它的作用域。只需要在bean定义的时候通过’scope’属性定义即可。例如,当Spring需要产生每次一个新的bean实例时,应该声明bean的scope属性为prototype。如果每次你希望Spring返回一个实例,应该声明bean的scope属性为singleton。
25.说一下Spring中支持的bean作用域
Spring框架支持如下五种不同的作用域:
singleton:在Spring IOC容器中仅存在一个Bean实例,Bean以单实例的方式存在。
prototype:一个bean可以定义多个实例。
request:每次HTTP请求都会创建一个新的Bean。该作用域仅适用于WebApplicationContext环境。
session:一个HTTP Session定义一个Bean。该作用域仅适用于WebApplicationContext环境.
globalSession:同一个全局HTTP Session定义一个Bean。该作用域同样仅适用于WebApplicationContext环境.
bean默认的scope属性是’singleton‘。
26.Spring框架中单例beans是线程安全的吗?
不是,Spring框架中的单例beans不是线程安全的。
27.解释Spring框架中bean的生命周期
Spring容器读取XML文件中bean的定义并实例化bean。
Spring根据bean的定义设置属性值。
如果该Bean实现了BeanNameAware接口,Spring将bean的id传递给setBeanName()方法。
如果该Bean实现了BeanFactoryAware接口,Spring将beanfactory传递给setBeanFactory()方法。
如果任何beanBeanPostProcessors 和该bean相关,Spring调用postProcessBeforeInitialization()方法。
如果该Bean实现了InitializingBean接口,调用Bean中的afterPropertiesSet方法。如果bean有初始化函数声明,调用相应的初始化方法。
如果任何beanBeanPostProcessors 和该bean相关,调用postProcessAfterInitialization()方法。
如果该bean实现了DisposableBean,调用destroy()方法。
28.哪些是最重要的bean生命周期方法?能重写它们吗?
有两个重要的bean生命周期方法。第一个是setup方法,该方法在容器加载bean的时候被调用。第二个是teardown方法,该方法在bean从容器中移除的时候调用。可以重写,bean标签有两个重要的属性(init-method 和 destroy-method),你可以通过这两个属性定义自己的初始化方法和析构方法。Spring也有相应的注解:@PostConstruct 和 @PreDestroy。
29.什么是Spring的内部bean?
当一个bean被用作另一个bean的属性时,这个bean可以被声明为内部bean。在基于XML的配置元数据中,可以通过把元素定义在或元素内部实现定义内部bean。内部bean总是匿名的并且它们的scope总是prototype。
30.如何在Spring中注入Java集合类?
Spring提供如下几种类型的集合配置元素:
list元素用来注入一系列的值,允许有相同的值。
set元素用来注入一些列的值,不允许有相同的值。
map用来注入一组”键-值”对,键、值可以是任何类型的。
props也可以用来注入一组”键-值”对,这里的键、值都字符串类型。
31.什么是bean wiring?
Wiring,或者说bean Wiring是指beans在Spring容器中结合在一起的情况。当装配bean的时候,Spring容器需要知道需要哪些beans以及如何使用依赖注入将它们结合起来。
32.什么是bean自动装配?
Spring容器可以自动配置相互协作beans之间的关联关系。通过检查BeanFactory 的内容里没有使用< property>元素。
33.解释自动装配的各种模式?
自动装配提供五种不同的模式供Spring容器用来自动装配beans之间的依赖注入:
no:默认的方式是不进行自动装配,通过手工设置ref 属性来进行装配bean。
byName:通过参数名自动装配,Spring容器查找beans的属性,这些beans在XML配置文件中被设置为byName。之后容器试图匹配、装配和该bean的属性具有相同名字的bean。
byType:通过参数的数据类型自动自动装配,Spring容器查找beans的属性,这些beans在XML配置文件中被设置为byType。之后容器试图匹配和装配和该bean的属性类型一样的bean。如果有多个bean符合条件,则抛出错误。
constructor:这个同byType类似,不过是应用于构造函数的参数。如果在BeanFactory中不是恰好有一个bean与构造函数参数相同类型,则抛出一个严重的错误。
autodetect:如果有默认的构造方法,通过construct的方式自动装配,否则使用 byType的方式自动装配。
34.自动装配有哪些局限性?
自动装配有如下局限性:
重写:你仍然需要使用 < property>设置指明依赖,这意味着总要重写自动装配。
原生数据类型:你不能自动装配简单的属性,如原生类型、字符串和类。
模糊特性:自动装配总是没有自定义装配精确,因此,如果可能尽量使用自定义装配。
35.你可以在Spring中注入null或空字符串吗?
完全可以。
Spring注解
36.什么是Spring基于Java的配置?给出一些注解的例子
基于Java的配置允许你使用Java的注解进行Spring的大部分配置而非通过传统的XML文件配置。以注解@Configuration为例,它用来标记类,说明作为beans的定义,可以被Spring IOC容器使用。另一个例子是@Bean注解,它表示该方法定义的Bean要被注册进Spring应用上下文中。
37.什么是基于注解的容器配置?
开发人员将直接在类中进行配置,通过注解标记相关的类、方法或字段声明,而不再使用XML描述bean之间的连线关系。
38.如何开启注解装配?
注解装配默认情况下在Spring容器中是不开启的。如果想要开启基于注解的装配只需在Spring配置文件中配置元素即可。
39.@Required注解
@Required表明bean的属性必须在配置时设置,可以在bean的定义中明确指定也可通过自动装配设置。如果bean的属性未设置,则抛出BeanInitializationException异常。
40.@Autowired注解
@Autowired注解提供更加精细的控制,包括自动装配在何处完成以及如何完成。它可以像@Required一样自动装配setter方法、构造器、属性或者具有任意名称和/或多个参数的PN方法。
41.@Qualifier 注解
当有多个相同类型的bean而只有其中的一个需要自动装配时,将@Qualifier注解和@Autowire 注解结合使用消除这种混淆,指明需要装配的bean。
Spring数据访问
42.在Spring框架中如何更有效的使用JDBC?
使用Spring JDBC框架,资源管理以及错误处理的代价都会减轻。开发人员只需通过statements和queries语句从数据库中存取数据。Spring框架中通过使用模板类能更有效的使用JDBC,也就是所谓的JdbcTemplate(例子)。
43.JdbcTemplate
JdbcTemplate类提供了许多方法,为我们与数据库的交互提供了便利。例如,它可以将数据库的数据转化为原生类型或对象,执行写好的或可调用的数据库操作语句,提供自定义的数据库错误处理功能。
44.Spring对DAO的支持
Spring对数据访问对象(DAO)的支持旨在使它可以与数据访问技术(如 JDBC, Hibernate 及JDO)方便的结合起来工作。这使得我们可以很容易在的不同的持久层技术间切换,编码时也无需担心会抛出特定技术的异常。
45.使用Spring可以通过什么方式访问Hibernate?
使用Spring有两种方式访问Hibernate:
1)使用Hibernate Template的反转控制以及回调方法
2)继承HibernateDAOSupport,并申请一个AOP拦截器节点
46.Spring支持的ORM
Spring支持一下ORM:
Hibernate、iBatis、JPA (Java -PersistenceAPI)、TopLink、JDO (Java Data Objects)、OJB
47.如何通过HibernateDaoSupport将Spring和Hibernate结合起来?
使用Spring的SessionFactory 调用LocalSessionFactory。结合过程分为以下三步:
1)配置Hibernate SessionFactory
2)继承HibernateDaoSupport实现一个DAO
3)使用AOP装载事务支持
48.Spring支持的事务管理类型
Spring支持如下两种方式的事务管理:
编程式事务管理:这意味着你可以通过编程的方式管理事务,这种方式带来了很大的灵活性,但很难维护。
声明式事务管理:这种方式意味着你可以将事务管理和业务代码分离。你只需要通过注解或者XML配置管理事务。
49.Spring框架的事务管理有哪些优点?
它为不同的事务API(如JTA, JDBC, Hibernate, JPA, 和JDO)提供了统一的编程模型。
它为编程式事务管理提供了一个简单的API而非一系列复杂的事务API(如JTA).
它支持声明式事务管理。
它可以和Spring 的多种数据访问技术很好的融合。
50.你更推荐那种类型的事务管理?
许多Spring框架的用户选择声明式事务管理,因为这种方式和应用程序的关联较少,因此更加符合轻量级容器的概念。声明式事务管理要优于编程式事务管理,尽管在灵活性方面它弱于编程式事务管理(这种方式允许你通过代码控制业务)。
Spring AOP
51.解释AOP
面向切面编程,或AOP允许程序员模块化横向业务逻辑,或定义核心部分的功能,例如日志管理和事务管理。
52.切面(Aspect)
AOP的核心就是切面,它将多个类的通用行为封装为可重用的模块。该模块含有一组API提供 cross-cutting功能。例如,日志模块称为日志的AOP切面。根据需求的不同,一个应用程序可以有若干切面。在Spring AOP中,切面通过带有@Aspect注解的类实现。
53.在Spring AOP中concern和 cross-cutting concern的区别是什么?
Concern(核心逻辑):表示在应用程序中一个模块的行为。Concern可以定义为我们想要实现的功能。
Cross-cutting concern(横向的通用逻辑):指的是整个应用程序都会用到的功能,它影响整个应用程序。例如,日志管理(Logging)、安全管理(Security)以及数据交互是应用程序的每个模块都要涉及到的,因此这些都属于Cross-cutting concern。
54.连接点(Join point)
连接点代表应用程序中插入AOP切面的地点。它实际上是Spring AOP框架在应用程序中执行动作的地点。
55.通知(Advice)
通知表示在方法执行前后需要执行的动作。实际上它是Spring AOP框架在程序执行过程中触发的一些代码。
Spring切面可以执行一下五种类型的通知:
before(前置通知):在一个方法之前执行的通知。
after(最终通知):当某连接点退出的时候执行的通知(包括正常、异常)。
after-returning(后置通知):在某连接点正常完成后执行的通知。
after-throwing(异常通知):在方法抛出异常退出时执行的通知。
around(环绕通知):在方法调用前后触发的通知。
56.切入点(Pointcut)
切入点是一个或一组连接点,通知将在这些位置执行。可以通过表达式或匹配的方式指明切入点。
57.什么是引入?
引入允许我们在已有的类上添加新的方法或属性。
58.什么是目标对象?
被一个或者多个切面所通知的对象。它通常是一个代理对象。也被称做被通知(advised)对象。
59.什么是代理?
代理是将通知应用到目标对象后创建的对象。从客户端的角度看,代理对象和目标对象是一样的。
60.有几种不同类型的自动代理?
BeanNameAutoProxyCreator:bean名称自动代理创建器
DefaultAdvisorAutoProxyCreator:默认通知者自动代理创建器
Metadata autoproxying:元数据自动代理
61.什么是织入?什么是织入应用的不同点?
织入是将切面和其他应用类型或对象连接起来创建一个通知对象的过程。织入可以在编译、加载或运行时完成。
62.解释基于XML Schema方式的切面实现
在这种情况下,切面由使用XML文件配置的类实现。
63.解释基于注解方式(基于@AspectJ)的切面实现
在这种情况下(基于@AspectJ的实现),指的是切面的对应的类使用Java 5注解的声明方式。
Spring的MVC框架
64.什么是Spring的MVC框架?
Spring提供了一个功能齐全的MVC框架用于构建Web应用程序。Spring框架可以很容易的和其他的MVC框架融合(如Struts),该框架使用控制反转(IOC)将控制器逻辑和业务对象分离开来。它也允许以声明的方式绑定请求参数到业务对象上。
65.DispatcherServlet
Spring的MVC框架围绕DispatcherServlet来设计的,它用来处理所有的HTTP请求和响应。
66.WebApplicationContext
WebApplicationContext继承了ApplicationContext,并添加了一些web应用程序需要的功能。和普通的ApplicationContext不同,WebApplicationContext可以用来处理主题样式,它也知道如何找到相应的servlet。
67.什么是Spring MVC框架的控制器?
控制器提供对应用程序行为的访问,通常通过服务接口实现。控制器解析用户的输入,并将其转换为一个由视图呈现给用户的模型。Spring 通过一种极其抽象的方式实现控制器,它允许用户创建多种类型的控制器。
68.@Controllerannotation
@Controller注解表示该类扮演控制器的角色。Spring不需要继承任何控制器基类或应用ServletAPI。
69.@RequestMappingannotation
@RequestMapping注解用于将URL映射到任何一个类或者一个特定的处理方法上。
Apache Spiro
权限管理基础:理解常见权限系统的两个大部分(分配权限和验证权限)、理解安全实体和权限的含义、理解权限的继承性、理解权限的最近匹配原则等
Shiro入门:是什么、能干什么、有什么、高层概览架构、完整架构、HelloWorld
Shiro的配置:程序配置、ini配置的方式(包括各个部分的配置)、权限字符串方式等
Shiro的身份认证:认证示例、理解Remembered和Authenticated、理解认证的流程、多个Realm等
Shiro的授权:授权的要素和粒度、编程授权、注解授权、理解授权的流程、理解ModularRealmAuthorizer等
Shiro:Apache Shiro 是一个强大易用的Java安全框架,提供了认证、授权、加密和会话管理等功能
加密:以更简洁易用的方式使用加密的功能,保护或隐藏数据防止被偷窥
Realms:聚集一个或多个用户安全数据的数据源,并作为一个单一的复合用户“视图”。
启用单点登录(SSO)功能。为没有关联到登录的用户启用"RememberMe"服务
Shiro组件
Authentication:有时也简称为“登录”,这是证明用户是他们所说的他们是谁的行为。
Authorization:访问控制的过程,也就是绝对“谁”去访问“什么”。
SessionManagement:管理用户特定的会话,即使在非Web 或EJB 应用程序。
Cryptography:通过使用加密算法保持数据安全同时易于使用
---------------------------------------------------------------------------------------------------------------------
Web Support:Web 支持,可以非常容易的集成到Web 环境;
Caching:缓存,比如用户登录后,其用户信息、拥有的角色/权限不必每次去查,这样可以提高效率;
Concurrency:shiro 支持多线程应用的并发验证,即如在一个线程中开启另一个线程,能
把权限自动传播过去;
Testing:提供测试支持;
Run As:允许一个用户假装为另一个用户(如果他们允许)的身份进行访问;
Remember Me:记住我,这个是非常常见的功能,即一次登录后,下次再来的话不用登录了。
从应用程序角度看Shiro
1、应用代码通过Subject来进行认证和授权,而Subject又委托给SecurityManager;
2、我们需要给Shiro 的SecurityManager注入Realm,从而让SecurityManager能得到合法的用户及其权限进行判断
可以看到:应用代码直接交互的对象是Subject,也就是说Shiro的对外API核心就是Subject
其每个API的含义:
Subject:Subject 都绑定到SecurityManager,与Subject的所有交互都会委托SecurityManager;可以把Subject认为是一个门面;SecurityManager才是实际的执行者
SecurityManager:安全管理器;即所有与安全有关的操作都会与SecurityManager 交互;且它管理着所有Subject;它是Shiro 的核心,它负责与后边介绍的其他组件进行交互
Realm:域,Shiro从Realm获取安全数据(如用户、角色、权限),就是说SecurityManager要验证用户身份,那么它需要从Realm获取相应的用户判断用户身份是否合法;可以把Realm看成DataSource,即安全数据源。
Shiro不提供维护用户/权限,而是通过Realm让开发人员自己注入。
Shiro架构
Subject:主体,可以看到主体可以是任何可以与应用交互的“用户”;
SecurityManager :Shiro的心脏,相当于SpringMVC中的DispatcherServlet 或者Struts2 中的FilterDispatcher;所有具体的交互都通过SecurityManager进行控制;它管理着所有Subject、且负责进行认证和授权、及会话、缓存的管理。
Authenticator:认证器,负责主体认证的,这是一个扩展点,如果用户觉得Shiro 默认的不好,可以自定义实现;其需要认证策略(Authentication Strategy),即什么情况下算用户认证通过了;
Authrizer:授权器,或者访问控制器,用来决定主体是否有权限进行相应的操作;即控制着用户能访问应用中的哪些功能;
Realm:可以有1个或多个Realm,可以认为是安全实体数据源,即用于获取安全实体的;可以是JDBC 实现,也可以是LDAP实现,或者内存实现等等由用户提供;注意:Shiro不知道你的用户/权限存储在哪及以何种格式存储;所以一般在应用中都需要实现自己的Realm;
SessionManager: SessionManager用来管理Session的生命周期;而Shiro 并不仅仅可以用在Web环境,也可以用在如,普通的JavaSE 环境、EJB 等环境;所以Shiro就抽象了一个自己的Session来管理主体与应用之间交互的数据;方便实现自己的分布式会话(如把数据放到Memcached服务器);
SessionDAO:DAO数据访问对象,用于会话的CRUD。当我们需要把Session保存到数据库,那么可以实现自己的SessionDAO,通过如JDBC 写到数据库;比如想把Session 放到Memcached 中,可以实现自己的MemcachedSessionDAO;另外SessionDAO中可以使用Cache进行缓存,以提高性能;
CacheManager:缓存控制器,来管理如用户、角色、权限等的缓存的;因为这些数据基本
上很少去改变,放到缓存中后可以提高访问的性能
Cryptography:密码模块,Shiro 提供了一些常见的加密组件用于如密码加密/解密的。
认证与授权
身份认证:需要提供principal(身份标识)、credential(证明)
认证流程
流程如下:
1、首先调用Subject.login(token)进行登录,其会自动委托给Security Manager,调用之前必须通过SecurityUtils. setSecurityManager()设置;
2、SecurityManager负责真正的身份验证逻辑;它会委托给Authenticator进行身份验证;
3、Authenticator才是真正的身份验证者,Shiro API中核心的身份认证入口点,此处可以自定义插入自己的实现;
4、Authenticator可能会委托给相应的AuthenticationStrategy进行多Realm身份验证,默认ModularRealmAuthenticator会调用AuthenticationStrategy进行多Realm身份验证;
5、Authenticator 会把相应的token 传入Realm,从Realm 获取身份验证信息,如果没有返回/抛出异常表示身份验证失败了。此处可以配置多个Realm,将按照相应的顺序及策略进行访问。
程序配置
创建SecurityManger示例
Realm r = new IniRealm();
DefaultSecurityManager s = newDefaultSecurityManager(r);
SecurityUtils.setSecurityManager(s);
SecurityUtils.setSecurityManager 方法调用在一个VM 静态单例中实例化SecurityManager 实例
SecurityManger的对象图
SecurityManager 实现实质上是一个特定安全的嵌套组件中的模块化对象图。因为它们也是兼容JavaBean 的,你可以调用任何嵌套组件的getter和setter 方法来配置SecurityManager以及它的内部对象图。
例如,如果你想配置SecurityManager实例来使用自定义的SessionDAO 来定制
Session Management,你可以通过嵌套的SessionManager 的setSessionDAO 方
法直接设置SessionDAO:
SessionDAO sessionDAO = newMySessionDAO();
((DefaultSessionManager)s.getSessionManager()).setSessionDAO(sessionDAO);
Ini配置
Permission配置
Authentication
基本概念
1:Principals(身份):是Subject 的‘identifyingattributes(标识属性)’。比如我们登录提交的用户名。
2:Credentials(凭证):通常是只被Subject 知道的秘密值,它用来作为一种起支持作用的证据,此证据事实上包含着所谓的身份证明。比如我们登录提供的密码
使用用户名/密码的样例
UsernamePasswordToken token = newUsernamePasswordToken(username,
password);
token.setRememberMe(true);
样例使用UsernamePasswordToken来支持最常见的用户名/密码的身份验
证方法。这是Shiro的org.apache.shiro.authc.AuthenticationToken 的接口,是
Shiro 代表提交的Principals(身份)和Credentials(凭证)的身份验证系统所使用的
基本接口的一个实现。
提交用户名/密码进行认证
Subject currentUser =SecurityUtils.getSubject();
currentUser.login(token);
处理认证成功和失败
如果认证成功,会没有返回,也没有例外,通过。
如果认证失败,会拋出例外,你可以在程序中捕获并处理,如下示例:
try {
currentUser.login(token);
} catch ( UnknownAccountException uae ) { …
} catch ( IncorrectCredentialsException ice) { …
} catch (LockedAccountException lae ) { …
} catch (ExcessiveAttemptsException eae ) {…
} … catch your own …
logout(注销)
currentUser.logout();
当你调用logout,任何现有的Session 都将会失效,而且任何身份都将会失去关联
(例如,在Web 应用程序中,RememberMe cookie 也将被删除)。在Subject 注销后,该Subject的实例被再次认为是匿名的,当然,除了Web 应用程序。
注意:由于在Web 应用程序记住身份往往是依靠Cookies,然而Cookies 只能在
Response 被committed 之前被删除,所以强烈建议在调用subject.logout()后立即将终端用户重定向到一个新的视图或页面。
这样能够保证任何与安全相关的Cookies 都能像预期的一样被删除。这是HTTP
cookies 的功能限制,而不是Shiro的。
Remembered和Authenticated
Remembered(记住我)
一个记住我的Subject 不是匿名的,是有一个已知的身份ID(也就是subject.getPrincipals()是非空的)。但是这个被记住的身份ID 是在之前的session 中被认证的。如果subject.isRemembered()返回true,则Subject 被认为是被记住的。
Authenticated(已认证)
一个已认证的Subject 是指在当前Session 中被成功地验证过了(也就是说,login方法被调用并且没有抛出异常)。如果subject.isAuthenticated()返回
true 则认为Subject 已通过验证。
注意他们是互斥的
Remembered 和Authenticated 是互斥的——若其中一个为真则另一个为假,反之亦然。
认证流程
Step1:应用程序代码调用Subject.login 方法,传递创建好的包含终端用户的Principals(身份)和Credentials(凭证)的AuthenticationToken 实例。
Step2:Subject实例,通常是DelegatingSubject(或子类)委托应用程序的SecurityManager通过调用securityManager.login(token)开始真正的验证。
Step3:SubjectManager接收token 以及简单地委托给内部的Authenticator实例通过调用authenticator.authenticate(token)。这通常是一个ModularRealmAuthenticator 实例,支持在身份验证中协调一个或多个Realm 实例。
Step4:如果应用程序中配置了一个以上的Realm,ModularRealmAuthenticator实例将利用配置好的AuthenticationStrategy 来启动Multi-Realm 认证尝试。在Realms 被身份验证调用之前,期间和以后,AuthenticationStrategy 被调用使其能够对每个Realm 的结果作出反应。
Step5:每个配置的Realm 用来帮助看它是否支持提交的AuthenticationToken。如果支持,那么支持Realm 的getAuthenticationInfo 方法将会伴随着提交的token 被调用。getAuthenticationInfo 方法有效地代表一个特定Realm的单一的身份验证尝试。
Authenticator的职责是验证用户帐号,是Shiro API中身份验证核心的入口点:
publicAuthenticationInfoauthenticate(AuthenticationToken authenticationToken)
throwsAuthenticationException;
如果验证成功,将返回AuthenticationInfo验证信息;此信息中包含了身份及凭证;如果验
证失败将抛出相应的AuthenticationException实现。
SecurityManager接口继承了Authenticator,另外还有一个ModularRealmAuthenticator实现,
其委托给多个Realm 进行验证,验证规则通过AuthenticationStrategy 接口指定,默认提供
的实现:
FirstSuccessfulStrategy:只要有一个Realm验证成功即可,只返回第一个Realm身份验证
成功的认证信息,其他的忽略;
AtLeastOneSuccessfulStrategy:只要有一个Realm验证成功即可,和FirstSuccessfulStrategy
不同,返回所有Realm身份验证成功的认证信息;
AllSuccessfulStrategy:所有Realm验证成功才算成功,且返回所有Realm身份验证成功的
认证信息,如果有一个失败就失败了。
ModularRealmAuthenticator默认使用AtLeastOneSuccessfulStrategy策略。
授权
授权,也叫访问控制。关键对象:主体(Subject)、资源(Resource)、权限(Permission)、
角色(Role)。
Shiro支持的三种授权方式:
编程式:通过写if/else 授权代码块完成;
注解式:通过在执行的Java方法上放置相应的注解完成;
JSP/GSP标签:在JSP/GSP 页面通过相应的标签完成。
Realm
概述
Realm 是一个能够访问应用程序特定的安全数据(如用户、角色及权限)的组件。
Realm 通常和数据源是一对一的对应关系,如关系数据库,LDAP 目录,文件系统,或其他类似资源。Realm 实质上就是一个特定安全的DAO。
因为这些数据源大多通常存储身份验证数据(如密码的凭证)以及授权数据(如角色或权限),每个Realm能够执行身份验证和授权操作。
Realm示例代码
public class MyRealm extends AuthorizingRealm {
@Override
protected AuthorizationInfo doGetAuthorizationInfo(
PrincipalCollectionprincipals) {
StringuserName = (String) getAvailablePrincipal(principals);
// 通过用户名去获得用户的所有资源,并把资源存入info中
if (null!= userName && "oschina".equals(userName)){
throw newAuthenticationException("认证失败");
}
SimpleAuthorizationInfoinfo = new SimpleAuthorizationInfo();
Set<String>s = new HashSet<String>();
s.add("p1");
s.add("p2");
info.setStringPermissions(s);
Set<String>r = new HashSet<String>();
r.add("r1");
r.add("r2");
info.setRoles(r);
return info;
}
@Override
protected AuthenticationInfodoGetAuthenticationInfo(
AuthenticationTokentoken) throws AuthenticationException{
// token中储存着输入的用户名和密码
UsernamePasswordTokenupToken = (UsernamePasswordToken) token;
Stringusername = upToken.getUsername();
Stringpassword = String.valueOf(upToken.getPassword());
// 通常是与数据库中用户名和密码进行比对,这里就省略了
// 比对成功则返回info,比对失败则抛出对应信息的异常AuthenticationException
SimpleAuthenticationInfoinfo = newSimpleAuthenticationInfo(username,
password.toCharArray(),getName());
return info;
}
}
配置多个Realm
在配置文件里面添加Realm的定义
myRealm1=cn.javass.hello.MyRealm
myRealm2=cn.javass.hello.MyRealm2
多个realm需要配置AuthenticationStrategy,而AuthenticationStrategy是跟Authenticator(认证器)相关的。
配置Authenticator和AuthenticationStrategy
authenticator =org.apache.shiro.authc.pam.ModularRealmAuthenticator
authcStrategy =org.apache.shiro.authc.pam.AllSuccessfulStrategy
authenticator.authenticationStrategy =$authcStrategy
authenticator.realms=$myRealm2,$myRealm1
最后把Authenticator设置给securityManager
securityManager.authenticator =$authenticator
关于AuthenticationStrategy的配置,有三种:
AtLeastOneSuccessfulStrategy :如果一个(或更多)Realm 验证成功,则整体的尝试被认为是成功的。如果没有一个验证成功,则整体尝试失败。
FirstSuccessfulStrategy 只有第一个成功地验证的Realm 返回的信息将被使用。所有进一步的Realm 将被忽略。如果没有一个验证成功,则整体尝试失败
AllSucessfulStrategy 为了整体的尝试成功,所有配置的Realm 必须验证成功。如果没有一个验证成功,则整体尝试失败。
ModularRealmAuthenticator 默认的是AtLeastOneSuccessfulStrategy
自定义AuthenticationStrategy,通常扩展自AbstractAuthenticationStrategy,示例如下:
public classMyAuthenticationStrategy extendsAbstractAuthenticationStrategy {
@Override
public AuthenticationInfo afterAttempt(Realmrealm,
AuthenticationTokentoken, AuthenticationInfo singleRealmInfo,
AuthenticationInfoaggregateInfo, Throwable t)
throws AuthenticationException {
if (realm.getName().equals("myRealm2")) {
if (singleRealmInfo == null
||singleRealmInfo.getPrincipals() == null){
throw newAuthenticationException("主战认证未通过");
}
}
return super.afterAttempt(realm,token, singleRealmInfo, aggregateInfo,
t);
}
}
多个Realm的验证顺序
非常重要的一点是:ModularRealmAuthenticator将与Realm 实例以迭代的顺序进行交互。在SecurityManager 中已经配置好了ModularRealmAuthenticator对Realm实例的访问。当执行一个认证尝试时,它将会遍历该集合,并对每一个支持提交AuthenticationToken 的Realm 调用Realm 的getAuthenticationInfo 方法。
隐式排列
当你配置多个realm的时候,处理的顺序默认就是你配置的顺序。
这种情况通常就是只定义了realm,而没有配置securityManager的realms
显示排列
也就是显示的配置securityManager.realms,那么执行的顺序就是你配置该
值的realm的顺序。通常更推荐显示排列。
Authorization
概述
授权,又称作为访问控制,是对资源的访问管理的过程。换句话说,控制谁有权限在应用程序中做什么。授权检查的例子是:该用户是否被允许访问这个网页,编辑此数据,查看此按钮,或打印到这台打印机?这些都是决定哪些是用户能够访问的。
授权的三要素
授权有着三个核心元素:权限、角色和用户。我们需要在应用程序中对用户和权限建立关联,通常的做法就是将权限分配给某个角色,然后将这个角色关联一个或多个用户。
权限
是Shiro安全机制最核心的元素。它在应用程序中明确声明了被允许的行为和表现。一个格式良好的权限声明可以清晰表达出用户对该资源拥有的权限。
权限声明和粒度
在shiro中主要通过前面学过的通配符表达式来完成。
角色
角色是一个命名的实体,通常代表一组行为或职责。这些行为演化为你在一个软件应用中能或者不能做的事情。角色通常是分配给用户帐户的,因此,通过分配,用户能够“做”的事情可以归属于各种角色。
Shiro支持的角色类型
1:隐式角色:一个角色代表着一系列的操作,当需要对某一操作进行授权验证时,只需判断是否是该角色即可。这种角色权限相对简单、模糊,不利于扩展。
2:显式角色:一个角色拥有一个权限的集合。授权验证时,需要判断当前角色是否拥有该权限。这种角色权限可以对该角色进行详细的权限描述,适合更复杂的权限设计。Shiro官方推荐使用这种方式。
Shiro的三种授权方式
1:编写代码——在Java 代码中用像if 和else 块的结构执行授权检查。
2:JDK 的注解——你可以添加授权注解给你的Java 方法。
3:JSP/GSP 标签库——你可以控制基于角色和权限的JSP 或者GSP 页面输出。
编程授权
通过使用subject的方法来实现角色的判断,常见的api:
hasRole(String roleName) :返回true 如果Subject 被分配了指定的角色
hasRoles(List<String> roleNames) :返回一个与方法参数中目录一致的hasRole结果的数组。
hasAllRoles(Collection<String>roleNames):返回true 如果Subject 被分配了所有的角色
断言支持
Shiro还支持以断言的方式进行授权验证。断言成功,不返回任何值,程序继续执行;断言失败时,将抛出异常信息。方法大致如下:
checkRole(String roleName) 、checkRoles(Collection<String>roleNames)、checkRoles(String… roleNames)
基于权限对象的实现
Permission printPermission = newPrinterPermission("laser400n", "print");
相关方法:isPermitted(Permissionp)、isPermitted(List<Permission> perms)、isPermittedAll(Collection<Permission> perms)
基于字符串的实现
if(currentUser.isPermitted("printer:print:laserjet4400n"))
相关方法:isPermitted(Stringperm)、isPermitted(String...perms)、
isPermittedAll(String... perms)
当然上述权限的实现也都可以采用断言的方式相关方法:
checkPermission(Permission p)
checkPermission(String perm)
checkPermissions(Collection<Permission>perms)
checkPermissions(String... perms)
基于注解的授权
注解需要有AOP的支持
Shiro提供的注解
1:@RequiresAuthentication :要求当前Subject 已经在当前的session 中被验证通过才能被注解的类/实例/方法访问或调用。
2:@RequiresGuest :要求当前的Subject 是一个“guest”,也就是他们必须是在之前的session中没有被验证或记住才能被注解的类/实例/方法访问或调用。
3:@RequiresPermissions:要求当前的Subject 被允许一个或多个权限,以便执行注解的方法,比如:@RequiresPermissions("account:create")
4:@RequiresRoles:要求当前的Subject 拥有所有指定的角色。如果他们没有,则该方法将不会被执行,而且AuthorizationException 异常将会被抛出。比如:@RequiresRoles("administrator")
5:@RequiresUser:需要当前的Subject 是一个应用程序用户才能被注解的类/实例/方法访问或调用。要么是通过验证被确认,或者在之前session 中的'RememberMe'服务被记住。
授权顺序
Step1:应用程序或框架代码调用任何Subject的hasRole*,checkRole*, isPermitted*,或者checkPermission*方法的变体,传递任何所需的权限或角色
Step2:Subject的实例,通常是DelegatingSubject(或子类)代表应用程序的SecurityManager 通过调用securityManager的几乎各自相同的方法。
Step3:SecurityManager,实现org.apache.shiro.authz.Authorizer接口,他定义了所有Subject具体的授权方法。默认情况下,authorizer 实例是一个ModularRealmAuthorizer实例,它支持协调任何授权操作过程中的一个或多个Realm 实例。
Step4:每个配置好的Realm 被检查是否实现了相同的Authorizer接口。如果是,Realm 各自的hasRole*,checkRole*,isPermitted*,或checkPermission*方法将被调用。
Session管理
Shiro提供了一个完整的企业级Session解决方案,可以为任意应用(web和非web)提供Session支持。
基本使用
可以通过与当前执行的Subject 交互来获取Session:
Subject currentUser =SecurityUtils.getSubject();
Session session = currentUser.getSession();
session.setAttribute("someKey",someValue);
SessionManager
SessionManager是用来管理Session的组件,包括:创建,删除,inactivity(失效)及验证,等等。SessionManager 也是一个由SecurityManager维护的顶级组件。并且shiro提供了默认的SessionManager实现。
设置Sessioin的过期时间
Shiro 的SessionManager 实现默认是30 分钟会话超时。你可以设置SessionManager 默认实现的globalSessionTimeout属性来为所有的会话定义默认的超时时间。例如,
[main]
# 3,600,000 milliseconds = 1 hour
securityManager.sessionManager.globalSessionTimeout= 3600000
Sessioin的事件监听
你可以实现SessionListener 接口(或扩展易用的SessionListenerAdapter)并与相应的会话操作作出反应。配置示例:
[main]
aSessionListener =com.foo.my.SessionListener
anotherSessionListener = com.foo.my.OtherSessionListener
securityManager.sessionManager.sessionListeners= $aSessionListener,$anotherSessionListener
SessionDAO
概述
每当一个会话被创建或更新时,它的数据需要持久化到一个存储位置以便它能够被稍后的应用程序访问,实现这个功能的组件就是SessionDAO。
你能够实现该接口来与你想要的任何数据存储进行通信。这意味着你的会话数据可以驻留在内存中,文件系统,关系数据库或NoSQL 的数据存储,或其他任何你需要的位置。
基本配置
SessionDAO是作为一个属性配置在默认的SessionManager 实例上
[main]
sessionDAO = com.foo.my.SessionDAO
securityManager.sessionManager.sessionDAO =$sessionDAO
这种SessionDAO主要在本地应用中起作用。
基于EHCache的SessionDAO,基本配置如下:
[main]
sessionDAO = org.apache.shiro.session.mgt.eis.EnterpriseCacheSessionDAO
securityManager.sessionManager.sessionDAO =$sessionDAO
cacheManager =org.apache.shiro.cache.ehcache.EhCacheManager
securityManager.cacheManager =$cacheManager
Shiro提供了默认的EHCache的配置xml,如果你要配置自己的EHCache.xml,需要注意以下几点:
1:overflowToDisk=“true” - 这确保当你溢出进程内存时,会话不丢失且能够被序列化到磁盘上。
2: eternal=“true” - 确保缓存项(Session 实例)永不过期或被缓存自动清除。这是很有必要的,因为Shiro 基于计划过程完成自己的验证。如果我们关掉这项,缓存将会在Shiro 不知道的情况下清扫这些Sessions,这可能引起麻烦。
3:如果你想使用一个不同的名字而不是默认的,你可以在EnterpriseCacheSessionDAO 上配置名字,例如:sessionDAO.activeSessionsCacheName = myname只要确保在ehcahe.xml 中有一项与这个名字匹配
Web应用中的Session
在web应用上,默认使用的是容器的会话,如果你想基于Web 应用程序启用SessionDAO 来自定义会话存储或会话群集,你将不得不首先配置一个本地的Web
会话管理器。例如:
[main]
sessionManager=org.apache.shiro.web.session.mgt.DefaultWebSessionManager
securityManager.sessionManager =$sessionManager
# Configure a SessionDAO and then set it:
securityManager.sessionManager.sessionDAO =$sessionDAO
在web应用上,如果想要在每一个请求的基础上启用或禁用会话的创建,可以在配置中的[urls] 里面,为相应的url设置一个noSessionCreation过滤器,如下:
[urls]
/rest/** = noSessionCreation, authcBasic
自定义SessionDAO
在某些场景中,我们需要管理用户的Session信息,比如把Session信息放到数据库中,这样就可以记录一个操作日志,或是统计在线人员等等。自定义SessionDAO也非常简单,通常是继承AbstractSessionDAO,实现对Session数据的CRUD。
集成Spring
Maven
Maven基础
Maven可翻译为,知识的积累或者专家、内行
构建:除了编写源码的其他工作(编译、运行单元测试、生成文档、打包和部署)
M2eclipse:在Eclipse中通过传统的方式安装m2eclipse插件。除了核心插件外,m2eclipse提供了额外组件,如:
Maven scm handler for subversion,帮助我们直接从subverion服务器签出Maven项目
Maven scm integration,利用各种scm工具如svn实现maven项目的签出和具体化操作
Project configurators for commonly useedmaven plugin(temporary),一个临时组件用来支持一些maven插件和Eclipse的集成
M2Eclipse某些功能要求使用jdk,Eclipse默认使用的是jre
设置MAVEN_OPTS环境变量“-Xms128m –Xmx512m”,因为java默认最大的可用内存往往不能满足Maven的运行要求。
$m2_home/conf/setting.xml全局范围、~/.m2/setting.xml用户范围。使用用户级别的setting.xml配置文件灵活性强。
Maven项目jdk版本和编码:由于maven的历史原因maven 核心包默认只支持较低版本。以及windows默认使用GBK编码,java项目通常使用utf8。在编译中文时容易出现乱码
<plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-compiler-plugin</artifactId> <version>3.1</version> <configuration> <source>1.6</source> <target>1.6</target> <encoding>UTF8</encoding> </configuration> </plugin> </plugins> |
坐标和依赖
仓库
生命周期和插件
聚合与继承
Maven进行测试
使用Hudson进行持续集成
Maven构建Web应用
版本管理
灵活的构建
生成项目站点
Archetype
Struts,Spring,Hibernate三大框架的面试
1.Hibernate工作原理及为什么要用?
原理: 1.读取并解析配置文件 2.读取并解析映射信息,创建SessionFactory3.打开Session 4.创建事务Transation 5.持久化操作 6.提交事务 7.关闭Session 8.关闭SesstionFactory
为什么要用: 1. 对JDBC访问数据库的代码做了封装,大大简化了数据访问层繁琐的重复性代码。 2. Hibernate是一个基于JDBC的主流持久化框架,是一个优秀的ORM实现。他很大程度的简化DAO层的编码工作 3. hibernate使用Java反射机制,而不是字节码增强程序来实现透明性。 4. hibernate的性能非常好,因为它是个轻量级框架。映射的灵活性很出色。它支持各种关系数据库,从一对一到多对多的各种复杂关系。
2.Hibernate是如何延迟加载?
1. Hibernate2延迟加载实现:a)实体对象 b)集合(Collection)
2. Hibernate3 提供了属性的延迟加载功能当Hibernate在查询数据的时候,数据并没有存在与内存中,当程序真正对数据的操作时,对象才存在与内存中,就实现了延迟加载,他节省了服务器的内存开销,从而提高了服务器的性能。
3.Hibernate中怎样实现类之间的关系?(如:一对多、多对多的关系)
类与类之间的关系主要体现在表与表之间的关系进行操作,它们都市对对象进行操作,我们程序中把所有的表与类都映射在一起,它们通过配置文件中的many-to-one、one-to-many、many-to-many
4.Struts1流程:
1、客户端浏览器发出HTTP请求。2、根据web.xml配置,该请求被ActionServlet接收。3、根据struts-config.xml配置, ActionServlet先将请求中的参数填充到ActionForm中,然后ActionServlet再将请求发送到Action 进行处理。4、是否验证,需要验证则调用ActionForm的validate方法,验证失败则跳转到input,成功则继续。5、Action从ActionForm获得数据,调用javabean 中的业务方法处理数据。6、Action返回ActionForward对象,跳转到相应JSP页面或Action。7、返回HTTP响应到客户端浏览器。
MVC设计模式:modal:“模型” 也称业务逻辑,是正真完成任务的代码,相当与JavaBeanview:视图,其实就是显示界面,相当于JSPcontroller:控制器,他控制模型和视图的交互过程,相当于servletstruts1是基于MVC设计模式hibernate是基于ORM对象关系映射
5.struts是什么?
struts1是基于JSP和servlet的一个开源的Web应用框架,使用的是MVC的设计模式struts2是基于webwork技术的框架,是sun和webwork公司联手开发的一个功能非常齐全的框架,struts2和struts1没有任何关系,是一个全新的框架
6.spring是什么?
spring是一个集成了许多第三方框架的大杂烩,其核心技术是IOC(控制反转,也称依赖注入)和AOP(面向切面编程)
7.hibernate是什么?
hibernate是基于ORM对象关系映射(完成对象数据到关系数据映射的机制)实现的,做数据持久化的工具
8.JSF是什么?
JavaServer Face是基于组件的web开发框架,跟sturts差不多的框架
9.数据库里面的索引和约束是什么?
索引是为了提高数据的检索速度,索引是建立在数据表上,根据一个或多个字段建立的约束是为了保持数据的完整性,约束有非空约束,主键约束,外键约束等等。
10.spring是什么
这个问题,往往可以通过我们为什么要使用spring这个问题来切入:AOP 让开发人员可以创建非行为性的关注点,称为横切关注点,并将它们插入到应用程序代码中。使用 AOP 后,公共服务 (比 如日志、持久性、事务等)就可以分解成方面并应用到域对象上,同时不会增加域对象的对象模型的复杂性。 IOC 允许创建一个可以构造对象的应用环境,然后向这些对象传递它们的协作对象。正如单词 倒置所表明的,IOC 就像反 过来的 JNDI。没有使用一堆抽象工厂、服务定位器、单元素(singleton)和直接构造(straight construction),每一个对象都是用其协作对象构造的。因此是由容器管理协作对象(collaborator)。 Spring即使一个AOP框架,也是一IOC容器。 Spring 最好的地方是它有助于您替换对象。有了 Spring,只要用 JavaBean 属性和配置文件加入依赖性(协作对象)。然后可以很容易地在需要时替换具有类似接口的协作对象。
11.用自己的话简要阐述struts2的执行流程。
Struts 2框架本身大致可以分为3个部分:核心控制器FilterDispatcher、业务控制器Action和业务逻辑组件。
核心控制器FilterDispatcher是Struts 2框架的基础,包含了框架内部的控制流程和处理机制。业务控制器Action和业务逻辑组件是需要用户来自己实现的。用户在开发Action和业务逻辑组件的同时,还需要编写相关的配置文件,供核心控制器FilterDispatcher来使用。
Struts2基本简要流程如下:1、客户端浏览器发出HTTP请求。2、根据web.xml配置,该请求被FilterDispatcher接收。3、根据struts.xml配置,找到需要调用的Action类和方法, 并通过IoC方式,将值注入给Aciton。4、Action调用业务逻辑组件处理业务逻辑,这一步包含表单验证。5、Action执行完毕,根据struts.xml中的配置找到对应的返回结果result,并跳转到相应页面。6、返回HTTP响应到客户端浏览器。
Java经典算法面试题
Java 影响效率问题
1、 I/O 操作
2、 数据库连接、预编译sql、批量执行sql
3、 多线程并发、并行
4、 Dao模式抽象出数据访问层提高可读性
Eclipse环境配置
下载jee版,添加如下插件svn、maven、propeEdit、jsonedit、log4e、easyexploer
skpet、findbugs、jrebel、模板代码配置(Java-editor-templates)
Spring问题总结
1、 spring dom4j学习
2、 spring factory-method 、factory-beasn
3、 内部bean的配置
4、 spring 的命名空间
5、 spring 配置文件(*.xml )beans 内部内容
6、 java.util.collection、java.util.properties 接口学习
7、 spring 3.0 SpEL表达式
8、 @autowired @Inject创建自定义注解(@resource)
9、 spring 切点执行execute 已经确定了具体的包名,为什么还需要添加within指定包名 ?
10、 spring aop ?
Java问题总结
1、 梳理框架,osgi、ssh、ssi(springmvc + spring +mybatis+easyui)。
2、 学习Java其他高级特性(文件读写、线程、并发)
3、 学习webservice、xml、json、junit、maven、jquery、ajax
4、 研究web容器 weblogic、tomcat
5、 需要学习设计模式、uml
6、 多线程学习
7、 了解大数据,并谈谈自己的看法
8、 map,collection等学习
9、 算法、数据结构学习
10、 Java反射
11、 增加Java大型公司的面试题目练习
12、 Jse原理、Javaweb原理
13、 类图
新框架问题总结(spring、spring mvc、mybatis)
注:maven、log4j、json、junit4、
1、 pom.xml 的 type元素使用
2、 使用log4e 添加logger
3、 log4j添加JSON.toJSONString(object)打印日志信息
4、 mybatis 多个参数,多个属性判断,加强mybatis的学习,多表联合查询
5、 junit4 单元测试,重点降低后期维护成本。下载junit api文档。hamcrest,mock
6、 添加spring-test,通过junit测试spring相关代码
7、 spring aop 切入点、事务管理,使用注解注入方式
8、 spring mvc requestmapping 配置,时间类型传值,model重写toString
9、 springmvc、ajax、jquery、easyui、json、xml
10、 spring 结合mybatis 体系沉淀
11、 实现从无到有的开发过程,重点在于自己搭建框架,结合前端
12、 spring requestmapping 中返回类型为 void、modelmap,逻辑视图名取决于对应请求的 URL
13、 spring mvc 自定义属性编辑器
14、 Eclipse 提供类似myeclipse的xml文件提示
15、 缓存,rinzo 提示包
16、 Form表单提交文件上传乱码
17、 重点理解Java基础理论。具体的知识先放在一边(jdbc、hibernate...)
18、 自动装箱,自动拆箱。-128~127之间数据判断是否相等?(享元模式)word中使用频率最大
19、 Java日期、以及复杂的数据格式
20、 将抽象方法定义nextDay就将大量的if.else语句转换成了一个个独立的类,枚举,使用普通类模拟枚举类。
WeekDay sun = new WeekDay(){
@override
Public WeekDay nextDay(){
Return new WeekDay()....
}
}
21、 要有胡子
22、 要学以致用,增加使用知识解决问题的经验。
23、 增加系统分析知识
24、 Memcached、redis、HBase或MongoDB等NoSql开发技术
25、 学习的新知识要做足够的练习。
26、 异常处理
27、 翻页、集合、api、网络编程、线程、io、jdbc、异常、算法、设计模式、uml
28、 Static关键字学习
29、 基于二进制的权限设计
30、 单例模式的线程安全问题?
技术学习思路总结
1、线程和并发作为重点学习目标
2、Aop编程、I/O、集合、jdbc 不做深入学习熟练即可(待定)
3、Uml、设计模式、数据库设计解决方案、算法以及相关数据结构熟悉即可
4、Nosql hadoopmongodb openstack 熟悉即可
5、学习方法采用,视频+专业教材+实际问题练习
6、适当扩展jquery等前段web技术以及相关容器的使用
Hadoop作为技术扩展,学习了解不做深入研究。深入理解大数据大发展趋势
Java web、多线程、jquery、oracle、linux、ssi、osgi。满足招聘的60-80%要求ok
舍得,有舍才会有得。学会选择