目录
1,注解:
1.1,内置注解
- @Override:定义在java.lang.Override中,此注解只适用于修饰方法,表示一个方法声明打算重写超类中的另一个方法声明
- @Deprecated:定义在java.lang.Deprecated中,此注释可用于修饰方法、属性、类,表示不鼓励程序员使用这样的元素,通常是因为它很危险或存在更好的选择
- @SuppressWarnings:定义在java.lang.SuppressWarnings中,用来抑制编译时的警告信息
1.2,自定义注解
使用@interface自定义注解时,自动继承了java.lang.annotation.Annotation接口
使用格式如下:
- @interface用来声明一个注解,格式为 public @interface 注解名{定义体}
- 其中的每一个方法实际上时声明了一个配置参数
- 方法的名称就是参数的名称
- 返回值类型就是参数的类型(返回值类型只能是基本类型、Class、String、enum)
- 可以通过default来声明参数的默认值
- 如果只有一个参数成员,一般参数名为valueElementType
1.3,元注解
元注解的作用就是负责注解其他注解。java定义了4个标准的meta-annotation类型,它们被用来提供对其他annotation的类型作说明,这些类型和它们所支持的类再java.lang.annotation包中可以找到
- @Target
- 作用:用于描述注解的使用范围(即:被描述的注解可以用在什么地方)
- 使用范围与取值对应如下:(ElementType是枚举类型,定义在java.lang.annotation中)
- package 包:ElementType.Package
- 类、接口、枚举、Annotation类型:ElementType.TYPE
- 类型成员(方法、构造方法、成员变量、枚举值):ElementType.CONSTRUCTOR用于描述构造器,ElementType.FIELD用于描述域,ElementType.METHOD用于描述方法
- 方法参数和本地变量:ElementType.LOCAL_VARIABLE用于描述局部变量,ElementType.PARAMETER用于描述参数
- 例:@Target(value=ElementType.TYPE)
- @Retention
- 作用:表示需要在什么级别保存该注释信息,用于描述注解的生命周期
- 作用与取值对应如下
- 在源文件中有效(即源文件保留):RetentionPolicy.SOURCE
- 在class文件中有效(即class保留):RetentionPolicy.CLASS
- 在运行时有效(运行时保留,可以被反射机制读取):RetentionPolicy.RUNTIME
- @Documented
- @Inherited
1.4,注解示例:
//定义注解
@Target(value= ElementType.TYPE)
@Retention(value= RetentionPolicy.RUNTIME)//在运行时也有效,可以被反射机制读取
public @interface DecorateCls {
String value();
}
@Target(value=ElementType.FIELD)
@Retention(value=RetentionPolicy.RUNTIME)
public @interface field {
String name();
String type();
int length();
}
//上面两个自定义注解用于为StudentName提供额外信息,这些额外信息可以被反射机制读取
@DecorateCls(value="td_table")
public class StudentName {
@field(name="uname",type="varchar",length=10)
private String studentName;
@field(name="id",type="int",length=10)
private int id;
@field(name="age",type="int",length=10)
private int age;
public String getStudentName() {
return studentName;
}
public void setStudentName(String studentName) {
this.studentName = studentName;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
}
2,反射
2.1,反射机制
- 指的是可以运行时加载、探知、使用编译期间完全未知的类。
- 程序在运行状态中,可以动态加载一个只有名称的类,对于任意一个已加载的类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性;
- 加载完类之后,在堆内存中,就产生了一个Class类型的对象(一个类只有一个Class对象),这个对象就包含了完整的 类的结构信息。我们可以通过这个对象看到类的结构。这个对象就像一面镜子,透过这个镜子看到类的结构,所以,我们形象的称之为:反射
2.2,获取Class对象
- 运用getClass();
- 运用Class.forName()(最常使用)
- 运行.class语法
2.3,反射机制的常见作用
- 动态加载类、动态获取类的信息(属性、方法、构造器)
- 动态构造对象
- 动他i调用类和对象的任意方法、构造器
- 动态调用和处理属性
- 获取泛型信息
- 处理注解
- 反射示例代码:
-
Class<ReflectClass> cls= (Class<ReflectClass>) Class.forName("注解与反射.ReflectClass"); //通过反射API调用构造方法 Constructor<ReflectClass> constructor=cls.getDeclaredConstructor(String.class,int.class,int.class); ReflectClass reflectClass=constructor.newInstance("nihao",2,2); System.out.println(reflectClass.getAge()); //通过反射API调用普通方法 Method method=cls.getDeclaredMethod("getName"); Method method1=cls.getDeclaredMethod("setName", String.class); method1.invoke(reflectClass,"LiHua"); String name= (String) method.invoke(reflectClass); System.out.println(name); //通过反射操作私有方法 Method method2=cls.getDeclaredMethod("p"); method2.setAccessible(true); method2.invoke(reflectClass); //通过反射API操作public属性 Field f=cls.getDeclaredField("name"); f.set(reflectClass,"nihao"); System.out.println(reflectClass.getName()); //通过反射API操作私有属性 System.out.println(reflectClass.getAge()); Field f2=cls.getDeclaredField("age"); f2.setAccessible(true);//为true时这个属性不会做安全检查,直接访问,也可以通过这种方式访问私有方法 f2.set(reflectClass,10); System.out.println(reflectClass.getAge()); System.out.println(f2.get(reflectClass));//访问f2对应属性的另一种方法
2.3,反射机制的性能问题
我们可以通过调用setAccessible方法来启用和禁用安全检查的开关,值为true,则知识反射的对象在使用时应该取消java语言访问检查。值为false则指示反射的对象应该实施java语言访问检查。并不是为true就能访问,为false就不能访问
2.4,反射操作泛型
- java采用泛型擦除的机制来引用泛型,java中的泛型仅仅是给编译器javac使用的,确保数据的安全性和免去强制类型转换中的麻烦。但是,一旦编译完成,所有的和泛型相关的类型全部擦除
- 为了通过反射操作这些类型以迎合实际开发的需要,java就新增了ParameterizedType,GenericArrayType,TypeVariable和WildcardType几种类型来表示不能被归一到Class类中的类型但是又和原始类型齐名的类型
- ParameterizedType表示一种无参化的类型,比如Collection<String>
- GenericArrayType表示一种元素类型必须是参数化类型或者类型变量的数组类型
- TypeVariable是各种类型变量的公共父接口
- WildcardType代表一种通配符类型表达式,比如?,? extends Number, ? super Integer
-
//获得指定方法参数泛型信息 Class<GmericDemo> cls=(Class<GmericDemo>)Class.forName("注解与反射.GmericDemo"); Method method=cls.getDeclaredMethod("test01", Map.class, List.class); Type[] t=method.getGenericParameterTypes(); for(Type x:t){ System.out.println("#"+x); if(x instanceof ParameterizedType){ Type[] genericTypes=((ParameterizedType) x).getActualTypeArguments(); for(Type genericType:genericTypes){ System.out.println("泛型类型:"+genericType); } } } System.out.println(); //获得指定方法的返回值参数类型 Type returnType=method.getGenericReturnType(); System.out.println("#"+returnType); if(returnType instanceof ParameterizedType){ Type[] genericTypes =((ParameterizedType) returnType).getActualTypeArguments(); for(Type type:genericTypes){ System.out.println("返回值,泛型类型"+type); } }
2.5,反射操作注解
//通过反射读取注解参数值
Class cls=Class.forName("注解与反射.StudentName");
//获取类的注解:方法一
// Annotation[] annotations=cls.getAnnotations();
// for(Annotation a:annotations){
// System.out.println(a);
// }
//获取类的注解:方法二
DecorateCls ano=(DecorateCls)cls.getAnnotation(DecorateCls.class);
System.out.println(ano.value());
//获取对应属性的注解
Field fd=cls.getDeclaredField("studentName");
field f=fd.getAnnotation(field.class);
//获取注解的参数值
System.out.println(f.name()+" "+f.type()+" "+f.length());
2.6,动态编译(通过反射调用 main方法)
- java6.0引入了动态编译机制。
- 动态编译的应用场景:
- 可以做一个浏览器端编写java代码,上传服务器编译和运行的在线评测系统
- 服务器动态加载某些类文件进行编译
- 动态编译的两种做法:
- 通过Runtime调用javac,启动新的进程去操作
- Runtime run=Runtime.getRuntime();
- Process process=run.exec("javac -cp 路径名 文件名");
- 通过JavaCompiler动态编译
- 动态编译:
JavaCompiler compiler= ToolProvider.getSystemJavaCompiler(); int result=compiler.run(null,null,null, "src/注解与反射/Test.java"); System.out.println(result==0?"编译成功":"编译失败");
- run函数的四个参数与返回值
- 函数声明为:int run(InputStream in, OutputStream out, OutputStream err, String... arguments);
- 第一个参数:为java编译器提供参数
- 第二个参数:得到java编译器的输出信息
- 第三个参数:接收编译器的错误信息
- 第四个参数:可变参数(是一个String数组)能传入一个或多个java源文件
- 返回值:0表示编译成功,非0表示编译失败
- run函数的四个参数与返回值
- 动态编译:
- 通过反射运行编译好的类(两种方法)
- 通过Runtime.getRuntime()运行启动新的进程运行
- Runtime run=Runtime.getRuntime();
- Process process=run.exec("java -cp"+dir+" "+classFile);
- 通过反射调用main方法时
-
//使用反射运行编译好的类 public static void runJavaClassByReflect(String dir, String classFile){ //通过反射运行编译好的类 try { URL[] url=new URL[]{new URL("file:/"+dir)}; URLClassLoader loader=new URLClassLoader(url); Class c=loader.loadClass(classFile); c.getMethod("main",String[].class).invoke(null,(Object)new String[]{}); } catch (Exception e) { e.printStackTrace(); } }
- 如:Method m=cls.getMethod('main",String[].class);m.invoke(null,(Object)new String[]{"aa","bb"});
- 因为可变参数是JDK5.0之后才有的,如果上面的newString[]{"aa","bb"}前没加(Object)的话,上面代码会编译成如m.invoke(null,"aa","bb")这般,就发生了参数个数不匹配的问题,所以,Object不能省
-
- 通过Runtime.getRuntime()运行启动新的进程运行
- 通过Runtime调用javac,启动新的进程去操作