黑马程序员_java基础笔记(10)...JDK1.5的新特性

时间:2022-04-27 18:40:23

—————————— ASP.Net+Android+IOS开发、.Net培训、期待与您交流! ——————————

1:静态导入。2:for—each循环。3:自动装箱/拆箱。4:可变参数。5:枚举。6:注解。7:泛型

1,静态导入

import语句可以导入一个类或某个包中的所有类
import static语句导入一个类中的某个静态方法或所有静态方法
 

2,for—each循环

语法:
for ( type 变量名:集合变量名 )  { … }
注意事项:
  迭代变量必须在( )中定义!
  只能用在数组或实现Iterable接口的集合类上。
举例:

public static int add(int x,int ...args) {

int sum = x;

for(int arg:args) {

  sum += arg;

  }

return sum;

}

3,自动装箱/拆箱

自动装箱:
  Integer num1 = 12;
自动拆箱:
  System.out.println(num1 + 12);
基本数据类型的对象缓存:

  Integer num1 = 12;

  Integer num2 = 12;

  System.out.println(num1 == num2);  //true

  Integer num3 = 129;

  Integer num4 = 129;

  System.out.println(num3 == num4);  //false

  Integer num5 = Integer.valueOf(12);

  Integer num6 = Integer.valueOf(12);

  System.out.println(num5 == num6);  //true

Flyweight(享元)模式是构造型模式之一,他通过与其他类似对象共享数据来减少内存占用。也就是说在一个系统中如果有多个相同的对象,那么只共享一份就可以了,不必每个都去实例化一个对象。在Flyweight模式中,由于要产生各种各样的对象,所以在Flyweight模式中常出现Factory模式。Flyweight的内部状态是用来共享的,Flyweight factory负责维护一个对象储存池(Flyweight Pool)来内存内部状态的对象。为了调用方便,FlyweightFactory类一般使用Singleton模式实现。Flyweight模式是一个提高程序效率和性能的模式,会大大加快程序的运行速度。

Flyweight模式的有效性很大程度上取决于如何使用它以及在何处使用它。当一下情况成立时使用Flyweight模式:1,一个应用程序使用了大量的对象。2.完全由于使用大量的对象,造成很大的储存开销。3,对象的大多数状态都可以变为外部状态。4,如果删除对象以外的状态那么可以用相对减少的共享对象取代很多组对象。5,应用程序不依赖于对象标识。

4,可变参数

Overlead重载   Override重写

特点:

  只能出现在参数列表的最后

  ...位于参数类型和参数名之间,前后有无空格都可以

  调用可变参数的方法时,编译器为该可变参数隐含创建一个数组,在方法体中以数组的形式访问可变参数。

举例代码:

public static void main(String[] args) {

  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;

}

5,枚举

枚举就是要让某个类型的变量的取值只能为若干个固定值中的一个,否则,编译器就会报错。
枚举可以让编译器在编译时就可以控制源程序中填写的非法值,普通变量的方式在开发阶段无法实现这一目标。
 
用普通类如何实现枚举功能。
  私有的构造方法
  每个元素分别用一个公有的静态成员变量表示
  可以有若干公有方法或抽象方法。采用抽象方法定义nextDay就将大量的if.else语句转移成了一个个独立的类。
 
枚举就相当于一个类,其中也可以定义构造方法、成员变量、普通方法和抽象方法。
枚举元素必须位于枚举体中的最开始部分,枚举元素列表的后要有分号与其他成员分隔。把枚举中的成员方法或变量等放在枚举元素的前面,编译器报告错误。

/*
编写一个关于星期几的枚举,要求:
1.枚举值:MON,TUE,WED,THU,FRI,SAT,SUN
2.该枚举要有一个方法,调用该方法返回中文格式的星期。
*/

public enum WeekDay {
  SUN(0,"星期天"){
    public WeekDay getNextDay(){
      return MON;
    }
  },
  MON(1,"星期一"){
    public WeekDay getNextDay(){
      return TUE;
    }
  },
  TUE(2,"星期二"){
    public WeekDay getNextDay(){
      return WED;
    }
  },
  WED(3,"星期三"){
    public WeekDay getNextDay(){
      return THU;
    }
  },
  THU(4,"星期四"){
    public WeekDay getNextDay(){
      return FRI;
    }
  },
  FRI(5,"星期五"){
    public WeekDay getNextDay(){
      return SAT;
    }
  },
  SAT(6,"星期六"){
    public WeekDay getNextDay(){
      return SUN;
    }
  };
  private String chineseFormat;//封装每个对象对应的中文格式
  private Integer digitalFormat;//封装每个对象对应的数字格式
  private WeekDay(Integer digitalFormat,String chineseFormat){
    this.digitalFormat = digitalFormat;
    this.chineseFormat = chineseFormat;
  }
  public String getChineseFormat(){
    return this.chineseFormat;
  }
  public Integer getDigitalFormat(){
    return this.digitalFormat;
  }
  public abstract WeekDay getNextDay();
}

 

6,注解

  注解相当于一种标记,在程序中加了注解就等于为程序打上了某种标记,以后,javac编译器,开发工具和其他程序可以用反射来了解你的类及各种元素上有无何种标记,看你有什么标记,就去干相应的事。标记可以加在包,类,字段,方法,方法的参数以及局部变量上。
  看java.lang包,可看到JDK中提供的最基本的annotation。

枚举和注解都是特殊的类,不能用new 创建它们的实例对象,创建枚举的实例对象就是在其中增加元素。

在程序中创建出一个注解的实例对象,直接用@放上一个标记即可

Tiger提供了三种标准的Annotation类型:
  @SuppressWarnings("deprecation") 会关掉class、method、field的编译器警告
  @Deprecated 指出一个属性或方法已经被废弃
  @Override 指出一个发方法覆写(override)父类的方法
注解的应用结构图
黑马程序员_java基础笔记(10)...JDK1.5的新特性
根据发射测试的问题,引出@Retention元注解的讲解,其三种取值:RetetionPolicy.SOURCE、RetetionPolicy.CLASS、RetetionPolicy.RUNTIME;分别对应:java源文件——>class文件——>内存中的字节码。
 
定义基本类型的属性和应用属性:
  在注解类中增加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。
 

7,泛型

ArrayList<E>类定义和ArrayList<Integer>类引用中涉及如下术语:
 
  ①整个称为ArrayList<E>泛型类型
 
  ②ArrayList<E>中的E称为类型参数变量
 
  ③整个ArrayList<Integer>称为参数化的类型 (ParameterizedType)
 
  ④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<? 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>();
提示:
  限定通配符总是包括自己。
  ?只能用作引用,不能用它去给其他变量赋值

Vector<? extends Number> y = new Vector<Integer>();

Vector<Number> x = y;

上面的代码错误,原理与Vector<Object > x11 = new Vector<String>();相似,只能通过强制类型转换方式来赋值。

 
能写出下面的代码即代表掌握了Java的泛型集合类:

HashMap<String,Integer> hm = new HashMap<String,Integer>();

hm.put("zxx",19);

hm.put("lis",18);

Set<Map.Entry<String,Integer>> mes= hm.entrySet();

for(Map.Entry<String,Integer> me : mes) {

  System.out.println(me.getKey() + ":" + me.getValue());

}

C++用模板函数引用自定义泛型

如下函数的结构很相似,仅类型不同:
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;

}

C++用模板函数解决,只写一个通用的方法,它可以适应各种类型,示意代码如下:

template<class T>

T add(T x,T y) {

return (T) (x+y);

}

定义泛型方法:

①:Java的泛型方法没有C++模板函数功能强大,java中的如下代码无法通过编译:

  <T> T add(T x,T y) {

  return (T) (x+y);

  //return null;

  }

  用于放置泛型的类型参数的尖括号应出现在方法的其他所有修饰符之后和在方法的返回类型之前,也就是紧邻返回值之前。按照惯例,类型参数通常用单个大写字母表示。
②:交换数组中的两个元素的位置的泛型方法语法定义如下:

  static <E> void swap(E[] a, int i, int j) {

  E t = a[i];

  a[i] = a[j];

  a[j] = t;

  }//或用一个面试题讲:把一个数组中的元素的顺序颠倒一下

③:只有引用类型才能作为泛型方法的实际参数,swap(new int[3],3,5);语句会报告编译错误。
④:除了在应用泛型时可以使用extends限定符,在定义泛型时也可以使用extends限定符,例如,Class.getAnnotation()方法的定义。并且可以用&来指定多个边界,如<V extends Serializable & cloneable> void method(){}
⑤:普通方法、构造方法和静态方法中都可以使用泛型。
⑥:也可以用类型变量表示异常,称为参数化的异常,可以用于方法的throws列表中,但是不能用于catch子句中。
⑦:在泛型中可以同时有多个类型参数,在定义它们的尖括号中用逗号分,例如:

  public static <K,V> V getValue(K key) { return map.get(key);}

1.Java中的泛型类型(或者泛型)类似于 C++ 中的模板。但是这种相似性仅限于表面,Java 语言中的泛型基本上完全是在编译器中实现,用于编译器执行类型检查和类型推断,然后生成普通的非泛型的字节码,这种实现技术称为擦除(erasure)(编译器使用泛型类型信息保证类型安全,然后在生成字节码之前将其清除)。这是因为扩展虚拟机指令集来支持泛型被认为是无法接受的,这会为 Java 厂商升级其 JVM 造成难以逾越的障碍。所以,java的泛型采用了可以完全在编译器中实现的擦除方法。

例如,下面这两个方法,编译器会报告错误,它不认为是两个不同的参数类型,而认为是同一种参数类型。

private static void applyGeneric(Vector<String> v){

}

private static void applyGeneric(Vector<Date> v){

}

2.测试代码:

String[] strs = new String[]{"a","b","c"};

swap(strs,1,2);

System.out.println(Arrays.asList(strs));

4. 只有引用类型才能作为泛型方法的实际参数,对于add方法,使用基本类型的数据进行测试没有问题,这是因为自动装箱和拆箱了。 swap(new int[3],3.5);语句会报告编译错误,这是因为编译器不会对new int[3]中的int自动拆箱和装箱了,因为new int[3]本身已经是对象了,你想要的有可能就是int数组呢?它装箱岂不弄巧成拙了。

5.用下面的代码说明对异常如何采用泛型:

private static <T extends Exception> sayHello() throws T

{

try{

}catch(Exception e){

throw (T)e;

}}

类型参数的类型推断

编译器判断范型方法的实际类型参数的过程称为类型推断,类型推断是相对于知觉推断的,其实现方法是一种非常复杂的过程。
根据调用泛型方法时实际传递的参数类型或返回值的类型来推断,具体规则如下:
  ①当某个类型变量只在整个参数列表中的所有参数和返回值中的一处被应用了,那么根据调用方法时该处的实际应用类型来确定,这很容易凭着感觉推断出来,即直接根据调用方法时传递的参数类型或返回值来决定泛型参数的类型。
例如: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);

定义泛型类型:主要分两大类:1,自定义泛型方法。2,自定义泛型类和反射泛型。

如果类的实例对象中的多处都要用到同一个泛型参数,即这些地方引用的泛型类型要保持同一个实际类型时,这时候就要采用泛型类型的方式进行定义,也就是类级别的泛型,语法格式如下:

  public class GenericDao<T> {

    private T field1;

    public void save(T obj){}

    public T getById(int id){}

  }

类级别的泛型是根据引用该类名时指定的类型信息来参数化类型变量的,
例如,如下两种方式都可以:
  GenericDao<String> dao = null;
  new genericDao<String>();
注意:
  在对泛型类型进行参数化时,类型参数的实例必须是引用类型,不能是基本类型。
  当一个变量被声明为泛型时,只能被实例变量、方法和内部类调用,而不能被静态变量和静态方法调用。因为静态成员是被所有参数化的类所共享的,所以静态成员不应该有类级别的类型参数。
 

—————————— ASP.Net+Android+IOS开发、.Net培训、期待与您交流! ——————————

 详细请查看:http://edu.csdn.net