1.静态导入
例:import static java.lang.math.*;
静态导入后,调用类中的静态方法时,不用在方法前加上类名(类名.方法)。
2.可变参数
例:public void function(int x, int...y):y就是可变参数
特点:1>只能位于参数列表最后面。
2>...位于变量类型和变量之间。前后有无空格都可以。
3>调用可变参数的方法示,编译器为该可变参数隐含创建一个数组,在方法中以数组的形式访问可变参数。
3.增强for
格式:for(数据类型 变量名 : 数组或Collection集合){执行语句;}
注意:1>迭代变量必须在()中定义。
2>集合变量可以是数组或实现了Iterable接口的集合类
4.基本数据类型的自动装箱与拆箱包装类
1>基本数据类型对应的
int -->Integer byte --> Byte
short -->Short long--> Long
char -->Character double --> Double
float -->Float boolean --> Boolean
2>装拆箱示例
基本---引用 Integer x =new Integer(x);
引用---基本 int num =x.intValue();
Integer i = 3;//装箱;把3封装成了Interger对象
System.out.println(i+12);//拆箱;Interger 对象时没有加法运行,因此自动拆箱了
3>享元模式
很多个小的对象,有很多属性相同把它们变成一个对象,不同的属性把它们变成方法的参数,称为外部状态;那些相同的属性称为内部状态。
5.枚举(枚举中记到一句话:枚举是类,枚举中的元素是该枚举的实例对象)
1>枚举就相当于一个类,枚举中的元素相当于该类的一个实例对象,其中也可以定义构造方法(必须是private),成员变量,普通方法,和抽象方法。
2>枚举元素必须位于枚举体中的最开始部分,枚举元素列表必须要有分号与其他成员分隔。把枚举中的成员方法或变量等放在枚举元素的前面,编译报错。
3>带构造函数的枚举(可以有多个构造方法)
构造方法必须是private的
枚举元素就是枚举类的实例对象,有多少个枚举元素就会执行多少次构造方法(可以是默认的,也可以是其他的)。
4>枚举只有一个成员时(即只有一个元素),就可以作为一种单例的实现方式。
普通类模拟的枚举示例:
public abstractclass WeekDay1 {
private WeekDay1(){}//私有构造方法
//匿名内部类实现
public final static WeekDay1 SUN = newWeekDay1(){
publicWeekDay1 nextDay() {
return MON;
}
};
public final static WeekDay1 MON = newWeekDay1(){
publicWeekDay1 nextDay() {
return SUN;
}
};
public abstract WeekDay1 nextDay();
/* public WeekDay nextDay(){
if(this== SUN){
return MON;
}else{
return SUN;
}
}
*/
public String toString(){
returnthis==SUN?"SUN":"MON";
}
}
当我们需要为抽象类实例化对象时,要不直接使用其非抽象子类,或者使用匿名内部类。
6>枚举的常用方法
a>name():获得该枚举常量的名称
b>ordinal():获得该枚举常量在枚举中的序数
c>valueOf(String s):获取该字符串在枚举中的枚举常量
d>values():返回的是一个数组,该数组获取的是枚举的所有常量
public classEnumTest {
public static void main(String[] args) {
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);
}
public enum WeekDay{
SUN,MON,TUE,WED,THI,FRI,SAT;
}
}
如果我们的枚举是个抽象类,则它的元素就是它的子类,因为它抽象了不可以实例化对象,只能由自己的子类实例化。则也必须在元素使用匿名内部类实现抽象方法。
如下例子:
public enum TrafficLamp{
RED(30){
public TrafficLamp nextLamp(){
return GREEN;
}
},
GREEN(45){
public TrafficLamp nextLamp(){
return YELLOW;
}
},
YELLOW(5){
public TrafficLamp nextLamp(){
return RED;
}
};
public abstract TrafficLamp nextLamp();//枚举中的抽象方法
private int time;
private TrafficLamp(int time){this.time =time;}//构造函数
}
6.反射 --->Class类(一定要获得字节码再操作)
1>Class类:java中的各个java类属于同一类事物,描述这类事物的java类就是Class。
a>Class cla =Person.class;//类的字节码就是Class对象
何得到各个字节码对应的实例对象( Class类型)
1).类名.class,例如,System.class
2).对象.getClass(),例如,new Date().getClass()
3).Class.forName("类名"),例如,Class.forName("java.util.Date");//常使用这种
b>九个预定义Class实例对象:八个基本类型的和一个void的。
int.class ==Integer.class//不相等,不是一份字节码.
int.class ==Integer.TYPE//相等,因为Integer.TYPE获取的就是其基本类型的字节码。
c>数组类型的Class实例对象:Class.isArray()
2>反射:反射就是把java类中的各种成分(变量,方法,构造方法,修饰符,包等)映射成相应的java类
变量,方法,构造方法,包相对应的类Field、Method、Contructor、Package
3>Contructor类:代表某个类中的一个构造方法类。
a>得到某个类所有的构造方法:
例子:Constructor [] constructors=
Class.forName("java.lang.String").getConstructors();
b>得到某一个构造方法:
例子:Constructor constructor = Class.forName(“java.lang.String”).getConstructor(StringBuffer.class);
//获得方法时要用到类型
c>创建实例对象:
通常方式:String str = new String(newStringBuffer("abc"));
反射方式: String str =(String)constructor.newInstance(new StringBuffer("abc"));
//调用获得的方法时要用到上面相同类型的实例对象
d>Class的newInstance()方法:
例子:String obj =(String)Class.forName("java.lang.String").newInstance();
该方法内部先得到默认的构造方法,然后用该构造方法创建实例对象。
该方法内部的具体代码是怎样写的呢?用到了缓存机制来保存默认构造方法的实例对象。
4>Field 类:代表某个类中的一个成员变量。
a>public FieldgetField(String name):获取类或接口的指定公共成员(public修饰的)字段(Class的方法)
b>public Objectget(Object obj):获取对象 obj 中的所表示字段的值(Field的方法)
c>public FieldgetDeclaredField(Stringname):获取类或接口的指定已声明字段,不管是否可见(Class的方法)
d>public void setAccessible(boolean flag):值为 true 则指示反射的对象在使用时应该取消 Java 语言访问检查。值为 false 则指示反射的对象应该实施 Java 语言访问检查。(Field 父类的方法)
ReflectPoint point = new ReflectPoint(1,7);
//获取public修饰的成员,此时y是public修饰的
//获取ReflectPoint类上的y,是ReflectPoint共有的y,而不是其某个对象的y.
//而是要用其去取某个对象上对应的y值
Field y =Class.forName("cn.itcast.corejava.ReflectPoint").getField("y");
System.out.println(y.get(point));//获取poit对象上的y,此时才可以取出7
//获取private修饰的成员,此时x是private修饰的(暴力反射)
Field x =Class.forName("cn.itcast.corejava.ReflectPoint").getDeclaredField("x");
x.setAccessible(true);//设置可以访问该变量
System.out.println(x.get(point));
5>Method类:代表某个类中的一个成员方法
a>得到类中的某一个方法:
例子:MethodcharAt =Class.forName("java.lang.String").getMethod("charAt",int.class);
b>调用方法:
通常方式:System.out.println(str.charAt(1));
反射方式:System.out.println(charAt.invoke(str, 1)); //str是该方法的对象
如果传递给Method对象的invoke()方法的第一个参数为null(即使用该方法不需要对象),这有着什么样的意义呢?说明该Method对象对应的是一个静态方法!
jdk1.4和jdk1.5的invoke方法的区别:
Jdk1.5:public Objectinvoke(Object obj,Object... args)
Jdk1.4:public Objectinvoke(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[]{new String[]{"xxx"}});
mainMethod.invoke(null,(Object)newString[]{"xxx"}); ,编译器会作特殊处理,编译时不把参数当作数组看待,也就不会数组打散成若干个参数了
6>数组的反射(Array工具类用于完成对数组的反射操作)
a>具有相同类型和维数的数组,反射后的字节码是同一个。
b>代表数组的Class实例对象的getSuperclass()方法返回的父类为Object类对应的Class。
c>Arrays.asList():将一个数组转换成List
JDK1.4:public staticList asList(Object[] a)
JDK1.5:publicstatic
int[] a = new int[]{1,2,3};
Arrays.asList(a):此时为了兼容1.4,1.5以后的版本将a数组整个当成一个Object对象,因此不能用System.out.println(Arrays.asList(a));打印出数组中的值,而是将数组整个当成一个Object对象打印。
1.内省
1>内省是用来对javaBean操作,javaBean是特殊的Java类。主要用于传递数据信息,这种java类中的方法主要用于访问私有的字段,且方法名符合某种命名规则(setxxx和getxxx方法)。
2>获取对象的属性
a>方法一通过我们想要获得的属性如x;然后再将所要操作的类对应的x属性的操作(如setxxx方法和getxxx方法)封装成类(PropertyDescriptor);该根据带有x操作对象(pd2)进行获取我们所需要的方法,并进行反射操作。
//创建一个ReflectPoint 对象,其中有想,x,y链各个属性
ReflectPoint pt1 = new ReflectPoint(3,5);
String propertyName = "x";//x属性名
int value = 4;
//获取pt1对象的x属性描述,并封装打pd2对象中
//此时x的属性有setxxx方法和getxxx方法,为了方便操作就封装成对象
PropertyDescriptor pd2 = newPropertyDescriptor(propertyName,pt1.getClass());
Method methodSetX = pd2.getWriteMethod();//获取x的setxxx方法
methodSetX.invoke(pt1,value);//使x的setxxx方法将value值付给x
b>方法二其实就是将所要操作的类当成JavaBean操作;然后获取所要操作的类的全部属性,再判断获取我们所需的属性如x;根据反射的方式获取到该属性的stexxx方法或getxxx方法,并进行操作。
//创建一个ReflectPoint 对象,其中有想,x,y链各个属性
ReflectPoint pt1 = new ReflectPoint(3,5);
String propertyName = "x";//x属性名
//将pt1所对应的类当做JavaBean类操作
//得到的beanInfo对象封装了把这个类当做JavaBean看的结果信息
BeanInfo beanInfo = Introspector.getBeanInfo(pt1.getClass());
//获取pt1对象所对应的类的全部属性
PropertyDescriptor[] pds = beanInfo.getPropertyDescriptors();
for(PropertyDescriptor pd : pds){
if(pd.getName().equals(propertyName))//判断取得x的属性
{
MethodmethodGetX = pd.getReadMethod();//获取x的getxxx方法
//通过x的getxxx方法获得x的值
retVal = methodGetX.invoke(pt1);
}
}
3>Beanutils工具包:需要自己下载,方便对内省的操作。
2.注解(一个注解就是一个类,使用注解就相当于创建了一个实例对象)
1>注解相当于一种标记,在程序中加了注解就等于为程序打上某种标记,javac编译器,开发工具和其他程序可以用反射来了解你的类及各种元素上有无何种标记,看你有什么标记,就去干相应的事。标记可以加在包,类,字段,方法,方法的参数,以及局部变量上
2>
@ override:覆盖父类方法
@Deprecated:过时方法
@SuppressWarnings("deprecation"):压缩警告(编译警告)
注解类的格式:
@ interface A{}
3>
a>@Retention 元注解:用于注解该被注解的保留到哪个时候
三种取值:RetetionPolicy.SOURCE、RetetionPolicy.CLASS、RetetionPolicy.RUNTIME; 分别对应:java源文件(编译器)-->class文件-->内存中的字节码。
例:@Retention(Retention ):保留到运行时
b>@Target 元注解:指明注解可以用于什么上面
例:@Target(ElementType.METHOD):表示此注解只能放在方法上
4>为注解增加基本属性:注解中的属性必须是String、基本数据类型、枚举、Class、其他注解、以上数据类型的一维数组。
例:定义的注解
public @Interface ItCastAnnotation{
//为注解增加了两个基本属性
Stting color() default “red”;// color()属性进行了缺省值设置
String value();
}
如果注解中有一个属性,名字叫value,并且整个注解只有这个属性需要被赋值,那么在赋值时可以不用指定名
在应用中 @ItCastAnnotation(color="red",value = “test”)
@ItCastAnnotation(“test”)//因为color设置了缺省值,所以可以不用给color设置,当注释中只有一个属性需要赋值,则可以直接给定一个对应类型的值就可以。
由于注释也是一个类,所以我们想要直接获取注解中属性的值,可以直接用:对象.属性获得。
@ItCastAnnotation(color="red",value =“test”)
class AnnotationTest{
public static void main(String[] args)
{
//反射方式,获得注解
MyAnnotation a =(MyAnnotation)AnnotationTest.class.getAnnotation(MyAnnotation.class);
System.out.println(a.color());//打印属性值
}
}
5>数组类型的属性
注解中增加的属性:int [] arrayAttr() default{1,2,3};
应用:@MyAnnotation(arrayAttr={2,3,4})
如果数组属性中只有一个元素,这时候属性值部分可以省略大括
6>枚举类型的属性
注解中增加的属性:TrafficLamp lamp() ;
应用:@MyAnnotation(lamp=TrafficLamp.GREEN)
7>注解类型的属性:
注解中增加的属性:
MetaAnnotation annotationAttr() default@MetaAnnotation("xxxx");
应用:@MyAnnotation(annotationAttr=@MetaAnnotation(“yyy”) )
可以认为上面这个@MyAnnotation是MyAnnotaion类的一个实例对象,同样的道理,可以认为上面这个@MetaAnnotation是MetaAnnotation类的一个实例对象,调用代码如下:
MetaAnnotation ma = myAnnotation.annotationAttr();
System.out.println(ma.value));
3.泛型
1>泛型是给编译器看的,运行时没有泛型。比如我们定义一个ArrayList
ArrayList
collection3.getClass().getMethod("add",Object.class).invoke(collection3, "abc");
System.out.println(collection3.get(0));
2>ArrayList
a>整个称为ArrayList
b>ArrayList
c>整个ArrayList
d>ArrayList
e>ArrayList
f>ArrayList称为原始类型
3>参数化类型与原始类型的兼容性:
参数化类型可以引用一个原始类型的对象,编译报告警告,例如,
Collection
原始类型可以引用一个参数化类型的对象,编译报告警告,例如,
Collection c = new Vector
4>参数化类型不考虑类型参数的继承关系:
Vector
Vector也错误!
5>编译器不允许创建泛型变量的数组。即在创建数组实例时,数组的元素不能使用参数化的类型,例如,下面语句有错误:
Vector
6>泛型的限定
ArrayList>: ?通配符。可以传入任何类型数据。(一般用于函数传递参数,可以接受任意类型数据)。
使用?通配符可以引用其他各种参数化的类型,?通配符定义的变量主要用作引用,可以调用与参数化无关的方法,不能调用与参数化有关的方法。
a>上限:?extends E 可以接收E类型或者E的子类型
b>下限:?super E 可以接收E类型或者E的父类型
4.类加载器
1>Java虚拟机中可以安装多个类加载器,系统默认三个主要类加载器,每个类负责加载特定位置的类:BootStrap,ExtClassLoader,AppClassLoader
2>加载器也是Java类,因为其他是java类的类加载器本身也要被类加载器加载,显然必须有第一个类加载器不是不是java类,这正是BootStrap。
3>类加载器的委托机制
a>当Java虚拟机要加载一个类时,到底派出哪个类加载器去加载呢?
首先当前线程的类加载器去加载线程中的第一个类。
如果类A中引用了类B,Java虚拟机将使用加载类A的类装载器来加载类B。
还可以直接调用ClassLoader.loadClass()方法来指定某个类加载器去加载某个类。
b>每个类加载器加载类时,又先委托给其上级类加载器。
当所有祖宗类加载器没有加载到类,回到发起者类加载器,还加载不了,则抛ClassNotFoundException,不是再去找发起者类加载器的儿子,因为没有getChild方法,即使有,那有多个儿子,找哪一个呢?
对着类加载器的层次结构图和委托加载原理,解释先前将ClassLoaderTest输出成jre/lib/ext目录下的itcast.jar包中后,运行结果为ExtClassLoader的原因。
5.代理
1>程序中的代理:要为已存在的多个具有相同接口的目标类的各个方法增加一些系统功能,例如:异常处理、日志、计算方法的运行时间、事务管理、等等。
动态代理类J:VM可以在运行期动态生成出类的字节码,这种动态生成的类往往被用作代理类。
JVM生成的动态类必须实现一个或多个接口,所以,JVM生成的动态类只能用作具有相同接口的目标类的代理。
CGLIB库可以动态生成一个类的子类,一个类的子类也可以用作该类的代理,所以,如果要为一个没有实现接口的类生成动态代理类,那么可以使用CGLIB库。
2>创建动态类的实例对象
a>用反射获得构造方法
b>编写一个最简单的InvocationHandler类
c>调用构造方法创建动态类的实例对象,并将编写的InvocationHandler类的实例对象传进去
d>打印创建的对象和调用对象的没有返回值的方法和getClass方法,演示调用其他有返回值的方法报告了异常。
e>将创建动态类的实例对象的代理改成匿名内部类的形式编写,锻炼大家习惯匿名内部类。
方式一:
Class clazzProxy1 =Proxy.getProxyClass(Collection.class.getClassLoader(), Collection.class);
Constructor constructor =clazzProxy1.getConstructor(InvocationHandler.class);
Collection proxy2 =(Collection)constructor.newInstance(new InvocationHandler(){
publicObject invoke(Object proxy, Method method, Object[] args)
throwsThrowable {
return null;
}
});
方式二:
Class clazzProxy1 =Proxy.getProxyClass(Collection.class.getClassLoader(), Collection.class);
final ArrayList target = new ArrayList(); //目标对象,此时是被内部类访问
Collection proxy3 =(Collection)getProxy(target,new MyAdvice());
//使用代理方法
proxy3.add("zxx");
proxy3.add("lhm");
proxy3.add("bxd");
private static Object getProxy(final Objecttarget,final Advice advice) {
Object proxy3 = Proxy.newProxyInstance(
target.getClass().getClassLoader(),
target.getClass().getInterfaces(),
new InvocationHandler(){
publicObject invoke(Object proxy, Method method, Object[] args)
throwsThrowable {
advice.beforeMethod(method);
Object retVal = method.invoke(target,args);
advice.afterMethod(method);
return retVal;
}
});
return proxy3;
}
3>我们使用代理对象去执行某个方发时,其实是调用代理对象中的InvocationHandler中的invoke()方法。那么public Object invoke(Object proxy, Method method, Object[] args)
中的三个参数分别代:代理对象,代理对象调用的方法,代理对象调用的方法的参数
例:
4>动态代理工作原理
a>将InvocationHandler对象传给代理的构造方法
b>通过代理的其他方法(test1()等)到InvocationHandler找invoke()方法
c>InvocationHandler的invoke()方法可以自己加入些日志功能等,还可以加入目标(log())。
d>在通过目标(log())去找目标方法。