废话很少说,直切进入正题。先来一张反射机制的图-->java
import java.util.Date;//先有类
public class ReflectTest1 {
public static void main(String[] args) {
Date date = new Date();//后有对象
System.out.println(date);
}
}复制代码
import java.util.Date;
public class ReflectTest2 {
public static void main(String[] args) {
Date date = new Date();
System.out.println(date.getClass());
}
}
"class java.util.Date"
复制代码
发现调用了getClas()方法后的输出,就输出了类的完整名称,等于找到了对象的出处。bash
java.lang.Class是一个类,这个类是反射操做的源头,即:全部的反射都要从类开始进行,而最关键的是这个类有三种实例化方法:app
1.调用Object类中的getClass()方法:ide
import java.util.Date;
public class ReflectTest3 {
public static void main(String[] args) {
Date date = new Date();
Class<?> cls = date.getClass();
System.out.println(cls);
}
}
"class java.util.Date"
复制代码
2. 使用“类.class”取得:ui
import java.util.Date;
public class ReflectTest4 {
public static void main(String[] args) {
Class<?> cls = Date.class;
System.out.println(cls);
}
}
"class java.util.Date"
复制代码
以前是在实例化了Date对象以后取得的Class类对象,可是如今并无实例化对象的产生。这种方法用在Hibernate、Mybatis、Spring等。this
3. 调用Class类提供的一个方法--实例化Class对象(在java.lang.Object下public static spa
Class<?> forName(String className) throws ClassNotFoundException):code
public class ReflectTest5 {
public static void main(String[] args) throws ClassNotFoundException {
Class<?> cls = Class.forName("java.util.Date");
System.out.println(cls);
}
}
"class java.util.Date"
复制代码
此时能够不使用import语句导入一个明确的类,而类的名称是采用字符串的形式进行描述的。cdn
当拿到一个类的时候,确定要直接使用关键字new 进行对象实例化操做这属于咱们习惯性的作法。若是拿到Class类对象,那么就能够作到利用反射来实例化对象操做:对象
public T newInstance() throws InstantiationException,IllegalAccessException
范例: 利用反射来实例化对象
/*正常实例化*/
class Book{
public Book(){
System.out.println("*** Book的构造方法 ***");
}
@Override
public String toString() {
return "--- 这是Book方法 ---";
}
}
public class ReflectTest6 {
public static void main(String[] args) throws ClassNotFoundException {
Book b = new Book();
System.out.println(b);
}
}
"*** Book的构造方法 *** --- 这是Book方法 ---"复制代码
package com.jkx.lzh.test;
/*反射实例化*/
class Book{
public Book(){
System.out.println("*** Book的构造方法 ***");
}
@Override
public String toString() {
return "--- 这是Book方法 ---";
}
}
public class ReflectTest7 {
public static void main(String[] args) throws InstantiationException,
IllegalAccessException, ClassNotFoundException{
Class<?> cls = Class.forName("com.jkx.lzh.test.Book");
Object obj = cls.newInstance();
}
}
"*** Book的构造方法 ***"复制代码
class Book{
public Book(){
System.out.println("*** Book的构造方法 ***");
}
@Override
public String toString() {
return "--- 这是Book方法 ---";
}
}
public class ReflectTest8 {
public static void main(String[] args) throws InstantiationException,
IllegalAccessException, ClassNotFoundException{
Class<?> cls = Class.forName("com.jkx.lzh.test.Book");
Object obj = cls.newInstance();
Book b = (Book) obj;
System.out.println(b);
}
}
"*** Book的构造方法 *** --- 这是Book方法 ---"复制代码
有了反射以后,之后进行对象化实例化的操做不在只是单独的依靠关键字new完成了,反射也一样能够,可是这并不表示new就被彻底取代了。
这样有人就会说:“下面两行代码至关于:Book b = new Book();这样不是更复杂了,么“。
Class<?> cls = Class.forName("com.jkx.lzh.test.Book");
Object obj = cls.newInstance();
复制代码
PS: 在任何的开发之中,new是形成耦合的最大元凶。一切的耦合都起源于new。
范例:观察工厂模式-->
interface Fruit{
void eat();
}
class Apple implements Fruit{
@Override
public void eat() {
System.out.println("* 吃苹果 *");
}
}
class Factory{
public static Fruit getInstance(String className){
if("apple" == className){
return new Apple();
}
return null;
}
}
public class TestFactory {
public static void main(String[] args) {
Fruit f = Factory.getInstance("apple");
f.eat();
}
}
"* 吃苹果 *"
复制代码
代码如上,可是此时,若是咱们要增长一个Fruit接口子类“orange”,就意味着咱们就要修改工厂模式的方法。
class Orange implements Fruit{
@Override
public void eat() {
System.out.println("* 吃橘子 *");
}
}
class Factory{
public static Fruit getInstance(String className){
if("apple" == className){
return new Apple();
}else if("orange" == className){
return new Orange();
}
return null;
}
}复制代码
因而可知,每增长一个Fruit接口子类,就要修改工厂类,那么若是随时须要增长子类呢?
由于如今工厂类都是new关键字直接实例化的,因此new就形成了全部问题的关键点。要想解决这一问题,就只能依靠反射完成。
修改工厂模式的方法以下:
package com.jkx.lzh.test;
interface Fruit{
void eat();
}
class Apple implements Fruit{
@Override
public void eat() {
System.out.println("* 吃苹果 *");
}
}
class Orange implements Fruit{
@Override
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 TestFactory {
public static void main(String[] args) {
Fruit f = Factory.getInstance("com.jkx.lzh.test.Apple");
f.eat();
}
}复制代码
此时的程序就真正的完成了解耦合的目的,并且可扩展性很是强。
在以前所编写的代码中,咱们都默认调用了类中的无参构造方法。但是类中也有可能不提供无参构造方法。
范例:观察当前程序的问题
Book.java
package com.jkx.lzh.po;
public class Book {
private String title;
private double price;
public Book(String title,double price){
this.title = title;
this.price = price;
}
public String toString(String title,double price) {
return "书名:"+ this.title + "价格: " + this.price ;
}
}复制代码
ReflectTest9.java
package com.jkx.lzh.test;
public class ReflectTest9 {
public static void main(String[] args) throws InstantiationException,
IllegalAccessException, ClassNotFoundException{
Class<?> cls = Class.forName("com.jkx.lzh.po.Book");
Object obj = cls.newInstance();
System.out.println(obj);
}
}复制代码
以上代码因为Book没有无参构造方法(<init>()),因此抛出如下异常:
Exception in thread "main" java.lang.InstantiationException: com.jkx.lzh.po.Book
at java.lang.Class.newInstance(Class.java:427)
at com.jkx.lzh.test.ReflectTest1.main(ReflectTest1.java:7)
Caused by: java.lang.NoSuchMethodException: com.jkx.lzh.po.Book.<init>()
at java.lang.Class.getConstructor0(Class.java:3082)
at java.lang.Class.newInstance(Class.java:412)
... 1 more复制代码
那么怎么解决这个问题呢?
在Class类中提供了这么一个构造方法能够取得构造:
以上两个方法返回都是“java.lang.reflect.Constructor”类的对象。在这个类中咱们提供一个明确传递有参构造内容实例化对象方法:
public T newInstance(Object... initargs) throws InstantiationException,IllegalAccessException,IllegalAccessException,IllegalArgumentException,InvocationTargetException;
范例:明确调用类中的构造方法
ReflectTest9.java修改以下:
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
public class ReflectTest9 {
public static void main(String[] args) throws InstantiationException,
IllegalAccessException, ClassNotFoundException, NoSuchMethodException,
SecurityException, IllegalArgumentException, InvocationTargetException{
Class<?> cls = Class.forName("com.jkx.lzh.po.Book");
//public Book(String title,double price);Constructor传的是有参构造的参数类型
Constructor<?> con = cls.getConstructor(String.class,double.class);
Object obj = con.newInstance("JAVA 反射,",82.22);
System.out.println(obj);
}
}
"书名:JAVA 反射价格: 82.22"
复制代码
以上给我一个警示:在咱们的简单java类的开发中无论提供多少构造方法,咱们至少要保留无参构造方法。
类中普通方法只有在这个类实例化对象以后才能被调用。而且实例化方法有三种(new,conle,反射)
范例:定义一个类
public class Book {
private String title;
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
}复制代码
这个类有无参构造方法,因此实例化对象的时候能够直接利用Class提供的newInstance方法
在Class类里面提供有如下取得类中Method()的操做:
1. 取得一个类中的所有方法: public Method[] getMethods() throws SecurityException;
public Method getMethod(String name,Class<?> ... ParameterTypes) throws NoSuchMethodException,SecurityException;
以上两个操做返回的是“java.lang.reflect.Method”类对象,在这里类里面咱们重点关注一个方法:调用方法-->
public Object invoke(Object obj,Object args) throws IllegalAccessException,IllegalArgumentException,InvocationTargetException;
范例:反射调用方法
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
public class ReflectTest10 {
public static void main(String[] args) throws InstantiationException,
IllegalAccessException, ClassNotFoundException, NoSuchMethodException,
SecurityException, IllegalArgumentException, InvocationTargetException{
String fileName = "title";//要操做的成员
Class<?> cls = Class.forName("com.jkx.lzh.po.Book");
Object obj = cls.newInstance();//必须实例化对象
Method setMet = cls.getMethod("set"+ inticap(fileName) , String.class);
Method getMet = cls.getMethod("get"+ inticap(fileName));
setMet.invoke(obj, "JAVA 反射");
System.out.println(getMet.invoke(obj));
}
public static String inticap(String str){
return str.substring(0,1).toUpperCase() + str.substring(1);
}
}
"JAVA 反射"复制代码
此时咱们彻底看不到操做类型,也就是说利用反射能够实现任意类的指定方法调用。
1. 取得所有成员:public Filed[] getDeclaredFileds() throws SecurityException;
2. 取得指定成员:public Filed getDeclaredFiled(String name) throws NoSuchMethodException,SecurityException;
1. 取得属性的内容:public Object getObject(Object obj) throws IllegalAccessException,IllegalArgumentException
2. 设置属性的内容:public void Object getObject(Object obj,Object values) throws IllegalAccessException,IllegalArgumentException
1. Executable下面继承了Constructor,Method;
2. Filed: 在这个类中提供有一个方法:public void setAccessible(boolean flag) throws SecurityException;设置是否取消封装
范例:如今提供以下类
package com.jkx.lzh.po;
public class Book {
private String title;
}复制代码
这个类中定义了一个私有属性,按照咱们原始的作法,此时它必定没法被外部所使用。
范例: 可是使用setAccessible取消封装就可使用了
import java.lang.reflect.Field;
public class ReflectTest11 {
public static void main(String[] args) throws NoSuchFieldException,
SecurityException, ClassNotFoundException,
InstantiationException, IllegalAccessException {
Class<?> cls = Class.forName("com.jkx.lzh.po.Book");
Object obj = cls.newInstance();//必须实例化对象
Field titleField = cls.getDeclaredField("title");
titleField.setAccessible(true);
titleField.set(obj, "JAVA 反射");//至关于:Book对象.title = "JAVA 反射";
System.out.println(titleField.get(obj));//至关于:Book对象.title;
}
}
"JAVA 反射"复制代码
构造方法和普通方法一样能够取消,只不过咱们不多这样去作,并且对于属性的访问仍是建议使用settr、gettr