---------- android培训、java培训、期待与您交流! ----------
1.静态导入
2.可变参数
问题:一个方法接受的参数个数不固定,例如:
System.out.println(countScore(2,3,5));
System.out.println(countScore(1,2,3,5));
只能出现在参数列表的最后;这个要记住
...位于变量类型和变量名之间,前后有无空格都可以;
调用可变参数的方法时,编译器为该可变参数隐含创建一个数组,在方法体中以数组的形式访问可变参数。
public static void main(String[] args) { // TODO Auto-generated method stub System.out.println(add(1,2,3,5)); System.out.println(add(2,3,5)); } public static int add(int x,int ...args) { int sum = x; for(int i=0;i<args.length;i++) { sum += args[i]; } return sum; }
3.增强for循环
语法:
for ( type 变量名:集合变量名 ) { … }
注意事项:
迭代变量必须在( )中定义!
集合变量可以是数组或实现了Iterable接口的集合类
举例:
public static int add(int x,int ...args) { int sum = x; for(int arg:args) { sum += arg; } return sum; }
4.基本数据类型的自动装箱与拆箱
基本数据类型(8种):boolean,byte,char,short,int,long,float,double
自动装箱:
Integer num1 = 12;
自动拆箱:
System.out.println(num1 + 12);
基础数据的包装类,除了Float和Double以外的都有缓冲池-127—128,在这个范围内的数据,如果存在,则不会创建新的对象,引用存在的。
享元设计模式 flyweight
很多个小对象,他们有很多属性相同,把它们相同的作为一个对象,那些不相同的作为外部属性,作为方法参数传入,称之为外部状态,那些相同的作为内部状态。
5.枚举
枚举的产生就是要让某个类型的变量的取值只能为若干个固定值中的一个,否则,编译器就会报错。枚举可以让编译器在编译时就可以控制源程序中填写的非法值,普通变量的方式在开发阶段无法实现这一目标
使用普通类模拟枚举类
可以有若干公有方法或抽象方法。采用抽象方法定义nextDay就将大量的if.else语句转移成了一个个独立的类 ,使用匿名类实现抽象类的子类功能
package cn.itcast.day1; public abstract class WeekDay1 { private WeekDay1(){}; public final static WeekDay1 SUN= new WeekDay1(){ @Override public WeekDay1 nextDay() { // TODO Auto-generated method stub return MON; } @Override public String toString() { // TODO Auto-generated method stub return "SUN"; } }; public final static WeekDay1 MON = new WeekDay1(){ @Override public WeekDay1 nextDay() { // TODO Auto-generated method stub return SUN; } @Override public String toString() { // TODO Auto-generated method stub return "MON"; } }; public abstract WeekDay1 nextDay(); public abstract String toString(); /* public WeekDay nextDay(){ if(this == SUN){ return MON; }else{ return SUN; } } public String toString(){ return this == SUN?"MON":"SUN"; }*/ }
枚举的基本应用
举例:定义一个Weekday的枚举。
扩展:枚举类的values,valueOf,name,toString,ordinal等方法
valueOf 可以将网页中的字符串转换为JAVA对象
WeekDay weekDay = WeekDay.FRI; System.out.println(weekDay.name());//FRI默认是重构了toString方法 System.out.println(weekDay.ordinal());//从0开始打印在枚举中的位子:5 System.out.println(weekDay.valueOf("FRI"));//可以将网页中的字符串转换为枚举对象 System.out.println(weekDay.values().length);//得到一个枚举数组
总结:枚举是一种特殊的类,其中的每个元素都是该类的一个实例对象,例如可以调用WeekDay.SUN.getClass().getName和WeekDay.class.getName()。
枚举的高级应用
枚举就相当于一个类,其中也可以定义构造方法、成员变量、普通方法和抽象方法。
枚举元素必须位于枚举体中的最开始部分,枚举元素列表的后要有分号与其他成员分隔。把枚举中的成员方法或变量等放在枚举元素的前面,编译器报告错误。
带构造方法的枚举:
构造方法必须定义成私有的
如果有多个构造方法,该如何选择哪个构造方法? :()中带参数
枚举元素MON和MON()的效果一样,都是调用默认的构造方法。
public enum WeekDay{ SUN(),MON(1),TUE,WED,THI,FRI,STA; private WeekDay(){System.out.println("null");} private WeekDay(int day){System.out.println(day);} }
带方法的枚举:
定义枚举TrafficLamp
实现普通的next方法
实现抽象的next方法:每个元素分别是由枚举类的子类来生成的实例对象,这些子类采用类似内部类的方式进行定义。
增加上表示时间的构造方法
写下面程序的方法:
带有抽象方法的枚举步骤和技巧:
1. enumTrafficLamp{RED,GREEN,YELLOW}
2. enumTrafficLamp{RED,GREEN,YELLOW;public abstract next();}
3.enum TrafficLamp{RED{},GREEN{},YELLOW{};public abstract next();}
4.填充各个{}中的next方法。
public enum Trafficlamp{ RED(50){ @Override public Trafficlamp nextLamp() { // TODO Auto-generated method stub return GREEN; } }, YELLOW(5){ @Override public Trafficlamp nextLamp() { // TODO Auto-generated method stub return RED; } }, GREEN(25){ @Override public Trafficlamp nextLamp() { // TODO Auto-generated method stub return YELLOW; } }; public abstract Trafficlamp nextLamp(); private int time; private Trafficlamp(int time){this.time = time;}; }
枚举只有一个成员时,就可以作为一种单例的实现方式。(见本博客中的单例设计模式)
6.注解
一个注解就是一个类,创建一个注解就是创建一个类
Java提供的几个基本的注解
@SuppressWarning("deprecation”)
用于取消显示的警告集
@Deprecated
用 @Deprecated 注释的程序元素,不鼓励程序员使用这样的元素,通常是因为它很危险或存在更好的选择。在使用不被赞成的程序元素或在不被赞成的代码中执行重写时,编译器会发出警告。
@Override
表示一个方法声明打算重写超类中的另一个方法声明。如果方法利用此注释类型进行注解但没有重写超类方法,则编译器会生成一条错误消息。
其他几个常用的Java注解
@Retention 指示注释类型的注释要保留多久。如果注释类型声明中不存在 Retention 注释,则保留策略默认为 RetentionPolicy.CLASS
RetentionPolcy value
枚举常量
CLASS 编译器将把注释记录在类文件中,但在运行时VM不需要保留注释
RUNTIME 编译器将把注释记录在类文件中,在运行VN时将保留注释,因此可以通过反射得到该注释对象
SOURCE 编译器将要丢弃的注释,不会保留在class文件中
@Target 指示注释类型所适用的元素的种类
ElementType[] value
枚举常量
ANNOTATION_TYPE 注释类型声明
CONSTRUCTOR 构造方法声明
FIELD 字段声明(包括枚举常量)
LOCAL_VARIABLE 局部变量声明
METHOD 方法声明
PACKAGE 包声明
PARAMETER 参数声明
TYPE 类、接口(包括注释类型)或枚举声明
为注解添加基础属性
定义基本类型的属性和应用属性:
在注解类中增加String color();
@MyAnnotation(color="red")
用反射方式获得注解对应的实例对象后,再通过该对象调用属性对应的方法
MyAnnotation a = (MyAnnotation)AnnotationTest.class.getAnnotation(MyAnnotation.class);
System.out.println(a.color());
可以认为上面这个@MyAnnotation是MyAnnotaion类的一个实例对象
为属性指定缺省值:
String color() default "yellow";
value属性:
String value() default "zxx";
如果注解中有一个名称为value的属性,且你只想设置value属性(即其他属性都采用默认值或者你只有一个value属性),那么可以省略value=部分,例如:@MyAnnotation("lhm")。
为数组添加高级属性
数组类型的属性
int [] arrayAttr() default {1,2,3};
@MyAnnotation(arrayAttr={2,3,4})
如果数组属性中只有一个元素,这时候属性值部分可以省略大括
枚举类型的属性
EnumTest.TrafficLamp lamp() ;
@MyAnnotation(lamp=EnumTest.TrafficLamp.GREEN)
注解类型的属性:
MetaAnnotation annotationAttr() default @MetaAnnotation("xxxx");
@MyAnnotation(annotationAttr=@MetaAnnotation(“yyy”) )
可以认为上面这个@MyAnnotation是MyAnnotaion类的一个实例对象,同样的道理,可以认为上面这个@MetaAnnotation是MetaAnnotation类的一个实例对象,调用代码如下:
MetaAnnotation ma = myAnnotation.annotationAttr();
System.out.println(ma.value());
注解的详细语法可以通过看java语言规范了解,即看java的language specification
打开j3toc.html页面后,搜索annotation,进入9.6节,在其中的第5个Discussion部分就有属性类型的讲解说明,可以知道枚举的属性类型包括:基本数据类型,String,Class,枚举,其他注解,以及这些类型的数组。
AnnotationTest:用于测试
package cn.itcast.day2; @MyAnnotation(arrs={1,2,1,3}) public class AnnotationTest { /** * @param args */ public static void main(String[] args) { // TODO Auto-generated method stub if(AnnotationTest.class.isAnnotationPresent(MyAnnotation.class)){ MyAnnotation myAnnotation = AnnotationTest.class.getAnnotation(MyAnnotation.class); System.out.println(myAnnotation.color()); System.out.println(myAnnotation.value()); System.out.println(myAnnotation.arrs().length); System.out.println(myAnnotation.annotationArr().age()); } } }
MyAnnotation:我创建的注解
package cn.itcast.day2; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; import java.lang.reflect.Method; import java.lang.reflect.Type; @Retention(RetentionPolicy.RUNTIME) @Target({ElementType.METHOD,ElementType.TYPE}) public @interface MyAnnotation { String color() default "asd"; String value() default "yyy"; int[] arrs() ; MetaAnnotation annotationArr() default @MetaAnnotation(age=1); }
MetaAnnotation
package cn.itcast.day2; public @interface MetaAnnotation { String value() default "aaa"; int age(); }
7.泛型
泛型出现的意义:希望在定义集合的时候,明确表示你要向集合中装哪种类型的数据,无法加入指定类型之外的数据
泛型是给Javac编译器看的
泛型是提供给javac编译器使用的,可以限定集合中的输入类型,让编译器挡住源程序中的非法输入,编译器编译带类型说明的集合时会去除掉“类型”信息,使程序运行效率不受影响,对于参数化的泛型类型,getClass()方法的返回值和原始类型完全一样。由于编译生成的字节码会去掉泛型的类型信息,只要能跳过编译器,就可以往某个泛型集合中加入其它类型的数据,例如,用反射得到集合,再调用其add方法即可。
//使用反射跳过泛型的类型限制 ArrayList<Integer> c = new ArrayList<Integer>(); c.getClass().getMethod("add", Object.class).invoke(c, "abc"); System.out.println(c.get(0));//打印结果为abc
//泛型会在编译时的时候去除“类型”的信息 Vector<Integer> vi = new Vector<Integer>(); Vector<String> vs = new Vector<String>(); System.out.println(vi.getClass()== vs.getClass()); //结果是true
深入的了解泛型
ArrayList<E>类定义和ArrayList<Integer>类引用中涉及如下术语:
整个称为ArrayList<E>泛型类型
ArrayList<E>中的E称为类型变量或类型参数
整个ArrayList<Integer>称为参数化的类型
ArrayList<Integer>中的Integer称为类型参数的实例或实际类型参数
ArrayList<Integer>中的<>念着typeof
ArrayList称为原始类型
参数化类型与原始类型的兼容性:
参数化类型可以引用一个原始类型的对象,编译报告警告,例如,
Collection<String> c = new Vector();//可不可以,不就是编译器一句话的事吗?
原始类型可以引用一个参数化类型的对象,编译报告警告,例如,
Collection c = new Vector<String>();//原来的方法接受一个集合参数,新的类型也要能传进去
参数化类型不考虑类型参数的继承关系:
Vector<String> v = new Vector<Object>(); //错误!///不写<Object>没错,写了就是明知故犯
Vector<Object> v = new Vector<String>(); //也错误!
编译器不允许创建泛型变量的数组。即在创建数组实例时,数组的元素不能使用参数化的类型,例如,下面语句有错误:
Vector<Integer> vectorList[] = new Vector<Integer>[10];
思考题:下面的代码会报错误吗?
Vector v1 = new Vector<String>();
Vector<Object> v = v1;
//编译通过
泛型中的?通配符
使用?通配符可以引用其他各种参数化的类型,?通配符定义的变量主要用作引用,可以调用与参数化无关的方法,不能调用与参数化有关的方法
例如:Collection<?> a可以与任意参数化的类型匹配,但到底匹配的是什么类型,只有以后才知道,所以,
a=new ArrayList<Integer>和a=new ArrayList<String>都可以,但a.add(new Date())或a.add(“abc”)都不行,
限定通配符的上边界:
正确:Vector<? extends Number> x = new Vector<Integer>();
错误:Vector<? extends Number> x = new Vector<String>();
限定通配符的下边界:
正确:Vector<? super Integer> x = new Vector<Number>();
错误:Vector<? super Integer> x = new Vector<Byte>();
只有引用类型才能作为泛型方法的实际参数
swap(new int[]{},3,5); //错误 swap(new String[]{},3,5); //正确 public static <E> void swap(E[] a, int i, int j){ E temp = a[i]; a[i] = a[j]; a[j] = temp; }
类型参数的类型的判断
编译器判断范型方法的实际类型参数的过程称为类型推断,类型推断是相对于知觉推断的,其实现方法是一种非常复杂的过程。
根据调用泛型方法时实际传递的参数类型或返回值的类型来推断,具体规则如下:
当某个类型变量只在整个参数列表中的所有参数和返回值中的一处被应用了,那么根据调用方法时该处的实际应用类型来确定,这很容易凭着感觉推断出来,即直接根据调用方法时传递的参数类型或返回值来决定泛型参数的类型,例如:
swap(new String[3],3,4) à static <E> void swap(E[] a, int i, int j)
当某个类型变量在整个参数列表中的所有参数和返回值中的多处被应用了,如果调用方法时这多处的实际应用类型都对应同一种类型来确定,这很容易凭着感觉推断出来,例如:
add(3,5) à static <T> T add(T a, T b)
当某个类型变量在整个参数列表中的所有参数和返回值中的多处被应用了,如果调用方法时这多处的实际应用类型对应到了不同的类型,且没有使用返回值,这时候取多个参数中的最大交集类型,例如,下面语句实际对应的类型就是Number了,编译没问题,只是运行时出问题:
fill(new Integer[3],3.5f) à static <T> void fill(T[] a, T v)
当某个类型变量在整个参数列表中的所有参数和返回值中的多处被应用了,如果调用方法时这多处的实际应用类型对应到了不同的类型,并且使用返回值,这时候优先考虑返回值的类型,例如,下面语句实际对应的类型就是Integer了,编译将报告错误,将变量x的类型改为float,对比eclipse报告的错误提示,接着再将变量x类型改为Number,则没有了错误:
int x =(3,3.5f) à static <T> T add(T a, T b)
参数类型的类型推断具有传递性,下面第一种情况推断实际参数类型为Object,编译没有问题,而第二种情况则根据参数化的Vector类实例将类型变量直接确定为String类型,编译将出现问题:
copy(new Integer[5],new String[5]) à static <T> void copy(T[] a,T[] b);
copy(new Vector<String>(), new Integer[5]) à static <T> void copy(Collection<T> a , T[] b);
定义泛型类
public class GenericDao<T> { private T field1; public void save(T obj){} public T getById(int id){} }
注意:
在对泛型类型进行参数化时,类型参数的实例必须是引用类型,不能是基本类型。
当一个变量被声明为泛型时,只能被实例变量、方法和内部类调用,而不能被静态变量和静态方法调用。因为静态成员是被所有参数化的类所共享的,所以静态成员不应该有类级别的类型参数
通过反射获得泛型的参数化类型(难)
Method methodApply = GenericTest.class.getDeclaredMethod("applyGeneric", Vector.class); Type[] paramTypes = methodApply.getGenericParameterTypes(); ParameterizedType param = (ParameterizedType)(paramTypes[0]); System.out.println(param.getRawType()); System.out.println(param.getActualTypeArguments()[0]); System.out.println(((Class)param.getRawType()).getName() + "<" + ((Class)param.getActualTypeArguments()[0]).getName() + ">"); public static void applyGeneric(Vector<String> v){ }
泛型方法的一些练习
1. public static <T> T autoConvertType(Object obj) { return (T)obj; } 测试代码: Object xxx = "abc"; String yyy = autoConvertType(xxx); 泛型方法的另外一个常见应用就是调用者无需对返回值进行类型转换,建议大家看看传智播客的巴巴运动网视频中的BaseDao的代码。 2. public static <T> T[] fill(T[] a, T v) { for(int i = 0; i < a.length; i++) a[i] = v; return a; } 3. public static <E> void printCollection(Collection<E> cols) { for(E obj:cols) { System.out.println(obj); } } 4.这个需要使用泛型方法进行定义,如果使用如下形式: static void copy(Collection a, Object[] b); 否则有可能出现A类型的数据复制进B类型的数组中的情况。 使用泛型方法的定义形式为: static <T> void copy(Collection<T> a,T[] b);
8.Lock、Condition
/* jdk1.5以后将同步和锁封装成了对象。 并将操作锁的隐式方式定义到了该对象中, 将隐式动作变成了显示动作。 Lock接口: 出现替代了同步代码块或者同步函数。将同步的隐式锁操作变成现实锁操作。 同时更为灵活。可以一个锁上加上多组监视器。 lock():获取锁。 unlock():释放锁,通常需要定义finally代码块中。 Condition接口:出现替代了Object中的wait notify notifyAll方法。 将这些监视器方法单独进行了封装,变成Condition监视器对象。 可以任意锁进行组合。 await(); signal(); signalAll(); */ import java.util.concurrent.locks.*; class Resource { private String name; private int count = 1; private boolean flag = false; // 创建一个锁对象。 Lock lock = new ReentrantLock(); //通过已有的锁获取该锁上的监视器对象。 // Condition con = lock.newCondition(); //通过已有的锁获取两组监视器,一组监视生产者,一组监视消费者。 Condition producer_con = lock.newCondition(); Condition consumer_con = lock.newCondition(); public void set(String name)// t0 t1 { lock.lock(); try { while(flag) // try{lock.wait();}catch(InterruptedException e){}// t1 t0 try{producer_con.await();}catch(InterruptedException e){}// t1 t0 this.name = name + count;//烤鸭1 烤鸭2 烤鸭3 count++;//2 3 4 System.out.println(Thread.currentThread().getName()+"...生产者5.0..."+this.name);//生产烤鸭1 生产烤鸭2 生产烤鸭3 flag = true; // notifyAll(); // con.signalAll(); consumer_con.signal(); } finally { lock.unlock(); } } public void out()// t2 t3 { lock.lock(); try { while(!flag) // try{this.wait();}catch(InterruptedException e){} //t2 t3 try{cousumer_con.await();}catch(InterruptedException e){} //t2 t3 System.out.println(Thread.currentThread().getName()+"...消费者.5.0......."+this.name);//消费烤鸭1 flag = false; // notifyAll(); // con.signalAll(); producer_con.signal(); } finally { lock.unlock(); } } } class Producer implements Runnable { private Resource r; Producer(Resource r) { this.r = r; } public void run() { while(true) { r.set("烤鸭"); } } } class Consumer implements Runnable { private Resource r; Consumer(Resource r) { this.r = r; } public void run() { while(true) { r.out(); } } } class ProducerConsumerDemo2 { public static void main(String[] args) { Resource r = new Resource(); Producer pro = new Producer(r); Consumer con = new Consumer(r); Thread t0 = new Thread(pro); Thread t1 = new Thread(pro); Thread t2 = new Thread(con); Thread t3 = new Thread(con); t0.start(); t1.start(); t2.start(); t3.start(); } }