Java反射快速学习

前言

反射是java语言一个非常重要的高级特性,很多优秀的框架诸如Spring的Ioc和mybatis等等都运用了Java的反射技术。反射使java插上了动态性的翅膀。
学好反射可以为后面java各种技术的学习打下坚实的基础

创建对象的几种方式

在介绍反射之前,我们先来看看在java中如何创建一个对象

1.使用new关键字创建

T name = new T();

优点:使用简单,易于理解
缺点:使程序显得固定,较死板

2.通过克隆创建

package 反射.关键类;

/**
 * @author pony
 * @date 2020/5/1
 */
public class CreateClassTest {
    public static void main(String[] args) throws CloneNotSupportedException {
        D d = new D();
        d.print();
        D d1 = (D)d.clone();
        d1.print();
        System.out.println(d1==d);  //false
        System.out.println(d1.equals(d));  //true
    }
}
//要想能具备克隆功能,必须实现Java的Cloneable接口
class D implements Cloneable{
    @Override
    protected Object clone() throws CloneNotSupportedException {
        return super.clone();
    }

    public void print(){
        System.out.println("hello in D");
    }
}

优点:创建对象的速度比较快
缺点:必须要求要克隆的类实现Cloneable接口,另外一个就是深度克隆和浅克隆的问题

3.使用序列化创建

序列化:就是将一个对象输出成一个文件流,变成一个文件。当要创建对象时,通过读取这个文件来创建对象。
优点:不用调用构造函数
却点:需要实现Serialization接口序列化由于需要和外部文件交互导致安全问题将不再被JDK所支持

4.通过反射创建

package 反射.关键类;

import java.lang.reflect.Constructor;
import java.lang.reflect.Method;

/**
 * @author pony
 * @date 2020/5/1
 */
public class CreateClassTest {
    public static void main(String[] args) throws Exception {
     	//通过反射创建
        D d1 = (D)Class.forName("反射.关键类.D").newInstance();
        Method m = Class.forName("反射.关键类.D").getMethod("print");
        m.invoke(d1);
        //通过反射的构造器来创建
        Constructor<D> constructor = D.class.getConstructor();
        D d = constructor.newInstance();
        d.print();
    }
}

优点:

  1. 使程序可以访问、检测和修改它自身的状态和行为。
  2. 可以在程序运行时加载、探索和使用编译期间完全未知的类
  3. 在运行时查看和操作对象:
    • 基于反射自由创建对象
    • 基于反射构建无法访问的类
    • set和get无法访问的变量
    • 调用不可访问的方法
      缺点:不容易理解算一点把😂

反射关键类

Class类

类型标识:jvm为每个对象都保留其类型标识信息

package 反射.关键类;

/**
 * @author pony
 * @date 2020/5/1
 */
public class ClassTest {
    public static void main(String[] args) throws ClassNotFoundException {
        //获取类字节码的三种方式
        String s1="abc";
        Class<? extends String> c1 = s1.getClass();
        System.out.println(c1.getName());
        Class<?> c2 = Class.forName("java.lang.String");
        System.out.println(c2.getName());
        Class<String> c3 = String.class;
        System.out.println(c3.getName());
    }
}

输出结果:
在这里插入图片描述

Field类

可以获取到类中的属性信息

package 反射.关键类;

import java.lang.reflect.Field;

/**
 * @author pony
 * @date 2020/5/1
 */
public class FieldTest {
    public static void main(String[] args) throws IllegalAccessException {
        A a = new A(100, "20");
        Class<? extends A> aClass = a.getClass();
        Field[] fs1 = aClass.getFields(); //获取类及其父类的所有public字段
        System.out.println(fs1[0].getName()+":"+fs1[0].get(a));
        System.out.println("------------------------------");
        Field[] fs2 = aClass.getDeclaredFields(); //获取本类所有自定义属性
        for (Field fs:fs2){
            fs.setAccessible(true);
            System.out.println(fs.getName()+":"+fs.get(a));
        }
    }
}
class  A{

    public   int age;
    private  String name;

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

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public String getName() {
        return name;
    }

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

运行结果:
在这里插入图片描述

Method类

package 反射.关键类;

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

/**
 * @author pony
 * @date 2020/5/1
 */
public class MethodTest {
    public static void main(String[] args) throws InvocationTargetException, IllegalAccessException {
        B b = new B();
        Class<? extends B> bClass = b.getClass();
        Method[] m1 = bClass.getMethods(); //得到所有包括父类的public方法
        for(Method m:m1){
            if("f1".equals(m.getName())){
                m.invoke(b);
            }
        }
        Method[] m2 = bClass.getDeclaredMethods(); //得到所有自定义方法
        for(Method m:m2){
            if("f2".equals(m.getName())){
                m.setAccessible(true);
                String s = (String) m.invoke(b, "123");
                System.out.println(s);
            }
        }
    }
}
class B{
    public void f1(){
        System.out.println("in B.f1()");
    }
    private String f2(String s){
        System.out.println("in B.f2()");
        return  s;
    }
}

Constructor类

package 反射.关键类;

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

/**
 * @author pony
 * @date 2020/5/1
 */
public class ConstructorTest {
    public static void main(String[] args) throws IllegalAccessException, InvocationTargetException, InstantiationException {
        C c = new C();
        Class<C> cClass = C.class;
        Constructor<?>[] constructors = cClass.getConstructors(); //得到所有构造函数
        for(Constructor con:constructors){
            if(con.getParameterCount()>0){
                C obj = (C)con.newInstance(10);
                obj.printNum();
            }
            else {
                C obj = (C) con.newInstance();
                obj.printNum();
            }
        }
    }
}
class C{
    private int num;

    public C(){}

    public C(int num) {
        this.num = num;
    }

    public void printNum(){
        System.out.println(num);
    }
}

反射的应用

反射的应用很广泛,而且很多地方的使用都很精妙。这里简要的介绍一些反射的应用,主要目的是认识到反射的强大之处和应用范围的宽广。起到抛砖引玉的效果

  • 数据库连接
    • JDBC:
Class.forName("com.mysql.jdbc.Driver");

在实际开发时,字符串一般是放在配置文件中的,这样就不用修改代码而实现不同数据库驱动的连接

  • 数组扩容
package 反射.关键类.应用;

import java.lang.reflect.Array;

/**
 * @author pony
 * @date 2020/5/8
 */
public class test {
    public static void main(String[] args) {
        int[] a = {1,2,3,4,5};
        int[] b = (int[])goodCopy(a,5);
        for(int i:b){
            System.out.println(i);
        }
    }
    public static Object goodCopy(Object oldArray,int  newLength){
        //Array类型
        Class<?> aClass = oldArray.getClass();
        //获取数组中单个元素类型
        Class<?> componentType = aClass.getComponentType();
        //获取旧数组长度
        int oldLength = Array.getLength(oldArray);
        //通过反射创建新数组
        Object newArray = Array.newInstance(componentType, newLength);
        //拷贝数据
        System.arraycopy(oldArray,0,newArray,0,oldLength);
        return newArray;
    }
}

运行结果:
在这里插入图片描述

  • Java和Json对象的互转
  • Tomcat的Servlet创建
  • Mybatis的OR/M
  • Spring的Bean容器

编译器API

上面我们了解java的反射,但反射的前提是class文件要存在。而即将介绍的编译器API可以避免这个问题
编译器API作用:

  1. 可以对.java文件进行即时编译,甚至可以将字符串进行编译
  2. 监听编译过程中的warning和error
  3. 在代码中运行编译器

javaComplier位于javax.tools包中,主要有两大方法:

  1. run()方法:较简单,可以编译java源文件,但不能指定输出路径,可以监控错误信息。调用后生成的.class文件就在源文件目录下
package 反射.编译器API;

import javax.tools.JavaCompiler;
import javax.tools.ToolProvider;
import java.io.ByteArrayOutputStream;

/**
* @author pony
* @date 2020/5/8
*/
public class SimpleJavaComplier {
  public static void main(String[] args) {
      successComplie();
      failComplie();
  }
  public static void successComplie(){
      JavaCompiler complier = ToolProvider.getSystemJavaCompiler();
      int rs = complier.run(null, null, null, "D:\\Desktop\\Hello1.java", "D:\\Desktop\\Hello2.java");
      System.out.println(0==rs? "success":"fail");
  }
  public static void failComplie(){
      ByteArrayOutputStream err = new ByteArrayOutputStream();
      JavaCompiler complier = ToolProvider.getSystemJavaCompiler();
      int rs = complier.run(null, null, err, "D:\\Desktop\\Hello3.java");
      System.out.println(0==rs? "success":"fail");
  }
}

运行结果:
在这里插入图片描述
2. getTask():更强大的功能,可以编译内存中字符串生成.clas文件

posted @ 2020-05-01 10:41  pony.ma  阅读(52)  评论(0编辑  收藏  举报