JavaseLearn24-反射

JavaseLearn24-反射

1.反射机制

1.1什么是反射机制,有什么用?

反射机制:可以操作字节码文件。(Class)

作用:让程序变得更加灵活。

1.2反射机制相关的重要的类

反射机制相关的类在:java.lang.reflect.*;包下。

  • java.lang.Class:代表整个字节码,代表一个类型,代表整个类。
    • java.lang.reflect.Method:代表字节码中的方法字节码。代表类中的方法。
    • java.lang.reflect.Constructor:代表字节码中的构造方法字节码。代表类中的构造方法。
    • java.lang.reflect.Field:代表字节码中的属性字节码,代表类中的成员变量(静态变量)
//Class:
public class User {
    //Field:
    private String name;
    //Constructor
    public User() {
    }
    public User(String name) {
        this.name = name;
    }
    //Method:
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
}

2.获取Class的三种方式

第一种:Class.forName()

  • 通过Class类直接调用静态方法forName

    • 1.静态方法。

    • 2.方法的参数是一个字符串。

    • 3.字符串需要的是一个完整类名。如:java.lang.String

    • Class c = Class.forName("java.lang.String");
      

第二种:对象.getClass()

  • 任何一个java对象都有getClass()方法,可以直接获取该对象的Class

    • String s = "张三";
      Class c = s.getClass();
      

第三种:类型.class

  • java中任何一种类型都有一个.class属性,包括基本数据类型

    • Class c = String.class;
      

这三种方式获取同一个Class,返回值相同。

package com.tsccg.java.reflect;

import java.util.Date;

/**
 * @Author: TSCCG
 * @Date: 2021/07/12 13:10
 * 获取Class的三种方式
 */
public class ReflectDemo01 {
    public static void main(String[] args) {
        /**
         * 第一种方式:Class.forName()
         */
        Class c1 = null;
        Class c2 = null;
        try {
            c1 = Class.forName("java.lang.String");
            c2 = Class.forName("java.util.Date");
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
        /**
         * 第二种方式:对象.getClass()
         */
        String s1 = "张三";
        Class l1 = s1.getClass();
        System.out.println(l1 == c1);

        Date d1 = new Date();
        Class l2 = d1.getClass();
        System.out.println(l2 == c2);

        /**
         * 第三种方式:类型.class
         * java中任何一种类型都有一个.class属性,包括基本数据类型
         */
        Class a1 = String.class;
        System.out.println(a1 == c1);
        Class a2 = Date.class;
        System.out.println(a2 == c2);

    }
}
true
true
true
true

3.通过反射实例化对象

获取了Class之后,就可以通过newInstance()方法调用该类型的无参构造方法来实例化对象。

//c1代表的就是String类型
Class c1 = Class.forName("java.lang.String");
//实例化一个String类型的对象
Object obj = c1.newInstance();

注意:

  • newInstance()底层调用的是该类型的无参构造方法
  • 如过该类型没有无参构造方法,那么会出现实例化异常。

通过反射实例化一个手写的User对象:

package com.tsccg.java.reflect;

/**
 * @Author: TSCCG
 * @Date: 2021/07/12 15:20
 * 通过反射实例化对象
 */
public class ReflectDemo02 {
    public static void main(String[] args) {
        try {
            //获取User的Class
            Class c1 = Class.forName("com.tsccg.java.reflect.User");
            //newInstance会调用User类中的无参构造方法,创建User对象。
            //newInstance在jdk9之后已过时
            Object obj = c1.newInstance();
            if (obj instanceof User) {
                User user = (User)obj;
                user.setName("张三");
            }
            System.out.println(obj);
        } catch (ClassNotFoundException | IllegalAccessException | InstantiationException e) {
            e.printStackTrace();
        }
    }
}
class User {
    private String name;
    public User() {
        System.out.println("执行无参构造方法");
    }
    public User(String name) {
        this.name = name;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    @Override
    public String toString() {
        return "User{" +
                "name='" + name + '\'' +
                '}';
    }
}
执行无参构造方法
User{name='张三'}

4.为什么要通过反射实例化对象

通过反射实例化对象更加灵活。

那么灵活到什么地方呢?

我们现在创建一个属性配置文件test.properties,在里面写入如下内容:

classname=java.util.Date

然后通过IO+Properties联合读这个属性配置文件,获取里面的类名。

然后就可以拿获取到的类名,通过反射实例化对象。

这种方式实例化的对象可以是多种,

  • 比如说这次配置文件里面写的是java.util.Date,那么我通过反射实例化的对象就是一个Date对象;
  • 下次将里面的数据修改为java.lang.String,那么我无需改动程序,再次运行,实例化的就是一个String对象。

这种方式比普通实例化对象的方式更加灵活。

  • 普通方式如:Date date = new Date();
    • 一旦写出来就写死了,只能创建一个Date类型的对象。
  • 通过IO+Properties+反射实例化对象的方式
    • 代码不需要改动。
    • 只需改动属性配置文件,即可创建不同类型的实例化对象。
package com.tsccg.java.reflect;

import java.io.FileReader;
import java.util.Properties;

/**
 * @Author: TSCCG
 * @Date: 2021/07/12 15:29
 */
public class IoPropertiesDemo01 {
    public static void main(String[] args) throws Exception{
        //创建字符输入流
        FileReader reader = new FileReader("D:\\code\\Java\\IdeaJava\\" +
                "JavaLearn\\Javase\\JavaseLearn24-Reflect\\test.properties");
        //创建Properties集合
        Properties pro = new Properties();
        //将属性配置文件中的数据流入集合中
        pro.load(reader);
        //关闭流
        reader.close();
        //通过key获取value,也就是属性配置文件中的类名
        String str1 = pro.getProperty("classname");
        System.out.println(str1);
        //通过反射实例化对象
        Class c1 = Class.forName(str1);
        Object obj1 = c1.newInstance();
        System.out.println(obj1);

    }
}
java.util.Date
Mon Jul 12 21:58:53 CST 2021

现将属性配置文件中的数据修改为:

classname=com.tsccg.java.reflect.User

代码无需改动,重新运行:

com.tsccg.java.reflect.User
执行无参构造方法
User{name='null'}

总结:

  • 反射机制让代码具有通用性
  • 可变化的内容都是写到配置文件中了,将来修改配置文件后,
  • 创建的对象不一样了,调用的方法也不同了,但是java代码不需要做任何改动
  • 这就是反射机制的魅力所在

5.只加载类中的静态代码块

如果你只希望一个类里的静态代码块执行,其他方法一概不执行,该怎么实现?

由上面的内容,我们了解到Class.forName("完整类名");的执行会导致类加载。

类加载时,类中的静态代码块就会执行。

那么就可以只使用:Class.forName("完整类名");

不需要接收返回值,因为在这里,我们对该方法执行的返回值没有兴趣,只是为了实现"类加载"这个动作。

这种方式在jdbc中会用到。

package com.tsccg.java.reflect;

/**
 * @Author: TSCCG
 * @Date: 2021/07/12 15:52
 */
public class ReflectDemo04 {
    public static void main(String[] args) throws ClassNotFoundException {
        Class.forName("com.tsccg.java.reflect.Student");
    }
}
class Student {
    static {
        System.out.println("静态代码块执行");
    }
    public Student() {
        System.out.println("无参构造方法执行");
    }

}
静态代码块执行

6.通用地获取文件绝对路径

6.1为什么要通用地获取文件绝对路径

在我们使用IO流时,如果使用如下方式传入文件路径:

FileReader reader = new FileReader("D:\\code\\Java\\IdeaJava\\" +
        "JavaLearn\\Javase\\JavaseLearn24-Reflect\\test.properties");

那么假设现在将整个项目文件都搬到Linux系统中运行,程序会无法运行。

因为Linux系统中没有C盘或D盘等盘符,无法读取该文件。

为了保证项目不受环境移植的影响,我们需要在所有环境下都能获取到文件的绝对路径,然后传入流对象中。

6.2通用地获取文件绝对路径的方式

下面的方法就可以完美解决这个问题:

//获取src目录下文件的绝对路径
String path = Thread.currentThread().getContextClassLoader()
        .getResource("source/test2.properties").getPath();
//将文件的绝对路径传入字符输入流
FileReader reader = new FileReader(path);

这种获取文件绝对路径的方式在所有环境下是通用的,使用的是类加载器中的方法。

但是需要将该文件放到src目录下才可以获取,src是源码的根目录。

这种方式默认将src目录作为当前路径,获取文件绝对路径时仅需要传入该文件在src目录下的相对路径即可。

如此,即使项目文件移植到了Linux系统下,只要该文件仍在src目录下,就可以获取该文件在linux文件系统中的绝对路径。

通过这种方式还可以直接返回一个字节流:

InputStream in = Thread.currentThread().getContextClassLoader()
        .getResourceAsStream("source/test2.properties");

6.3改进IO+Properties联合使用方法

package com.tsccg.java.reflect;

import java.io.FileReader;
import java.util.Properties;

/**
 * @Author: TSCCG
 * @Date: 2021/07/12 15:29
 */
public class IoPropertiesDemo01 {
    public static void main(String[] args) throws Exception{
        //改进前:
        //创建字符输入流
        /*
        FileReader reader = new FileReader("D:\\code\\Java\\IdeaJava\\" +
                "JavaLearn\\Javase\\JavaseLearn24-Reflect\\test.properties");
        */
        
        //改进后的:
        //通用地获取属性配置文件的绝对路径
        String path = Thread.currentThread().getContextClassLoader()
                .getResource("source/test2.properties").getPath();
        //创建字符输入流,将绝对路径传进去
        FileReader reader = new FileReader(path);
        
        //创建Properties集合
        Properties pro = new Properties();
        //将属性配置文件中的数据流入集合中
        pro.load(reader);
        //关闭流
        reader.close();
        //通过key获取value,也就是属性配置文件中的类名
        String str1 = pro.getProperty("classname");
        System.out.println(str1);
        //通过反射实例化对象
        Class c1 = Class.forName(str1);
        Object obj1 = c1.newInstance();
        System.out.println(obj1);

    }
}

我们还可以直接用返回的字节流来改进:

//直接返回一个流(更高级的形式)
InputStream in = Thread.currentThread().getContextClassLoader()
    .getResourceAsStream("source/test2.properties");
//创建Properties集合
Properties pro = new Properties();
//将文件数据流入集合
pro.load(in);
//关闭流
in.close();
//通过key获取value,也就是属性配置文件中的类名
String str1 = pro.getProperty("classname");
System.out.println(str1);
//通过反射实例化对象
Class c1 = Class.forName(str1);
Object obj1 = c1.newInstance();
System.out.println(obj1);
java.util.Date
Mon Jul 12 22:13:54 CST 2021

6.4资源绑定器

java.util包下提供了一个资源绑定器,便于获取属性配置文件中的数据。

使用资源绑定器的时候,属性配置文件xxx.properties必须放在src路径下,

并且资源绑定器只能绑定xxx.properties文件。

传路径时,路径后的拓展名不能写。

package source;

import java.util.ResourceBundle;

/**
 * @Author: TSCCG
 * @Date: 2021/07/12 18:05
 * 资源绑定器
 */
public class ResourceBundleDemo01 {
    public static void main(String[] args) {
        ResourceBundle bundle = ResourceBundle.getBundle("source/test2");
        //通过key获取value
        String classname = bundle.getString("classname");
        //打印value
        System.out.println(classname);
        //通过反射实例化对象
        try {
            Class c1 = Class.forName(classname);
            Object obj = c1.newInstance();
            System.out.println(obj);
        } catch (ClassNotFoundException | IllegalAccessException | InstantiationException e) {
            e.printStackTrace();
        }
    }
}

结果:

java.util.Date
Mon Jul 12 18:19:32 CST 2021

6.5类加载器(了解)

6.5.1什么是类加载器?

类加载器是专门负责加载类的命令或者说是工具。

ClassLoader

我们上面在通用地获取文件绝对路径的方式中,使用的就是类加载器中的方法。

6.5.2JDK中自带的3个类加载器

  • 启动类加载器:rt.jar
  • 拓展类加载器:ext\*.jar
  • 应用类加载器:classpath

6.5.3类加载过程

假设现在有这段代码:

String s = "张三";

代码在执行前,会将所需要的类通过类加载器全部加载到JVM中。

看到上面的代码,类加载器会找String.class文件,找到就加载:

  • 首先通过启动类加载器加载:
    • 启动类加载器专门加载:C:\Program Files\Java\jdk1.8.0_51\jre\lib\rt.jar
    • rt.jar中都是JDK最核心的类库。
  • 如果启动类加载器加载不到,会通过拓展类加载器:
    • 拓展类加载器专门加载:C:\Program Files\Java\jdk1.8.0_51\jre\lib\ext\*.jar
  • 如果通过拓展类加载器也加载不到,那么会通过应用类加载器加载:
    • 应用类加载器专门加载:classpath中的类(环境变量)。

6.5.4双亲委派机制

java中为了保证类加载的安全,使用双亲委派机制。

  1. 优先从启动类加载器("父")中加载。
  2. 如果"父"加载不到,再从拓展类加载器("母")中加载。
  3. 如果都加载不到,才会考虑从应用类加载器中加载,直到加载到为止。

假设一个黑客写了一个java.lang.String的程序,在里面植入了后门程序。

那么此时,黑客写的程序是只能由应用类加载器加载的。

java会优先从启动类加载器中加载java.lang.String,不会优先加载黑客写的程序。如此便可以保证程序的安全性。

7.反射Field

7.1获取Field

我们先创建一个User类,其中有各种修饰符修饰的各种类型的属性:

/**
 * Class:整个类
 */
public class User {
    /**
    Field:属性
     */
    public String name;
    protected int age;
    private int no;
    double salary;
    private static final String ADDRESS = "河南";
}

然后通过反射的方式获取User类中的属性Field:

获取Field对象的方法:

  1. Field getField(String name)

    • 返回一个由public修饰的Field对象
    • 需要输入属性名
  2. Field[] getFields()

    • 获取一个类中所有的public修饰的Field对象。

    • 返回一个数组

    • //获取整个User类
      Class c1 = Class.forName("com.tsccg.java.reflect.User");
      //获取所有public修饰的属性,返回一个Field数组
      Field[] f1 = c1.getFields();
      System.out.println(f1.length);//1 (User类中只有一个属性"name"是public修饰的)
      //取出该属性
      Field field1 = f1[0];
      //打印该属性的变量名
      System.out.println(field1.getName());//"name"
      
  3. Field getDeclaredField(String name)

    • 返回一个 Field 对象
    • 无视修饰符的影响
    • 需要输入属性名
  4. Field[] getDeclaredFields()

    • 获取一个类中所有的Field对象,

    • 返回一个数组

    • 无视修饰符的影响。

    • //获取整个User类
      Class c1 = Class.forName("com.tsccg.java.reflect.User");
      //获取所有属性,无视修饰符限制,返回一个Field数组
      Field[] f2 = c1.getDeclaredFields();
      //System.out.println(f2.length);//5(无视修饰符限制,能获取到所有属性)
      
      //遍历Field数组
      for (Field field : f2) {
          
          //获取修饰属性的修饰符
          int i = field.getModifiers();//调用getModifiers返回修饰符所代表的数字
          String mname = Modifier.toString(i);//Modifier类里有一个静态方法toString专门负责将修饰符代表的数字转换为字符串
          System.out.print(mname + " ");
          
          //获取属性所属类型
          Class ftc = field.getType();//调用getType方法返回属性类型的Class
          //String fname = ftc.getName();//获取完整类名(包括包名)
          String tname = ftc.getSimpleName();//只获取类名
          System.out.print(tname + " ");
      
          //获取属性的变量名
          String fname = field.getName();
          System.out.println(fname);
      }
      

      运行结果:

      public String name
      protected int age
      private int no
       double salary
      private static final String ADDRESS
      

7.2反编译Field

实现将一个类里所有的属性都反编译

package com.tsccg.java.reflect;

import java.lang.reflect.Field;
import java.lang.reflect.Modifier;

/**
 * @Author: TSCCG
 * @Date: 2021/07/13 18:21
 * 反编译Field
 */
public class ReflectDemo05 {
    public static void main(String[] args) throws ClassNotFoundException {
        //由于涉及过多字符串的拼接操作,故使用StringBuilder
        StringBuilder s = new StringBuilder();
        //获取一个类的Class
        Class c1 = Class.forName("com.tsccg.java.reflect.User");
//        Class c1 = Class.forName("java.lang.String");
        //开始反编译该类的属性
        s.append(Modifier.toString(c1.getModifiers())).append(" class ").append(c1.getSimpleName()).append(" {\n");
        //获取该类所有属性,返回一个Field数组
        Field[] fields = c1.getDeclaredFields();
        //遍历Field数组
        for (Field field : fields) {
            //缩进符
            s.append("\t");
            //打印修饰属性的修饰符
            s.append(Modifier.toString(field.getModifiers())).append(" ");
            //打印属性所属类型
            s.append(field.getType().getSimpleName()).append(" ");
            //打印属性的变量名
            s.append(field.getName()).append(";\n");
        }
        s.append("}");
        //将反编译得到的代码输出
        System.out.println(s);
    }
}

结果:

public class User {
	public String name;
	protected int age;
	private int no;
	 double salary;
	private static final String ADDRESS;
}

将类名换成java.util.String:

Class c1 = Class.forName("java.lang.String");

再次运行:

public class String {
	private final char[] value;
	private int hash;
	private static final long serialVersionUID;
	private static final ObjectStreamField[] serialPersistentFields;
	public static final Comparator CASE_INSENSITIVE_ORDER;
}

7.3通过反射机制访问对象属性

既然可以获取到属性对象,那么我们就可以对属性进行操作了:

package com.tsccg.java.reflect;

import java.io.FileInputStream;
import java.lang.reflect.Field;

/**
 * @Author: TSCCG
 * @Date: 2021/07/13 18:22
 * 通过反射机制访问对象属性
 */
public class ReflectDemo06 {
    public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException, IllegalAccessException, InstantiationException {
        System.out.println("-------通过普通方式访问对象属性--------");
        //普通方式访问对象属性
        User user = new User();
        user.name = "张三";
        System.out.println(user.name);

        //获取User类
        Class classname = Class.forName("com.tsccg.java.reflect.User");
        //通过反射实例化User对象
        Object obj = classname.newInstance();
        
        System.out.println("-------通过反射访问非私有属性--------");
        //获取name属性
        Field field = classname.getDeclaredField("name");
        //给name赋值
        field.set(obj,"李四");
        //打印name
        System.out.println(field.get(obj));

        System.out.println("-------通过反射访问私有属性--------");
        //获取私有的no属性
        Field field2 = classname.getDeclaredField("no");
        //打破封装,使得可以访问私有属性(反射机制的缺点,容易打破封装,给黑客留下机会)
        field2.setAccessible(true);
        //给no赋值
        field2.set(obj,12345);
        //打印no
        System.out.println(field2.get(obj));
    }
}

结果:

-------通过普通方式访问对象属性--------
张三
-------通过反射访问非私有属性--------
李四
-------通过反射访问私有属性--------
12345

8.反射Method

8.1可变长度的参数

反射Method相关的方法里有可变长度参数,故先在此介绍。

int... args 这就是一个可变长度的参数

语法是:类型... (只能是三个点)

  1. 可变长度参数要求的参数个数是:0~N个。
  2. 可变长度参数在参数列表中必须在最后一个位置上。
  3. 可变长度参数在一个参数列表中只能有一个。
  4. 可变长度参数可以当作是一个数组来看待。
package com.tsccg.java.reflect;

/**
 * @Author: TSCCG
 * @Date: 2021/07/13 19:01
 * 可变长度参数
 */
public class ReflectDemo07 {
    public static void main(String[] args) {
        //1.当方法中只有一个参数时
        m1();
        m1(10);
        m1(10,20);
        m1(10,20,30);
//        m1("abc");//参数为String类型,报错
        System.out.println("-------------------");
        //2.当方法中有多个参数时
//        m2();报错
        m2(10);
        m2(10,"阿巴");
        m2(10,"阿巴","阿巴阿巴");
        m2(10,"阿巴","阿巴阿巴","阿巴阿巴阿巴");
        System.out.println("--------------------");
        //3.可变长度可看作是一个数组
//        m3(new String[]{"张三","李四","王五"});//可以,但没必要
        m3("张三","李四","王五");

    }
    /**
    * 1.方法中只有一个参数
    */
    private static void m1(int... i) {
        System.out.println("HelloWorld");
    }

    /**
     * 2.方法中有两个以上参数
     * 可变参数必须在参数列表的最后,且只能有一个
     * public static void m2(int... num, String... args){}报错
     * @param num int类型参数
     * @param args 可变String类型参数
     */
    public static void m2(int num, String... args) {
        System.out.println("HelloAgain");
    }

    /**
     * 3.可变长度参数可看作是一个数组
     * @param args
     */
    public static void m3(String... args) {
        //有length属性,说明args是一个数组
        for (int i = 0; i < args.length; i++) {
            System.out.println(args[i]);
        }
    }
}

8.2获取Method

获取Method的方法与获取Field的方法大同小异:

package com.tsccg.java.reflect;

import java.lang.reflect.Method;
import java.lang.reflect.Modifier;

/**
 * @Author: TSCCG
 * @Date: 2021/07/13 19:04
 * 反射Method
 */
public class ReflectDemo08 {
    public static void main(String[] args) throws Exception {
        //获取Class
        Class methodClass = Class.forName("com.tsccg.java.reflect.Login");
//        Object obj = methodClass.newInstance();
        //获取一个类中所有方法,返回一个Method数组
        Method[] methods = methodClass.getDeclaredMethods();
        //遍历Method数组
        for (Method method : methods) {
            //获取修饰符
            int i = method.getModifiers();
            String modname = Modifier.toString(i);
            System.out.print(modname + " ");
            
            //获取方法返回值
            Class returnTypeClass = method.getReturnType();
            String returnTypeName = returnTypeClass.getSimpleName();
            System.out.print(returnTypeName + " ");
            
            //获取方法名
            String methodName = method.getName();
            System.out.print(methodName + " ");
            
            //获取参数类型列表,返回一个Class数组
            Class[] parameterTypes = method.getParameterTypes();
            //遍历Class数组,打印参数类型名
            System.out.print("(");
            for (int j = 0; j < parameterTypes.length; j++) {
                System.out.print(parameterTypes[j].getSimpleName());
                if (j != parameterTypes.length - 1) {
                    System.out.print(",");
                }
            }
            System.out.println(")");
        }
    }
}
class Login {
    public boolean login(String name,String password) {
        if ("张三".equals(name) && "123".equals(password)) {
            return true;
        }
        return false;
    }
    private static void register() {
        System.out.println("toRegister");
    }
    public void logout() {
        System.out.println("退出登录");
    }
}
private static void register ()
public void logout ()
public boolean login (String,String)

8.3反编译Method

通过反射反编译一个类中所有的Method。

使用StringBuilder。

package com.tsccg.java.reflect;

import java.lang.reflect.Method;
import java.lang.reflect.Modifier;

/**
 * @Author: TSCCG
 * @Date: 2021/07/14 10:51
 */
public class ReflectDemo09 {
    public static void main(String[] args) throws Exception {
        StringBuilder s = new StringBuilder();
        //获取Class
        Class methodClass = Class.forName("com.tsccg.java.reflect.Login");
//        Class methodClass = Class.forName("java.lang.String");
        //获取类的修饰符
        s.append(Modifier.toString(methodClass.getModifiers()));
        s.append(" ");
        s.append("class");
        s.append(" ");
        //获取类名
        s.append(methodClass.getSimpleName());
        s.append("{\n");

        //获取一个类中所有方法,返回一个Method数组
        Method[] methods = methodClass.getDeclaredMethods();
        //遍历Method数组
        for (Method method : methods) {
            //缩进两格
            s.append("\t");
            //获取修饰符
            s.append(Modifier.toString(method.getModifiers()));
            s.append(" ");
            //获取方法返回值
            s.append(method.getReturnType().getSimpleName());
            s.append(" ");
            //获取方法名
            s.append(method.getName());
            s.append("(");
            //获取参数类型列表,返回一个Class数组
            Class[] parameterTypes = method.getParameterTypes();
            //遍历Class数组,打印参数类型名
            for (int j = 0; j < parameterTypes.length; j++) {
                s.append(parameterTypes[j].getSimpleName());
                if (j != parameterTypes.length - 1) {
                    s.append(",");
                }
                /*s.append(",");
                if (j == parameterTypes.length -1) {
                    s.deleteCharAt(s.length() - 1);
                }*/
            }
            s.append("){}\n");
        }
        s.append("}");
        System.out.println(s);
    }
}
public class Login{
	private static void register(){}
	public boolean login(String,String){}
	public void logout(){}
}

8.4反射机制调用方法

通过反射机制调用方法的方式和通过反射机制访问属性的大体流程一致。

  1. 先获取类的Class,
  2. 然后获取目标方法,
  3. 再调用方法。

8.4.1通过反射获取目标方法

不同的是,若想获取目标方法,不仅需要方法名,还需要方法中的参数列表。

这是因为方法可以重载,参数列表用于区分。

方法中有几个参数就需要几个参数对应类型的class:

//获取有两个参数的login方法
Method method1 = loginClass.getDeclaredMethod("login", String.class,String.class);
//获取只有一个参数的login方法
Method method2 = loginClass.getDeclaredMethod("login", String.class);

8.4.2调用该方法

获取目标方法后,就可以进行调用了。

调用方法有四个要素:

  1. 通过反射获取到的目标方法:method1
  2. 通过反射实例化的类对象:loginObj
  3. 需要传入的实参:"张三","123"
  4. 返回值:value
Object value = method1.invoke(loginObj,"张三","123");

8.4.3完整例子

package com.tsccg.java.reflect;

import java.lang.reflect.Method;

/**
 * @Author: TSCCG
 * @Date: 2021/07/14 11:27
 * 通过反射调用方法
 */
public class ReflectDemo10 {
    public static void main(String[] args) throws Exception {
        /*
        普通方式调用方法
         */
        Login o = new Login();
        boolean loginResult = o.login("张三","123");
        System.out.println(loginResult ? "登录成功" : "登录失败");
        /*
        通过反射调用方法
         */
        //获取类的Class
        Class loginClass = Class.forName("com.tsccg.java.reflect.Login");
        //通过反射创建类对象
        Object loginObj = loginClass.newInstance();
		//获取类中的方法
        //不仅需要方法名,还需要方法中的参数列表。
        //方法中有几个参数,就写几个参数对应类型,以指明是哪个具体的方法
        Method method1 = loginClass.getDeclaredMethod("login", String.class,String.class);
        Method method2 = loginClass.getDeclaredMethod("login", String.class);

        /*
        *非常重要:invoke*
        调用方法
         */
        Object value = method1.invoke(loginObj,"张三","123");
//        System.out.println(value);//true
        if (value instanceof Boolean) {
            Boolean result = (Boolean)value;
            System.out.println(result ? "登录成功" : "登录失败");
        }
    }
}
class Login {
    public boolean login(String name,String password) {
        if ("张三".equals(name) && "123".equals(password)) {
            return true;
        }
        return false;
    }
    //重载
    public boolean login(String name) {
        if ("张三".equals(name)) {
            return true;
        }
        return false;
    }
    private static void register() {
        System.out.println("toRegister");
    }
    public void logout() {
        System.out.println("退出登录");
    }
}
登录成功
登录成功

9.反射Constructor(了解)

9.1获取Constructor

package com.tsccg.java.reflect;

import java.lang.reflect.Constructor;
import java.lang.reflect.Modifier;

/**
 * @Author: TSCCG
 * @Date: 2021/07/14 17:14
 * 反编译构造方法
 */
public class ReflectDemo11 {
    public static void main(String[] args) throws Exception{
        StringBuilder s = new StringBuilder();
        Class animalClass = Class.forName("com.tsccg.java.reflect.Animal");
//        Class animalClass = Class.forName("java.util.Date");
        Object animalObj = animalClass.newInstance();
        //追加类的修饰符
        s.append(Modifier.toString(animalClass.getModifiers()));
        s.append(" ");
        s.append("class ");
        //追加类名
        s.append(animalClass.getSimpleName());
        s.append(" {\n");

        Constructor[] cons = animalClass.getDeclaredConstructors();
        for (Constructor con : cons) {
            //追加缩略符
            s.append("\t");
            //追加构造方法修饰符
            s.append(Modifier.toString(con.getModifiers()));
            s.append(" ");
            //追加构造方法名,也就是类名
            s.append(animalClass.getSimpleName());
            s.append("(");
            //追加参数列表
            Class[] paraClass = con.getParameterTypes();
            for (int i = 0; i < paraClass.length; i++) {
                s.append(paraClass[i].getSimpleName());
                if (i != paraClass.length - 1) {
                    s.append(",");
                }
            }
            s.append("){}\n");
        }
        s.append("}");
        //打印获取到的数据
        System.out.println(s);

    }
}
class Animal {
    private String name;
    private int age;
    private String type;
    private String hobby;
    //无参构造
    public Animal() {
    }
    //1参构造
    public Animal(String name) {
        this.name = name;
    }
    //2参构造
    public Animal(String name, int age) {
        this.name = name;
        this.age = age;
    }
    //3参构造
    public Animal(String name, int age, String type) {
        this.name = name;
        this.age = age;
        this.type = type;
    }
    //4参构造
    public Animal(String name, int age, String type, String hobby) {
        this.name = name;
        this.age = age;
        this.type = type;
        this.hobby = hobby;
    }
}
 class Animal {
	public Animal(String,int,String,String){}
	public Animal(String,int,String){}
	public Animal(String,int){}
	public Animal(String){}
	public Animal(){}
}

9.2反射机制调用构造方法

package com.tsccg.java.reflect;

import java.lang.reflect.Constructor;

/**
 * @Author: TSCCG
 * @Date: 2021/07/14 17:51
 * 通过反射调用构造方法
 */
public class ReflectDemo12 {
    public static void main(String[] args) throws Exception{
        /*
        1.通过普通方式调用构造方法
         */
        Animal a1 = new Animal();
        Animal a2 = new Animal("大哈");
        Animal a3 = new Animal("大哈",4,"哈士奇","装修");
        /*
        2.通过反射调用构造方法
         */
        Class animalClass = Class.forName("com.tsccg.java.reflect.Animal");
        //2.1直接用类Class调用无参构造(在jdk9之后已过时)
        Object obj0 = animalClass.newInstance();
        System.out.println(obj0);
        //2.2调用有参构造
        //第一步:获取目标有参构造方法
        Constructor con4 = animalClass.getDeclaredConstructor(
                String.class,int.class,String.class,String.class);
        //第二步:调用构造方法创建对象
        Object obj4 = con4.newInstance("二哈",3,"哈士奇","修理物品");
        System.out.println(obj4);
        //2.3通过获取Constructor调用无参构造方法
        Constructor con0 = animalClass.getDeclaredConstructor();
        System.out.println(con0.newInstance());
    }
}
class Animal {
    private String name;
    private int age;
    private String type;
    private String hobby;
    //无参构造
    public Animal() {
    }
    //1参构造
    public Animal(String name) {
        this.name = name;
    }
    //2参构造
    public Animal(String name, int age) {
        this.name = name;
        this.age = age;
    }
    //3参构造
    public Animal(String name, int age, String type) {
        this.name = name;
        this.age = age;
        this.type = type;
    }
    //4参构造
    public Animal(String name, int age, String type, String hobby) {
        this.name = name;
        this.age = age;
        this.type = type;
        this.hobby = hobby;
    }

    @Override
    public String toString() {
        return "Animal{" +
                "name='" + name + '\'' +
                ", age=" + age +
                ", type='" + type + '\'' +
                ", hobby='" + hobby + '\'' +
                '}';
    }
}

结果:

Animal{name='null', age=0, type='null', hobby='null'}
Animal{name='二哈', age=3, type='哈士奇', hobby='修理物品'}
Animal{name='null', age=0, type='null', hobby='null'}

10.通过反射获取父类和父接口

10.1通过反射获取一个类的父类

Class strclass = Class.forName("java.lang.String");
//获取父类
Class superClass = strclass.getSuperclass();
System.out.println(superClass.getName());
java.lang.Object

10.2通过反射获取一个类实现的所有接口

Class strclass = Class.forName("java.lang.String");
//获取实现的所有接口
Class[] interClass = strclass.getInterfaces();
for (Class aClass : interClass) {
    System.out.println(aClass.getName());
}
java.io.Serializable
java.lang.Comparable
java.lang.CharSequence
posted @ 2021-07-12 22:16  TSCCG  阅读(58)  评论(0编辑  收藏  举报