天天看点

Java反射体系详解

文章目录

    • 1.获取一个类的对应的Class对象 (Class的C是大写)
        • (1)调用Object提供的getClass方法
        • (2)类名称 . class
        • (3)调用Class类提供的静态方法:Class . forName(类的全名称)
    • 2.当我们拿到一个类的class对象后,可以做什么呢?
      • 1. 创建该类的新对象
      • 2. 取得包名、父类、父接口信息
      • 3. 取得构造方法、普通方法、普通属性(⭐)
        • (1)取得所有参数构造方法:
        • (2)取得指定参数的构造方法
        • (3)创建新对象
          • (1):通过Class类的newInstance()方法
          • (2)通过Constructor的newInstance方法获取指定参数构造方法实例化的对象
        • (4)取得方法和调用方法(⭐)
          • 1.获取所有普通方法(返回值类型都为Method[ ])
          • 2.获取指定参数的普通方法
          • 3. 获取私有方法以及调用私有方法(setAccessible⭐)
        • (5)取得属性
          • 1. 获取所有属性或者单个属性
          • 2. 操作属性(Field类中的两个方法)
      • 4. Java反射的继承结构

定义:根据现有对象倒推类的组成

最核心:在JVM中任何一个类都有一个唯一的Class对象 ,此对象记录该类的组成结构,通过该class对象,可以反向查找到这个类的信息,称之为反射;

class对象是在当类加载时由JVM产生,用户只能取得此对象,无法创建此对象; ?

1.获取一个类的对应的Class对象 (Class的C是大写)

要想在Java中应用反射,首先取得该类的Class对象;有三种方法可以获取:

(1)调用Object提供的getClass方法

class Person {

}
public class Test1 {
    public static void main(String[] args) {
        Person per = new Person();
        System.out.println(per.getClass());
    }
}
           

(2)类名称 . class

public class Test1 {
    public static void main(String[] args) throws ClassNotFoundException{
        Class cla = Test1.class;
        System.out.println(cla);
    }
}
           

(3)调用Class类提供的静态方法:Class . forName(类的全名称)

class Person {

}

public class Test1 {
    public static void main(String[] args){
        try {
            Class<?> cla = Class.forName("com.yy.readBlackTree.Person");
            System.out.println(cla.getName());
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
    }
}
           
输出:com.yy.readBlackTree.Person

2.当我们拿到一个类的class对象后,可以做什么呢?

1. 创建该类的新对象

反射与工厂模式

package www.bitten.java;

/**
 * @Author : YangY
 * @Description :   反射与工厂模式
 * @Time : Created in 9:37 2019/3/17
 */
interface Buycomputer {
    void buy();
}
class Lenove implements Buycomputer {
    @Override
    public void buy() {
        System.out.println("买一台联想电脑");
    }
}
class Mac implements Buycomputer {
    @Override
    public void buy() {
        System.out.println("买一台苹果电脑");
    }
}
class ComputerFactory {
    public static Buycomputer getInstance(String computerClass) {
        try {
            Class<?> cls = Class.forName(computerClass);
            Buycomputer computer = (Buycomputer) cls.newInstance();
            return computer;
        }catch (ClassNotFoundException e) {
            e.printStackTrace();
        }catch (IllegalAccessException e){
            e.printStackTrace();
        }catch (InstantiationException e) {
            e.printStackTrace();
        }
        return null;
    }
}

public class Test1 {
    public static void main(String[] args) {
        ComputerFactory.getInstance("www.bitten.java.Lenove").buy();
    }
}
           

2. 取得包名、父类、父接口信息

interface ILife {

}
interface IAnimal {

}
class Person implements ILife,IAnimal {

}
public class Test1 {
    public static void main(String[] args) throws ClassNotFoundException{
        Class<?> cla = Person.class;
        //打印包名
        System.out.println(cla.getPackage().getName()); 
        //打印它的父类名称
        System.out.println(cla.getSuperclass().getName());
        //用这个类的Class对象生成它的父接口数组
        Class<?>[] clasArr = cla.getInterfaces();  //也可写成Class[] ....
        //输出父接口
        for(Class<?> element: clasArr) {
            System.out.println(element);
        }

    }
}
           

输出:

www.bitten.java

java.lang.Object

interface www.bitten.java.ILife

interface www.bitten.java.IAnimal

3. 取得构造方法、普通方法、普通属性(⭐)

Constructor :用来描述一个类的构造方法的类

(1)取得所有参数构造方法:

  • Class 类提供的getConstructors() :只能取得权限为public的构造方法 ;(包访问权限都不行,只能public)
  • Class类提供的getDeclaredConstructors():取得所有权限的构造方法 ;
class Person  {
    private String name;
    private int age;
    public Person() {

    }
    Person(String name) {

    }
    private Person(String name, int age) {

    }
}
public class Test1 {
    public static void main(String[] args) throws NoSuchMethodException {
        Class<?> cla = Person.class;
        //只能取得权限为public的构造方法
        Constructor<?>[] constructor = cla.getConstructors();
        //取得所有权限的构造方法
        Constructor<?>[] constructors = cla.getDeclaredConstructors();
        //输出权限为public的所有构造方法
        for (Constructor<?> constructor1 : constructor) {
            System.out.println(constructor1);
        }
        //输出所有权限的构造方法
        System.out.println("--------------------------");
        for (Constructor<?> constructor2 : constructors) {
            System.out.println(constructor2);
        }
    }
}
           

输出:

public www.bitten.java.Person()

public www.bitten.java.Person()

www.bitten.java.Person(java.lang.String)

private www.bitten.java.Person(java.lang.String,int)

(2)取得指定参数的构造方法

  • Class类提供的getConstructor(参数) : 只能取得权限为public的构造方法 (包访问都不行,只能public)
  • Class类提供的getDeclaredConstructor(参数) : 可以取得类中所有构造方法,与权限无关

例码:

class Person  {
    private String name;
    private int age;
    public Person() {

    }
    public Person(String name) {

    }
    public Person(String name, int age) {

    }
}
public class Test1 {
    public static void main(String[] args)throws NoSuchMethodException{
        Class<?> cla = Person.class;
        //注意:getConstructor这个方法只能取得权限为public的构造方法
        Constructor constructor = cla.getConstructor(String.class,int.class);
        System.out.println(constructor.getName());
    }
}
           

输出:

www.bitten.java.Person

(3)创建新对象

利用反射创建新对象有两种方法:

  • 通过Class类的newInstance()方法 ,此方法只能调用无参构造来产生实例化对象;
  • 通过Constructor的newInstance方法获取指定参数构造方法实例化的对象;
(1):通过Class类的newInstance()方法
class Person  {
    private String name;
    private int age;
    public Person() {

    }
    public Person(String name) {

    }
    public Person(String name, int age) {

    }
    public void fun() {
        System.out.println("hello");
    }
}
public class Test1 {
    public static void main(String[] args)throws InstantiationException,IllegalAccessException{
        Class<?> cla = Person.class;
        //Class类的newInstance方法只能获取无参构造实例化的对象
        Person per =(Person) cla.newInstance();
        System.out.println(per);
        per.fun();
    }
}
           

输出:

[email protected]

hello

结论:我们在编写Java程序时要养成保留无参构造的习惯 (⭐)

(2)通过Constructor的newInstance方法获取指定参数构造方法实例化的对象
class Person  {
        private String name;
        private int age;
        public Person() {

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

        }
        public void fun() {
            System.out.println("hello");
        }
        public String getName() {
            return name;
        }

    }
    public class Test1 {
        public static void main(String[] args)throws NoSuchMethodException,InstantiationException
                ,IllegalAccessException,InvocationTargetException {
            Class<?> cla = Person.class;
            Constructor constructor = cla.getConstructor(String.class);
            Person per =(Person) constructor.newInstance("yy");
            System.out.println(per);
            System.out.println(per.getName());
        }
}
           

(4)取得方法和调用方法(⭐)

Class类中提供了获取所有普通方法和获取指定参数普通方法的方法;

1.获取所有普通方法(返回值类型都为Method[ ])
  • (1):getMethods(),该方法返回对应的所有普通方法(不能返回private修饰的方法) ,它还返回父类,包括Object类的相关方法,也能返回所实现接口的普通方法;
  • (2):getDeclaredMethods(),该方法返回对应的所有普通方法,包括private修饰的普通方法,它不会返回父类的方法,但它能返回所实现接口中的方法;

最大区别:(1)方法能返回所继承父类的方法,而(2)不能;⭐

2.获取指定参数的普通方法
public Method getMethod(String name, Class<?>... parameterTypes)
           
class PP {
    public void funfun() {

    }
}
class Person extends PP{
    private String name;
    public Person() {
    }

    public String getName() {
        return name;
    }
    public void fun() {

    }

    public void setName(String name) {
        this.name = name;
    }

}
public class Test1 {
    public static void main(String[] args) throws Exception{
        Class cla = Person.class;
        Person per = (Person)cla.newInstance();
        //通过class对象的getDeclaredMethods()方法获取所有普通方法;
        Method[] arrMethod = cla.getDeclaredMethods();
        for(Method method:arrMethod) {
            System.out.println(method);
        }
        Method method1 = cla.getMethod("setName", String.class);
        Method method2 = cla.getMethod("getName");  //该方法需要抛出NoSuchMethodException;
        //要调用普通方法,必须得先创建它的实例化对象,per则是实例化
        method1.invoke(per,"yy");
        System.out.println(method2.invoke(per));   //输出yy
    }
}
           

输出:

public void www.bitten.java.Person.fun()

public java.lang.String www.bitten.java.Person.getName()

public void www.bitten.java.Person.setName(java.lang.String)

yy

可见getDeclaredMethods方法没有返回父类的方法;

3. 获取私有方法以及调用私有方法(setAccessible⭐)
class Person {
    String name;

    private void setName(String name) {
        this.name = name;
    }

    public String getName() {
        return name;
    }
}

public class Test1 {
    public static void main(String[] args) throws Exception {
        //获取Person类的Class对象
        Class<?> cla = Class.forName("com.yy.readBlackTree.Person");
        //获取该类的实例化对象,通过无参构造生成(上面的Person类默认有无参构造,但如果上面只有有参构造的话,则必须显示的加上无参构造)
        Person person = (Person) cla.newInstance();
        //获取方法对象,因为这个方法是private修饰的,所以必须用getDeclaredMethod而不是getMethod
        Method method = cla.getDeclaredMethod("setName", String.class);
        //通过设置这个可以调用private修饰的方法⭐,否则会报错
        method.setAccessible(true);
        //调用方法,参数需要该方法的一个实例化对象,还有它的所有参数;必须填完所有参数,否则抛出NoSuchMethodException;
        method.invoke(person, "Bob");
        System.out.println(person.getName());   //输出:Bob
    }
}
           

特别注意:

  • 要想调用一个private修饰的方法,必须调用这个方法对象的setAccessible方法,并且设置参数为true,不调用该方法的时候默认为false;
  • 另外,获取私有方法对象需要用getDeclaredMethod方法;

(5)取得属性

前提:类中的所有属性一定在类对象实例化之后才会进行空间分配,所以此时如果要想调用类的属性,必须保证有实例化对象。通过反射的newInstance()可以直接取得实例化对象(Object类型)

在Class类中提供有两组取得属性的方法:

  1. 第一组(父类中)-取得类中全部属性: public Field[] getFields() throws SecurityException
  2. 第一组(父类中)-取得类中指定名称属性: public Field getField(String name) throws

    NoSuchFieldException, SecurityException

  3. 第二组(本类中)-取得类中全部属性: public Field[] getDeclaredFields() throws SecurityException
  4. 第二组( 本类中)-取得类中指定名称属性 : public Field getDeclaredField(String name) throws NoSuchFieldException, SecurityException
而后就需要关注属性的核心描述类:java.lang.reflect.Field,在这个类之中有两个重要方法:
  1. 设置属性内容 : public void set(Object obj, Object value) throws IllegalArgumentException,

    IllegalAccessException

  2. 取得属性内容 : public Object get(Object obj) throws IllegalArgumentException,

    IllegalAccessException

1. 获取所有属性或者单个属性
class Person {
    private String name;
    public Integer age;
    String adress;

}

public class Test1 {
    public static void main(String[] args) throws Exception {
        //获取Person类的Class对象
        Class<?> cla = Class.forName("com.yy.readBlackTree.Person");
        System.out.println("==================获取权限为public的所有属性====================");
        Field[] arr = cla.getFields();
        for(Field field: arr) {
            System.out.println(field.getName());
        }
        System.out.println("==================获取所有属性====================");
        Field[] arr2 = cla.getDeclaredFields();
        for(Field field: arr2) {
            System.out.println(field.getName());
        }
        System.out.println("==================获取指定属性=====================");
        //因为name这个属性是private修饰的,所以不能用getField()方法;
        Field field = cla.getDeclaredField("name");
        System.out.println(field.getName());
    }
}
           

输出:

获取权限为public的所有属性==

age

获取所有属性==

name

age

adress

获取指定属性===

name

2. 操作属性(Field类中的两个方法)
class Person {
    private String name;
    public Integer age;
    String adress;

    @Override
    public String toString() {
        return "Person{" +
                "name='" + name + '\'' +
                ", age=" + age +
                ", adress='" + adress + '\'' +
                '}';
    }
}

public class Test1 {
    public static void main(String[] args) throws Exception {
        //获取Person类的Class对象
        Class<?> cla = Class.forName("com.yy.readBlackTree.Person");
        Person person = (Person) cla.newInstance();
        Field field = cla.getDeclaredField("name");
        //因为name属性是private修饰的,所以需要设置为true,除了private,其他的访问权限都不需要设置setAccessible方法为true;
        field.setAccessible(true);
        field.set(person, "Bob");
        System.out.println(person);

        //注意哟:get方法传入的参数是实例化对象而不是Class对象哟!!⭐
        System.out.println(field.get(person));
    }
}
           

输出:

Person{name=‘Bob’, age=null, adress=‘null’}

Bob

4. Java反射的继承结构

Java反射体系详解

注意:

  • 绿色箭头都是继承(extends) 关系;
  • AccessibleObject类提供了setAccessible()方法,这是一个很重要的方法,能够让反射变得更将大,即能够操作由private访问权限的构造器、方法、属性;
    public void setAccessible(boolean flag) throws SecurityException
               
  • 可以看到这些类都是在reflect包下,注意的是Class类与其余几个类都没有直接关系,只是Class类提供了获取那几个类的对象的方法(上面已经详细讲述过了);

继续阅读