Java-反射再学习
反射之中包含了一个“反”的概念,所以要想解释反射就必须先从“正”开始解释,一般而言,当用户使用一个类的时候,应该先知道这个类,而后通过这个类产生实例化对象,但是“反”指的是通过对象找到类。
- package cn.mldn.demo;
- class Person
- {
- }
- public class TestDemo
- {
- public static void main(String[] args) throws Exception
- {
- Person per = new Person(); // 正着操作
- System.out.println(per.getClass().getName()); // 反着来
- }
- }
以上的代码使用了一个getClass()方法,而后就可以得到对象所在的“包.类”名称,这就属于“反”了,但是在这个“反”的操作之中有一个getClass()就作为发起一切反射操作的开端。
Person的父类是Object类,而上面所使用getClass()方法就是Object类之中所定义的方法。
·取得Class对象:public final Class<?> getClass(),反射之中的所有泛型都定义为?,返回值都是Object。
而这个getClass()方法返回的对象是Class类的对象,所以这个Class就是所有反射操作的源头。但是在讲解其真正使用之前还有一个需要先解释的问题,既然Class是所有反射操作的源头,那么这个类肯定是最为重要的,而如果要想取得这个类的实例化对象,Java中定义了三种方式:
方式一:通过Object类的getClass()方法取得,基本不用:
- package cn.mldn.demo;
- class Person
- {
- }
- public class TestDemo
- {
- public static void main(String[] args) throws Exception
- {
- Person per = new Person(); // 正着操作
- Class<?> cls = per.getClass(); // 取得Class对象
- System.out.println(cls.getName()); // 反着来
- }
- }
方式二:使用“类.class”取得,在日后学习Hibernate开发的时候使用
- package cn.mldn.demo;
- class Person
- {
- }
- public class TestDemo
- {
- public static void main(String[] args) throws Exception
- {
- Class<?> cls = Person.class; // 取得Class对象
- System.out.println(cls.getName()); // 反着来
- }
- }
方式三:使用Class类内部定义的一个static方法,主要使用
·取得Class类对象:public static Class<?> forName(String className) throws ClassNotFoundException;
- package cn.mldn.demo;
- class Person
- {
- }
- public class TestDemo
- {
- public static void main(String[] args) throws Exception
- {
- Class<?> cls = Class.forName("cn.mldn.demo.Person"); // 取得Class对象
- System.out.println(cls.getName()); // 反着来
- }
- }
那么现在一个新的问题又来了,取得了Class类的对象有什么用处呢?对于对象的实例化操作之前一直依靠构造方法和关键字new完成,可是有了Class类对象之后,现在又提供了另外一种对象的实例化方法:
·通过反射实例化对象:public T newInstance() throws InstantiationException, IllegalAccessException;
范例:通过反射实例化对象
- package cn.mldn.demo;
- class Person
- {
- @Override
- public String toString()
- {
- return "Person Class Instance .";
- }
- }
- public class TestDemo
- {
- public static void main(String[] args) throws Exception
- {
- Class<?> cls = Class.forName("cn.mldn.demo.Person"); // 取得Class对象
- Object obj = cls.newInstance(); // 实例化对象,和使用关键字new一样
- Person per = (Person) obj; // 向下转型
- System.out.println(per);
- }
- }
那么现在可以发现,对于对象的实例化操作,除了使用关键字new之外又多了一个反射机制操作,而且这个操作要比之前使用的new复杂一些,可是有什么用?
对于程序的开发模式之前一直强调:尽量减少耦合,而减少耦合的最好做法是使用接口,但是就算使用了接口也逃不出关键字new,所以实际上new是造成耦合的关键元凶。
范例:回顾一下之前所编写的工厂设计模式
- package cn.mldn.demo;
- interface Fruit
- {
- public void eat();
- }
- class Apple implements Fruit
- {
- public void eat()
- {
- System.out.println("吃苹果。");
- };
- }
- class Factory
- {
- public static Fruit getInstance(String className)
- {
- if ("apple".equals(className))
- {
- return new Apple();
- }
- return null;
- }
- }
- public class FactoryDemo
- {
- public static void main(String[] args)
- {
- Fruit f = Factory.getInstance("apple");
- f.eat();
- }
- }
以上为之前所编写最简单的工厂设计模式,但是在这个工厂设计模式之中有一个最大的问题:如果现在接口的子类增加了,那么工厂类肯定需要修改,这是它所面临的最大问题,而这个最大问题造成的关键性的病因是new,那么如果说现在不使用关键字new了,变为了反射机制呢?
反射机制实例化对象的时候实际上只需要“包.类”就可以,于是根据此操作,修改工厂设计模式。
- package cn.mldn.demo;
- interface Fruit
- {
- public void eat();
- }
- class Apple implements Fruit
- {
- public void eat()
- {
- System.out.println("吃苹果。");
- };
- }
- class Orange implements Fruit
- {
- public void eat()
- {
- System.out.println("吃橘子。");
- };
- }
- class Factory
- {
- public static Fruit getInstance(String className)
- {
- Fruit f = null;
- try
- {
- f = (Fruit) Class.forName(className).newInstance();
- } catch (Exception e)
- {
- e.printStackTrace();
- }
- return f;
- }
- }
- public class FactoryDemo
- {
- public static void main(String[] args)
- {
- Fruit f = Factory.getInstance("cn.mldn.demo.Orange");
- f.eat();
- }
- }
发现,这个时候即使增加了接口的子类,工厂类照样可以完成对象的实例化操作,这个才是真正的工厂类,可以应对于所有的变化。如果单独从开发角度而言,与开发者关系不大,但是对于日后学习的一些框架技术这个就是它实现的命脉,在日后的程序开发上,如果发现操作的过程之中需要传递了一个完整的“包.类”名称的时候几乎都是反射机制作用。
3.12.2 、反射的深入应用
以上只是利用了Class类作为了反射实例化对象的基本应用,但是对于一个实例化对象而言,它需要调用类之中的构造方法、普通方法、属性,而这些操作都可以通过反射机制完成。
1、调用构造
使用反射机制也可以取得类之中的构造方法,这个方法在Class类之中已经明确定义了:
以下两个方法
取得一个类的全部构造:
public Constructor<?>[] getConstructors() throws SecurityException
取得一个类的指定参数构造:
public Constructor<T> getConstructor(Class<?>... parameterTypes) throws NoSuchMethodException, SecurityException
现在发现以上的两个方法返回的都是java.lang.reflect.Constructor类的对象。
范例:取得一个类之中的全部构造
- package cn.mldn.demo;
- import java.lang.reflect.Constructor;
- class Person
- { // CTRL + K
- public Person()
- {
- }
- public Person(String name)
- {
- }
- public Person(String name, int age)
- {
- }
- }
- public class TestDemo
- {
- public static void main(String[] args) throws Exception
- {
- Class<?> cls = Class.forName("cn.mldn.demo.Person"); // 取得Class对象
- Constructor<?> cons[] = cls.getConstructors(); // 取得全部构造
- for (int x = 0; x < cons.length; x++)
- {
- System.out.println(cons[x]);
- }
- }
- }
- public cn.mldn.demo.Person()
- public cn.mldn.demo.Person(java.lang.String)
- public cn.mldn.demo.Person(java.lang.String,int)
验证:在之前强调的一个简单Java类必须存在一个无参构造方法
范例:观察没有无参构造的情况
- package cn.mldn.demo;
- class Person
- { // CTRL + K
- private String name;
- private int age;
- public Person(String name, int age)
- {
- this.name = name;
- this.age = age;
- }
- @Override
- public String toString()
- {
- return "Person [name=" + name + ", age=" + age + "]";
- }
- }
- public class TestDemo
- {
- public static void main(String[] args) throws Exception
- {
- Class<?> cls = Class.forName("cn.mldn.demo.Person"); // 取得Class对象
- Object obj = cls.newInstance(); // 实例化对象
- System.out.println(obj);
- }
- }
- Exception in thread "main" java.lang.InstantiationException: cn.mldn.demo.Person
- at java.lang.Class.newInstance0(Unknown Source)
- at java.lang.Class.newInstance(Unknown Source)
- at cn.mldn.demo.TestDemo.main(TestDemo.java:26)
此时程序运行的时候出现了错误提示“java.lang.InstantiationException”,因为以上的方式使用反射实例化对象时需要的是类之中要提供无参构造方法,但是现在既然没有了无参构造方法,那么就必须明确的找到一个构造方法,而后利用Constructor类之中的新方法实例化对象:
·实例化对象:public T newInstance(Object... initargs) throws InstantiationException, IllegalAccessException,IllegalArgumentException, InvocationTargetException
- package cn.mldn.demo;
- import java.lang.reflect.Constructor;
- class Person
- { // CTRL + K
- private String name;
- private int age;
- public Person(String name, int age)
- {
- this.name = name;
- this.age = age;
- }
- @Override
- public String toString()
- {
- return "Person [name=" + name + ", age=" + age + "]";
- }
- }
- public class TestDemo
- {
- public static void main(String[] args) throws Exception
- {
- Class<?> cls = Class.forName("cn.mldn.demo.Person"); // 取得Class对象
- // 取得指定参数类型的构造方法
- Constructor<?> cons = cls.getConstructor(String.class, int.class);
- Object obj = cons.newInstance("张三", 20); // 为构造方法传递参数
- System.out.println(obj);
- }
- }
- Person [name=张三, age=20]
很明显,调用无参构造方法实例化对象要比调用有参构造的更加简单、方便,所以在日后的所有开发之中,凡是有简单Java类出现的地方,都一定要提供无参构造。
2、调用普通方法
当取得了一个类实例化对象之后,下面最需要调用的肯定是类之中的方法,所以可以继续使用Class类取得一个类中所定义的方法定义:
·取得全部方法:public Method[] getMethods()
throws SecurityException;
·取得指定方法:public Method getMethod(String
name, Class<?>... parameterTypes) throws NoSuchMethodException,
SecurityException
发现以上的方法返回的都是java.lang.reflect.Method类的对象。
范例:取得一个类之中所定义的全部方法
- package cn.mldn.demo;
- import java.lang.reflect.Method;
- class Person
- {
- private String name;
- public void setName(String name)
- {
- this.name = name;
- }
- public String getName()
- {
- return name;
- }
- }
- public class TestDemo
- {
- public static void main(String[] args) throws Exception
- {
- Class<?> cls = Class.forName("cn.mldn.demo.Person"); // 取得Class对象
- Method met[] = cls.getMethods(); // 取得全部方法
- for (int x = 0; x < met.length; x++)
- {
- System.out.println(met[x]);
- }
- }
- }
- public java.lang.String cn.mldn.demo.Person.getName()
- public void cn.mldn.demo.Person.setName(java.lang.String)
- public final native java.lang.Class java.lang.Object.getClass()
- public native int java.lang.Object.hashCode()
- public boolean java.lang.Object.equals(java.lang.Object)
- public java.lang.String java.lang.Object.toString()
- public final native void java.lang.Object.notify()
- public final native void java.lang.Object.notifyAll()
- public final native void java.lang.Object.wait(long) throws java.lang.InterruptedException
- public final void java.lang.Object.wait(long,int) throws java.lang.InterruptedException
- public final void java.lang.Object.wait() throws java.lang.InterruptedException
但是取得了Method类对象最大的作用不再于方法的列出(方法的列出都在开发工具上使用了),但是对于取得了Method类对象之后还有一个最大的功能,就是可以利用反射调用类中的方法:
·调用方法:public Object invoke(Object obj, Object... args) throws IllegalAccessException,IllegalArgumentException, InvocationTargetException
之前调用类中方法的时候使用的都是“对象.方法”,但是现在有了反射之后,可以直接利用Object类调用指定子类的操作方法。(同时解释一下,为什么setter、getter方法的命名要求如此严格)。
范例:利用反射调用Person类之中的setName()、getName()方法
- package cn.mldn.demo;
- import java.lang.reflect.Method;
- class Person
- {
- private String name;
- public void setName(String name)
- {
- this.name = name;
- }
- public String getName()
- {
- return name;
- }
- }
- public class TestDemo
- {
- public static void main(String[] args) throws Exception
- {
- Class<?> cls = Class.forName("cn.mldn.demo.Person"); // 取得Class对象
- Object obj = cls.newInstance(); // 实例化对象,没有向Person转型
- String attribute = "name"; // 要调用类之中的属性
- Method setMet = cls.getMethod("set" + initcap(attribute), String.class);// setName()
- Method getMet = cls.getMethod("get" + initcap(attribute));// getName()
- setMet.invoke(obj, "张三"); // 等价于:Person对象.setName("张三")
- System.out.println(getMet.invoke(obj));// 等价于:Person对象.getName()
- }
- public static String initcap(String str)
- {
- return str.substring(0, 1).toUpperCase().concat(str.substring(1));
- }
- }
张三
在日后的所有框架技术开发之中,简单Java类都是如此应用的,所以必须按照标准进行。
3.12.2 .3、调用成员
类之中最后一个组成部分就是成员(Field,也可以称为属性),如果要通过反射取得类的成员可以使用方法如下:
·取得本类的全部成员:public Field[] getDeclaredFields() throws SecurityException;
·取得指定的成员:public Field getDeclaredField(String name) throws NoSuchFieldException, SecurityException;
这两个方法的返回值类型是java.lang.reflect.Field类的对象,下面首先观察如何取得一个类之中的全部属性。
范例:取得一个类之中的全部属性
packagecn.mldn.demo;
importjava.lang.reflect.Field;
classPerson {
privateString name;
}
publicclassTestDemo {
publicstaticvoidmain(String[] args) throwsException {
Class<?> cls = Class.forName("cn.mldn.demo.Person") ; // 取得Class对象
Field field [] = cls.getDeclaredFields() ; // 取得全部属性
for(intx = 0; x < field.length; x++) {
System.out.println(field[x]);
}
}
}
但是找到Field实际上就找到了一个很有意思的操作,在Field类之中提供了两个方法:
·设置属性内容(类似于:对象.属性= 内容):public void set(Object obj,
Object value)
throws
IllegalArgumentException, IllegalAccessException;
·取得属性内容(类似于:对象.属性):public Object get(Object obj)
throws
IllegalArgumentException, IllegalAccessException
可是从类的开发要求而言,一直都强调类之中的属性必须封装,所以现在调用之前要想办法解除封装。
·解除封装:public void
setAccessible(boolean flag) throws SecurityException;
范例:利用反射操作类中的属性
packagecn.mldn.demo;
importjava.lang.reflect.Field;
classPerson {
privateString name;
}
publicclassTestDemo {
publicstaticvoidmain(String[] args) throwsException {
Class<?> cls = Class.forName("cn.mldn.demo.Person"); // 取得Class对象
Object obj = cls.newInstance(); // 对象实例化属性才会分配空间
Field nameField = cls.getDeclaredField("name") ; // 找到name属性
nameField.setAccessible(true) ; // 解除封装了
nameField.set(obj, "张三") ; // Person对象.name = "张三"
System.out.println(nameField.get(obj)); // Person对象.name
}
}
Java-反射再学习的更多相关文章
- JAVA反射机制—学习总结
最近收到很多关于Java反射机制的问题留言,其实Java反射机制技术方面没有太多难点,或许是大家在学习过程中遗漏了细小知识点,导致一些问题无法彻底理解,现在我们简单的总结一下,加深印象.什么是反射机制 ...
- 云笔记项目-Java反射知识学习
在云笔记项目中,补充了部分反射的知识,反射这一部分基础知识非常重要,前面学习的框架Spring和MyBatis读取xml配置文件创建对象,以及JDBC加载驱动等都用了反射,但只知道有这个东西,具体不知 ...
- Java反射机制——学习总结
前几天上REST课,因为涉及到Java的反射机制,之前看过一直没有用过,有些遗忘了,周末找了些资料来重新学习,现在总结一下,加深印象. 什么是反射机制? 参考百度百科对java反射机制的定义: “JA ...
- Java反射机制学习与研究
Java反射机制:可以获取正在运行时的Java对象. 1.判断运行时对象对象所属的类. 2.判断运行时对象所具有的成员变量和方法. 3.还可以调用到private方法,改变private变量的值. S ...
- Java 反射机制学习资料
Java反射——引言 Java反射——Class对象 Java反射——构造函数 Java反射——字段 Java反射——方法 Java反射——Getter和Setter Java反射——私有字段和私有方 ...
- Java反射机制学习
Java 反射是Java语言的一个很重要的特征,它使得Java具体了“动态性”. 在Java运行时环境中,对于任意一个类,能否知道这个类有哪些属性和方法?对于任意一个对象,能否调用它的任意一个方法?答 ...
- JAVA反射机制学习随笔
JAVA反射机制是用于在运行时动态的获取类的信息或者方法,属性,也可以用来动态的生成类,由于所有类都是CLASS的子类,我们可以用一个CLASS类的实例来实例化各种类 例如: Class<?&g ...
- Java反射篇学习笔记
今天重新学习了java中的反射,写一篇学习笔记总结一下.代码基本都是照着两篇博客敲的: 参考一: https://blog.csdn.net/sinat_38259539/article/deta ...
- java反射机制学习笔记
内容引用自:https://www.cnblogs.com/wkrbky/p/6201098.html https://www.cnblogs.com/xumBlog/p/8882489.html,本 ...
- java反射机制学习代码
根据 http://www.iteye.com/topic/137944 文档进行学习 代码如下: package reflectTest; import java.lang.reflect.*; i ...
随机推荐
- Linux下jvm、tomcat、mysql、log4j优化配置笔记[转]
小菜一直对操作系统心存畏惧,以前也很少接触,这次创业购买了Linux云主机,由于木有人帮忙,只能自己动手优化服务器了.... 小菜的云主机配置大致为:centeos6(32位),4核心cpu,4G内存 ...
- MFC 一个类訪问还有一个类成员对象的成员变量值
作者:卿笃军 原文地址:http://blog.csdn.net/qingdujun/article/details/35263857 MFC中一个类要訪问另外一个类的的对象的成员变量值,这就须要获得 ...
- mysql 学习笔记5-- 数据库优化
ext4:(rw,noatime,nodiratime,nobarrier,data=ordered)xfs: (rw,noatime,nodiratim,nobarrier,logbufs=8,lo ...
- SQL Server存储内幕系列
http://blog.itpub.net/355374/list/1/?cid=75087
- xhEditor与Java结合使用
xhEditor是一个轻量级的html编辑器,使用它可以非常方便的编辑图文内容,然而官方文档中只有php的演示,没有Java版的,最近两天参考网上各种各样的文档,琢磨了一下用法,现已可以正常运行,现在 ...
- Android UI 开发
今天主要学习了Android UI开发的几个知识 6大布局 样式和主题→自定义样式.主题 JUnit单元测试 Toast弹窗功能简介 6大布局 RelativeLayout LinearLayout ...
- System Operations on AWS - Lab 3W - Managing Storage (Windows)
创建一个名叫Processor的EC2实例,登陆到CommandHost实例,通过AWS CLI对Processor实例的EBS卷做snapshot,设置周期性snapshot的计划任务, 登陆到Pr ...
- MySQL存储过程详解 mysql 存储过程
原文地址:MySQL存储过程详解 mysql 存储过程作者:王者佳暮 mysql存储过程详解 1. 存储过程简介 我们常用的操作数据库语言SQL语句在执行的时候需要要先编译,然后执行,而存储 ...
- (转)css换行样式:word-wrap同word-break的区别
以下是引用片段: word-wrap:break-word; overflow:hidden; 而不是 以下是引用片段: word-wrap:break-word; word-break:break- ...
- Ionic 弹出窗
Ionic弹窗服务允许程序创建.显示弹出窗口,需要用户继续响应. 弹窗系统支持更多灵活的构建alert(),prompt()和confirm()功能版本,以及用户习惯,除了允许查看完全自定义内容的的弹 ...