java基础类库——反射机制(七)

java基础类库——反射机制(七),第1张

反射机制

1.1 认识反射
正常的思路中,只有知道一个类之后才可以产生实例化对象。

范例:正常的 *** 作

package day2;

import java.util.Date;

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

所谓的反,就是通过对象找到来源,那么在程序中,对象要想找到他的来源,就必须依靠Object类提供的一个方法**:public final Class getClass()**

范例:观察反射

package day2;

import java.util.Date;

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

现在发现通过getClass()取得的Class类对象,就可以直接找到该对象的来源。

1.2 实例化Class类对象
Class 类是反射 *** 作的源头,也就是说所有的反射 *** 作都应该通过Class类完成,但是最有意思的是这个类的对象可以通过三种方式实例化。

范例1:利用Object类中的getClass()方法

package day2;

import java.util.Date;

public class TestDemo {
    public static void main(String[] args) throws Exception {
        Date date = new Date();
        Class cls = date.getClass();
        System.out.println("全包名:"+cls.getName());
        System.out.println("全类名:"+cls.getSimpleName());
    }
}

全包名:java.util.Date
全类名:Date

利用Object类中的getClass()方法,必须有明确的实例化对象后才能调用。

范例2:利用类“class” 取得Class类的实例化对象

package day2;

import java.util.Date;

public class TestDemo {
    public static void main(String[] args) throws Exception {
        Class cls = Date.class;
        System.out.println("全包名:"+cls.getName());
        System.out.println("全类名:"+cls.getSimpleName());
    }
}
全包名:java.util.Date
全类名:Date

范例3:利用Class类中的static方法取得
public static Class forName(String className)throws ClassNotFoundException 实例化Class对象:

package day2;

import java.util.Date;

public class TestDemo {
    public static void main(String[] args) throws Exception {
        Class cls = Class.forName("java.util.Date");
        System.out.println("全包名:"+cls.getName());
        System.out.println("全类名:"+cls.getSimpleName());
    }
}
全包名:java.util.Date
全类名:Date

现在最为重要的模式就是Class.forName()方法,它的好处是可以直接编写字符串。

1.3 通过反射实例化对象
如果要产生实例化对象,本质上来讲必须有关键字new才可以实现,但是这一切有了反射之后改变了。
在Class类中提供有如下一个方法: public T newInstance()throws InstantiationException,
IllegalAccessException

范例:正常的实例化对象

package day2;

import java.util.Date;

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

范例:反射实例化对象

package day2;

import java.util.Date;

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

范例:反射自定义的实例化类对象

package day2;

import java.util.Date;

class Person{
    public Person(){
        System.out.println("**************自定义的反射实例化对象**********");
    }

    @Override
    public String toString() {
        return "自定义Person类";
    }
}
public class TestDemo {
    public static void main(String[] args) throws Exception {
        Class cls = Class.forName("day2.Person");
        Person p = (Person)cls.newInstance();
        System.out.println(p);
    }
}
**************自定义的反射实例化对象**********
自定义Person类

反射实例化对象最终也调用无参构造方法进行实例化 *** 作

1.4 工厂设计模式的修正
工厂设计模式的本质就在于要想取得的实例化对象不要直接使用关键字new,而应该通过工厂取得

范例:传统工厂设计

package day2;

interface Fruit{
    public 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){
        if("apple".equals(className)){
            return new Apple();
        }
        if("orange".equals(className)){
            return new Orange();
        }
        return null;
    }
}
public class TestDemo {
    public static void main(String[] args) throws Exception {
        Fruit fruit = Factory.getInstance("apple");
        fruit.eat();
    }
}

************吃苹果

但是修正对于工厂模式却提出了一个挑战,如果现在增加了子类,那么意味着工厂类需要修改,那么如果假如这个接口可能源源不断产生新的子类,所以这个时候工厂类的设计就出现了新的问题了。那么新的关键问题就在new上面了,new是造成本次设计失败的最大因素。那么此时我们就可以通过反射类实现工厂类 *** 作。

范例:利用反射类改进工厂模式

package day2;

interface Fruit{
    public 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){
        try {
            Fruit fruit = (Fruit)Class.forName(className).newInstance();
            return fruit;
        }catch (Exception e){
            e.printStackTrace();
        }
        return null;
    }
}
public class TestDemo {
    public static void main(String[] args) throws Exception {
        Fruit fruit = Factory.getInstance("day2.Orange");
        fruit.eat();
    }
}
************吃橘子

以上代码分析出了关键字new实例化对象与构造方法实例化对象的区别,但是并不是意味着不使用new关键字了。

1.5 调用构造方法
利用Class类的 newInstance()方法其主要的目的是可以取得类的实例化对象,但是这种取得 *** 作本身是存在问题的,因为它只能够调用类中的无参构造

范例:调用构造方法

package day2;

class Person{
    private String name;
    private int age;
     public Person(String name,int age){
         this.name = name;
         this.age = age;
     }

    @Override
    public String toString() {
        return  "姓名:" + this.name + ",年龄:" + this.age;
    }
}
public class TestDemo {
    public static void main(String[] args) throws Exception {
        Class cls = Class.forName("day2.Person");//利用Class中的forName实例化
        Person person =(Person) cls.newInstance();//newInstance(),通过反射实例化对象
        System.out.println(person);
    }
}

执行报错了,因为没有无参构造!!!

如果类中没有提供无参构造方法,那么就必须明确的调用指定的构造方法实现对象的实例化 *** 作。要想实现这样的 *** 作,那么必须要找到类中的构造方法定义:
public ConstructorgetConstructor(Class… parameterTypes)
throws NoSuchMethodException, SecurityException;

找到了Constructor类之中提供了一个实例化对象的方法;
(1)实例化对象:public T newInstance(Object … initargs) throws InstantiationException,
IllegalAccessException,IllegalArgumentException, InvocationTargetException

范例:调用指定构造实例化对象

package day2;

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;
     }

    @Override
    public String toString() {
        return  "姓名:" + this.name + ",年龄:" + this.age;
    }
}
public class TestDemo {
    public static void main(String[] args) throws Exception {
        Class cls = Class.forName("day2.Person");//利用Class中的forName实例化
        Constructor constructor = cls.getConstructor(String.class,int.class);
        Person person =(Person)constructor.newInstance("张三",10);//newInstance(),通过反射实例化对象
        System.out.println(person);
    }
}
姓名:张三,年龄:10

1.6 调用方法
类中的普遍方法是取得了本类的实例化对象之后才可以进行调用的 *** 作。所以即便使用了反射方法的调用,那么也必须使用newInstance()取得实例化对象,在Class类中定义有如下取得方法对象的 *** 作:取得方法: public Method getMethod(String name, Class… parameterTypes)throws NoSuchMethodException, SecurityException;

getMethod()方法返回的是java.lang.reflect.Method类,在这个类中有一个很重要的方法,调用方法: public Object invoke(Object obj, Object… args) throws IllegalAccessException, IllegalArgumentException,InvocationTargetException

范例:利用反射调用方法

package day2;

import java.lang.reflect.Method;

class Person{
    private String userName;
    public String getUserName() {
        return userName;
    }
    public void setUserName(String userName) {
        this.userName = userName;
    }
}
public class TestDemo {
    public static void main(String[] args) throws Exception {
        String attribute  = "userName";
        Class cls = Class.forName("day2.Person");//利用Class中的forName实例化
       Object obj = cls.newInstance();
        Method seMethod = cls.getMethod("set"+initcap(attribute),String.class);
        Method geMethod = cls.getMethod("get"+initcap(attribute));
        seMethod.invoke(obj,"张三");//等价于:setUserName("张三")
        System.out.println(geMethod.invoke(obj));//张三
    }
    public static String initcap(String str){
        return str.substring(0,1).toUpperCase()+str.substring(1);
    }
}

以后程序如果给了属性名称,类的完整名称,而后可以进行自动的赋值 *** 作,那么基本上就是反射机制在里面起作用。

1.7 调用成员

在类中的成员需要考虑两种成员:父类继承的成员、本类定义的成员
父类继承的成员:public Field getField(String name)throws NoSuchFieldException,SecurityException
本类定义的成员: public Field getDeclaredField(String name)throws NoSuchFieldException, SecurityException

在java中使用java.lang.reflect.Field描述成员,在这个类中有两个方法:
(1)取得成员内容:public Object get(Object obj)throws IllegalArgumentException, IllegalAccessException
(2)设置成员内容:public void set(Object obj, Object value)throws IllegalArgumentException, IllegalAccessException

范例:直接进行属性调用

package day2;

import java.lang.reflect.Field;

class Person{
    private String userName;
}
public class TestDemo {
    public static void main(String[] args) throws Exception {
        Class cls = Class.forName("day2.Person");//利用Class中的forName实例化
       Object obj = cls.newInstance();

        Field userNameField = cls.getDeclaredField("userName");
        userNameField.set(obj,"张三胖");//对象.userName = "张三胖"
        System.out.println(userNameField.get(obj));
    }
}
报错:Exception in thread "main" java.lang.IllegalAccessException: Class day2.TestDemo can not access a member of class day2.Person with modifiers "private"
	at sun.reflect.Reflection.ensureMemberAccess(Reflection.java:102)
	at java.lang.reflect.AccessibleObject.slowCheckMemberAccess(AccessibleObject.java:296)
	

注:需要注意的是几乎所有的属性都要使用private封装,所以要想解决封装的困扰,可以利用Field父类(ava.lang.reflect.AccessibleObject)方法完成:
取消封装:public void setAccessible(boolean flag) throws SecurityException

范例:解决private封装

package day2;

import java.lang.reflect.Field;

class Person{
    private String userName;
}
public class TestDemo {
    public static void main(String[] args) throws Exception {
        Class cls = Class.forName("day2.Person");//利用Class中的forName实例化
       Object obj = cls.newInstance();

        Field userNameField = cls.getDeclaredField("userName");
        userNameField.setAccessible(true); //private 消失
        userNameField.set(obj,"张三胖");//对象.userName = "张三胖"
        System.out.println(userNameField.get(obj));
    }
}
张三胖

欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/langs/866995.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-05-12
下一篇 2022-05-12

发表评论

登录后才能评论

评论列表(0条)

保存