【JavaSE】之反射

1.认识反射机制

1.1初识反射

反射指得是对象的反向处理。根据对象倒推类的组成。

例:观察正常处理

import java.util.Date;

public class Test{
    public static void main(String[] args){
        Date date = new Date();
    }
}

所谓的“反”指的是根据对象来取得对象的来源信息,反射的核心处理在于Object类的一个方法:取得类的class对象

public final native Class<?> getClass();

该方法返回的是一个Class对象,这个Class描述的就是类。

在反射的世界里,看中的不再是一个对象,而是对象身后的组成(构造方法、普通方法、普通属性)

1.2Class对象的三种实例化方式

任何一个类的Class对象由JVM加载类后产生(该对象在JVM中全局唯一),用户只能调用指定方法来取得该对象。

I.任何类的对象可以通过Object类提供的getClass()取得该类的Class对象。

II.“类名称.class”可以直接根据某个具体类来取得其Class对象

III.调用Class类的静态方法Class.forName(String className)传入类的全名称来取得其Class对象

public class Test2{
    public static void main(String[] args) throws ClassNotFoundException {
        //根据类正向产生对象
        Date date = new Date();
        //1.通过类对象.getClass()
        System.out.println(date.getClass());
        //2.通过类名称.class
        System.out.println(Date.class);
        //3.通过调用Class类提供的静态方法forName(String className)
        System.out.println(Class.forName("java.util.Date"));
    }
}

取得一个类的Class对象后:可以通过反射来实例化对象

在Class类中有如下方法:只能调用类中无参构造且无参构造必须是public权限

public T[Object] newInstance() throws InstantiationException,IllegalAccessException
public class Test2{
    public static void main(String[] args) throws Exception {
        //Date date = new Date();
        //1.取得类的Class对象
        Class<Date> cls = (Class<Date>)Class.forName("java.util.Date");
        //通过反射取得Date类实例化对象
        Date date = cls.newInstance();
        System.out.println(date);
    }
}

例:使用反射实现简单工厂

interface IFruit{
    void eat();
}

class Apple implements IFruit{
    @Override
    public void eat() {
        System.out.println("eat an apple");
    }
}
class Pear implements IFruit{
    @Override
    public void eat() {
        System.out.println("eat a pear");
    }
}
//反射修改工厂方法
class Factory{
    private Factory(){}
    //工厂方法模式
    public static IFruit getInstance(String className){
        IFruit fruit = null;
        try {
            //取得任意子类反射对象
            Class<?> cls = Class.forName(className);
            //通过反射取得实例化对象
            fruit = (IFruit) cls.newInstance();
        } catch (Exception e) {
            e.printStackTrace();
        }
        return fruit;
    }
}
public class Test2{
    public static void main(String[] args) {
        IFruit fruit = Factory.getInstance("www.bit.java.Pear");
        fruit.eat();
    }
}

2.反射与类操作(与反射相关的第一步都是取得Class对象)

2.1反射取得父类、父接口信息

2.1.1取得类的包名称

public Package getPackage();

例:

public class Test2{
    public static void main(String[] args) throws Exception {
        //1.取得Class对象
        Class<?> cls = Date.class;
        //只取得包名,不要版本号
        System.out.println(cls.getPackage().getName());
    }
}

2.1.2取得父类的Class对象(父类单继承,只可能有一个)

public native Class<? super T> getSuperClass();

2.1.3取得实现的父接口(可以实现多个接口)

public Class<?>[] getInterfaces();

例:

class Person{}
interface INews{}
interface IMessage{}

class Student extends Person implements INews,IMessage{}

public class Test2{
    public static void main(String[] args) throws Exception {
        Class<?> cls = Student.class;
        System.out.println(cls.getSuperclass().getName());
        Class<?>[] classes = cls.getInterfaces();
        for(Class<?> classs : classes){
            System.out.println(classs.getName());
        }
    }
}

注意:基本数据类型在JVM中也有类对象

Class<?> cls = int.class;
Class<?> cls1 = Integer.class;
System.out.println(cls);
System.out.println(cls1);

2.2反射调用类中构造方法-Constructor(描述类中构造方法)

取得类中指定参数类型的构造方法

//只能取得类中public权限的构造方法
public Constructor<T> getConstructor(Class<?> ... parameterTypes)
//可以取得类中全部构造方法,包含私有构造
public Constructor<T> getDeclaredConstructor(Class<?> ... parameterTypes)

取得类中所有构造方法

//只能取得类中public权限的构造方法
public Constructor<?>[] getConstructors() throws SecurityException
//可以取得类中全部构造方法,包含私有构造
public Constructor<?>[] getDeclaredConstructors() throws SecurityException

例:

class Person{
    private Person(){}
    protected Person(String name){}
    public Person(String name, int age){}
}
public class Test2{
    public static void main(String[] args) throws Exception {
        Class<?> cls = Person.class;
        {
            Constructor<?>[] constructors = cls.getConstructors();
            for(Constructor<?> constructor : constructors){
                System.out.println(constructor);
            }
        }
        System.out.println("-------------------------------");
        {
            Constructor<?>[] constructors = cls.getDeclaredConstructors();
            for(Constructor<?> constructor : constructors){
                System.out.println(constructor);
            }
        }
    }
}

Constructor类提供了实例化对象的方法:

public T newInstance(Object ... initargs):可以调用类中其他有参构造

Class类提供实例化对象的方法:

public T(Object) newInstance():只能调用无参构造,且无参构造必须是public权限

例:

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 "Person{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}
public class Test2{
    public static void main(String[] args) throws Exception {
        Class<?> cls = Person.class;
        Constructor<?> constructor = cls.getDeclaredConstructor(String.class, int.class);
        Person per = (Person) constructor.newInstance("zhangan",45);
        System.out.println(per);
    }
}

        如果将有参构造的public改为private,getDeclaredConstructor虽然可以拿到此方法,但是不能在Person类外部new一个对象。会出现访问权限异常。

2.3反射调用类中普通方法-Method(描述类中普通方法)

取得类中指定名称的普通方法

public Method getMethod(String name,Class<?> ... parameterTypes)
public Method getDeclearedMethod(String name,Class<?> ... parameterTypes)

取得类中全部普通方法

//取得本类以及父类中所有public方法
public Method[] getMethods() throws SecurityException
//取得本类中全部普通方法,包含私有方法
public Method[] getDeclearedMethods() throws SecurityException

例:

class Person{
    private void test(){}
    protected void fun(){}
    public void haha(){}
}
class Student extends Person{
    private void test1(){}
    protected void fun1(){}
    public void haha1(){}
}
public class Test2{
    public static void main(String[] args) {
        Class<?> cls = Student.class;
        {
            Method[] methods = cls.getMethods();
            for(Method method : methods){
                System.out.println(method);
            }
        }
        System.out.println("------------------------");
        {
            Method[] methods = cls.getDeclaredMethods();
            for(Method method : methods){
                System.out.println(method);
            }
        }
    }
}

Method类中提供调用类中普通方法的API

public Object invoke(Object obj,Object ... args)
class Person{
    private String name;
    private int age;
    public Person(){}
    @Override
    public String toString() {
        return "Person{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public int getAge() {
        return age;
    }
    public void setAge(int age) {
        this.age = age;
    }
}
public class Test2{
    public static void main(String[] args) throws Exception {
        //1.拿到PersonClass对象
        Class<?> cls = Person.class;
        //2.创建Person实例化对象
        Person per = (Person) cls.newInstance();
        //3.拿到setName的Method对象
        Method setMethod = cls.getMethod("setName",String.class);
        //4.通过invoke进行调用
        setMethod.invoke(per,"zhangsan");
        System.out.println(per);
    }
}

2.4反射调用类中属性Field(描述类中普通属性)

取得类中指定属性

public Field getField(String name)
public Field getDeclaredField(String name)

取得类中全部属性

//取得本类以及父类中所有public属性
public Field[ ] getFields() throws SecurityException
//取得本类中全部属性,包含私有属性
public Field[ ] getDeclaredFields() throws SecurityException

Field类提供设置与取得属性方法:

设置属性:

public void set(Object obj,Object value)

取得属性:

public Object get(Object obj)

取得属性类型:

public Class<?> getType()

2.5动态设置封装

Constructor、Method、Field类都是AccessibleObject子类。

AccessibleObject提供动态设置封装方法(在本次JVM进程中有效且只能通过反射调用)

public void setAccessible(boolean flag) throws SecurityException
class Person{
    private String name;
    private int age;
    public Person(){}
    @Override
    public String toString() {
        return "Person{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public int getAge() {
        return age;
    }
    public void setAge(int age) {
        this.age = age;
    }
}
public class Test2{
    public static void main(String[] args) throws Exception {
        //1.拿到PersonClass对象
        Class<?> cls = Person.class;
        //2.创建Person实例化对象
        Person per = (Person) cls.newInstance();
        //3.拿到name的Field对象
        Field field = cls.getDeclaredField("age");
        //动态设置封装
        field.setAccessible(true);
        field.set(per,18);
        System.out.println(field.get(per));
    }
}

3.ClassLoader类加载器

3.1认识ClassLoader

首先通过Class类观察如下方法:

public ClassLoader getClassLoader();

类加载器:通过一个类的全名称来获取此类的二进制字节流,实现操作的代码模块称为类加载器。

public class Test2{
    public static void main(String[] args) {
        Class<?> cls = Test2.class;
        System.out.println(cls.getClassLoader());
        //取得父类加载器
        System.out.println(cls.getClassLoader().getParent());
        System.out.println(cls.getClassLoader().getParent().getParent());
    }
}

JDK中内置的三大类加载器:

Bootstrap(启动类加载器):

I.使用C++实现,是JVM的一部分,其他所有类加载器均使用Java实现。

II.负责将放于Java_HOME\lib目录下的能被JVM识别的类库加载到JVM中。

III.启动类加载器无法被Java程序直接引用。

ExtClassLoader(扩展类加载器):

I.使用Java实现,并且可以被Java程序直接引用。

II.加载Java_Home\lib\ext目录下能被识别的类库。

APPClassLoader(应用程序类加载器):

I.负责加载用户路径(classPath)上指定的类库。

II.如果应用程序中没有自定义类加载器,则此加载器就是Java程序中默认的类加载器。

3.2类加载器双亲委派模型-JDK1.2引入

        定义:JDK内置的三种类加载器与用户自定义类加载器之间的层次关系成为类加载器的双亲委派模型。要求除了顶层的父类加载器外,其余的类加载器都应有自己的父类加载器。

        执行流程:如果一个类加载器收到了类加载的请求,它首先不会自己尝试加载此类,而是把类加载请求委托给父类加载器完成,每一个层次类加载器均是如此。只有当父类加载器无法完成加载请求时(在自己搜索范围内没有找到此类),子类加载器才会尝试自己去加载。

        意义:双亲委派模型保证了Java程序稳定运行。Java中基础类一定由顶层BootStrap类加载器加载,因此,诸如Object等核心类在各种类加载器环境下都是同一个类。

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

gx1500291

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值