泛型是 Java 中另一个使用非常广泛的特性,泛型中的「泛」指的是参数类型不固定, 也就是说什么数据类型都可以,它还有另一个名字,就是「参数化类型」——也就是说不仅 数据本身是参数,数据类型也可以被指定为参数——类、接口或者方法可以被不同类型的参数所重用。你只需告诉编译器要使用什么类型,剩下的细节交给它来处理。
通过泛型可以完成对一组类的操作对外开放相同的接口
//在实例化泛型类时,必须指定T的具体类型 public class Generic<T>{ //key这个成员变量的类型为T,T的类型由外部指定 private T key; public Generic(T key) { //泛型构造方法形参key的类型也为T,T的类型由外部指定 this.key = key; } public T getKey(){ //泛型方法getKey的返回值类型为T,T的类型由外部指定 return key; } } //可以传入类型不同的参数 Generic generic = new Generic("1"); Generic generic1 = new Generic(1); Generic generic2 = new Generic(1.11); Generic generic3 = new Generic(false);
未传入泛型实参时,在声明类的时候,需将泛型的声明也一起加到类中。
public interface Generator<T> { public T next(); } class FruitGenerator<T> implements Generator<T>{ @Override public T next() { return null; } }
传入实参时:
public class FruitGenerator implements Generator<String> { private String[] fruits = new String[]{"Apple", "Banana", "Pear"}; @Override public String next() { Random rand = new Random(); return fruits[rand.nextInt(3)]; } }
//可变参数 public <T> void printMsg( T... args){ for(T t : args){ Log.d("泛型测试","t is " + t); } } printMsg("111",222,"aaaa","2323.4",55.55);
练习1: 上界通配符有一个特性:只能获取,不能添加,请通过搜索引擎了解这句话的含义并 用代码实现它
答:上界只取不放是指当操作一个泛型参数为上界通配符<? extends T>的引用时,只能操作其返回泛型参数相关的方法(返回值为T的方法)而无法操作设置泛型参数相关(修改T)的方法。
练习2: 下界通配符有一个特性:只能添加,不能获取,请通过搜索引擎了解这句话的含义并 用代码实现它。
答:而下界只存不取则是指当操作一个泛型参数为下届通配符<? super T>的引用时,只能操作设置泛型参数相关(修改T)而无法操作返回泛型参数相关的方法(返回值为T的方法)。
package com.company; public class Task08_01 { public static void main(String[] args){ /* * 上界<? extends T>:不能往里存,只能往外取 * <? extends Fruit>会使盘子里放东西的set()方法失效,但是get()方法还有效 * */ Plate<? extends Fruit> p1 = new Plate<Apple>(new Apple()); /* * p.set(new Fruit()); 报错 * p.set(new Fruit());报错 */ //读取出来的东西只能存放在Fruit或者它的基类里 Fruit newFruit1 = p1.get(); Object newFruit2 = p1.get(); //Apple newFruit3 = p.get();报错 /* * 下界<? super T>:不影响往里存,但是往外取的只能放在Object对象里 * <? super Fruit>:get()方法获得的对象只能放在Object对象里,set()方法正常 * */ Plate<? super Fruit> p2 = new Plate<Fruit>(new Fruit()); //存入元素正常 p2.set(new Fruit()); p2.set(new Apple()); //取出来的东西只能放在Object对象中 Object newFruit4 = p2.get(); } static class Fruit{ } static class Apple extends Fruit{ } static class Plate<T>{ private T item; public Plate(T t){ item = t; } public void set(T t){ item = t; } public T get(){ return item; } } }
总结:
Java 有一种特性,能够在程序运行时获取某个类对象的所有的属性、方法、包信息和 注解等等,并且还能改变对象的属性值、调用对象方法、验证注解信息,这种动态获取信息、 动态改变属性值和动态调用方法的能力,就是反射(Reflection)。反。反射是将类中各个成分映射成各个对象。
**练习:**使用反射解析某个你创建的类,把类中所有的属性、方法、接口都提取出来。要求: 通过三种不同的方式获取 Class 对象; 修改解析出来的类的属性值,然后再给类添加新的属性; 调用类方法(包括私有方法、静态方法和构造方法),执行后打印结果; 解析出该类的父类对象,并同样修改其父类的属性值并调用父类的方法。
package com.company; import java.awt.datatransfer.FlavorEvent; import java.lang.reflect.Constructor; import java.lang.reflect.Field; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; public class Task09_反射 { public static void main(String[] args) throws NoSuchFieldException, NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException { //第一种方式获得Class对象 Student student1 = new Student(); Class stuClass = student1.getClass(); //第二种方式获得Class对象 Class stuClass2 = Student.class; //第三种方式获得Class对象 try{ Class stuClass3 = Class.forName("com.company.Student"); }catch(ClassNotFoundException e){ e.printStackTrace(); } //获取Student类的所有属性 System.out.println("获取所有属性:"); Field[] fieldArray = stuClass.getDeclaredFields(); for(Field f : fieldArray) System.out.println(f); //为属性设置值 System.out.println("为属性设置值:"); Field f = stuClass.getField("name"); Object obj = stuClass.getConstructor().newInstance();//产生Student对象 f.set(obj,"FXW"); Student stu = (Student)obj; System.out.println("验证名字"+stu.name); //调用构造方法 System.out.println("调用构造方法"); Constructor con = stuClass.getConstructor(null); System.out.println(con); Object obj2 = con.newInstance(); //调用私有方法 System.out.println("调用私有方法"); Method m = stuClass.getDeclaredMethod("show",int.class); System.out.println(m); m.setAccessible(true);//解除私有限定 Object result = m.invoke(obj,20); System.out.println(result); } } class Student{ public String name; protected int age; char sex; private String number; public Student(){ System.out.println("共有无参构造方法"); } public Student(String name,int age){ System.out.println("共有有参构造方法"); } private String show(int age){ System.out.println("私有方法"); return "a"; } }
练习1: 自定义一个 ElementType 类别为 METHOD,RetentionPolicy 类别为 RUNTIME 的 注解,任务要求:创建一个类,类的方法使用自定义的注解; 通过反射解析类注解,判断该方法是否有注解,如果有则打印出注解的内容。
注:
package com.company; import java.lang.annotation.*; import java.lang.reflect.Method; public class Task10_01 { public static void main(String[] args) throws NoSuchMethodException { //获取类 Class stuClass = Student.class; //获取方法 Method method = stuClass.getMethod("show"); //获取注解对象 M m = method.getAnnotation(M.class); System.out.println(m.value()); } @Retention(RetentionPolicy.RUNTIME) @Target(value = {ElementType.METHOD}) public @interface M { String value() default "这是默认注释内容"; } class Student{ @M public void show(){ System.out.println("a"); } } }
练习2: 自定义一个 ElementType 类别为 FIELD,RetentionPolicy 类别为 CLASS 的注解, 任务要求: 创建一个类,给类的某个字段使用自定义的注解; 通过反射解析类注解,判断该方法是否有注解,如果有则打印出注解的内容。
package com.company; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; import java.lang.reflect.Field; public class Task10_02 { public static void main(String[] args) throws NoSuchFieldException { Class stuClass = Student.class; Field f = stuClass.getDeclaredField("name"); M m = f.getAnnotation(M.class); System.out.println(m.value()); } @Target({ElementType.FIELD}) @Retention(RetentionPolicy.RUNTIME) public @interface M { String value() default "这是默认注释内容"; } public class Student{ @M public String name; } }
总结:
@Target(ElementType.TYPE) //接口、类、枚举
@Target(ElementType.FIELD) //字段、枚举的常量
练习3: 模仿上面的嵌套注解,除了可以字段类型的注解外,再加上字段长度和是否可以为空 的注解,并加入到@TableColumn 注解中去
package com.company; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; public class Task10_03 { @Target(ElementType.FIELD) @Retention(RetentionPolicy.RUNTIME) public @interface ColumnLength{ int value() default 0; } public @interface ColumnEmptyOrNot{ @Target(ElementType.FIELD) @Retention(RetentionPolicy.RUNTIME) public @interface ColumnLength{ boolean value() default true; } } @Target(ElementType.FIELD) @Retention(RetentionPolicy.RUNTIME) public @interface TableColumn{ ColumnLength columnlength() default @ColumnLength; ColumnEmptyOrNot columnEmptyOrNot() default @ColumnEmptyOrNot; } }
练习4: 自定义嵌套注解,用来描述用户类中的「岗位」字段。
第一层注解定义岗位所在的部门
第二层注解定义岗位名称和描述。
//岗位名称 @Target({ElementType.FIELD}) @Retention(RetentionPolicy.RUNTIME) public @interface PostName { String postName() default ""; } //岗位描述 @Target({ElementType.FIELD}) @Retention(RetentionPolicy.RUNTIME) public @interface PostDescription { String postDescription() default ""; } //岗位所在部门 @Target({ElementType.FIELD}) @Retention(RetentionPolicy.RUNTIME) public @interface Department { String departmentName() default ""; PostName postName() default @PostName; PostDescription postDescription() default @PostDescription; } public class Position { @Department(postName = @PostName , postDescription = @PostDescription) private String postName; private String postDescription; }