2022-08-10 day27 第一小组 王鸣赫
注解和反射
反射
反射的概念
什么是反射
反射 (Reflection) 是 Java 的特征之一,它允许运行中的 Java 程序获取自身的信息,并且可以操作类或对象的内部属性。
每个类都有一个自身的 Class 对象,包含了与类有关的信息。当编译一个新类时,会产生一个同名的 .class 文件,该文件内容保存着 Class 对象。类加载相当于 Class 对象的加载,类在第一次使用时才动态加载到 JVM 中。
反射可以提供运行时的类信息,并且这个类可以在运行时才加载进来,甚至在编译时期该类的 .class 不存在也可以加载进来。
Class 和 java.lang.reflect 一起对反射提供了支持,java.lang.reflect 类库主要包含了以下三个类:
在运行时判断任意一个对象所属的类;
在运行时构造任意一个类的对象;
在运行时判断任意一个类所具有的成员变量和方法(通过反射甚至可以调用 private 方法);
在运行时调用任意一个对象的方法
反射的优点
可扩展性:应用程序可以利用全限定名创建可扩展对象的实例,来使用来自外部的用户自定义类。
类浏览器和可视化开发环境:一个类浏览器需要可以枚举类的成员。可视化开发环境(如 IDE)可以从利用反射中可用的类型信息中受益,以帮助程序员编写正确的代码。
调试器和测试工具:调试器需要能够检查一个类里的私有成员。测试工具可以利用反射来自动地调用类里定义的可被发现的 API 定义,以确保一组测试中有较高的代码覆盖率。
反射的缺点
性能开销:反射涉及了动态类型的解析,所以 JVM 无法对这些代码进行优化。因此,反射操作的效率要比那些非反射操作低得多。应该避免在经常被执行的代码或对性能要求很高的程序中使用反射。
安全限制:使用反射技术要求程序必须在一个没有安全限制的环境中运行。如果一个程序必须在有安全限制的环境中运行,如 Applet,那么这就是个问题了。
内部暴露:由于反射允许代码执行一些在正常情况下不被允许的操作(比如访问私有的属性和方法),所以使用反射可能会导致意料之外的副作用,这可能导致代码功能失调并破坏可移植性。反射代码破坏了抽象性,因此当平台发生改变的时候,代码的行为就有可能也随着变化。
反射构造对象
点击查看代码
package cn.dioxide.cn;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
public class arraylist_t {
public static void main(String[] args) throws ClassNotFoundException, InstantiationException, IllegalAccessException, NoSuchMethodException, InvocationTargetException, NoSuchFieldException {
//获得一个class类对象
Class c1 = Class.forName("cn.dioxide.cn.User");
User user = (User) c1.newInstance(); //调用类的无参构造器
System.out.println(user);
//通过构造器创建对象
Constructor con = c1.getDeclaredConstructor(String.class, int.class, int.class);
User user2 = (User) con.newInstance("李四", 001, 18);
System.out.println(user2);
//通过反射调用普通方法
User user3 = (User) c1.newInstance();
Method setName = c1.getDeclaredMethod("setName", String.class);
setName.invoke(user3, "张三"); //invoke方法激活:("方法名", "对象值")
System.out.println(user3.getName());
//通过反射操作属性
User user4 = (User) c1.newInstance();
Field name = c1.getDeclaredField("name");
name.setAccessible(true); //绕过private保护
name.set(user4, "王五");
System.out.println(user4.getName());
}
}
反射与注解
点击查看代码
package cn.dioxide.cn;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
public class arraylist_t {
//普通构造器 19ms
public static void demo1() {
User u1 = new User();
long startTime = System.currentTimeMillis();
for (int i = 0; i < 1000000000; i++) {
u1.getName();
}
long endTime = System.currentTimeMillis();
System.out.println("cost time: " + (endTime - startTime) + "ms");
}
//反射构造器 2183ms
public static void demo2() throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
User u1 = new User();
Class c1 = u1.getClass();
Method getname = c1.getDeclaredMethod("getName", null);
long startTime = System.currentTimeMillis();
for (int i = 0; i < 1000000000; i++) {
getname.invoke(u1, null);
}
long endTime = System.currentTimeMillis();
System.out.println("cost time: " + (endTime - startTime) + "ms");
}
//反射构造器 权限越级 1138ms
public static void demo3() throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
User u1 = new User();
Class c1 = u1.getClass();
Method getname = c1.getDeclaredMethod("getName", null);
getname.setAccessible(true);
long startTime = System.currentTimeMillis();
for (int i = 0; i < 1000000000; i++) {
getname.invoke(u1, null);
}
long endTime = System.currentTimeMillis();
System.out.println("cost time: " + (endTime - startTime) + "ms");
}
public static void main(String[] args) throws InvocationTargetException, NoSuchMethodException, IllegalAccessException {
demo1();
demo2();
demo3();
}
}
反射操作泛型
ava采用泛型擦除的机制来引入泛型,Java中的泛型静静是给编译器javac使用的,确保数据的安全性和免去强制转换问题,但是,一旦编译完成,所有和泛型有关的类型全部擦除;
为了通过反射操作这些类型,Java新增了ParameterizedType, GenericArrayType, TypeVariable 和 WildcardType 几种类型来代表不能被归一到Class类中的类型但是又和原始类型齐名的类型;
ParameterizedType:表示一种参数化类型,比如Collection
GenericArrayType:表示一种元素类型是参数化类型或者类型变量的数组类型;
TypeVariable:是各种类型变量的公共父接口;
WildcardType:代表一种通配符类型表达式;
点击查看代码
package cn.dioxide.cn;
import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.List;
import java.util.Map;
public class arraylist_t {
//反射解析泛型
public static void demo1(Map < String, User > map, List < User > list) {
System.out.println("test01");
return;
}
public Map < String, User > demo2() {
System.out.println("test02");
return null;
}
public static void main(String[] args) throws NoSuchMethodException {
Method method = arraylist_t.class.getMethod("demo1", Map.class, List.class);
//参数化类型
Type[] genericParameterTypes = method.getGenericParameterTypes();
for (Type genericParameterType: genericParameterTypes) {
System.out.println("#" + genericParameterType);
if (genericParameterType instanceof ParameterizedType) {
//获得参数类型
Type[] actualTypeArguments = ((ParameterizedType) genericParameterType).getActualTypeArguments();
for (Type actualTypeArgument: actualTypeArguments) {
System.out.println("demo1 " + actualTypeArgument);
}
}
}
method = arraylist_t.class.getMethod("demo2", null);
Type genericReturnType = method.getGenericReturnType();
if (genericReturnType instanceof ParameterizedType) {
//判断参数类型
Type[] actualTypeArguments = ((ParameterizedType) genericReturnType).getActualTypeArguments();
for (Type actualTypeArgument: actualTypeArguments) {
System.out.println("demo2 " + actualTypeArgument);
}
}
}
}
反射操作注解
点击查看代码
package cn.dioxide.cn;
import java.lang.annotation.*;
import java.lang.reflect.Field;
public class arraylist_t {
//反射操作注解
public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException {
Class c1 = Class.forName("cn.dioxide.cn.student");
//通过反射获得注解类
Annotation[] annotations = c1.getAnnotations();
for (Annotation annotation: annotations) {
System.out.println(annotation);
}
//获得注解的value值
Dioxide dioxide = (Dioxide) c1.getAnnotation(Dioxide.class);
String value = dioxide.value();
System.out.println(value);
//获得类指定的注解
Field f = c1.getDeclaredField("name");
FieldDioxide annotation = f.getAnnotation(FieldDioxide.class);
System.out.println(annotation.columnName());
System.out.println(annotation.type());
System.out.println(annotation.length());
}
}
@Dioxide(value = "db_student")
class student {
@FieldDioxide(columnName = "db_id", type = "int", length = 10)
private int id;
@FieldDioxide(columnName = "db_age", type = "int", length = 10)
private int age;
@FieldDioxide(columnName = "db_name", type = "varchar", length = 10)
private String name;
public student() {}
public student(int id, int age, String name) {
this.id = id;
this.age = age;
this.name = name;
}
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;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "student{" +
"id=" + id +
", age=" + age +
", name='" + name + '\'' +
'}';
}
}
//类名注解
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@interface Dioxide {
String value();
}
//属性注解
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@interface FieldDioxide {
String columnName();
String type();
int length();
}
注解Annotation
什么是注解
Annotation是从JDK5.0开始引入的新技术。
Annotation的作用:
不是程序本身,可以对程序作出解释。(这一点和注释(comment)没有什么区别)
可以被其他程序(比如:编译器等)读取。
Annotation的格式:
注解是以“@注释名”在代码中存在的,还可以添加一些参数值,例如:@SuppressWarnings(value=“unchecked”)。
Annotation在哪里使用:
可以附加在package,class,method,filed等上面,相当于给他们添加了额外的辅助信息,
我们可以通过反射机制编程实现对这些元数据的访问。
内置注解
@Override:定义在java.lang.Override中,此注解只适用于修辞方法,表示一个方法声明打算重写超类中的另一个方法声明。
@Deprecated:定义在java.lang.Deprecated中,此注解可以用于修饰方法,属性,类,表示不鼓励程序员使用这样的元素,通常是因为他很危险或者是存在更好的选择。
@SuppressWarning:定义在java.lang.SuppressWarning中,用来抑制编译时的警告信息。
他与前两个注解有所不同,你需要添加一些参数才能正确使用,这些参数都是已经定义好了的,我们选择性的使用就好了。
@SuppressWarning(”all“)镇压全部
@SuppressWarning(”unchecked“)镇压危险
@SuppressWarning(value={”unchecked“,”deprecation“})还可以同时镇压多种
点击查看代码
//什么是注解
@SuppressWarnings("all")
public class Test01 extends Object {
//Override 重写的注解
@Override
public String toString() {
return super.toString();
}
//Deprecated 不推荐使用或者存在更好的方法,但是此方法也可以使用
@Deprecated
public static void test() {
System.out.println("注解");
}
@SuppressWarnings("all")//使此方法中不出警报信息
public static void test1() {
}
public static void main(String[] args) {
//不建议使用,并不是不能使用
test();
}
}
元注解
元注解的作用就是负责注解其他注解,java定义了四个标准的meat-annotation类型,他们被用来提供对其他annotation类型做说明。
这些类型和它们所支持的类在java.lang. annotation中可以找到。
(@Target、@Retention、@Document、@Inherited)
@Target :用于描述注解的使用范围(即:被描述的注解可以用在什么地方)
@Retention:表示需要在什么级别保存该注释信息,用于描述注解的生命周期(SOURCE<CLASS<RUNTIME)
@Document:说明该注解将被包含在javaDoc中 文档中
@Inherited:说明子类可以继承父类中的该注解
点击查看代码
//元注解
@MyAnnotation
public class Test02 {
public void test(){
}
}
//Target表示我们的注解需要使用到什么地方
@Target(value = {ElementType.METHOD,ElementType.TYPE})
//表示我们的注解在什么地方还有效
//runtime>class>source
//runtime>class>source
//runtime运行时有效
//class 编译成class文件使有效
//source 在源码是有效
@Retention(value = RetentionPolicy.RUNTIME)
//表示是否将我们的注解生成到javadoc中 文档中
@Documented
//子类可以继承父类的注解
@Inherited
@interface MyAnnotation{
}
自定义注解
使用@interface自定义注解时,自动继承了java.lang.annotation.Annotation接口
分析:
@interface用来声明一个注解,格式:public @interface 注解名 {定义内容}
其中的每一个方法实际上是声明了一个配置参数。
方法的名称就是参数的名称。
返回值类型就是参数的类型(返回值只能是基本类型,Class,String,enum)。、
可以通过default来声明参数的默认值
如果只有一个参数成员,一般参数名为value
注解元素必须要有值,我们定义注解元素时, 经常使用空字符串,0作为默认值
点击查看代码
//自定义注解
public class Test03 {
//注解可以显示赋值,如果没有默认值,我们就必须给注解赋值
@MyAnnotation2(name = "zz",schools = {"北京","菜鸟"})
public void test(){}
@MyAnnotation3("小明")//当参数名为value的时候可以直接写参数,
public void test2(){}
}
//可以作用在类上和方法上
@Target({ElementType.TYPE,ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@interface MyAnnotation2{
//注解的参数:参数类型 + 参数名 +()
String name() default "";//设置参数,也可以定义一个默认值用default
int age() default 0;
int id() default -1;//如果默认值为-1,代表不存在,indexof,如果找不到就返回-1
String[] schools() default {"清华","北大"} ;
}
@Target({ElementType.TYPE,ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@interface MyAnnotation3{
String value(); //一个注解中只有一个参数的时候推荐使用value作为参数名
}

浙公网安备 33010602011771号