反射(Constructor、Field、Method、类加载器)

一:什么是反射
在认识反射之前,首先回顾下什么是正向处理。如果我们需要实例化一个对象,首先需要导入这个类的包,在这个包中找这个类:

package CODE.反射;

import java.util.Date;

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

实例化一个Date对象,需要根据包名.类名来找到Date类。
而反射是相反:是根据对象来取得对象的来源信息。
也就是对象的反向处理。根据对象倒推类的组成。
反射核心处理在于Object类的方法:

//取得类的Class 对象(Class是一个类)
public final native Class<?> getClass();
package CODE.反射;

import java.util.Date;

public class Fan {
    public static void main(String[] args) {
        Date date =new Date();
        //取得data对象的Date类信息
        System.out.println(date.getClass()); //class java.util.Date
    }
}

Class对象的3种实例化方法:
任何一个类的Class对象由JVM加载类后产生(该对象在JVM全局唯一)。用户调用指定方法来取得该类对象。

  • 任何类的对象可以通过Object类提供的getClass()取得该类Class对象
  • “类名称.class”可以直接根据某个具体的类来取得Class对象
  • 调用Class类的静态方法Class.forName(String className) throws
    ClassNotFoundException,传入类的全名称来取得其Class对象。
import java.util.Date;

public class Fan {
    public static void main(String[] args) throws ClassNotFoundException {
        Date date =new Date();
        Class<?> cls1 =date.getClass();
        Class<?> cls2=Class.forName("java.util.Date");
        Class<?> cls3=Date.class;
        System.out.println(cls1); //class java.util.Date   
        System.out.println(cls1); //class java.util.Date
        System.out.println(cls1); //class java.util.Date
    }
}

class java.util.Date 前面的class表明是一个类对象。
从理论和实例看出,只有第一种方式需要实例化对象来取得Class对象,其余2种方式不会产生实例化对象。那么取得了Class类对象后可以通过反射来实例化对象。

用反射来实例化对象
在Class类中有一个方法可以通过Class对象来实例化对象:

public T newInstance()  throws InstantiationException, IllegalAccessException

只能调用类中无参构造,且无参构造只能是public权限。
只有当实例化出Class对象后才可以用Class对象调newInstance。
例:

////通过反射实例化对象
import java.util.Date;

public class Fan {
    public static void main(String[] args) throws IllegalAccessException, InstantiationException {
       Class<?> cls1=Date.class;  //Class对象
       Object obj=cls1.newInstance(); //实例化对象,相当于new java.util.Date()
       System.out.println(obj);
    }
}

完善简单工厂模式----利用反射
之前在写工厂模式时,在Factory里new对象,但是有一个缺陷,就是新增一个类,需要在Factory里再添加一个分支来new对象。可以参考这篇博客:
但是有了反射后,可以用Class 对象来实例化对象(有2种产生Class对象不需要实例化对象:Class.forName()和类名.class)
代码如下:

package CODE.反射;

//工厂模式---反射

interface Study
{
    void need();
}
class Pen implements Study
{
   public void need()
   {
       System.out.println("需要一直笔");
   }
}
class Book implements Study
{
    public void need()
    {
        System.out.println("需要一个本子");
    }
}
class Pencil implements Study
{
    public void need()
    {
        System.out.println("需要一只铅笔");
    }
}
class Factory1
{
    public static Study getStudy(String className)
    {
        Study study=null;
        try {
            //cls是一个Class类对象  通过className取得具体Class对象
            Class<?> cls=Class.forName(className);
            //通过反射实例化对象
            //因为newInstance返回的是Object,所以需要强转称为一个类型
            study=(Study)cls.newInstance();
        } catch (Exception e) {
            e.printStackTrace();
        }
        return study;
    }
}
public class FanSheFac {
    public static void main(String[] args)
    {
        Study study1=Factory1.getStudy("CODE.反射.Pen");
        study1.need();  //需要一直笔
        Study study2=Factory1.getStudy("CODE.反射.Pencil");
        study2.need();  //需要一只铅笔
    }
}

在这里插入图片描述

反射与类

取得类的包名:

 public Package getPackage() {
        return Package.getPackage(this);
    }

如:

import java.util.Date;

public class Fan {
    public static void main(String[] args)  {
        Class<?> cls1=Date.class;
        System.out.println(cls1.getPackage()); //package java.util, Java Platform API Specification, version 1.8
        System.out.println(cls1.getPackage().getName()); //java.util 利用getName取得具体包名
    }
}

1.反射取得父类、父接口信息

  • 取得父类的Class对象:public native Class<? super T> getSuperclass();
  • 取得父接口的Class对象:public Class<?>[ ] getInterfaces();(接口在JVM中也是Class因为接口有多继承,所以父接口可能有多个)
package CODE.反射;

//反射与类操作
//取得父类的Class对象
class A{}
interface IB{}
interface  IC{}
class ABC extends A implements IB,IC{}
public class FanClass {
    public static void main(String[] args) {
        Class<?> cls=ABC.class; //获得Class对象

        //获得父类Class对象
        Class<?> cls1=cls.getSuperclass();
        System.out.println("父类Class对象:"+cls1);
        //父类Class对象:class CODE.反射.A

        //获得父接口Class对象
        Class<?>[] cls2=cls.getInterfaces();
        for(Class<?> clas:cls2)
        {
            System.out.println("父接口Class对象:"+clas);
            //父接口Class对象:interface CODE.反射.IB
            //父接口Class对象:interface CODE.反射.IC
        }
    }
}

2.反射调用类中构造方法–Constructor类(描述类中构造方法)

一个类有多个构造,如果要想取得类中构造方法,可以使用Class类提供的2个方法:

  • 取得指定参数类型的构造:
public Constructor<T> getConstructor(Class<?>... parameterTypes)   throws NoSuchMethodException, SecurityException
只能取得类中public权限的指定构造方法
public Constructor<T> getDeclaredConstructor(Class<?>... parameterTypes)    throws NoSuchMethodException, SecurityException
可以取得类中全部指定构造方法,包含私有构造

参数是类型的类对象,比如类型是String,那么参数是String.class。

  • 取得类中所有构造方法
public Constructor<?>[] getConstructors() throws SecurityException
取得所有public权限构造方法
public Constructor<?>[] getDeclaredConstructors() throws SecurityException
取得所有权限构造方法(包括私有权限)

动态设置封装(只能通过反射调用)
Constructor /Method/Field类都AccessibleObjet的子类。
AccessibleObjet提供动态设置封装方法
public void setAccessible(boolean flag) throws SecurityException
setAccessible只能在本次JVM进程中有效,重新启动时,private权限方法依然不可以在外部使用,也就是说只是动态破坏了本次的封装。

Constructor提供了实例化对象的方法:
public T newInstance(Object … initargs) :可以调用类中其他有参构造。

Constructor类的newInstance(Object … initargs)与Class类的newInstance( )区别:
1.Constructor类的实例化对象方法参数有可变参数,即可以有参数,也可以没有参数,Class类提供的实例化对象方法必须是无参。
2.当有了Constructor对象后,也就是取得类的构造方法,假如构造方法是私有权限,可以利setAccessible(true)动态破坏私有构造用newInstance实例化对象,而Class类的newInstance只有用public权限构造方法来实例化对象。

代码如下:

package CODE.反射;

import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;

////反射与构造
class Student
{
    private String name;
    private int sno;

    public Student()
    {
    }
    private Student(String  name, int sno) {
        this.name = name;
        this.sno = sno;
    }
    public String toString()
    {
        return "姓名:"+name+"学号"+sno;
    }
}
public class FanConstruct {
    public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
        Class<?> cls=Student.class;
        Constructor<?> constructor=cls.getDeclaredConstructor(String.class,int.class);
        constructor.setAccessible(true);
        //Constructor类提供的newInstance()方法实例化对象 ,参数是要设置的值
        //可以通过Constructor类提供的setAccessible使动态破坏权限,设置为true后,private权限可以在外部使用
        //但是也是在在本次JVM进程有效,重新启动后依然是private权限
        System.out.println(constructor.newInstance("pick",10)); //姓名:pick学号10
    }
}

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

  • 描述类中指定名称的普通方法
public Method getMethod(String name, Class<?>... parameterTypes)  throws NoSuchMethodException, SecurityException
取得本类和父类中public权限指定方法
public Method getDeclaredMethod(String name, Class<?>... parameterTypes)  throws NoSuchMethodException, SecurityException
取得本类任何权限的指定方法(私有、公有、继承权限)

参数中String nama :方法名
parameterTypes:参数类型的类对象,如String.class ,既要传方法名,又要传类型是为了重载。

  • 取得类中全部普通方法
public Method[] getMethods() throws SecurityException 
取得本类和父类中所有public权限方法
public Method[] getDeclaredMethods() throws SecurityException
取得本类中所有权限方法(注意只有本类)

例:

package CODE.反射;

import java.lang.reflect.Method;

class Person1
{
    public void print1(){}
    private void print2(){}
    public void print3(String str){}
}
class Student1 extends Person1
{
    public void print2(int m){
    }
    private void print3(int a){}
}
public class FanSheMeth {
    public static void main(String[] args) throws IllegalAccessException, InstantiationException, NoSuchMethodException {
        Class<?> cls=Student1.class;

        Student1 student1=(Student1)cls.newInstance(); //通过反射取得实例化对象
        Method method1[]=cls.getDeclaredMethods();
        Method method2[]=cls.getMethods();
        Method method3=cls.getMethod("print3",String.class);
        Method method4=cls.getDeclaredMethod("print3", int.class);
        System.out.println("getDeclaredMethods取得本类中所有权限方法:");
        for(Method me1:method1)
        {
            System.out.println(me1);
        }
        System.out.println("getMethods取得本类和父类中所有public权限方法");
        for(Method me2:method2)
        {
            System.out.println(me2);
        }
        System.out.println("getMethod取得本类和父类所有public权限指定方法:");
        System.out.println(method3);
        System.out.println("getDeclaredMethod取得本类中public权限指定方法");
        System.out.println(method4);
    }
}

在这里插入图片描述

Method类中提供调用类中普通方法的API:
public Object invoke(Object obj, Object… args)

如:

package CODE.反射;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

class Person
{
    private String name;
    private int age;
    public void print(String name,int age)
    {
        this.name=name;
        this.age=age;
        System.out.println("name:"+this.name+" age:"+this.age);
    }
    private void print(String str)
    {
        System.out.println(str);
    }
}

public class Meth
{
    public static void main(String[] args) throws IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException {
        Class<?> cls=Person.class;
        Person person=(Person)cls.newInstance();
        Method printMeth=cls.getMethod("print",String.class,int.class);
        printMeth.invoke(person,"pick",10); //name:pick age:10

        //Method对象调set
        Method printStr=cls.getDeclaredMethod("print", String.class); //取得本类本任意权限方法
        printStr.setAccessible(true);  //动态破坏权限,使私有权限可见
        printStr.invoke(person,"nice day"); //nice day
    }
}

4.反射调用类中属性----Field(描述类中普通属性)

  • 取得类中指定属性
public Field getField(String name) throws NoSuchFieldException, SecurityException
取得本类和父类中public指定权限属性
public Field getDeclaredField(String name)
throws NoSuchFieldException, SecurityException
取得本类任意权限属性(私有、公有、继承、默认)
  • 取得类中所有属性
public Field[] getFields() throws SecurityException
取得本类以及父类中所有public属性
public Field[] getDeclaredFields() throws SecurityException
取得本类中全部普通属性(包括私有属性)

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

设置属性:
public void set(Object obj,Object value);
obj:实例化对象,value:具体的值
取得属性:
pulic Object get(Object obj);
取得属性的类型:
pubic Class<?> getType

对上述方法进行验证和练习:

package CODE.反射;

import com.sun.corba.se.impl.orbutil.concurrent.Sync;

import java.lang.reflect.Field;

class Person2
{
    private String name;
    private int age;
    public int count=10;
}

class Student2 extends Person2
{
    public int num=1;
    private String sno; //课程号
}
public class FanSheField {
    public static void main(String[] args) throws IllegalAccessException, InstantiationException, NoSuchFieldException {
        Class<?> cls=Student2.class;
        Student2 student2=(Student2)cls.newInstance();
        System.out.println("getField取得本类和父类中public权限属性:");
        Field count=cls.getField("count");
        count.set(student2,1);  //设置值
        System.out.println("Field取得本类公有属性值: "+count+":"+count.get(student2));

        System.out.println("getDeclaredField取得本类任意权限属性");
        Field sno=cls.getDeclaredField("sno");
        sno.setAccessible(true);  //破坏私有权限
        sno.set(student2,"3");
        System.out.println("Field取得本类私有权限属性值: "+sno+":"+sno.get(student2));
        System.out.println("取得属性类型:"+sno.getType());

        System.out.println("getFields取得本类和父类所有公有权限属性");
        Field field1[]=cls.getFields();
        for(Field f1: field1)
        {
            System.out.println(f1);
        }

        System.out.println("getDeclaredFields取得本类所有权限属性");
        Field field2[]=cls.getDeclaredFields();
        for(Field f2:field2)
        {
            System.out.println(f2);
        }
    }
}

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
反射应用:
实现任意类属性设置。
如果希望对一个类的属性进行设置,当输入字符串是"emp.name:pick | emp.job:cook"时,emp是真实类中属性,name和job是类中属性名称,“pick"和"cook"是要设置的值,两个属性用”|"进行分割,如果可以将属性设置为此种格式,可以一次性将属性设置完毕。
代码如下:
emp是真实类,EmpAction是面向用户的类,BeanOperation是公共操作类(任意类通过这个类来设置属性):
Emp.java

package CODE.反射与简单Java类;

public class Emp {
    private String name;
    private String job;

    public String getName() {
        return name;
    }

    public String getJob() {
        return job;
    }

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

    public void setJob(String job) {
        this.job = job;
    }
    public String toString()
    {
         if(job==null)
          return "name :"+name;
        return "job:"+job;
    }
}

EmpAction.java

package CODE.反射与简单Java类;

import java.lang.reflect.InvocationTargetException;

public class EmpAction {
    private Emp emp=new Emp();
    public void setValue(String value) throws Exception {
     BeanOperation.setBeanValue(this,value);
    }
    public Emp getEmp()
    {
        return emp;
    }
}

BeanOperation .java :

package CODE.反射与简单Java类;
//


import javax.management.ObjectName;
import java.lang.reflect.Method;

import java.lang.reflect.InvocationTargetException;

public class BeanOperation {

    public static void setBeanValue(Object ActionObj ,String str) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
        // emp.name:pick|emp.job:cooker
        String tmp[]=str.split("\\|");
        //emp.name:pick
        //emp.jon:cooker
        for(int i=0;i<tmp.length;i++)
        {
            String msg[]=tmp[i].split(":");
            String attribute=msg[0];   //属性前缀 emp.name
            String value=msg[1];   //属性值  pick
            //取得真实类
            String realClassName=attribute.split("\\.")[0]; //emp
            String attrValue=attribute.split("\\.")[1];  //name
            Object realClass=getRealClass(ActionObj,realClassName);
            //获得真实类后setValue
            setValue(realClass,attrValue,value);
            System.out.println(realClass);
        }
    }
    public static Object getRealClass(Object ActionObj,String realClassName ) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
        Object obj=null;
        Class<?> actionCls=ActionObj.getClass();  //类对象
        String methName="get"+upFirst(realClassName);  //取得要访问的方法名称
        Method method=actionCls.getMethod(methName);   //通过反射获得方法
        obj=method.invoke(ActionObj);  //调用获得方法
        return obj;
    }
    public static String upFirst(String str)
    {
        return str.substring(0,1).toUpperCase()+str.substring(1);
    }
    public static  void setValue(Object realclass,String arrtrValue,String value) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
        Class<?> cls=realclass.getClass();
        String MethodName="set"+upFirst(arrtrValue);
        Method setMethod=cls.getMethod(MethodName,String.class);
        setMethod.invoke(realclass,value);
    }
}


Test类:

package CODE.反射与简单Java类;

import java.lang.reflect.InvocationTargetException;

public class Test {
    public static void main(String[] args) throws Exception {
        EmpAction empAction=new EmpAction();
        empAction.setValue("emp.name:pick|emp.job:cook");
    }
}

在这里插入图片描述

类加载器

什么是类加载器:
类加载器:
通过一个类的全名称来获取此类的二进制字节流,实现这个操作的代码模块为类加载器。
通过 Class类的getClassLoader可以获取到类加载器:
public ClassLoader getClassLoader() ;

编写一个简单的反射程序,来观察ClassLoader的存在:

package CODE.反射;
public class Loader {
    public static void main(String[] args) {
        Class<?> cls=Loader.class;
        System.out.println(cls.getClassLoader()); //sun.misc.Launcher$AppClassLoader@18b4aac2
        System.out.println(cls.getClassLoader().getParent());//sun.misc.Launcher$ExtClassLoader@4554617c
        System.out.println(cls.getClassLoader().getParent().getParent()); //null
    }
}

可以看到有2个类加载器:ExtClassLoader和AppClassLoader。
实际 上在JDK中内置有3大类加载器。
3大类加载器的关系:
在这里插入图片描述
1.Bootstarp(启动类加载器):

  • 使用c++实现,是JVM的一部分,(无法用,所以上述代码为空),其他所有类加载器均使用Java实现。
  • 负责将存放于Java_HOME\lib目录下的能被JVM识别的类库(tr.jar—存放了Java所有基础类库,java.lang,java.util)加载到JVM中
  • 启动类加载器无法被Java程序直接引用
    如Object包由Bootstarp加载。

2.ExtClassLoader(扩展类加载器)

  • 使用Java实现,并且可以被Java程序直接引用
  • 加载Java_Home\lib\ext目录下能被识别的类库。

3.AppClassLoader(应用程序类加载器)

  • 负责加载用户路径(classPath)上指定的类库
  • 如果用户程序中没有自定义类加载器,则此加载器就是Java程序中默认的类加载器。

猜你喜欢

转载自blog.csdn.net/sophia__yu/article/details/84567763