从学习Java开始,就入手面向对象、Java反射等等,也懵逼在面向对象的世界中,反问道个人对象是谁,很差意思,可能尚未建立!java
反射是Java的一个特色,也是使本来为静态语言的Java,多了那么一些灵活性,在理解各个框架源码以及组件内容的时候是一个不错的知识点,好比注解,这是一个很是常见,又很好使的玩意,以前也有简单的学习---Java 注解 基础、Java 注解 实践 (最好先学习Java反射再去玩注解,会颇有帮助)spring
从主要如下几点开始学习数组
在面向对象的环境中,万事万物皆对象,但也总有例外,Java中有两个不属于对象,一个是普通数据类型,一个是静态的成员框架
普通数据类型有封装类的弥补,静态的属于类,那么类是否是对象呢,类是对象,是java.lang.Class类的实例对象,看文字还比较容易理解,中文说出来就比较绕口, 英文: there is a class named Classeclipse
一个普通的类的实例对象表示函数
public class Coo { //Doo的实例对象 以doo表示 Doo doo=new Doo(); } class Doo{}
那么一个Class类的实例对象,有三种表示方式,但不能是new Class,由于下面源码中也解释为何工具
/* * Private constructor. Only the Java Virtual Machine creates Class objects. * This constructor is not used and prevents the default constructor being * generated. */ private Class(ClassLoader loader) { // Initialize final field for classLoader. The initialization value of non-null // prevents future JIT optimizations from assuming this final field is null. classLoader = loader; }
上面是Class类中的一个构造器,是私有的,并且注释说只有JVM建立Class对象,在之前的Java版本中你可能会看到一个无参的构造器,不要紧,那你的构造器也绝对是私有,不能直接建立,在上面中出现了一个JIT编译的关键词,有兴趣的小伙伴能够研究扩展学习
任何类都是Class的实例对象,下面三种方式:this
public class Coo { //Doo的实例对象 以doo表示 Doo doo=new Doo(); //①能够看出Doo类有一个隐含的静态成员变量class Class first=Doo.class; //②已知类的对象,经过getClass获取 Class second=doo.getClass(); //重理解一次:doo表明Doo类的实例对象,first、second表明的是Class的实例对象 //这个Class的实例对象又证实说Doo这个类自己是一个实例对象的存在 //一本正经的胡说八道,那么给一个官方给出的说法是这样的 :first、second表示了Doo类的类 类型(class type) //全部东西都是对象,类也是对象,是Class的实例对象,这个对象称为该类的类类型 //就能够分析到,Doo的对象是doo,Doo的类类型是Class的对象first、second //无论哪一种表达方式表示Doo的类类型,一个类只多是Class类的一个实例对象,因此first == second //③须要异常处理,参数为类的全称"com.cloud.eureka.Doo" Class third=null; { try { third = Class.forName("com.cloud.eureka.Doo"); } catch (ClassNotFoundException e) { e.printStackTrace(); } } //依然存在 first == second == third //因而可知,咱们能够经过类的类类型建立该类的对象,经过first、second、third建立 //须要异常处理,是谁的类的类类型对象,建立的对象就是谁,须要强转 //newInstance前提须要无参构造方法 { try { Doo dooFirst= (Doo) first.newInstance(); } catch (InstantiationException e) { e.printStackTrace(); } catch (IllegalAccessException e) { e.printStackTrace(); } } } class Doo{}
三种表示Class的实例对象中,第三种具备很好的动态加载类③spa
不少时候,你们都是经过工具(IDEA、eclipse等)进行办公或者学习,编译和运行都是由工具来辅助完成的,那么咱们须要知道编译、运行的区别
1.2..3...好,咱们获得了编译、运行知识的技能
只要是在类里面用到的,都隐含class,对应的类的类类型,以下:
public class Coo { Class c0=int.class; Class c1=String.class; Class c2=Double.class; Class c3=void.class; // package不是在类里面的,error // Class c4=package.class; }
在Doo类中,写个方法
class Doo{ public static void staticVoidMethod(Object o){ //传递的是什么类型,就是什么类型 Class co=o.getClass(); } }
o传递的是什么对象,co就是该类的类类型,那么底层怎么实现的,可能会比较复杂,贴一份源码
public final native Class<?> getClass();
这是一个native声明的一个方法,称为本地方法,Java中有一项技术JNR,使用Java声明,C语言实现,Java 中调用...一堆,有兴趣的能够了解了解,效果就是上面说的,返回类的类类型
下面是简单的经过Class获取类的信息:
class Doo { public static void staticVoidMethod(Object o) { //传递的是什么类型,就是什么类型 Class co = o.getClass(); System.out.println("类的全名称:" + co.getName()); System.out.println("类的名字:" + co.getSimpleName()); //Method类,方法对象 //一个成员方法 就是 一个Method对象 //getMethods 获取全部public的方法,其中包括父类继承的函数 Method[] allMethods = co.getMethods(); //getDeclaredMethods获取该类本身声明的方法 Method[] thisMethods = co.getDeclaredMethods(); for (Method method : allMethods) { //method.getReturnType()获得的是类的类类型 //好比返回值是String,那么获得的是String.class的类类型,经过getName获取名称 System.out.println("返回类型:" + method.getReturnType().getName()); System.out.println("方法名称:" + method.getName()); //获取参数类型 Class[] parameterTypes = method.getParameterTypes(); for (Class c : parameterTypes) { System.out.println("参数类型:" + c.getName()); } System.out.println("===================================="); } //成员变量 =》对象 //属于java.lang.reflect.Field //Field封装了关于成员变量的操做 //getFields获取全部public的成员变量 Field[] field=co.getFields(); //获得本身声明的成员变量 Field[] declaredFields=co.getDeclaredFields(); for (Field fields:field) { System.out.println("成员变量类型"+fields.getType()); System.out.println("成员变量名称"+fields.getName()); } } }
简单来一个main方法,加入一个String类
public class Coo { public static void main(String[] args) { String hello=new String(); Doo.staticVoidMethod(hello); } }
控制台打印 , 全部String内的方法信息:
类的全名称:java.lang.String 类的名字:String 返回类型:boolean 方法名称:equals 参数类型:java.lang.Object ==================================== 返回类型:java.lang.String 方法名称:toString ==================================== 返回类型:int 方法名称:hashCode ==================================== 返回类型:int 方法名称:compareTo 参数类型:java.lang.Object ==================================== //......
能够总结出来,getDeclaredXXX()方法都是获取本身声明的内容,包括成员变量,构造器,方法等等,直接的getXXX()方法部分会获取全部内容包括父类的内容,另外数组是一个特殊的存在,打印的是“0]”差很少的样子,在JVM对数组的存储方式也比较VIP,有兴趣的能够理解扩展
上面有获取全部的方法的示例,下面来学习如何获取某一个方法以及方法的反射操做
①方法的名称和方法的参数列表能够惟必定位某一个方法
②method.invoke(对象,参数列表)
public class MethodReflect { //获取getMethod方法,获取①号 public static void main(String[] args) { MethodDemo demo = new MethodDemo(); //1.获取类信息 Class c0 = demo.getClass(); //2.获取方法 try { //第一种写法 Method method1 = c0.getDeclaredMethod("getMethod", new Class[]{String.class, String.class}); //第二种写法 Method method2 = c0.getDeclaredMethod("getMethod", String.class, String.class); //平时正常的调用方法: demo.getMethod(str0,str1) //如今使用method1来调用--public Object invoke(Object obj, Object... args) //第一个参数是调用的类,第二个参数是可用可无,按定义的方法来录入(str0,str1) //invoke的方法若是有返回值,则返回Object的值,void的返回值为null try { Object object1 = method1.invoke(demo, new Object[]{"hello", " world"}); Object object2 = method2.invoke(demo, "hello", " world"); } catch (IllegalAccessException e) { e.printStackTrace(); } catch (InvocationTargetException e) { e.printStackTrace(); } } catch (NoSuchMethodException e) { e.printStackTrace(); } } } class MethodDemo { //① public void getMethod(String a, String b) { System.out.println("concat: " + a + b); } //② public void getMethod(int a, int b) { System.out.println("sum: " + a + b); } }
泛型不说了,很是的经常使用,好比list,map等等,约定类型,很少作解释,直接先来一个操做,比对List和List<String>是否相等
public class Coo { public static void main(String[] args) { //无泛型 List list0=new ArrayList(); //String泛型 List<String> list1=new ArrayList<>(); list1.add("hello"); Class c0=list0.getClass(); Class c1=list1.getClass(); //输出 System.out.println(c0==c1); } }
输出的结果是true, 编译后的class文件也能够当成字节码,说明反射的操做都是编译以后的操做,并且返回true说明编译以后list的泛型被抹去了,去泛型化的,获得Java的泛型是一种规范,只在编译时有效,跳过编译编译就无效了,为了验证这一点,恰好可使用反射来作一个验证
//获取list1<String> 中的 add方法 ,向里面加一个int类型的值 Method method=c1.getMethod("add",Object.class); method.invoke(list1,100); System.out.println(list1.size());
list1的大小改变了,说明添加成功了,也验证了Java泛型只在编译期有效,运行时则去泛型化,若是去遍历这个list1是会报类型转化异常的
反射的用处有不少,好比工具类,源码理解,注解解析等等,再例如excel导出导入这样的操做,网上也有很是多的poi操做案例,也能够用反射+注解的方式很是简洁的实现; 例如spring源码中不少的注解@Autowired、@SpringCloudApplication、@Service...等等不少不少
好好学习,每天向上
------------------------------------------------------------