2022.0916学习Java笔记之反射机制入门

认识反射

既然有反,那么一定存在正,正是指的通过类产生对象,而后通过对象执行操作。而反呢,通过对象找到他所在的类信息。所有的对象都支持反这一操作,因为Object类有一个方法:

        public final Class<?>getClass();

package cn.mldn.demo;

import java.util.Date;
public class TextDemo {
    public static void main(String[] args) throws Exception {
        Date date = new Date();
        System.out.println(date.getClass());
    }
}

输出结果:class java.util.Date

现在的代码可直接通过对象取得他对应的类型,那么就不难理解instanceof关键字的处理方式

实例化Class对象

 在Object类之中定义的getClass()方法,发现里面返回的类型是Class,而Class是作为一切反射操作的源头表示。首先一定要清楚如何实例化Class对象,实际上此类对象由三类实例化方式:

  • 方式一:利用Object类之中的getClass()方法;
package cn.mldn.demo;

import java.util.Date;
public class TextDemo {
    public static void main(String[] args) throws Exception {
        Date date = new Date();
        Class<?> cls = date.getClass();
        System.out.println(cls);
    }
}
  • 方式二:利用“类.class”取得
package cn.mldn.demo;

import java.util.Date;
public class TextDemo {
    public static void main(String[] args) throws Exception {
        Class<?> cls = Date.class;
        System.out.println(cls);
    }
}
  • 方式三:Class内部提供了一个static方法:“public static Class<?>forName(String className)throws ClassNotFoundException”
package cn.mldn.demo;

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

以上三种代码最没用的就是第一种,第二中是学Hibernate的,第三种是Spring的实现基础

利用反射实例化对象

  对象的实例化操作在之前只能够利用关键字new实现,但是从今天开始,对象实例化也可以利用反射机制进行处理,因为在Class类之中定义了一个方法:“public T newInstance() throws InstantiationException, IllegalAccessException.”

范例:利用反射实例化对象

package cn.mldn.demo;

class Person{
    public String toString(){
        return "是个人";
    }
}
public class TextDemo {
    public static void main(String[] args) throws Exception {
        Class<?> cls = Class.forName("cn.mldn.demo.Person");
        Person per = (Person) cls.newInstance();    //Person per = new Person()
        System.out.println(per);
    }
}

输出结果:是个人

思考:既然关键字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 fru = Factory.getInstance("apple");
        fru.eat();
    }
}

输出结果:吃苹果

如果说现在添加一个Fruit接口子类,那么就需要修改Factory类,很明显这种方法很蠢,希望最合适的工厂是不做任何修改而适应所有的子类。但是此代码不行,因为关键性问题在于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 Banana implements Fruit{
    public void eat(){
        System.out.println("吃香蕉");
    }
}
class Factory{
    public static Fruit getInstance(String ClassName){
        Fruit fruit = null;
        try{
            fruit = (Fruit) Class.forName(ClassName).newInstance();
        }catch(Exception e){
            e.printStackTrace();
        }
        return fruit;
    }
}
public class FactoryDemo {
    public static void main(String[] args){
        Fruit fru = Factory.getInstance("cn.mldn.demo.Banana");
        fru.eat();
    }
}

使用反射操作类结构

 类的结构就三部分:构造方法、普通方法、属性,而这些操作有了反射,一切都可以轻松面对。

1、调用构造

  构造方法是在类实例化的时候默认调用的,一个类之中可以提供有多个构造方法,而且一个类如果没有定义任何一个构造方法的话,则会自动的生成一个无参的,什么都不做的构造方法。

  在Class类之中定义了如下取得构造方法的操作:

  • 取得全部构造:public Constructor<?>[] getCnstuctors() throws SecunityException;
  • 取得指定参数构造:public Constructor<T>getConstructor(Class<?>parameterTypes) throws NoSuchMethodException, SecurityException。

范例:观察问题

package cn.mldn.demo;

class Person{
    private String name;
    private int age;
    public Person(String name, int age){
        this.name = name;
        this.age = age;
    }
    public String toString(){
        return "姓名:" + this.name + ",年龄:" + this.age;
    }
}
public class TextDemo {
    public static void main(String[] args) throws Exception {
        Class<?> cls = Class.forName("cn.mldn.demo.Person");
        Person per = (Person) cls.newInstance();    
        System.out.println(per);
    }
}

 问题:Exception in thread "main" java.lang.InstantiationException: cn.mldn.demo.Person

  在使用Class类对象反射实例化对象的时候,类之中必须提供有无参构造,如果不提供就出错了。那么如果说现在类中就没有无参构造的话,那么就必须利用反射找到指定的构造方法明确传入所需要的参数才可以实例化对象,这个就需要通过 Constructor 类完成了。 

package cn.mldn.demo;

import java.lang.reflect.Constructor;
class Person{
    private String name;
    private int age;
    public Person(String name, int age){
        this.name = name;
        this.age = age;
    }
    public String toString(){
        return "姓名:" + this.name + ",年龄:" + this.age;
    }
}
public class TextDemo {
    public static void main(String[] args) throws Exception {
        Class<?> cls = Class.forName("cn.mldn.demo.Person");
        //根据构造方法的参数类型找到一个指定的构造
        Constructor<?> cons = cls.getConstructor(String.class, int.class);
        Person per = (Person) cons.newInstance("张三",20);    //Person per = new Person()
        System.out.println(per);
    }
}

为了统一,还是要保留无参构造

2、调用方法

除了使用对象调用类中的方法之外,也可以利用反射完成,在Class类里面定义了如下方法:

  • 取得全部方法:public Method[] getMethodsO throws SecurityException;
  • 取得指定方法:public Method getMethod(String name, Class<?>... parameterTypes) throws NoSuchMethodException, SecurityException。

在Method类之中存在一个方法:public Object invoke(Object obj, Object... args) throws IllegalAccessException, IllegalArgumentException, InvocationTargetException,指的是调用反射定义的方法。

不管是否使用反射,如果需要调用类中的方法请一定要保证实例化对象。

范例:通过反射调用类中的 setter、gettere

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 TextDemo {
    public static void main(String[] args) throws Exception {
        String attribute = "name";      //操作的是类中的name属性
        Class<?> cls = Class.forName("cn.mldn.demo.Person");
        Object obj = cls.newInstance();     //实例化对象
        Method setMethod = cls.getMethod("set" + initcap(attribute),String.class);
        setMethod.invoke(obj,"张三");     //对象。setName("张三")
        Method getMethod = cls.getMethod("get" + initcap(attribute));
        System.out.println(getMethod.invoke(obj));  //对象.getName()
    }
    public static String initcap(String str){
        return str.substring(0,1).toUpperCase().concat(str.substring(1).toLowerCase());
    }
}

输出结果:张三

3、 调用属性

  类之中的属性一定是使用private封装的,但是由于其也是组成的部分,所以也可以利用反射操作,通过Class类:

  • 取得定义的全部属性:public Field[ getDeclaredFieldsO throws SecurityException;
  • 取得指定的属性:public Field getDeclaredField(String name) throws NoSuchFieldException,SecurityException。

这个时候返回的是一个Field类的对象,而这个类里面定义两个方法:

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

范例:操作属性

package cn.mldn.demo;

import java.lang.reflect.Field;
class Person{
    private String name;
}
public class TextDemo {
    public static void main(String[] args) throws Exception {
        Class<?> cls = Class.forName("cn.mldn.demo.Person");
        Object obj = cls.newInstance();     //实例化对象
        Field nameField = cls.getDeclaredField("name");     //取得name属性
        nameField.setAccessible(true);  //取消封装
        nameField.set(obj,"张三");     //对象.name = "张三"
        System.out.println(nameField.get(obj));
    }

}

但是所有的属性一定要通过setter、getter设置和取得,以上代码只是针对于Field操作做一个说明。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值