java反射

目录

1、认识反射

2、Class 类实例化

3、反射实例化对象

4、使用发射调用构造

5、反射调用方法

6、反射调用成员变量


1、认识反射

反射的话先通过“反”来理解,既然有“反”就一定有“正”,在正常情况下,一定是先有类而后再产生对象。

public class Demo {
    public static void main(String[] args) {
        Date date=new Date();
        System.out.println(date);
    }
}

所谓的“反”就是指可以利用对象找到对象的出处,在Object类里面提供有一个方法:

  • 取得Class对象:public final Class<?> getClass。
public class Demo {
    public static void main(String[] args) {
        Date date=new Date();
        System.out.println(date.getClass());
    }
}

输出:

class java.util.Date

发现调用了getClass()方法后的输出就输出了类的完整名称,等于是找到了对象的出处。

2、Class 类实例化

java.lang.Class 是一个类,这个类是反射操作的源头,即:所有的反射都要从此类开始进行。

三种获得Class的方式:

  • 第一种:调用Object 类中的getClass方法。
import java.util.Date;

public class Demo {
    public static void main(String[] args) {
        Date date=new Date();
        Class<?> cls=date.getClass();
        System.out.println(cls);
    }
}
  • 第二种:使用“类.class” 取得,Hibernate、MyBatis、Spring使用的此种方式。
public class Demo {
    public static void main(String[] args) {       
        Class<?> cls=Date.class;
        System.out.println(cls);
    }
}

之前在产生了类的实例化对象之后取得Class类对象,但是此时并没有实例化对象。

  • 第三种:调用Class类提供的一个方法:

   |-实例化Class类对象:public static Class<?> forName(String className) throws ClassNotFoundException

public class Demo {
    public static void main(String[] args) throws ClassNotFoundException {
        Class<?> cls=Class.forName("java.util.Date");
        System.out.println(cls);
    }
}

此时可以不使用import 语句导入一个明确的类,而类名称是采用字符串的形式进行描述的。

3、反射实例化对象

拿到一个类的时候,肯定要直接使用关键字new 进行对象的实例化操作,这属于习惯性的做法,但是如果有了Class类对象

那么就可以做到,利用反射实现对象实例化操作。

利用反射实例化对象

public class Book {
    public Book(){
        System.out.println("******** BOOK类的无参构造方法 *********");
    }

    @Override
    public String toString() {
        return "这是一本书";
    }
}


public class Demo {

    public static void main(String[] args) throws ClassNotFoundException {

        Class<?> cls=Class.forName("com.man.java8.reflect.Book");
        try {
            //相当于调用无参构造实例
            Book book=(Book)cls.newInstance();
            System.out.println(book);
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }
    }
}

有了反射之后,以后进行对象实例化的操作不再只是单独的依靠关键字new完成了,反射并不表示new就被完全取代了。

在任何的开发中,new是造成耦合的最大元凶,一切的耦合都起源于new。

package com.man.java8.reflect;

interface Fruit{
    public void eat();
}

class Apple implements Fruit{

    @Override
    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 TestFactory {
    public static void main(String[] args) {
        Fruit f=Factory.getInstance("apple");
        f.eat();
    }
}

如果此时增加了Fruit接口子类,程序要修改工厂类

class Orange implements Fruit{

    @Override
    public void eat() {
        System.out.println("**** 吃橘子*****");
    }
}

class Factory{
    public static Fruit getInstance(String className){
        if("apple".equals(className)){
            return new Apple();
        }else if("orange".equals(className)){
            return new Orange();
        }
        return null;
    }
}

每增加一个类就要去修改工厂类,那么如果随时都可能增加子类呢? 工厂类一直被修改。

因为现在工厂类中的对象都是通过关键字new直接实例化的,而new就成了所有问题的关键点。要解决这一问题就只能够依靠反射完成。

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.man.java8.reflect.Apple");
        f.eat();
    }
}

此时的程序就真正完成了解耦合的目的,而且可扩展性非常的强。

4、使用反射调用构造

package com.man.java8.reflect;

public class Book {
    private String title;
    private Double price;

    public Book(String title,double price){
        this.title=title;
        this.price=price;
    }
    @Override
    public String toString() {
        return "这是一本书";
    }
}


public class Demo {

    public static void main(String[] args) throws ClassNotFoundException {

        Class<?> cls=Class.forName("com.man.java8.reflect.Book");
        try {
            Book book=(Book)cls.newInstance();
            System.out.println(book);
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }
    }
}

调用cls.newInstance(); 报错

java.lang.InstantiationException: com.man.java8.reflect.Book
	at java.lang.Class.newInstance(Class.java:427)
	at com.man.java8.reflect.Demo.main(Demo.java:9)
Caused by: java.lang.NoSuchMethodException: com.man.java8.reflect.Book.<init>()
	at java.lang.Class.getConstructor0(Class.java:3082)
	at java.lang.Class.newInstance(Class.java:412)
	... 1 more

出现以上错误,当前Book类没有无参构造方法,所以程序无法进行对象实例化;

这种情况下,只能调用有参构造方法;

在Class类里面提供有一个方法可以取得构造。

以上两个返回java.lang.reflect.Constructor类的对象。在这个类中提供有一个明确传递有参构造内容的实例化对象方法:

public T newInstance(Object... initargs) throws InstantiationException,IllegalAccessException,IllegalArgumentException,InvocationTargetException

范例:明确调用类中的有参构造

package com.man.java8.reflect;

public class Book {
    private String title;
    private Double price;

    public Book(String title,double price){
        this.title=title;
        this.price=price;
    }
    @Override
    public String toString() {
        return "Book{" +
                "title='" + title + '\'' +
                ", price=" + price +
                '}';
    }
}

package com.man.java8.reflect;
import java.lang.reflect.Constructor;

public class Demo {

    public static void main(String[] args) throws Exception {
        Class<?> cls=Class.forName("com.man.java8.reflect.Book");
        Constructor<?> con=cls.getConstructor(String.class,double.class);
        Object obj=con.newInstance("hibernate实战",78.8);
        System.out.println(obj);
    }
}

输出结果:

Book{title='hibernate实战', price=78.8}

5、反射调用方法

普通方法只有一个类产生实例化对象之后,才可以调用,并且实例化对象的方式有三种:

  1. new
  2. 克隆
  3. 反射

在Class类里面提供有以下取得类中Method的操作:

以上的两个操作返回的是java.lang.reflect.Method 类的对象,在里面重点关注:

  • 调用方法:public Object invoke(Object obj,Object... args)
                  throws IllegalAccessException,
                         IllegalArgumentException,
                         InvocationTargetException

例:

package com.man.java8.reflect;

public class Book {
    private String title;

    public String getTitle() {
        return title;
    }
    public void setTitle(String title) {
        this.title = title;
    }

}

package com.man.java8.reflect;
import java.lang.reflect.Method;

public class Demo {

    public static void main(String[] args) throws Exception {
        Class<?> cls=Class.forName("com.man.java8.reflect.Book");
        //这个类有无参构造方法,所以实例化对象的时候可以直接用Class类中提供的newInstance完成。
        Object obj=cls.newInstance();//必须给出实例化对象

        Method setMet=cls.getMethod("setTitle", String.class);
        setMet.invoke(obj,"java开发");

        Method getMet=cls.getMethod("getTitle");
        System.out.println(getMet.invoke(obj));
    }
}

6、反射调用成员变量

类中的属性一定要在本类实例化对象产生之后才可以分配内存空间。在Class里面有取得成员的方法。

  • 取得全部成员:public Field[] getDeclaredFields() throws SecurityException
  • 取得指定成员:public Field getDeclaredField(String name) throws NoSuchFieldException,SecurityException

返回的类型是java.lang.reflect.Field类,在这个类里面的两个重要方法

  • 取得属性内容:public Object get(Object obj) throws IllegalArgumentException, IllegalAccessException
  • 设置属性内容:public void set(Object obj,Object value)throws IllegalArgumentException,IllegalAccessException

 

package com.man.java8.reflect;

public class Book {
    private String title;    
}


package com.man.java8.reflect;
import java.lang.reflect.Field;
public class Demo {
    public static void main(String[] args) throws Exception {
        Class<?> cls=Class.forName("com.man.java8.reflect.Book");
        //这个类有无参构造方法,所以实例化对象的时候可以直接用Class类中提供的newInstance完成。
        Object obj=cls.newInstance();//必须给出实例化对象

        Field f=cls.getDeclaredField("title");
        f.setAccessible(true);//取消封装了
        f.set(obj,"java开发");
        System.out.println(f.get(obj));
    }
}

构造方法与普通方法也同样可以取消封装,只不过是很少这样去做,对属性不要直接访问,推荐通过getter和setter去访问。

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值