java 反射,注解,泛型,内省(高级知识点)
Java反射
1.Java反射是Java被视为动态(或准动态)语言的一个关键性质。这个机制允许程序在运行时透过Reflection APIs 取得任何一个已知名称的class的内部信息,
包括其modifiers(诸如public, static 等)、 superclass(例如Object)、实现之interfaces(例如Cloneable),也包括fields和methods的所有信息, 并可于运行时改变fields内容或唤起methods。
2.获取类的Class对象的几种方式:
(1)类名.class。例:Boolean.class。 (2)对象.getClass()。例:new Date().getClass(); (3)Class.forName(“类名”);例:Class.forName("java.util.Date");
3.Constructor(获取类的构造方法)
(1)得到某个类的所有构造方法。例:
- Constructor []constructors = Class.forName("java.lang.String").getConstructors();
(2)得到某个类的某一个构造方法。例:
- Constructor constructor = Class.forName("java.lang.String")
- .getConstructo(StringBuffer.class);
(3)利用默认的构造方法创建Class对象所表示的类的新实例。例:
- String str = (String)Class.forName("java.lang.String").newInstance();
4.Field(获取类的成员变量)
(1)getField(""), 获取指定名称的公共成员变量。
(2)getFields(),获取所有的公共成员变量。
(3)getDeclaredField(“”),获取指定名称的声明变量,包含私有的。
(4)getDeclaredFields(),获取所有的声明变量,包含私有的。
示例代码如下:
- Order order = new Order(Long.parseLong("1001"),
- "订单1",
- Double.parseDouble("150.80"));
- order.setName("1001");
- // 获取成员变量Id,getField("")获取的成员变量必须是公共成员变量,否则抛异常。
- Field field = order.getClass().getField("id");
- // 获取成员变量name,getDeclaredField("")获取声明的所有成员变量,包含私有的。
- Field field2 = order.getClass().getDeclaredField("name");
- // 私有成员变量需采用暴力反射才能访问应用。
- field2.setAccessible(true);
- System.out.println(field2.get(order));
注意:获取到的成员变量是类上面的,而不是对象上的,可通过成员变量.get(实例对象)获取到指定实例对象的成员变量值。
5.Method(获取类中的成员方法)
(1)getMethod(String name, Class<?>... parameterTypes),获取指定方法名称和参数类型的公共成员方法。
(2)getMethods(),获取所有的公共成员方法。
(3)getDeclaredMethod(String name,Class<?>... parameterTypes),
获取指定方法名称和参数类型的声明成员方法,包含私有成员方法在内。
(4)getDeclaredMethods(),获取所有声明的成员方法,包含私有成员方法在内,但不包括继承的方法。
示例代码如下:
- Method substring = Class.forName("java.lang.String")
- .getMethod("substring", int.class);
- String strAt = (String) substring.invoke(str, 3);
- // 获取私有的成员方法
- Method getHello = order.getClass().getDeclaredMethod("getHello");
- // 私有成员方法需暴力反射才能访问调用
- getHello.setAccessible(true);
- // 实例对象为null,表示该方法是static方法。
- System.out.println(getHello.invoke(null));
6.其它说明
(1)基本类型的一维数组可以被当做Object类型使用,不能当作Object[]类型使用,非基本类型的一维数组既可以被当做Object类型使用,又可以当作Object[]类型使用。
(2)框架与工具类的区别在于,工具类被用户的类调用,而框架则是调用用户提供的类,由于在设计框架时无法知道用户提供的类,无法实例化具体的对象,需采用反射的方式来实现。
二、内省
1、内省是 Java 语言对 Bean 类属性、事件的一种缺省处理方法。也就是说给定一个javabean对象,我们就可以得到/调用它的所有的get/set方法。
例如类A中有属性name,那我们可以通过getName,setName来得到其值或者设置新的值。通过 getName/setName 来访问 name 属性,这就是默认的规则。
Java 中提供了一套 API 用来访问某个属性的 getter/setter方法,通过这些API可以使你不需要了解这个规则,这些API存放于包java.beans中。
例:通过类 Introspector 来获取某个对象的 BeanInfo信息,然后通过 BeanInfo来 获取属性的描述器(PropertyDescriptor),通过这个属性描述器就可以获取某个属性 对应的 getter/setter 方法,然后我们就可以通过反射机制来调用这些方法。
- Order order = new Order();
- order.setName( "ZX001" );
- //如果不想把父类的属性也列出来的话,那getBeanInfo的第二个参数填写父类的信息
- BeanInfo bi = Introspector.getBeanInfo(order.getClass(), Object.class);
- PropertyDescriptor[] props = bi.getPropertyDescriptors();
- for(int i=0;i<props.length;i++){
- System.out.println(props[i].getName()+ "=" +
- props[i].getReadMethod().invoke(order, null ));
- }
2、Apache的commons包封装了很多对Java对象的基本操作,例如BeanUtils,PropertyUtils等, 可使用其对Java对象进行操作。
三、 注解
1、注解相当于一种标记,注解可以加在包,类,字段,方法,方法的参数以及局部变量上。 注解使得Java源代码中不但可以包含功能性的实现代码,还可以添加元数据。
注解的功能类似于代码中的注释, 所不同的是注解不是提供代码功能的说明,而是实现程序功能的重要组成部分。
2、java1.5起默认的三个annotation类型: 一个是@Override:只能用在方法之上的,用来告诉别人这一个方法是改写父类的。
一个是@Deprecated:建议别人不要使用旧的API的时候用的,编译的时候会用产生警告信息, 可以设定在程序里的所有的元素上. 一个是@SuppressWarnings:这一个类型可以来暂时把一些警告信息消息关闭.
3、自定义注解类(@interface)
- * RetentionPolicy.RUNTIME 内存中的字节码
- * RetentionPolicy.SOURCE Java源文件
- * RetentionPolicy.CLASS Class文件
- */
- @Retention(RetentionPolicy.RUNTIME)
- @Target({ElementType.METHOD,ElementType.TYPE})
- public @interface MyAnnotation {
- // 注解属性(注解属性也可以是数组,枚举,注解等)
- String color() default "blue";
- String value();
- }
@Retention用于描述注解的生命周期: SOURCE代表的是这个Annotation类型的信息只会保留在程序源码里,源码如果经过了编译之后,
Annotation的数据就会消失,并不会保留在编译好的.class文件里面。 ClASS的意思是这个Annotation类型的信息保留在程序源码里,同时也会保留在编译好的.class文件里面,
系统默认值是CLASS. RUNTIME,表示在源码、编译好的.class文件中保留信息,在执行的时候会把这一些信息加载到JVM中去的.
@Target用于描述注解的使用范围: TYPE(类型), FIELD(属性), METHOD(方法), PARAMETER(参数), CONSTRUCTOR(构造函数), LOCAL_VARIABLE(局部变量), ANNOTATION_TYPE,PACKAGE(包),
其中的TYPE(类型)是指可以用在Class,Interface,Enum和Annotation类型上.
4、注解类的反射调用,例:
- @MyAnnotation(value = "test")
- public class MyAnnotationTest {
- public static void main(String []args){
- // 判断是否加上MyAnnotation类型的注解
- if(MyAnnotationTest.class.isAnnotationPresent(MyAnnotation.class)){
- MyAnnotation annation = MyAnnotationTest.class.getAnnotation(MyAnnotation.class);
- System.out.println(annation.color()+"#####"+annation.value());
- }
- }
- }
四、 泛型
1.泛型(Generic type或者generics)是对Java语言的类型系统的一种扩展,以支持创建可以按类型 进行参数化的类。可以把类型参数看作是使用参数化类型时指定的类型的一个占位符,就像方法的形式参数 是运行时传递的值的占位符一样。 好处:泛型的主要目标是提高 Java 程序的类型安全。通过知道使用泛型定义的变量的类型限制, 编译器可以在一个高得多的程度上验证类型假设。将类型检查从运行时挪到编译时有助于您更容易找到错误, 并可提高程序的可靠性。 泛型的一个附带好处是,消除源代码中的许多强制类型转换。这使得代码更加可读,并且减少了出错机会。
2.Java编译器编译生成的字节码会去掉泛型的类型信息,只要能通过编译器,就能往某个泛型集合中 加入其它类型的数据。事例代码如下:
- public static void main(String[] args) throws Exception{
- ArrayList<Integer> collection1=new ArrayList<Integer>();
- collection1.add(1);
- collection1.add(2);
- collection1.add(3);
- collection1.getClass().getMethod("add",
- Object.class).invoke(collection1, "abcdefg");
- System.out.println(collection1.get(3));
- }
3.泛型相关术语: ArrayList<E>,泛型类型。 ArrayList<E>中的E称为类型变量或类型参数。 ArrayList<Integer>称为参数化的类型。 ArrayList<Integer>中的Integer称为类型参数的实例或实际类型参数。 ArrayList<Integer>中的<>称为typeof。 ArrayList称为原始类型。 参数化类型与原始类型可相互引用,但会出现警告。例:
- Map<String,String> map = new HashMap();
- Map map = new HashMap<String,String>();
参数化类型不考虑类型参数的继承关系,例:
- Map<String,String> map= new HashMap<String,Object>();//编译器错误
数组元素不能使用参数化的类型,可通过Array.newInstance(Classtype,int size)的方式来创建数组。
4.泛型通配符? 使用?通配符可以引用其它各种参数化的类型,?通配符定义的变量主要用作引用,可以调用与参数化无关的方法, 不能调用与参数化有关的方法。例:
- public static void printCollection(Collection<?> cols){
- for(Object col:cols){
- System.out.println(col);
- }
- //cols.add("hello");// 编译错误,?通配符不能调用与参数化有关的方法
- cols.size();// ?通配符可以调用与参数化无关的方法
- cols = new HashSet<Date>();// 引用通配符可以引用各种其它参数化的类型
- }
(1) 限定通配符的上边界
- // 正确
- List<? extends Number> list= new ArrayList<Integer>();
- // 错误
- List<? extends Number> list2= new ArrayList<String>();
(2)限定通配符的下边界
- // 正确
- List<? super Integer> list= new ArrayList<Number>();
- // 错误
- List<? super Integer> list2= new ArrayList<Byte>();
5、泛型方法 例:
- static <E> void swap(E[] a,int i,int j){
- E t = a[i];
- a[i]= a[j];
- a[j] = t;
- }
(1)用于放置泛型的类型参数的尖括号应出现在方法的其它所有修饰符之后和方法的返回类型之前, 也就是紧邻返回值之前。类型参数通常用单个大写字母表示。 (2)只有引用类型才能作为泛型方法的实际参数,swap(new int[3],3,5)会报编译错误。 (3)泛型不仅在应用时可以使用extends限定符,在定义泛型时也可以使用extends限定符, 并且可以用&来指定多个边界,如<E extends Serializable & Cloneable> void method(){} (4)泛型也可以用类型变量来表示异常,称为参数化的异常,也可以用于方法的throws列表中,但不能用于catch子句中。 (5)在泛型中可以同时有多个类型参数,在定义它们的尖括号中用逗号分开,例: public static <K,V> V getValues(K key){}
6.类型参数的类型推断
编译器判断泛型方法的实际参数的过程称为类型推断。根据调用泛型方法时实际传递的参数类型或返回类型来推断,具体规则如下: (1)当某个类型变量只在整个参数列表中的所有参数和返回值中的一处被应用了,那么根据调用方法时该处的实际应用 类型来确定。例:swap(new String[3],3,4)->static<E> void swap(E[] a,int i,int j) (2)当某个类型变量在整个参数列表中的所有参数和返回值中的多处被应用了,如果调用方法时多处的实际应用类型都 对应同一种类型,则根据实际应用类型来确定。例:add(3,5)->static<T> T add(T a,T b) (3)当某个类型变量在整个参数列表中的所有参数和返回值中的多处被应用了,如果调用方法时多处的实际应用类型 对应到了不同的类型,且没有返回值,这时取多个参数中的最大交集类型。例:下面语句对应的类型就是Number了。 Fill(new Integer[3],3.5f)->static<T> void fill(T[] a,T v) (4)当某个类型变量在整个参数列表中的所有参数和返回值中的多处被应用了,如果调用方法时多处的实际应用类型 对应到了不同的类型,并且使用返回值,这时优先考虑返回值的类型,例如,下面语句实际对应的类型就是Integer了, 编译器报告错误,将变量类型改为float,对比编译器的错误提示,再将变量类型改为Number。 (5)参数类型的类型推断具有传递性(a=c,b=c推出a=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)
7、泛型类型
(1)如果类的实例对象中的多处都要用到同一个泛型参数,这时采用泛型类型的方式进行定义,也就是类级别的泛型。 以下示例为将方法级别的泛型改进为类级别的泛型:例:
- public class GenericDao {
- public <T> void save(T po){}
- public <T> T getById(String id){return null;}
- }
改进为:
- public class GenericDao<T> {
- public T field1;
- public void save(T po){}
- public T getById(String id){return null;}
- }
(2)类级别的泛型是根据引用该类名时指定的类型信息来参数化类型变量的,以下两种方式都可以:
- GenericDao<String> dao = null;
- new GenericDao<String>();
(3)在对泛型类型进行参数化时,类型参数的实例必须是引用类型,不能是基本类型。当一个变量被声明为泛型时, 只能被实例变量和方法调用,不能被静态变量和静态方法调用。
8、利用反射获得泛型的参数化类型
- public class GenericReflection {
- private Vector<Date> dates = new Vector<Date>();
- public void setDates(Vector<Date> dates) {
- this.dates = dates;
- }
- public static void main(String args[]){
- Method []methods = GenericReflection.class.getMethods();
- for(Method method:methods){
- if(method.getName().equals("setDates")){
- ParameterizedType paramType = (ParameterizedType)
- method.getGenericParameterTypes()[0];
- // 获得参数类型
- System.out.println(((Class)paramType.getRawType()).getName());
- // 获得泛型的参数化类型
- System.out.println(((Class)paramType.getActualTypeArguments()[0]).getName());
- }
- }
- }
- }
java 反射,注解,泛型,内省(高级知识点)的更多相关文章
- 黑马程序员:Java基础总结----泛型(高级)
黑马程序员:Java基础总结 泛型(高级) ASP.Net+Android+IO开发 . .Net培训 .期待与您交流! 泛型(高级) 泛型是提供给javac编译器使用的,可以限定集合中的输入类型 ...
- 【译】8. Java反射——注解
原文地址:http://tutorials.jenkov.com/java-reflection/annotations.html ================================== ...
- Java反射给泛型集合赋值
Java反射给泛型集合赋值 泛型 Java泛型简单描述下: 比如创建一个List集合,我想在里边只放Student对象信息,就需要写成 List<Student> studentList ...
- 深入分析Java反射(三)-泛型
前提 Java反射的API在JavaSE1.7的时候已经基本完善,但是本文编写的时候使用的是Oracle JDK11,因为JDK11对于sun包下的源码也上传了,可以直接通过IDE查看对应的源码和进行 ...
- 2015第22周六Java反射、泛型、容器简介
Java的反射非常强大,传递class, 可以动态的生成该类.取得这个类的所有信息,包括里面的属性.方法以及构造函数等,甚至可以取得其父类或父接口里面的内容. obj.getClass().getDe ...
- java 反射 子类泛型的class
很早之前写过利用泛型和反射机制抽象DAO ,对其中获取子类泛型的class一直不是很理解.关键的地方是HibernateBaseDao的构造方法中的 Type genType = getClass() ...
- Java反射,泛型在Json中的运用
最近项目中遇到了Json数据自动获取的功能,不然令人想起java的反射,已经很长时间没复习java了正好一块连java的这一块内容一起过一遍.java中的反射无疑就相当于java开发者的春天,在众多的 ...
- java 反射和泛型
反射 在计算机科学中,反射是指计算机程序在运行时(Run time)可以访问.检测和修改它本身状态或行为的一种能力.[1]用比喻来说,反射就是程序在运行的时候能够“观察”并且修改自己的行为. 要注意术 ...
- java反射--注解的定义与运用以及权限拦截
自定义注解类编写的一些规则: 1. Annotation型定义为@interface, 所有的Annotation会自动继承java.lang.Annotation这一接口,并且不能再去继承别的类或是 ...
- java反射注解妙用-获取所有接口说明
转载请注明出处:https://www.cnblogs.com/wenjunwei/p/10293490.html 前言 最近在做项目权限,使用shiro实现restful接口权限管理,对整个项目都进 ...
随机推荐
- Linux 日常使用指令大全
001.日常维护常用查询命令 #top 显示系统进程 #clear 清理屏幕信息 #cat /etc/redhat-release 查看系统版本信息 #cat /proc/version ...
- C#利用NPOI生成具有精确列宽行高的Excel文件
前言 NPOI是操作Excel的神器,导出导入快如闪电, 但是SetColumnWidth函数个人感觉不会用,怎么弄都无法控制好,因为他是以字符数量去设置宽度,实际上Excel列宽还有个像素的概念,更 ...
- win7 下安装oracle 10g
oracle 10g 在win7下安装,提示程序异常终止,发生未知错误 在网上搜结果: 修改Oracle 10G\database\stage\prereq\db\refhost.xml 在 < ...
- [PeterDLax著泛函分析习题参考解答]第5章 赋范线性空间
1. (a) 证明 (6) 定义了范数. (b) 证明它们在 (5) 式意义下是等价的. 证明: $$\bex |(z,u)|'\leq |(z,u)|\leq 2|(z,u)|',\quad |(z ...
- C# Login方法
public static bool User_Login(string url, string uname, string password, out string[] userInfo) { st ...
- 在windows下编辑shell脚本注意点
编辑脚本是直接在windows下写的,并没有使用特定的编辑器或者其他工具,所以很有可能出现一些莫名其妙的异常,这些错误是我们眼睛看不到的,遇到这个情况,例如如下异常或者提示语法错误 Java代码 ...
- ASP.NET (HttpModule,HttpHandler)
asp.net 事件模型机制 ----------------------- 一 客户的请求页面由aspnet_isapi.dll这个动态连接库来处理,把请求的aspx文件发送给CLR进行编译执行,然 ...
- selenium webdriver(1)---浏览器操作
启动浏览器 如何启动浏览器已在上篇文章中说明,这里还是以chrome为例,firefox.IE启动方式相同. //启动浏览器 import org.openqa.selenium.WebDriver; ...
- [MarsZ]Unity3d游戏开发之Unity3d全策划配置新手指引
Unity3d全策划配置新手指引 前言... 2 版本... 2 作者... 2 功能... 2 类型... 2 触发类型... 2 步骤类型... 3 实现... 4 简要... 4 策划方面... ...
- Java中url传递中文参数取值乱码的解决方法
java中URL参数中有中文值,传到服务端,在用request.getParameter()方法,得到的常常会是乱码,这将涉及到字符解码操作. 方法一: http://xxx.do?ptname=’我 ...