Java提升篇11(Java其它高级特性——注解与反射)

  • 注解(Annotation)
    • JDK内置注解
    • 自定义注解
      • 元注解(meta-annotation)
  • 反射(reflection)
    • 动态语言
    • 反射机制的概念
    • 反射操作注解(ORM实例)
    • 反射操作泛型

注解(Annotation)

注解时JDK5.0引入的新技术,它不是程序本身,却可以对程序做出解释。编辑器可以对其进行读取。注解可以附加在package、class、method和field上面,给它们添加相应的辅助消息,比如我们之前常用的**@Override**。注意注释不是注解,注释是没有任何意义的,换句话说注释是给我们看的,而注解是给编译器看的。

JDK内置注解

  • @Override 表示当前方法覆盖了父类的方法,加了这个注解不重写方法会有横线提示。
  • @Deprecation 表示方法已经过时,方法上有横线,使用时会有警告。
  • @SuppviseWarnings 表示关闭一些警告信息(通知java编译器忽略特定的编译警告)

其中@SuppviseWarnings中可以添加相关参数来指定需要压制的警告种类,如:
@SuppviseWarnings(“unchecked”)
@SuppviseWarnings(value = {“unchecked”,“deprecation”})
实例:
接口:

public interface Person {public String name();public int age();//2.加上@Deprecated这个注解,表示这个方法已经过时了(然后就自动带上灰色横线)@Deprecatedpublic void sing();
}

实现类:

public class Child implements Person{//1.当孩子类继承person类的时候,系统会提示加上@Override注解,此时@Override注解告诉我们,也告诉编译器当前类的name方法已经覆盖了person类的方法@Overridepublic String name() {return null;}@Overridepublic int age() {return 0;}@Overridepublic void sing() {System.out.println("啦啦啦");}}

main方法

public class test {//3.此时想要在调用这个唱歌的sing方法,就得加上@SuppressWarnings("deprecation"),该注解会忽略警告的功能@SuppressWarnings("deprecation")public static void main(String[] args) {Person a=new Child();a.sing();}
}

自定义注解

处理JDK给我们提供的注解之外我们还可以自己定义一些注解,一般通过@interface关键字来实现,使用时会自动继承java.lang.annotation.Annotation接口。

元注解(meta-annotation)

元注解的作用就是负责注解其他注解,Java定义了4个标准的元注解类型,它们被用来对其他的annotation类型作说明。
@Target
用于描述注解的使用范围(被描述的注解可以用在什么地方)可以对其设置value值,比如:@Target(value=ElementType.TYPE)
value可取值如下:

public enum ElementType {TYPE, // 类、接口、枚举类FIELD, // 成员变量(包括:枚举常量)METHOD, // 成员方法PARAMETER, // 方法参数CONSTRUCTOR, // 构造方法LOCAL_VARIABLE, // 局部变量ANNOTATION_TYPE, // 注解类PACKAGE, // 可用于修饰:包TYPE_PARAMETER, // 类型参数,JDK 1.8 新增TYPE_USE // 使用类型的任何地方,JDK 1.8 新增
}

@Retention
表示需要在什么级别保存该注解信息,用于描述注解生命周期,它的可取值如下:

public enum RetentionPolicy {SOURCE,    // 源文件保留CLASS,       // 编译期保留,默认值RUNTIME   // 运行期保留,可通过反射去获取注解信息
}

@Documented
描述在使用 javadoc 工具为类生成帮助文档时是否要保留其注解信息。这个注解没有参数,添加该注解后如果我们生成一份JDKAPI文档时会保留注解信息。
@Inherited
该注解只能修饰类,使被它修饰的注解具有继承性。什么意思那,被改注解修饰的类的注解参数信息在子类中通过反射也可以获得(反射后面再说)。

四个元注解用法:

@Inherited
@Documented
@Retention(RetentionPoilcy.RUNTIME)
@Target(ElementType.TYPE)//只有一个参数时value=可以不写
public @interface Field{String nationality() default "China";//参数名和参数类型String skinColour(); //不设置默认值则必须在使用时赋值
}

该注解使用时:

@Field(skinColour = "yellow")//默认添加nationality属性时"China",也可以手动修改
public interface Person {public String name();public int age();//2.加上@Deprecated这个注解,表示这个方法已经过时了(然后就自动带上灰色横线)@Deprecatedpublic void sing();
}

自定义注解在ORM框架中运用较广,我们可以通过注解将数据库表和类对应起来。等讲完反射后通过一个案例一起演示。

反射(reflection)

动态语言

在之前的学习中我们知道,Java在执行过程中需要先编译成class文件,在由JVM对其进行加载执行,这就说明Java语言在程序执行过程中不可以改变程序结构或变量类型。而像Python、Javascript这样的语言在却可以实现上面的功能,比如Javascript中:

function test(){var s="var a =3;alert(a);"eval(s);
}

像Javascript这类可以在程序执行过程中不可以改变程序结构或变量类型的语言称为动态语言。
Java虽然不是动态语言,但它有一定的动态性,我们可以利用反射机制、字节码操作获得类似动态语言的特征。

反射机制的概念

反射机制是指可以在运行时加载、探知或者使用未知的类。
通俗来说,在Java程序运行过程中可以动态加载一个只有名称的类,对于一个已经加载的类,可以获取该类的所有属性和方法,对于任意一个对象,也可以调用它的方法和属性。
反射的的根源是java.lang.Class类,针对任何想动态加载、运行的类都必须先获得相应的Class对象。Class类非常特殊,用来表示java中的类型,Class类的对象包含了某一个被加载类的结构,当一个类被加载时,JVM会自动产生一个Class对象。下面通过一段实例来演示Class类的用法:
首先定义一个person类

public class Proson {//私有属性private String name;//公有属性public  Integer age;//无参构造public Proson() {}//有参构造public Proson(String name, Integer age) {this.name = name;this.age = age;}//私有方法private void method1(){System.err.println("method1——run");}//公有方法public void method2(String param){System.err.println("method1=2——run :"+param);}@Overridepublic String toString() {return "Proson{" +"name='" + name + '\'' +", age=" + age +'}';}
}

通过这个类来演示反射的过程:

public static void main(String[] args) throws MalformedURLException {String className="com.bean.Proson";//通常创建对象Proson p=new Proson();//寻找该名称的类文件,加载进内存,并创建Class对象,类的整个结构信息都会存放在Class对象中。Class clazz = Class.forName(className);Class clazz2 = Class.forName(className);//注意对于同一个类型,通过它的任意对象获得的class均相同System.out.println(clazz==clazz2);//打印true,同一个类只会被加载一次//通过Class对象的newInstance()创建类对象。默认调用无参构造器,如果没有会报错Object o = clazz.newInstance();//通过对象获取类信息Class strClazz = className.getClass();//className为String类型,调用会获取String类信息Class strClazz2 = String.class;//获取String类信息的另一种方法}

通过Class对象可以很方便的获取类信息。

public static void main(String[] args) throws MalformedURLException {String className="com.bean.Proson";Class clazz = Class.forName(className);//获取类名System.out.println(clazz.getName());//获取包名+类名com.bean.ProsonSystem.out.println(clazz.getSimpleName());//获取类名Proson//获取属性信息Field[] fields = clazz.getFields();//获取类的所有public属性Field[] fields = clazz.getDeclaredFields();获取类的所有属性Field field = clazz.getDeclaredField("name");//获取指定属性System.out.println(field);//打印private java.lang.String com.bean.Proson.name//获取方法Method[] methods = clazz.getDeclaredMethods();//获取所有方法Method m01 = clazz.getgetDeclaredMethod("method1",null);//获取指定方法,第二个参数为方法的参数类型,void为null,加这个参数的目的是可以获取重载的方法Method m02 = clazz.getgetDeclaredMethod("method2", String.class);//获得构造器信息Constructor[] constructors = clazz.getDeclaredConstructors();Constructor c = clazz.getDeclaredConstructor(String.class,Integer.class);//可以通过传入不同的参数类型获得不同的构造器,无参构造器为nullPerson p = c.newInstance("Bob",18);//通过有参构造器实例化对象//通过反射调用方法m02.invoke(p ,"hello");//相当于p.method2("hello");//通过反射设置属性field.setAccessible(true);//设置之后可以直接访问private属性field.set(p ,"Tom");//相当于p.name="Tom",通过反射可以修改private属性System.out.println(field.get(p));//打印Tom,通过反射可以访问private属性}

在反射过程中如果启用安全检查会影响性能,我们可以根据需求使用的setAcceptable这个方法来启用或禁用安全检查(上例中已有使用)。一般情况下性能会差30倍左右,下面是一个方法执行10亿次的三种结果,大家有兴趣可以自己做尝试。

反射操作注解(ORM实例)

反射操作注解与操作属性或者方法时采用的方法差不多,这里我们通过一个ORM实例来将注解与反射做一个综合讲解。
首先给大家普及一下ORM(如果大家对数据库不了解可以先去学习一下MySQL或其他数据库技术,数据库也是Java开发方向的重点),它一般指对象关系映射。简单来说,我们Java程序与数据库交互时,会用一个类与数据库的一个表相对应,这个类也叫做javabean类,获取数据时,通过实例化这个类的对象与数据库中一条一条的数据做对应。基于这个思想我们可以做出自己的代码实现。
首先我们创建一个修饰类的注解:

@Target(ElementType.TYPE)//只能在类型或接口前
@Retention(RetentionPolicy.RUNTIME)//运行期保留,只有这样的注解才能被反射获取
@interface TableStudent{String value();
}

在创建一个修饰属性的注解:

@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@interface Filed{String columnName();String type();int length();
}

在类中使用这两个注解:

@TableStudnet("student")
class Studnet{@Filed(columnName = "db_id",type="int",length=10)private int id;@Filed(columnName = "db_age",type="int",length=10)private int age;@Filed(columnName = "db_name",type="varchar",length=3)private String name;//...
}

这样在运行时

    public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException {//获得Class c1 = Class.forName("li.Studnet");//获取TableStudnet注解的value的值TableStudnet annotation = (TableStudnet) c1.getAnnotation(TableStudnet.class);String value = annotation.value();System.out.println(value);//获得Filed注解上属性的值Field[] f = c1.getDeclaredFields();for (Field field : f) {Filed fAnnotation  = field.getAnnotation(Filed.class);System.out.println("columnName="+fAnnotation.columnName());//打印columnName=db_idSystem.out.println("type="+fAnnotation.type());//打印type=intSystem.out.println("length="+fAnnotation.length());//打印length=10}}

这样我们就可以通过注解将bean对象与数据库表名以及字段类型对应起来,进而与数据库进行交互。

反射操作泛型

当然通过反射还可以对泛型进行操作,Java有ParameterizedType,GenericArrayType,TypeVariable和WildcardType四个类来操作泛型,我们在这里只介绍ParameterizedType,其他类感兴趣可以自己去学习,我们直接举例来说明:

//Proson还是上面的Proson类
public class Demo{  //定义两个带泛型的方法public void test01(Map<String,Proson> map){System.out.println("方法一");}   public Map<Integer,Proson> test02(){System.out.println("方法二");return null;}
}

操作过程中:

    public static void main(String[] args) {try {           //获得指定方法参数泛型信息Method m = Demo.class.getMethod("test01", Map.class,List.class);Type[] t = m.getGenericParameterTypes();for (Type paramType : t) {System.out.println("#"+paramType);//打印java.util.Map< java.lang.String, java.lang.String> if(paramType instanceof ParameterizedType){//获取泛型中的具体信息Type[] genericTypes = ((ParameterizedType) paramType).getActualTypeArguments();for (Type genericType : genericTypes) {System.out.println("泛型类型:"+genericType);//第一次循环打印泛型类型:class java.lang.String //第二次循环打印泛型类型:class com.bean.Proson}}}   //获得指定方法返回值泛型信息Method m2 = Demo.class.getMethod("test02", null);Type returnType = m2.getGenericReturnType();if(returnType instanceof ParameterizedType){Type[] genericTypes = ((ParameterizedType) returnType).getActualTypeArguments();for (Type genericType : genericTypes) {System.out.println("返回值,泛型类型:"+genericType);//第一次循环打印返回值,泛型类型:class java.lang.Integer //第二次循环打印返回值,泛型类型:class com.bean.Proson}                   }       } catch (Exception e) {e.printStackTrace();}   }

上一篇:菜鸟学习笔记:Java提升篇10(网络2——UDP编程、TCPSocket通信、聊天室案例)
下一篇:菜鸟学习笔记:Java提升篇12(Java动态性2——动态编译、javassist字节码操作)

菜鸟学习笔记:Java提升篇11(Java动态性1——注解与反射)相关推荐

  1. 决策树算法学习笔记(提升篇)

    声明:本文虽有部分自己理解成分,但是大部分摘自以下链接. 决策树(decision tree)(三)--连续值处理 决策树参数讲解+实例 数据挖掘十大算法 C4.5算法的改进: 用信息增益率来选择属性 ...

  2. 菜鸟学习笔记:Java提升篇12(Java动态性2——动态编译、javassist字节码操作)

    菜鸟学习笔记:Java提升篇12(Java动态性2--动态编译.javassist字节码操作) Java的动态编译 通过脚本引擎执行代码 Java字节码操作 JAVAssist的简单使用 常用API ...

  3. 菜鸟学习笔记:Java提升篇10(网络2——UDP编程、TCPSocket通信、聊天室案例)

    菜鸟学习笔记:Java提升篇10(网络2--UDP编程.TCPSocket通信) UDP编程 TCP编程(Socket通信) 单个客户端的连接 多个客户端的连接(聊天室案例) UDP编程 在上一篇中讲 ...

  4. Java学习笔记之基础篇

    Java学习笔记之基础篇 目录 Java如何体现平台的无关性? 面向对象(OO)的理解 面向对象和面向过程编程的区别 面向对象三大特征 静态绑定和动态绑定(后期绑定) 延伸:类之间的关系 组合(聚合) ...

  5. 菜鸟学习笔记:Java提升篇3(容器3——泛型、排序)

    菜鸟学习笔记:Java容器3--泛型.排序 泛型 泛型类 泛型接口 泛型方法 泛型继承 通配符"?" 泛型知识点补充 容器排序 Comparable接口与compareTo方法 C ...

  6. 菜鸟学习笔记:Java提升篇9(网络1——网络基础、Java网络编程)

    菜鸟学习笔记:Java提升篇9(网络1--网络基础.Java网络编程) 网络基础 什么是计算机网络 OS七层模型 Java网络编程 InetAddress InetSocketAddress URL类 ...

  7. 菜鸟学习笔记:Java提升篇8(线程2——线程的基本信息、线程安全、死锁、生产者消费者模式、任务调度)

    菜鸟学习笔记:Java提升篇8(线程2--线程的基本信息.线程安全.死锁.生产者消费者模式.任务调度) 线程的基本信息 线程同步 线程安全 死锁 生产者消费者模式 任务调度(了解) 线程的基本信息 J ...

  8. 菜鸟学习笔记:Java提升篇6(IO流2——数据类型处理流、打印流、随机流)

    菜鸟学习笔记:Java IO流2--其他流 字节数组输入输出流 数据类型处理流 基本数据类型 引用类型 打印流 System.in.System.out.System.err 随机流RandomAcc ...

  9. 菜鸟学习笔记:Java提升篇5(IO流1——IO流的概念、字节流、字符流、缓冲流、转换流)

    菜鸟学习笔记:Java IO流1--IO流的概念.字节流.字符流.缓冲流.转换流 IO流的原理及概念 节点流 字节流 文件读取 文件写出 文件拷贝 文件夹拷贝 字符流 文件读取 文件写出 处理流 缓冲 ...

最新文章

  1. Windows Phone 7 IEnumerableT.Select和SelectMany的区别
  2. python用类名直接调用方法_Python类的实例方法、静态方法、类方法详解,附代码示例...
  3. 换种方式去分页(转)
  4. [渝粤教育] 中国地质大学 Windows程序设计 复习题 (2)
  5. 第一百零五期:5年前,跳槽涨薪,你笑了,5年后,跳槽降薪,你慌了!
  6. 7天后自动更新cookie
  7. 3dvary灯光材质为什么不亮_关机后电脑灯光闪瞎狗眼?在这里关闭它
  8. 基于Python+Django的在线习题考试测试管理系统
  9. Windows编程中各种操作文件的方法
  10. 获取Access数据库中的用户表名
  11. 如何修改docker swarm节点的hostname
  12. python写完程序怎么运行
  13. ionic之自动生成app图标和启动页面
  14. AB PLC Modbus TCP通讯测试
  15. bldc 原理 方波控制_浅析无刷直流电机FOC、方波、正弦波控制!
  16. GIF:推荐两款好用的gif免费制作软件
  17. np.take()函数用法 python numpy
  18. Uniapp子组件不显示
  19. 花儿还有重开日,人生没有再少年.
  20. MAC安装chromedriver碰到的问题

热门文章

  1. docker 删除镜像时报错Error response from daemon: conflict: unable to delete xxx (must be forced) -
  2. matlab数字调音台,软件说明
  3. 终于明白上一篇的一顿误操作是什么了,是$,不是S !!!!!
  4. 修改一个CGRect的值
  5. linux网络编程之socket:使用fork并发处理多个client的请求
  6. ProgressBar的小细节,设置style与setIndeterminate()
  7. 红外技术如何应用在 3D 电视
  8. Tcl8.6原生支持oop了
  9. 父窗体中弹出新窗体,然后获取弹出窗体的返回值。
  10. 入侵检测规则匹配算法--单模匹配算法、多模匹配算法、hyperscan