可变参数
可变参数的特点:|---只能出现在参数列表的最后; (int x,int...args) 这样可以
|---位于变量类型和变量名之间,前后无空格都可以; (int x,int...args,int y) 这样不对
|---调用可变参数的方法时,编译器为该可变参数隐含创建一个数组,在方法体中以数组的形式访问可变参数
public static int add(int x, int... args) {
int sum = x;
for(int i=0;i<args.length;i++)
{
sum += args[i];
}
return sum;
}
enum
为什么要有枚举问题:要定义星期几或性别的变量,该怎么定义?假设用1-7分别表示星期一到星期日,但有人可能会写成int weekday = 0;或即使使用常量方式也无法阻止意外。
枚举就是要让某个类型的变量的取值只能为若干个固定值中的一个,否则,编译就会报错,枚举可以让编译器在编译时就可以控制源程序中填写的非法值,普通变量的方式在开发阶段无法实现这个目标
枚举的高级应用
枚举就相当于一个类,其中也可以定义构造方法、成员变量、普通方法和抽象方法。
枚举元素必须位于枚举体中的最开始部分,枚举元素列表的后要有分号与其他成员分隔。把枚举中的成员方法或变量等放在枚举元素的前面,编译器报告错误。
public class WeekDay
{
private WeekDay()();//首先将构造方法创建成私有的 别人不能创建对象
public final static WeekDay SUN = new WeekDay();//定义一个静态变量
WeekDay weekDay = WeekDay.MON; //MON是一个常量,是 WeekDay这个类型的对象
public WeekDay nextDay() { //这个方法不能是静态了, 这个是SUN对象身上的方法,
if(this==SUN){
return MON;
}
else{
return SUN;
}
}
public String toString() { // 打印枚举对象
return this == SUN?"SUN":"MON" ;
}
每一个枚举元素都是一个对象
可以有若干公有方法或抽象方法。采用抽象方法定义nextDay就将大量的if.else语句转移成了一个个独立的类
public static void main(String[] atgs){
WeekDay week = WeekDay.FRI; //用枚举定义变量
System.out.println(week); //自动实现了toString方法
System.out.println(week.name()); // 和上一句打印结果相同
System.out.println(week.ordinal()); //自己的排行数列 从0开始
System.out.println(weekDay.valueOf("SUN").toString());//把字符串变成对应的那个枚举元素
System.out.println(weekDay.values().length); //返回一个数组
}
public enum WeekDay{ //基本的枚举类
SUN,MON,TUE,WED,THU,FRI,SAT
}
反射概念:就是把Java类中的各个成分映射成相应的Class类
|---Class类的获取
1,类名.class 例如,System.class2,对象.getClass() 例如, new Data().getClass();
3,Class.forName("类名"),例如Class.forName("java.util.Date");
9个预定义Class实例对象:
boolean byte char short int long float double void
判断是否为基本类型用 .isPrimtive();
数据类型的Class实例对象
Class.isArray();
总之:只要是在源程序中出现的类型,都有各自的Class实例对象,例如int[] void
class ReflectTest {
public static void main(String[] args)throws Exception
{
byteCode();
}
public static void byteCode()throws Exception
{ //先来搞一下3份字节码
String str1 = "abc";
Class cla1 = str1.getClass();
Class cla2 = String.class;
Class cla3 = Class.forName("java.lang.String");
System.out.println(cla1==cla2);
System.out.println(cla1==cla3);
System.out.println("Primitive:"+cla1.isPrimitive());//判断一下是否是基本类型的字节码
System.out.println(int.class.isPrimitive());
System.out.println(int.class==Integer.class);
System.out.println(int.class==Integer.TYPE);//这个表示的是Integer内包装的基本类型
System.out.println(int[].class.isPrimitive());//判断数组是否为基本类型.
}
}
构造方法:
得到某个类所有的构造方法:Constructor[] constructor = Class.forName("java.lang.String").getConstructors();
得到某个类指定的构造方法:
Constructor constructor = Class.forName("java.lang.String").getConstructor(StringBuffer.class);
成员变量:
Field field = pt1.getClass().getDeclaredField("x");// 只要是里边声明过的,即使private修饰符field.setAccessible(true); //设置可以访问private 暴力获取
System.out.println(field.get(pt1));
成员方法:
Method toString = Class.forName("java.lang.String").getMethod("toString", null);toString.invoke(obj,args);
数组:
启动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,new String[]{“xxx”}),javac只把它当作jdk1.4的语法进行理解,而不把它当作jdk1.5的语法解释,因此会出现参数类型不对的问题。解决办法:
mainMethod.invoke(null,new Object[]{new String[]{"xxx"}});
mainMethod.invoke(null,(Object)new String[]{"xxx"});编译器会作特殊处理,编译时不把参数当作数组看待,也就不会数组打散成若干个参数了
Class clazz = Class.forName(arg[0]);
Method mMain = clazz.getMethod("main", String[].class);
mMain.invoke(null,new Object[]{new String[]{"aaa","bbb"}});//jdk1.4
mMain.invoke(null,(Object)new String[]{"aaa","bbb"});
数组与Object的关系及其反射类型
基本类型的一维数组可以被当作Object类型使用,不能当作Object[]类型使用;非基本类型的一维数组,既可以当做Object类型使用,又可以当做Object[]类型使用。ArrayList与HashSet的比较
ArrayList是一种有顺序的集合 就相当于一种数组,当你要放一个对象要进来的时候,它首先找到第一个空位置放进去,不是真正的把对象放进去了, 而是将对象的引用在数组中记住了,当你再放一个对象进去时,它会按顺序找到第二个位置 >>>HashSet(); 你要放进去的时候,先判断里面有没有这个对象,就是比较两个对象是否相等,如果一旦有了 就不放;如果你要想放一个对象覆盖掉原来的对象(你必须要把原来的删除掉remove)再把新的给插进去
hashCode方法的作用
HashSet就是采用哈希算法存取对象的集合,它内部采用对某个数字n进行取余的方式对哈希吗进行分组和划分对象的存储区域。Object类中共蒂尼了一个hashCode()方法来返回每个java对象的哈希吗。当从HashSet集合中查找某个对象时,java系统首先调用对象的hashCode()方法获得该对象的哈希码,然后根据哈希吗找到相应的存储区域,最后取出该存储区域内的每个元素与该对象进行equals方法比较,这样不用遍历集合中的所有元素就可以得到结论,可见,HashSet集合具有很好的对象检索性能,但是,HashSet集合存储对象的效率相对要低些,因为向hashset集合中添加一个对象时,要先计算出对象的哈希吗和根据这个哈希吗确定对象在集合中的存放位置。用类加载器的方式管理资源和配置文件
InputStream ips = RefiectTest.class.getClassLoader().getResourceAsStream("config.properties");String path = Hello.class.getClassLoader().getResource("config.properties").getPath();
内省_JavaBean
JavaBean是一个特殊的Java类.要注意:Age-->如果第二个字母是 小写,则把第一个字母变成小写-->age
CPU-->如果第二个字母是 大写,则把第一个字母也继续大写-->CPU
比如:
gettime->time
setTime->time
getCPU->CPU
内省访问JavaBean属性的两种方式:
通过PropertyDescriptor类操作Bean的属性
通过Instospector类获得Bean对象的BeanInfo,然后通过BeanInfo来获取属性的描述器(PropertyDescriptor),
通过这个属性描述其就可以获取某个属性对应的getter/setter方法,然后通过反射机制调用这些方法.
工具包内常用的类
BeanUtils
PropertyUtils
ConvertUtils.regsiter(Converter convert,Class clazz)
//需求:用内省的方式来读取JavaBean的x属性.
import java.beans.IntrospectionException;
import java.beans.PropertyDescriptor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
public class IntroSpectorTest {
public static void main(String[] args)throws Exception
{
ReflectPoint pt1 = new ReflectPoint(3,4);
String propertyName = "x";
//普通方法:"x"-->"X"-->"getX"-->"Method getX"-->得到值
//内省的方法:PropertyDescriptor(属性名,JavaBean类),属性描述符
Object retVal = getProperty(pt1, propertyName);
System.out.println(retVal);
//接下来,set一个值
Object value = 1;
setProperty(pt1, propertyName, value);
System.out.println(pt1.getX());
}
private static Object getProperty(Object pt1, String propertyName)
throws IntrospectionException, IllegalAccessException,
InvocationTargetException {
//PropertyDescriptor(属性名,JavaBean类),属性描述符
PropertyDescriptor pd1 = new PropertyDescriptor(propertyName,pt1.getClass());
Method methodGetX=pd1.getReadMethod();
Object retVal = methodGetX.invoke(pt1);
return retVal;
}
private static void setProperty(Object pt1, String propertyName,
Object value) throws IntrospectionException,
IllegalAccessException, InvocationTargetException {
PropertyDescriptor pd2 = new PropertyDescriptor(propertyName,pt1.getClass());
Method methodSetX = pd2.getWriteMethod();
methodSetX.invoke(pt1,value);
}
}
对JavaBean的复杂内省操作
采用遍历BeanInfo的所有属性方法来查找和设置某个ReflectPoint对象的x属性.
在程序中把一个类当做JavaBean来看,就是调用了IntroSpector.getBeanInfo方法,
得到的BeanInfo对象封装了把这个类当做JavaBean的结果信息
在程序中把一个类当做JavaBean来看,就是调用了IntroSpector.getBeanInfo方法,
得到的BeanInfo对象封装了把这个类当做JavaBean的结果信息
//获取值
private static Object getProperty(Object pt1, String propertyName)
throws IntrospectionException, IllegalAccessException,
InvocationTargetException {
//比较复杂的方式
BeanInfo beanInfor = Introspector.getBeanInfo(pt1.getClass());
PropertyDescriptor[] pds = beanInfor.getPropertyDescriptors();
Object retVal = null;
for(PropertyDescriptor pd : pds)
{
if(pd.getName().equals(propertyName))
{
Method methodGetX = pd.getReadMethod();
retVal = methodGetX.invoke(pt1);
break;
}
}
return retVal;
}
java7 的新特性
Map[] map = {name:"cwk",age:12};
BeanUtils.setProperty(map, "name", "kangwen");
PropertyUtils.setProperty(stu,"x",9);
注解(annotation)
@SuppressWarnings("deprecation")//忽略已经过时的方法-->编译阶段;
@Deprecated//标注:此方法过时了-->是字节码阶段,class
@Override//判断是否覆盖父类方法-->编译阶段;
@Retention(RetentionPolicy....)表示注解运行到哪一个阶段
@RetentionPolicy.SOURCE//编译阶段
@RetentionPolicy.CLASS//class文件阶段(默认值)
@RetentionPolicy.RUNTIME//运行时
@Target({ElementType.METHOD,ElementType.TYPE})//Target,表示注解只能放置的位置,可以接受数组类型,其中 METHOD方法TYPE,类
注意:信息的信息为元信息,数据的数据为元数据,注解的注解为元注解
package com.cwk.demo;
//在注解中添加注解的那个类
public @interface MetaAnnotation {
String value();
}
//注解类
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
//注解第一步分类
@Retention(RetentionPolicy.RUNTIME)//元注解
@Target({ElementType.METHOD,ElementType.TYPE})//表示这个注解可以放在方法上,类上
public @interface ItcastAnnotation {
String color() default "blue";
String value();
int[] arrayAttr() default {4,5,5};
EnumDemo.TrafficLamp lamp() default EnumDemo.TrafficLamp.RED;
MetaAnnotation annotationAttr() default @MetaAnnotation("lhm");
}
//注解第二部分类
@ItcastAnnotation(annotationAttr=@MetaAnnotation("flx"),color="red",value = "abc",arrayAttr = {4,68})
public class AnnotationTest {
@SuppressWarnings("deprecation")//忽略已经过时的方法
@ItcastAnnotation("sst")//这里如果是就一个元素,那么此处可以省略=前面的值.另一种方法就是在注释类中其它属性定义default(缺省)属性.
public static void main(String[] args) throws Exception{
System.runFinalizersOnExit(true);//此方法已经过时
//注解,第三部分检查这个类上是否有某些东西
if(AnnotationTest.class.isAnnotationPresent(ItcastAnnotation.class))
{//如果在,就返回true,进行下一步造作
ItcastAnnotation annotation = (ItcastAnnotation)AnnotationTest.class.getAnnotation(ItcastAnnotation.class);
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());
}
}
}
泛型:
内部原理:是提供给javac编译器使用的,可以限定集合中的输入类型,让编译器挡住源程序中的非法输入编译器编译带类型说明的集合时会除去掉"类型"信息,使原程序运行效率不受影响.
对于参数变化的反省类型,getClass()方法的返回值与原始类型完全一样.由于编译生成的字节码会去除掉泛型
的类型信息.只要能跳过编译器,就可以往某个反省集合中加入其它的数据,-->用反射得到集合,再调用其add方法即可.
ArrayList<E>类定义和ArrayList<Integer>类引用中设计如下术语:
|--整个称为ArrayList<E>泛型类型
|--ArrayList<E>中E称为类型变量或类型参数
|--整个ArrayList<Integer>称为参数化的类型
|--ArrayList<Integer>中的Integer称为类型参数的实例或实际类型参数
|--<>叫做typeof
|--ArrayList称为原始类型
参数化类型与原始类型的兼容性:
|--参数化类型可以引用一个原始类型的对象,编译报告警告,例如:Collection<String> c = new ArrayList();
|--原始类型可以引用一个参数化类型的对象,编译报告警告,例如:Collection c = new ArrayList<String();
参数化类型不考虑类型参数的继承关系:
Vector<String> v = new Vector<Object>();//错误
Vector<Object> v = new Vector<String>();//错误
在创建数组实例时,数组的元素不能用参数化的类型
Vector<Integer> vectorList[] = new Vector<Integer>[10];//这里会报错,不能用参数化的类型
注意 , 下面的代码不会报错.原因:按照编译器的思想一行一行来推理
Vertor v1 = new Vector<String>;
Vector<Object> v= v1;
通配符<?>
限定通配符上边界:(此处,必须为Number或者Number的子类)
Vector<? extends Number> x = new Vector<Integer>();
限定通配符下边界:(此处,必须为Integer或者Integer的父类)
Vector<? super Integer> x = new Vector<Number>();
注意点:
1,定义的泛型必须在修饰符后面,返回值类型前面,泛型通常为大写
2, 注意!泛型<T>只能是引用类型,不能是基本类型.
3,除了在应用泛型使用extends限定符,在定义泛型时也可以使用extends限定符
例如:Class.getAnnotation()方法的定义,并且可以使用&来制定多个边界
如<V extends Serializable&coloneable>void mehtod(){}
4,普通,构造,静态方法中都可以使用泛型.
5,可以使用类型变量表示异常,可以用于方法的throws列表中,但是不能用于catch子句中.
如:
private static<T extends Exception> sayHello throws T
{
try{}
catch(T e)//出错
{
throw(T)e;
}
}
6,在泛型中可以有多个类型参数,在定义它们的尖括号有逗号分隔
如:putlic static <K,V> V getValue(K key){retrun map.get(key);}
通过反射获得泛型
main:
//字节码,方法,("applyVector",参数类型)
Method applyMethod = GenericTest.class.getMethod("applyVector", Vector.class);
//getGenericParameterTypes参数的泛型类型
Type[] types = applyMethod.getGenericParameterTypes();
//参数化的类型
ParameterizedType pType = (ParameterizedType)types[0];
//获取返回的实际类型
System.out.println(pType.getRawType());
System.out.println(pType.getActualTypeArguments()[0]);
method:
public static void applyVector(Vector<Date> v1){
}
类加载器及其委托机制
BootStrapExtClassLoader
AppClassLoader
委托机制:
|--每个类加载器加载类,又先委托给上级加载器.上级给上级
|--当所有父类及父类的父类加载器都没有加载到类,会到发起者加载器,还加载不到则抛ClassNotFoundException异常
AOP
简述:AOP(Aspect Oriented Program)即面向方面的编程。系统中存在着交叉业务,一个交叉业务就是要切入到系统中的一个方面。
安全、事务、日志等功能要贯穿于好多个模块中,所以他们就是交叉业务。
交叉业务的编程问题即面向方面的编程(AOP),AOP的目标就是使交叉业务模块化,可以采用将切面代理移动到原始方法的周围,这与直接在方法中编写切面代理的过程效果是一样的,如图:
------------------------------------------------------切面
func1 func2 func3
{ { {
.... .... ......
} } }
------------------------------------------------------切面
因此使用代理技术正好可以解决这种问题,代理是实现AOP功能的核心和关键技术。
动态代理
1、要为系统中的各种接口的类增加代理功能,那将需要太多的代理类,这时就不能采用静态代理方式,需用动态代理技术。2、动态代理类:JVM可在运行时,动态生成类的字节码,这种动态(不是代理,只是拿出来作为代理类)生成的类往往被用作代理类,即动态代理类。
注:JVM生成的动态类必须实现一或多个接口,所以JVM生成的动态代理类只能用作具有相同接口的目标类代理。
3、CGLIB库可以动态生成一个类的子类,一个类的子类也可以作为该类的代理,所以,如果要为一个没有实现接口的类生成动态代理,那么可以使用CGLIB库。
4、代理类各个方法通常除了调用目标相应方法和对外返回目标返回的结果外,还可以再代理方法中其余的代码。
InvocationHandler对象的内部功能.
总结:让jvm创建动态类及实例对象,需要给它提供那些信息?三个方面:
1,生成的类中有哪些方法,通过让其实现哪些接口的方式进行告知
2,产生的类字节码必须有一个关联的加载器对象
3,生成的类中的方法的代码是怎么样的,也由得我们提供.把我们的代码卸载一个约号了的接口对象的方法中,
把对象传给他,它调用的我的方法,即相当于插入了我的代码.提供执行代码的对象,就是那个InvocationHandler对象.
它是在创建动态类的实例对象的构造方法时传递进去的.在上面的InvocationHandler对象的invoke方法中