------- android培训、java培训、期待与您交流! ----------
静态导入
格式:import static语句导入一个类中的某个静态方法或所有静态方法(静态导入在JDK1.5出现的)
可变参数
可以解决一个方法参数的个数不固定(JDK1.5出现)
可变参数的特点:
1、 只能出现在参数列表的最后
2、 …位于变量类型和变量名之间,前后有无空格都可以
3、 调用可变参数的方法时,编译器为该可变参数隐含创建一个数组,在方法体中以数组的形式访问可变参数。
JDK1.5的增强for
多行注释快捷键ctrl+shift+/
格式:for(Type 变量名:集合变量名){。。。。。。}
注意:
1、 迭代变量必须在()中定义
2、 集合变量可以是数组或实现了Iterable接口的集合类。
package cn.itcast.day1;
publicabstractclassWeekDay1 {
private WeekDay1(){}
publicfinalstatic WeekDay1 SUN = new WeekDay1(){
@Override
public WeekDay1 nextDay() {
// TODO自动生成的方法存根
returnMON;
}
};
publicfinalstatic WeekDay1 MON = new WeekDay1(){
@Override
public WeekDay1 nextDay() {
// TODO自动生成的方法存根
returnSUN;
}
};
publicabstract WeekDay1nextDay();
/*public WeekDay nextDay(){
if(this==SUN){
returnMON;
// }else{
returnSUN;
}
}*/
public String toString(){
returnthis==SUN?"SUN":"MON";
}
}
基本数据的自动拆箱及享元设计模式
基本数据自动装箱拆箱在基础编有,这里注意以下几点
1、 只要在byte类型以内的数(-128——127),在创建基本数据对象时,两个大小相同,视为相同
如:Integer i1=12;
Integeri2=12;
System.out.println(i1==i2);//结果为true
2、 这种模式叫享元模式(flyweight)
i.display(int x,int y)
枚举的作用介绍
为什么要有枚举?
枚举就是要让某个类型的变量的取值只能为若干个固定值中的一个,否则,编译器就会报错。
枚举可以让编译器在编译时就可以控制源程序中填写的非法值,普通变量的方法在开发阶段无法实现这一目标。
普通类如何实现枚举功能?
1、 私有的构造方法
2、 每个元素分别用一个公有的静态成员变量表示
3、 可以有若干公有方法或抽象方法。
枚举是一个特殊的类,其中每个元素都是该类的一个实例对象
。
基本枚举类
定义枚举关键字——enum
publicclassEnumTest {
publicstaticvoid main(String[] args) {
// TODO自动生成的方法存根
WeekDayweekDay2=WeekDay.FRI;
System.out.println(weekDay2);
System.out.println(weekDay2.name());
System.out.println(weekDay2.ordinal());
System.out.println(WeekDay.valueOf("SUN").toString());
System.out.println(WeekDay.values().length);
}
//模拟交通灯的枚举
publicenum TrafficLamp{
RED(30) {
@Override
public TrafficLamp nextLamp(){
// TODO自动生成的方法存根
returnGREEN;
}
},GREEN(45) {
@Override
public TrafficLamp nextLamp(){
// TODO自动生成的方法存根
returnYELLOW;
}
},YELLOW(5) {
@Override
public TrafficLamp nextLamp(){
// TODO自动生成的方法存根
returnRED;
}
};
publicabstractTrafficLamp nextLamp();
privateinttime;
//枚举中的构造方法
private TrafficLamp(int time){
this.time=time;
}
}
}
枚举只有一个成员时,就可以作为一种单例的实现方式。
package cn.itcast.day1;
publicclassReflectPoint {
privateintx;
publicinty;
public String str1="abll";
public String str2="basketball";
public String str3="itcast";
public ReflectPoint(int x, int y) {
super();
this.x = x;
this.y = y;
}
@Override
public String toString(){
returnstr1+":"+str2+":"+str3;
}
@Override
publicint hashCode() {
finalint prime = 31;
int result = 1;
result= prime *result + x;
result= prime *result + y;
return result;
}
@Override
publicboolean equals(Object obj) {
if (this == obj)
returntrue;
if (obj == null)
returnfalse;
if (getClass() !=obj.getClass())
returnfalse;
ReflectPointother = (ReflectPoint) obj;
if (x != other.x)
returnfalse;
if (y != other.y)
returnfalse;
returntrue;
}
}
透彻分析反射的基础
类Class:代表java程序中的各个java类的字节码,Class就是描述这类事物的java类名
得到字节码的方法:
1、 类名。class;
2、 对象。getClass;
3、 Class.forName(“java.lang.String”);//作用:返回字节码。返回方式:1、已经加载进jVM中的直接返回;2、JVM中没有则用类加载器加载,再返回。(做反射时用这种)
八个基本的数据类型分别对应的Class对象,还有个void也有对应的Class对象。共九个。
package cn.itcast.day1;
publicclassReflectTest {
publicstaticvoid main(String[] args) throws ClassNotFoundException{
// TODO自动生成的方法存根
Stringstr1="abc";
Classcls1=str1.getClass();
Classcls2=String.class;
Classcls3=Class.forName("java.lang.String");
System.out.println(cls1==cls2);//为true说明为同一份字节码
System.out.println(cls1==cls3);//为true说明为同一份字节码
System.out.println(cls1.isPrimitive());
System.out.println(int.class.isPrimitive());//是不是原始的字节码
System.out.println(int.class==Integer.class);//false
System.out.println(int.class==Integer.TYPE);//Integer.TYPE是所包装的原始的字节码,所以为true
System.out.println(int[].class.isPrimitive());//数组是不是原始类型(不是)
System.out.println(int[].class.isArray());//数组类型的Class实例对象用(是不是数组):Class.isArray();
}
}
总之,只要是在源程序中出现的类型,都有各自的Class实例对象,例如int[],void
理解反射的概念
反射就是把java类中的各种成分映射成相应的java类。
成员变量,方法,构造方法,包对应的类实例分别是Field、Method、Contructor、Package
构造方法反射应用
方法:getConstructors();得到所有的构造方法
getConstructor();得到某个构造方法。
//获取对应想要的构造方法
Constructorconstructor1=String.class.getConstructor(StringBuffer.class);//StringBuffer.class表示只接受一个的参数StringBuffer的构造方法
/*String.class.getConstructor(StringBuffer.class,int.class);//StringBuffer.class,int.class表示只接受两个参数一个StringBuffer类型另一个int类型的构造方法*/
//用反射创建一个String对象
Stringstr2 = (String)constructor1.newInstance(new StringBuffer("abc"));//new StringBuffer("abc")表示构造方法接受的参数
System.out.println(str2.charAt(2));
Class.newInstance()方法:
例子:String obj=(String)Class.forName(“java.lang.String”).newInstance();
该方法内部先得到默认的构造方法,然后用该方法创建实例对像。
成员变量的反射
ReflectPointpt1=newReflectPoint(3,5);//对ReflectPoint类的x(private)、y(public)变量赋值。
//对公有的变量反射
FieldfieldY=pt1.getClass().getField("y");
//fieldY的值是多少?是5,错!fieldY不是对象身上的值,而是类上,要用它去取某个对象上对应的值
System.out.println(fieldY.get(pt1));
//对私有的变量反射
FieldfieldX=pt1.getClass().getDeclaredField("x");
fieldX.setAccessible(true);
System.out.println(fieldX.get(pt1));
成员方法的反射
//str1.charAt(1);
MethodmethodCharAt=String.class.getMethod("charAt",int.class);//获取String类的charAt方法,这个方法接收的参数类型是int的
System.out.println(methodCharAt.invoke(str1,1));//invoke是运行这个获得的方法调用它的对象是str1,接收的参数是1. 当str1这个位置上时null那么对应的是静态方法,因为他不需要对象调用
jdk1.4方法调用
System.out.println(methodCharAt.invoke(str1,new Object[]{2}));
对接收数组参数的成员方法进行反射
//TestArguments.main(newString[]{"3","3","3"});
StringstartingClassName = args[0];//《1》
Method mainMethod=Class.forName(startingClassName).getMethod("main", String[].class);
mainMethod.invoke(null, (Object)new String[]{"11","2","3"});//《2》
《1》、此时数组为null,因为主函数中没有传入要执行的类。所以要主函数传值,传入的这个值正好还是一个类。然后下面可以获得这个类的main方法,并进行操作。
《2》、(Object)此处有一个知识点,因为new String[]{"11","2","3"}这个根据jdk1.4特性会被拆包一次,因为我们是传入的数组,不想让他拆包,所以加个强转(Object);也可以在加一层包new Object[]{new String[]{"11","2","3"}}去真的让他去拆这个包,剩下的就是我们要传入的数组了。
在eclipse中如何传值的:右击—>run As——>运行配置——>自变量——>程序自变量下填入完整的类名就行了。
数组与Object的关系及其反射类型
每一个具有相同的元素类型以及具有相同的维数的数组都是同一个class.
int[] a1=newint[]{1,2,3};
int[] a2=newint[4];
int[][] a3=newint[2][3];
String[]a4=newString[]{"a","b","c"};
System.out.println(a1.getClass()==a2.getClass());
// System.out.println(a1.getClass()==a4.getClass());
// System.out.println(a1.getClass()==a3.getClass());
System.out.println(a1.getClass().getName());
System.out.println(a1.getClass().getSuperclass().getName());//获得父类名字
System.out.println(a4.getClass().getSuperclass().getName());//获得父类名字
/*可知String数组和int数组的父类都是Object.所以下面*/
ObjectaObj1=a1;
ObjectaObj2=a4;
//Object[] aObj3=a1;错误,Object数组中放的应该是对象,int数组中的元素不是数组,所以就不能这样赋值。
Object[]aObj4=a3;//a3是个二维数组,数组中放数组,也就是可以看做数组中放的是对象,所以可以这样赋值。
Object[]aObj5=a4;
System.out.println(Arrays.asList(a1));//把数组变成list集合,因为集合中放的是对象,所以打印的是数组的地址
System.out.println(Arrays.asList(a4));//把数组变成list集合,因为集合中放的是对象,又String中的元素都是对象,所以存放到list集合中的是String数组的元素,打印的自然是数组中的元素
数组的反射应用
printObject("xyz");
printObject(a4);
}
privatestaticvoidprintObject(Object obj) {
//TODO自动生成的方法存根
Classclazz=obj.getClass();
if(clazz.isArray()){//如果是数组
int len=Array.getLength(obj);
for(int i=0;i<len;i++){
System.out.println(Array.get(obj,i));
}
}else{//不是就直接打印
System.out.println(obj);
}
}
ArrayList、HashSet的比较及Hashcode分析
ArrayList是有顺序的集合,其中放的对象的引用。
Hashcode值是通过内存地址换算过了的
哈希算法是将集合分成若干个存储区域,每个对象可以计算出一个哈希码,可以将哈希码分组,每组分别对应某个存储区域,根据一个对象的哈希码就可以确定该对象应该存储在那个区域。
当从HashSet集合中查找某个对象时,java系统首先调用对象的hashCode方法获得该对象的哈希码,然后根据哈希码找到相应的的存储区域,最后取出该存储区域内的每个元素与对象进行equals方法比较,这样不用遍历集合中的所有元素就可以得到结论。
当一个对象被存储进HashSet集合中以后,就不要修改这个对象中的那些参与运算哈希值的字段了,否则,对象修改后的哈希值与最初存储进HashSet集合中时的哈希值就不同了,在这种情况下,即使在contains方法使用该对象的当前引用作为的参数去HashSet集合中检索对象,也将返回找不到对象的结果,这会导致无法从HashSet集合中单独删除当前对象,从而造成内存泄露。
publicstaticvoid main(String[] args) {
//TODO自动生成的方法存根
Collectioncollections=newHashSet();
ReflectPointpt1=new ReflectPoint(3,3);
ReflectPointpt2=newReflectPoint(5,5);
ReflectPointpt3=newReflectPoint(3,3);
collections.add(pt1);
collections.add(pt2);
collections.add(pt3);
collections.add(pt1);
pt1.y=7;
System.out.println(collections.remove(pt1));//由于t1.y=7;所以在这就删除不了,
System.out.println(collections.size());
}
}
框架概念及用反射技术开发框架的原理
新建文件config。properties文件,内容为:className=java.util.HashSet
InputStream ips=new FileInputStream("config.properties");
Propertiesprops=newProperties();
props.load(ips);//从输入流中读取属性列表(键和元素对)。
ips.close();
StringclassName = props.getProperty("className");
Collectioncollections=(Collection)Class.forName(className).newInstance();//调用了不带参数的构造方法
//Collection collections=new HashSet();
ReflectPointpt1=newReflectPoint(3,3);
ReflectPointpt2=newReflectPoint(5,5);
ReflectPointpt3=newReflectPoint(3,3);
collections.add(pt1);
collections.add(pt2);
collections.add(pt3);
collections.add(pt1);
System.out.println(collections.size());
框架与框架要解决的核心问题:
工具类被用户的类调用,而框架则是调用用户提供的类。
因为在写程序时无法知道要被调用的类名,所以,在程序张无法直接new某个类的实例对象,而要用反射方式来做。
用类加载器的方式管理资源和配置文件
配置文件存位置放问题
1、 绝对路径,这个路径是是以某种方式get出来的(比如软件安装路径要用户输入)
一定要记住用完整的路径,但完整的路径不是硬编码,而是运算出来的
2、 用类加载器加载
eclipse会把源目录下的所有。java文件自动编译成。class然后放到classPath指定的目录下去,把非,java文件原封不动的搬到classPath目录下去。
//InputStream ips=newFileInputStream("config.properties");
/*一下两种方式只读,没有写入方法*/
InputStreamips=ReflectTest2.class.getClassLoader().getResourceAsStream("cn/itcast/day1/config.properties");// getClassLoader()获得类加载器;.getResourceAsStream()加载要加载的文件。这里的路径是相对路径最前面不加“/”.
//简化不用类加载器,类文件直接有加载配置文件的功能getResourceAsStream()不过这里不用写路径只要配置文件名就可以了,因为它给类在同一目录级别里。或者不是放在classPath目录下,那么就要用绝对路径,也就是最前面带/然后加路径名加文件名。
InputStream ips=ReflectTest2.class.getResourceAsStream("config.properties");
对javaBean的简单内省操作
与反射有关的内省。
/*获取值*/
StringpropertyName="x";
PropertyDescriptorpd=newPropertyDescriptor(propertyName,pt1.getClass());//PropertyDescriptor对属性描述的一个类;从这个javaBean类--pt1.getClass()中获得属性名为propertyName的属性。
MethodmethodGetX=pd.getReadMethod();//getReadMethod()得到这个属性的读的方法也就是javaBean中的这个属性的get方法
ObjectretVal=methodGetX.invoke(pt1);
System.out.println(retVal);
/*设置值*/
MethodmethodSetX=pd.getWriteMethod();//getWriteMethod()得到这个属性的写的方法也就是javaBean中的这个属性的set方法
methodSetX.invoke(pt1,7);//运行这个方法
与上面方法比较javaBean的复杂内省操作
采用遍历BeanInfo的所有属性方式来查找和设置某个RefectPoint对象的x属性。在程序中把一个类当做javaBean来看,就是调用IntroSpector.getBeanInfo方法,得到的BeanInfo对象封装了把这个类当做javaBean看的结果信息。
privatestaticObjectgetProperty(Object pt1, String propertyName)
throws IntrospectionException,IllegalAccessException,
InvocationTargetException{
/*PropertyDescriptorpd=newPropertyDescriptor(propertyName,pt1.getClass());//PropertyDescriptor对属性描述的一个类;从这个javaBean类--pt1.getClass()中获得属性名为propertyName的属性。
MethodmethodGetX=pd.getReadMethod();//getReadMethod()得到这个属性的读的方法也就是javaBean中的这个属性的get方法
ObjectretVal=methodGetX.invoke(pt1);*/
/*用复杂的操作实现上面的想要的结果*/
BeanInfobeanInfo=Introspector.getBeanInfo(pt1.getClass());//.getBeanInfo(pt1.getClass());把java类当做javaBean来看,会看出yield结果来
PropertyDescriptor[]pds=beanInfo.getPropertyDescriptors();//.getPropertyDescriptors()得到所有的属性描述
ObjectretVal=null;
for(PropertyDescriptorpd:pds){
if(pd.getName().equals(propertyName)){//查找到属性名与要修改或查找的属性名相同时
MethodmethodGetX=pd.getReadMethod();//获得这个属性的读的方法
retVal=methodGetX.invoke(pt1);
break;
}
}
return retVal;
}
使用BeanUtils工具包操作JavaBean
首先把BeanUtils。jar包和logging。jar包导进来。
//用阿帕奇提供的BeanUtils工具包,获取javaBean属性值
System.out.println(BeanUtils.getProperty(pt1,"x"));
//用阿帕奇提供的BeanUtils工具包,设置javaBean属性值
BeanUtils.setProperty(pt1,"x","9");//"9"此处注意是字符串,会自动转换,不想让他转或转换错误就用下面的。
System.out.println(pt1.getX());
PropertyUtils.setProperty(pt1,"x",9); 9此处注意是int
System.out.println(PropertyUtils.getProperty(pt1,"x").getClass());
----------------------------------------------
/*publicclassReflectPoint {
private Datebrithday=new Date();
public Date getBrithday() {
returnbrithday;
}
publicvoid setBrithday(Datebrithday) {
this.brithday = brithday;
}*/
BeanUtils.setProperty(pt1,"brithday.time","1111");
System.out.println(BeanUtils.getProperty(pt1,"brithday.time"));
jdk1.7
Map map=(name:"zxx",age:"18");
BeanUtils.setProperty(map,"name","1hm");
了解和入门注解的应用
注解相当于一种标记,在程序中加了注解就等于为程序打上了某种标记,没加,则等于没有某种标记,以后,java编译器,开发工具和其他程序可以用反射来了解你的类及各种元素上有无何种标记,有什么样的标记就做什么样的事
标记可以加在包,类,字段,方法的参数以及局部变量上。
@Deprecated//使方法过时的注解(标记某个方法过时)
publicstaticvoidsayHello(){
System.out.println("itcast");
}
@SuppressWarnings("deprecation")//使过时的方法不再在编译时提示过时的注解
publicstaticvoid main(String[] args) {
//TODO自动生成的方法存根
System.runFinalizersOnExit(true);
}
@Override//提示错吴,是不是对父类的覆盖
publicboolean equals(Object obj) {
if (this == obj)
returntrue;
if (obj ==null)
returnfalse;
if (getClass() !=obj.getClass())
returnfalse;
ReflectPointother = (ReflectPoint) obj;
if (x != other.x)
returnfalse;
if (y != other.y)
returnfalse;
returntrue;
}
注解的定义与反射调用
注解就相当于一个你的源程序中要调用的一个类,要在源程序中应用某个注解,的先准备好了这个注解类,就像你要调用某个类,得先有开发好的这个类。
@ReTention元注解,其三种取值:RetetionPolicy.SOURCE、RetentionPolicy.CLASS、RetetionPolicy.RUNTIME;分别对应:java源文件——》class文件——》内存中的字节码。时去除注解。(默认值在CLASS阶段)
@Retention(RetentionPolicy.RUNTIME)//这个叫元注解(注解的注解);这个注解说明注解一直保留到运行期间
public@interfaceItcastAnnotation{
}
--------------------------------------------------------------
@ItcastAnnotation
publicclassAnnotationTest {
@SuppressWarnings("deprecation")//使过时的方法不再在编译时提示过时
publicstaticvoid main(String[] args) {
//TODO自动生成的方法存根
System.runFinalizersOnExit(true);
if(AnnotationTest.class.isAnnotationPresent(ItcastAnnotation.class)){//在AnnotationTest类里是否存在ItcastAnnotation注解,存在返回true
ItcastAnnotationannotation=(ItcastAnnotation)AnnotationTest.class.getAnnotation(ItcastAnnotation.class);//从AnnotationTest获取ItcastAnnotation注解类对象
System.out.println(annotation);
}
}
为注解增加各种属性
《1》注解的属性名为value并且只有这一个属性需要设置值时
@Retention(RetentionPolicy.RUNTIME) public@interfaceItcastAnnotation {
String color() default"blue";
String value();//如果此处只需要value属性设值(color设置了默认值不需要设值),那么下面。
}
------------
@ItcastAnnotation("name")//为注解设置属性可以不需要(value=”name”)这样写,如果属性名不是value,或者多个值需要设值得话还是需要(value=”name”)写的
publicclassAnnotationTest {
@SuppressWarnings("deprecation")//使过时的方法不再在编译时提示过时
publicstaticvoid main(String[] args) {
//TODO自动生成的方法存根
System.runFinalizersOnExit(true);
if(AnnotationTest.class.isAnnotationPresent(ItcastAnnotation.class)){//在AnnotationTest类里是否存在ItcastAnnotation注解,存在返回true
ItcastAnnotation annotation=(ItcastAnnotation)AnnotationTest.class.getAnnotation(ItcastAnnotation.class);//从AnnotationTest获取ItcastAnnotation注解类对象
System.out.println(annotation.color());
}
}
《2》为数组设置值
@ItcastAnnotation(value="name",arrayAttr={1,2,3},arrayAttr1=1)//给注解中的数组设置属性时,如果数组属性中只有一个元素,这时属性值部分可以省略大括号:如上arrayAttr1
publicclassAnnotationTest {
--------------------------------------------------------------@Retention(RetentionPolicy.RUNTIME)//这个叫元注解(注解的注解);这个注解说明注解一直保留到运行期间
public@interfaceItcastAnnotation{
String color() default"blue";
String value();
int[] arrayAttr()default {3,4,5};
int[] arrayAttr1();
}
==============================================================
《3》枚举类型的属性
public@interfaceItcastAnnotation{
String color() default"blue";
String value();
int[] arrayAttr()default {3,4,5};
int[] arrayAttr1();
EnumTest.TrafficLamp lamp() default numTest.TrafficLamp.RED;//定义一个枚举类EnumTest.TrafficLamp对象lamp()默认值为numTest.TrafficLamp.RED
}
-------------------------------------------------------------
@ItcastAnnotation(value="name",arrayAttr={1,2,3},arrayAttr1=1)//给注解中的数组设置属性,如果数组属性中只有一个元素,这时属性值部分可以省略大括号
publicclassAnnotationTest {
@SuppressWarnings("deprecation")//使过时的方法不再在编译时提示过时
//@ItcastAnnotation("blue")
publicstaticvoid main(String[] args) {
//TODO自动生成的方法存根
System.runFinalizersOnExit(true);
if(AnnotationTest.class.isAnnotationPresent(ItcastAnnotation.class)){//在AnnotationTest类里是否存在ItcastAnnotation注解,存在返回true
ItcastAnnotation annotation=(ItcastAnnotation)AnnotationTest.class.getAnnotation(ItcastAnnotation.class);//从AnnotationTest获取ItcastAnnotation注解类对象
System.out.println(annotation.color());
System.out.println(annotation.value());
System.out.println(annotation.arrayAttr().length);
System.out.println(annotation.lamp().nextLamp().name());调用注解对象annotation的lamp属性,因为这个属性时枚举所以,有调用了这个枚举的.nextLamp()方法(因为这个方法返回的也是那个枚举的元素)所以可以打印name。
}
}
==============================================================================
《4》注解类型的属性:
public@interfaceMetaAnnotation{
Stringvalue();
}
--------------------------------------------------------------------------------------------------------------------------------
@Retention(RetentionPolicy.RUNTIME)//这个叫元注解(注解的注解);这个注解说明注解一直保留到运行期间
public@interfaceItcastAnnotation{
String color() default"blue";
String value();
int[] arrayAttr()default {3,4,5};
int[] arrayAttr1();
EnumTest.TrafficLamp lamp() default EnumTest.TrafficLamp.RED;
MetaAnnotationannotationAttr()default @MetaAnnotation("lhm");//为注解属性从新赋值
}
---------------------------------------------------------------------------------------------------------------------------------
@ItcastAnnotation(annotationAttr=@MetaAnnotation("flx"),value="name",arrayAttr={1,2,3},arrayAttr1=1)//给注解中的数组设置属性,如果数组属性中只有一个元素,这时属性值部分可以省略大括号
publicclassAnnotationTest {
@SuppressWarnings("deprecation")//使过时的方法不再在编译时提示过时
//@ItcastAnnotation("blue")
publicstaticvoid main(String[] args) {
//TODO自动生成的方法存根
System.runFinalizersOnExit(true);
if(AnnotationTest.class.isAnnotationPresent(ItcastAnnotation.class)){//在AnnotationTest类里是否存在ItcastAnnotation注解,存在返回true
ItcastAnnotation annotation=(ItcastAnnotation)AnnotationTest.class.getAnnotation(ItcastAnnotation.class);//从AnnotationTest获取ItcastAnnotation注解类对象
System.out.println(annotation.color());
System.out.println(annotation.value());
System.out.println(annotation.arrayAttr().length);
System.out.println(annotation.lamp().nextLamp().name());
System.out.println(annotation.annotationAttr().value());
}
}
入门泛型的基本应用
JDK1.5
没有使用泛型时,只要是对象,不管是什么类型的对象,都可以存储进同一个集合中,使用泛型集合,可以将一个集合中的元素限定为一个特定类型,集合中只能存储同一个类型的对象,这样更安全,并且从集合获取一个对象时,编译器也可以知道这个对象的类型,不需要对对象进行强制类型转换,这样更方便。
泛型的内部原理及更深应用
泛型是提供给javac编译器使用的,可以限定集合中的输入类型,编译器编译带类型说明的集合时回去除掉类型信息。,是程序运行效率不受影响。叫去类型化
通过反射去类型化然后添加元素
ArrayList<Integer> collection3=newArrayList<Integer>();
System.out.println(collection3.getClass()==collection1.getClass());
//collection3.add("abc");
collection3.getClass().getMethod("add", Object.class).invoke(collection3,"abc");
System.out.println(collection3.get(0));
泛型所涉及到的术语:
如:ArrayList<E>类定义和ArrayList<Integer>
1、整个成为ArrayList<E>泛型类型
2、ArrayList<E>中的E称为类型变量或类型参数
3、整个ArrayList<Integer>称为参数化的类型
4、ArrayList<Integer>中的Integer称为类型参数的实例或实际类型参数
5、ArrayList<Integer>中的<>念typeof
6、ArrayList称为原始类型
参数化类型不考虑继承关系
Vector<String> v=newVector<Object>();
Vector<Object> v=new Vector<String> ();
这两个都错。
在创建数组实例时,数组的元素不能使用参数化的类型
Vector<Integer> vectorList[] = newVector<Integer>[10];
错误。
泛型的通配符扩展应用
publicstaticvoidprintCollection(Collection<?> collection){
collection.add(1)//使用通配符不可以调用与类型有关系的方法,因为,不知道它里面装的什么类型。此处错误,1自动转型Integer,如果传入的是String类型的对象呢?
System.out.println(collection.size());
for(Objectobj:collection){
System.out.println(obj);
}
}
总结:使用?通配符可以引用其他各种参数化的类型,?通配符定义的变量主要用作引用,可以调用与参数化无关的方法,不能调用与参数化无关的方法。
限定通配符的上边界:
正确:Vector<? extedent Number> x=new Vector<Integer>();
因为r<? extedent Number>接收的是Number类及其子类对象,Integer是Number的子类
限定符的下边界:
正确:Vector<? super Integer> x=newVector<Number>();.
因为<? super Integer>接收的是Integer类及其父类对象。
泛型集合的综合应用案例
Entry是Map的一个内部类,所以是Map.Entry
HashMap<String,Integer> maps=newHashMap<String,Integer>();
maps.put("zxx", 28);
maps.put("lhm", 35);
maps.put("flx", 33);
Set<Map.Entry<String,Integer>>entrySet=maps.entrySet();
for(Map.Entry<String,Integer> entry:entrySet){
System.out.println(entry.getKey()+":"+entry.getValue());
}
自定义泛型方法及其应用
用于放置泛型的类型参数的尖括号应出现方法的其他所有修饰符之后和在方法的返回类型之前,也就是紧邻返回值之前,按照惯例,类型参数通常用单个大写字母表示。如下红字T
add(3,5);//3和5都是int,泛型的类型为int;这里自动装箱了,因为下面红字
Numberd=add(3.5,3);//3.5是double;3为int,那么他们的返回就是他们的上级Number
Objecto=add(3,"abc");//3为int,"abc"为String,那么int和String的上级就是Object
}
privatestatic <T> T add(T i, T j) {
returnnull;
//TODO自动生成的方法存根
}
------------------------------------------------------
swap(new String[]{"abc","xyz","itcast"},1,2);
//swap(newint[]{1,3,5,4,5},3,4);//错误,(数组中的元素无法自动装箱)。因为下面红字
swap(newInteger[]{1,3,5,4,5},3,4);//这个就正确
}
privatestatic <T>void swap(T[] a,int i,int j){
Ttmp=a[i];
a[i]=a[j];
a[j]=tmp;
}
只有引用类型才能作为泛型方法的实际参数
在泛型中可以同时有多个类型参数,在定义他们的尖括号中用逗号分开,例如:
public static <K,V> V getValue {
return map.get(key);
}
自定义泛型类的应用
如果类的是对象中的多处都要用到同一个泛型参数,即这些地方引用的泛型类型保持同一个实际类型时,这时候就要采用泛型类的方法进行定义,也就是类级别的泛型
publicclassGenericDao<E> {
publicvoid add(E x) {
}
public E findById(int id){
returnnull;
}
publicvoid delete(E obj){
}
publicvoid update(E obj){
}
publicE findByUserName(String name){
returnnull;
}
public Set <E>findByConditions(String where){
returnnull;
}
}
类级别的泛型是根据引用该类名是指定的类型信息来参数化类型变量的。
注意:
在对泛型类型进行参数化时,类型参数的实例必须是引用类型,不能是基本类型
当一个变量被声明为泛型时,只能被实例变量和方法调用(还有内嵌类型),而不能被静态变量和静态方法调用。因为静态成员是被所有参数化的类所共享,所以静态成员不应该有类级别的类型参数。
privatestatic<T>voidfillArray(T[] a,T obj){
for(int i=0;i<a.length;i++){
a[i]=obj;
}
}
如果只有一个方法需要使用泛型,应使用方法级别的;多个方法需要使用泛型,使用类级别的。
通过反射获得泛型的实际类型参数
Method applyMethod=GenericTest.class.getMethod("applyVector",Vector.class);//得到方法
Type[]types=applyMethod.getGenericParameterTypes();//.getGenericParameterTypes()得到参数额泛型类
ParameterizedTypepType=(ParameterizedType)types[0];
System.out.println(pType.getRawType());//.getRawType()得到原始的类型、
System.out.println(pType.getActualTypeArguments()[0]);//.getActualTypeArguments()实际化的类型参数数组。
}
----------------------------------------------------------
publicstaticvoidapplyVector(Vector<Date> v1){
}
类加载器及其委托机制的深入分析
类加载器:加载类的工具。
java虚拟机中可以安装多个类加载器,系统默认三个主要类加载器,每个类加载器负责特定位置的类:BootStrap、ExcClassLoader、AppClassLoader
第一个类加载器是BootStrap。
java虚拟机中的所有类加载器采用具有父子关系的树形结构进行组织,在实际化每个类加载器对象时,需要为其指定一个父级类装载器对象或者,默认采用系统类装载器为其父级类加载。
publicstaticvoidmain(String[] args) {
System.out.println(ClassLoaderTest.class.getClassLoader().getClass().getName());//.getClassLoader()获得类加载器
System.out.println(System.class.getClassLoader());
}
类加载器的委托加载机制
写自己的类加载器必须继承ClassLoader
当java虚拟机要加载一个类时,到底派出哪个类加载器去加载呢?
1、 首先当前线程的类加载器去加载线程的第一个类
2、 如果类A中引用了类B,java虚拟机将使用加载类A的加载器去加载类B
3、 还可以直接调用ClassLoader。loadClass()方法来指定某个类加载器去加载某个类。
每个类加载器加载类时,又先委托某上级类加载器。
1、 当所有祖宗类加载器没有加载到类,回到发起者类加载器,还加载不了,则抛出ClassNotFoundException,不是再去找发起者类加载器的子类加载器。
自定义类加载器的编写原理分析
ClassLoader类中有loadClass(String name);
loadClass首先找父类,找完父类,再用本类的findClass找。
也就是说loadClass里有委托机制,如果重写了这个方法,那么久不委托父类了(除非你自己要在这个loadClass中写委托父类的方法),而是自己加载。所以在写类加载器时,如果需要委托机制,只需继承ClassLoader覆写findClass即可。
得到class文件的转换成字节码用——》definClass();方法。
分析代理类的作用与原理及AOP概念
编写一个与目标类具有相同接口的代理类,代理类的每个方法调用目标类的相同方法,并在调用方法时加上系统功能的代码
如果采用工厂模式和配置文件的方式进行管理,则不需要修改客户端程序,在配置文件中配置时使用目标类、还是代理类,这样以后横容易切换。
面向方面编程
JVM可以在运行期动态生成出类的字节码,这种动态生成往往被用作代理类,及动态代理。
JVM生成的动态类必须实现一个或多个接口,所以,JVM生成的动态类只能用作具体相同接口的目标类的代理。
CGLIB库可以动态生成一个类的子类,一个类的子类也可以用作该类的代理,所以,如果要为一个没有实现接口的类生成代理的类,那么可以使用CGLIB库。
代理类的各个方法中通常除了要调用目标的相应方法和对外返回目标返回的结果外,还可以在代理方法中的如下四个位置上系统功能代码:
1、 在调用目标方法之前
2、 在调用目标方法之后
3、 在调用目标方法前后
4、 在处理目标方法异常的catch块中
创建动态类及查看其方法列表信息
java。lang。reflect。Proxy类
其中有方法:getProxyClass(ClassLoader loader,Class<?>… interfaces)
指定类加载器,和实现的接口
package cn.itcast.day3;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.Collection;
publicclassProxyTest {
publicstaticvoid main(String[] args) {
ClassclazzProxy1 = Proxy.getProxyClass(Collection.class.getClassLoader(), Collection.class);//自动生成的代理类,类加载器为Collection.class.getClassLoader(),接口为Collection.class
System.out.println(clazzProxy1.getName());
System.out.println("-----------------begin Constructorlist--------");
Constructor[]constructors=clazzProxy1.getConstructors();//获取这个类的所有构造方法
for(Constructorconstructor:constructors){
Stringname=constructor.getName();
StringBuildersBuilder=newStringBuilder(name);//构造方法的名字
sBuilder.append('(');
Class[]clazzParams=constructor.getParameterTypes();//构造方法的参数类型
for(ClassclazzParam:clazzParams){
sBuilder.append(clazzParam.getName()).append(',');
}
if(clazzParams!=null&&clazzParams.length!=0)
sBuilder.deleteCharAt(sBuilder.length()-1);
sBuilder.append(')');
System.out.println(sBuilder.toString());
}
System.out.println("-----------------begin methods list--------");
Method[]methods=clazzProxy1.getMethods();
for(Method method:methods){
Stringname=method.getName();
StringBuildersBuilder=newStringBuilder(name);
sBuilder.append('(');
Class[]clazzParams=method.getParameterTypes();
for(ClassclazzParam:clazzParams){
sBuilder.append(clazzParam.getName()).append(',');
}
if(clazzParams!=null&&clazzParams.length!=0)
sBuilder.deleteCharAt(sBuilder.length()-1);
sBuilder.append(')');
System.out.println(sBuilder.toString());
}
}
}
这段代码自动创建的代理类的构造方法是sun.proxy.$Proxy0(java.lang.reflect.InvocationHandler)
接受的参数对象时InvocationHandler的对象。
创建动态类的实例对象及调用其方法
InvocationHandler是一个接口,在调用对象时无法传参为InvocationHandler的对象,只能是其子类对象。
完成InvocationHandler对象的内部功能
package cn.itcast.day3;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.ArrayList;
import java.util.Collection;
publicclassProxyTest {
publicstaticvoid main(String[] args)throws Exception,SecurityException {
ClassclazzProxy1 = Proxy.getProxyClass(Collection.class.getClassLoader(), Collection.class);//自动生成的代理类,类加载器为Collection.class.getClassLoader(),接口为Collection.class
System.out.println(clazzProxy1.getName());
System.out.println("-----------------begin create instancelist--------");
System.out.println("方法一--------");
Constructorconstructor =clazzProxy1.getConstructor(InvocationHandler.class);//InvocationHandler.class为构造方法的参数
//方法一:内部类形式
class MyInvocationHandler1implementsInvocationHandler{
@Override
public Object invoke(Objectproxy, Method method, Object[] args)
throws Throwable {
//TODO自动生成的方法存根
returnnull;
}
}
Collectionproxy1=(Collection)constructor.newInstance(new MyInvocationHandler1());
System.out.println(proxy1);//结果为null
proxy1.clear();//。调用没有返回的方法不报错
//proxy1.size();//调用有返回的方法报错,因为在调用size方法是,会运行上面的invoke方法,这个方法返回的是null,size方法要把null转换为整数,就会报错了。
System.out.println("方法二--------");
//方法二,用匿名内部类
Collectionproxy2=(Collection)constructor.newInstance(new InvocationHandler(){
@Override
public Object invoke(Objectproxy, Method method, Object[] args)
throws Throwable {
//TODO自动生成的方法存根
returnnull;
}
});
System.out.println(proxy2);//为什么这里打印的代理类的对象名为null呢?
因为,调用代理对象从Object继承的hashCode,equals,或toString这三个方法时,代理对象将调用请求转发给InvocationHandler对象,对于其他方法,则不转发调用请求。
System.out.println("方法三--------");
//方法三,创建类和创建实例方法一步到位;Proxy提供了这样的一个方法
//Foo f=(Foo)Proxy.newProxyInstance(Foo.class.getClassLoader(),newClass[]{Foo.class},handler);里面分别放的是类加载器,接口类,创建对象的对象的参数
Collectionproxy3=(Collection)Proxy.newProxyInstance(
Collection.class.getClassLoader(),
new Class[]{Collection.class},
new InvocationHandler(){
ArrayListtarget=newArrayList();
@Override
publicObject invoke(Object proxy,Method method,
Object[]args)throwsThrowable {
long beginTime=System.currentTimeMillis();
ObjectretVal=method.invoke(target, args);
long endTime=System.currentTimeMillis();
System.out.println(method.getName()+"running time"+(endTime-beginTime));
returnretVal;这里的返回值就被下面同一颜色的obj接收(不明白看下面的分析InvocationHandler)
}
}
);
proxy3.add("zxx");//每次运行这个add方法一次就会调用上面的invoke方法一次,看下图就知道为什么要这样了。就因为他的参数(”zxx”)变化了
Objectobj=proxy3.add("lhm");//这里的Object就是上面invoke方法上同一颜色的Object
proxy3.add("bxd");
System.out.println(proxy3.size());
System.out.println(proxy3);
}
}
分析InvocationHandler对象的运行原理
1、动态生成的类实现了Collection接口(可以实现若干接口),生成的类有Collection接口中的所有方法和一个如下接收InvocationHandler参数的构造方法
2、构造方法接受一个InvocationHandler对象,InvocationHandler对象有一个incok方法,该方法接收三个参数,这三个参数分别是从哪来的呢?
是代理对象、方法、和参数,对于proxy3.add(“lhm”);就是proxy3、add、“lhm”;
总结分析动态代理类的设计原理与结构
编写可生成代理和插入通告的通用方法
package cn.itcast.day3;
import java.lang.reflect.Method;
publicinterfaceAdvice {
publicvoid beforeMethod(Methodmethod);
publicvoid afterMethod(Methodmethod);
}
----------------------------------------------------------------------------------------------------------------------------------
package cn.itcast.day3;
import java.lang.reflect.Method;
publicclassMyAdviceimplementsAdvice {
longbeginTime=0;
@Override
publicvoid beforeMethod(Methodmethod) {
//TODO自动生成的方法存根
System.out.println("开学了");
beginTime=System.currentTimeMillis();
}
@Override
publicvoid afterMethod(Methodmethod) {
//TODO自动生成的方法存根
System.out.println("放学了");
long endTime=System.currentTimeMillis();
System.out.println(method.getName()+"running time"+(endTime-beginTime));
}
}
--------------------------------------------------------------------------------------------------------------------------------
package cn.itcast.day3;
importjava.lang.reflect.Constructor;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.ArrayList;
import java.util.Collection;
publicclassProxyTest {
publicstaticvoid main(String[] args)throws Exception,SecurityException {
finalArrayListtarget=newArrayList();
Collectionproxy3 = (Collection)getProxy(target,new MyAdvice());
proxy3.add("zxx");//每次运行这个add方法一次就会调用上面的invoke方法一次
proxy3.add("lhm");
proxy3.add("bxd");
System.out.println(proxy3.size());
System.out.println(proxy3);
}
privatestatic Object getProxy(final Object target,final Advice advice) {
Objectproxy3=Proxy.newProxyInstance(
target.getClass().getClassLoader(),
/*new Class[]{Collection.class},*/
target.getClass().getInterfaces(),//获取接口
new InvocationHandler(){
@Override
public Object invoke(Objectproxy, Method method,
Object[]args)throwsThrowable {
/*long beginTime=System.currentTimeMillis();
ObjectretVal=method.invoke(target,args);
longendTime=System.currentTimeMillis();
System.out.println(method.getName()+"runningtime"+(endTime-beginTime));
returnretVal;*/
advice.beforeMethod(method);
ObjectretVal=method.invoke(target, args);
advice.afterMethod(method);
return retVal;
}
}
);
return proxy3;
}
}
------- android培训、java培训、期待与您交流! ----------
详细请查看:http://edu.csdn.net