java--反射机制

内容大纲

最重要的是Method和Field

反射机制(比较简单,因为只要会查帮助文档,就可以了。)
1.1、什么是反射机制?反射机制有什么用?
反射机制:可以操作字节码文件
作用:可以让程序更加灵活。

1.2、反射机制相关的类在哪个包下?
	java.lang.reflect.*;

1.3、反射机制相关的主要的类?
	java.lang.Class
	java.lang.reflect.Method;
	java.lang.reflect.Constructor;
	java.lang.reflect.Field;

1.4、在java中获取Class的三种方式?
	第一种:	 
		Class c = Class.forName("完整类名");
	第二种:
		Class c = 对象.getClass();
	第三种:
		Class c = int.class;
		Class c = String.class;

1.5、获取了Class之后,可以调用无参数构造方法来实例化对象

	//c代表的就是日期Date类型
	Class c = Class.forName("java.util.Date");

	//实例化一个Date日期类型的对象
	Object obj = c.newInstance();

	一定要注意:
		newInstance()底层调用的是该类型的无参数构造方法。
		如果没有这个无参数构造方法会出现"实例化"异常。

1.6、如果你只想让一个类的“静态代码块”执行的话,你可以怎么做?
	Class.forName("该类的类名");
	这样类就加载,类加载的时候,静态代码块执行!!!!
	在这里,对该方法的返回值不感兴趣,主要是为了使用“类加载”这个动作。

1.7、关于路径问题?

	String path = Thread.currentThread().getContextClassLoader()
					  .getResource("写相对路径,但是这个相对路径从src出发开始找").getPath();	

	String path = Thread.currentThread().getContextClassLoader()
					  .getResource("abc").getPath();	//必须保证src下有abc文件。

	String path = Thread.currentThread().getContextClassLoader()
					  .getResource("a/db").getPath();	//必须保证src下有a目录,a目录下有db文件。
	
	String path = Thread.currentThread().getContextClassLoader()
					  .getResource("com/bjpowernode/test.properties").getPath();	
					  //必须保证src下有com目录,com目录下有bjpowernode目录。
					  //bjpowernode目录下有test.properties文件。

	这种方式是为了获取一个文件的绝对路径。(通用方式,不会受到环境移植的影响。)
	但是该文件要求放在类路径下,换句话说:也就是放到src下面。
	src下是类的根路径。

	直接以流的形式返回:
	InputStream in = Thread.currentThread().getContextClassLoader()
							.getResourceAsStream("com/bjpowernode/test.properties");

1.8、IO + Properties,怎么快速绑定属性资源文件?

	//要求:第一这个文件必须在类路径下
	//第二这个文件必须是以.properties结尾。
	ResourceBundle bundle = ResourceBundle.getBundle("com/bjpowernode/test");
	String value = bundle.getString(key);

如何获取到Class实例

要操作一个类的字节码,需要首先获取到这个类的字节码,怎么获取java.lang.Class实例?
三种方式

  •   第一种:Class c = Class.forName("完整类名带包名");
    
  •   第二种:Class c = 引用.getClass();
    
public class ReflectTest01 {
    public static void main(String[] args) {
        /*
        * Class.forName()
        * 1、静态方法
        * 2、字符串需要的是一个完整类名。
        * 3、完整类名必须带有包名。java.lang包也不能省略。
        * */
        Class c1 = null;
        try {
            c1 = Class.forName("java.lang.String");//c1代表String.class文件,或者说代表String类型
            Class c2 = Class.forName("java.util.Date");
            Class c3 = Class.forName("java.lang.Integer");
            Class c4 = Class.forName("java.lang.System");
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }

   java中任何一个对象都有一个方法:getClass()
        String s = "abc";
        Class x = s.getClass();//x代表String.class文件,或者说代表String类型
        System.out.println(c1 == x);//true 地址相同

        //第三种方式,java语言中任何一种类型,包括基本数据类型,它都有.class属性。
        Class z = String.class;//z代表String类型
        System.out.println(x == z);//true
    }
}

获取到实例能干什么

反射机制更具有灵活性

public class ReflectTest02 {
    public static void main(String[] args) {
        //通过反射机制,获取Class,通过Class来实例化对象
        try {
            Class c = Class.forName("com.bjpowernode.java.bean.User");
            //newInstance()这个方法会调用User这个类的无参数构造方法,完成对象的创建。
            //重点是:newInstance()调用的是无参构造,必须保证无参构造是存在的!
            //如果User里写了有参构造,而不手动写出无参构造,无参构造就失效了
          	 Object obj = c.newInstance();
            System.out.println(obj);
        } catch (ClassNotFoundException | InstantiationException | IllegalAccessException e) {
            e.printStackTrace();
        }
    }

}

验证反射机制的灵活性

创建com.bjpowernode.java.reflect.ReflectTest03

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

public class ReflectTest03 {
    public static void main(String[] args) throws  Exception {
        //通过IO流读取classinfo.properties文件
        FileReader reader = new FileReader("chapter15/classinfo.properties");
        //创建属性类对象Map
        Properties pro = new Properties();//key value都是String
        //加载
        pro.load(reader);
        //关闭流
        reader.close();

        //通过key获取value
       String className = pro.getProperty("className");
        System.out.println(className);

        //通过反射机制实例化对象
       Class c = Class.forName(className);
       Object obj = c.newInstance();
        System.out.println(obj);

    }
}

com.bjpowernode.java.bean.User

public class User {
    public User() {
        System.out.println("无参数构造方法!");
    }

    //这里注意如果写了有参构造,而不手动写出无参构造,无参构造就失效了
}

在同一工程下创建classinfo.properties配置文件
className=com.bjpowernode.java.bean.User

此时输出结果为
com.bjpowernode.java.bean.User IO流结果
无参数构造方法!
com.bjpowernode.java.bean.User@5674cd4d 反射机制

这时我们只需要更改配置文件中的
如改为className=java.util.Date
从而改变了反射机制创建的对象

结果变为:
java.util.Date
Sat May 02 09:52:35 CST 2020

结论:java代码写一遍,在不改变java源代码的基础之上,可以做到不同对象的实例化。非常之灵活。(符合OCP开闭原则:对扩展开放,对修改关闭。可以修改配置文件,创建除不同的实例对象。
1、可以访问class文件
2、反射机制使程序更加灵活

后期学习的是高级框架,而工作过程中,也都是使用高级框架,

  • 包括:ssh ssm
  • Spring SpringMVC MyBatis
  • Spring Structs Hibernate
  • 这些高级框架底层实现原理:都采用了反射机制。所以反射机制还是重要的。
  • 有利于剖析框架底层的源代码。

Class.forName()发生了什么?

记住,重点:

  •   如果你只是希望一个类的静态代码块执行,其它代码一律不执行,
    
  •   你可以使用:
    
  •       Class.forName("完整类名");
    
  •   这个方法的执行会导致类加载,类加载时,静态代码块执行。
    

示例:

public class ReflectTest04 {

    public static void main(String[] args) {
        try {
            Class.forName("com.bjpowernode.java.reflect.MyClass");
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
    }
}

class MyClass{

    //静态代码块在类加载时执行,并且只执行一次。
    static {
        System.out.println("MyClass类的静态代码块执行了!");
    }

}

结果:MyClass类的静态代码块执行了!

文件路径的问题

通用路径:注意使用以下通用方式的前提是,这个文件必须在类路径下。
即src下的都是类路径。src是类的根路径。

public class AboutPath {
    public static void main(String[] args) throws Exception{
        /*这种方式的路径缺点是:移植性差,默认是project的根路径,离开IDEA就不是了。
        * FileReader reader = new FileReader("chapter15/classinfo.properties");
        * */

        //通用路径:注意使用以下通用方式的前提是,这个文件必须在类路径下。
        //即src下的都是类路径。src是类的根路径。

        /*Thread.currentThread() 当前线程对象
        * getContextClassLoader() 是线程对象的方法,可以获取到当前线程的类加载器对象
        * getResource() 【获取资源】这是类加载器对象的方法,当前线程的类加载器默认从类的根路径下加载资源。
        * */

        //采用以上的代码可以拿到一个文件的绝对路径
        ///D:/learn/Java%20IDEA/out/production/chapter15/classinfo2.properties
        String path = Thread.currentThread().getContextClassLoader()
                .getResource("classinfo2.properties").getPath();
        System.out.println(path);

    }
}

以流的形式返回

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

public class IoPropertiesTest {

    public static void main(String[] args)throws Exception{
        //可以获取一个文件的绝对路径了 通用
        /*String path = Thread.currentThread().getContextClassLoader()
                .getResource("classinfo2.properties").getPath();

        FileReader reader = new FileReader(path);*/

        //上面可以合二为一 流
        InputStream reader = Thread.currentThread().getContextClassLoader()
                .getResourceAsStream("classinfo2.properties");

        Properties pro = new Properties();
        pro.load(reader);
        reader.close();
        //通过key获取value
        String className = pro.getProperty("className");
        System.out.println(className);
    }

}

资源绑定器

好用,但是有使用限制:
1、java.util包下提供了一个资源绑定器,便于获取属性配置文件中的内容。使用以下这种方式的时候,属性配置文件xxx.properties必须放到src类路径下。
2、只能绑定xxx.properties文件,扩展名必须是properties
3、在写路径的时候,路径后面的扩展名不能写。

public class ResourceBundleTest {
    public static void main(String[] args) {
        //只能绑定xxx.properties文件,扩展名必须是properties
        //在写路径的时候,路径后面的扩展名不能写。

        //ResourceBundle bundel = ResourceBundle.getBundle("classinfo2");
        //如果在别的路径下
        ResourceBundle bundel = ResourceBundle.getBundle("com/bjpowernode/java/bean/db");

        String className = bundel.getString("className");
        System.out.println(className);
    }

}

获取field(了解)

//反射属性Field

public class Student {
    //Field翻译为字段,其实就是属性/成员
    //4个
    private String name;//Field对象
    protected  int age;
    boolean sex;
    public  int no;

}

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

public class ReflectTest05 {

    public static void main(String[] args) throws ClassNotFoundException {
        //获取类中所有的public修饰的Field
        Class studentClass = Class.forName("com.bjpowernode.java.bean.Student");
        String className = studentClass.getName();
        System.out.println("完整类名" + className);
        String simpleName = studentClass.getSimpleName();
        System.out.println(simpleName);

        //获取类中所有的Field
        Field[] fields = studentClass.getFields();
        System.out.println(fields.length);//测试数组中只有一个元素
        //取出这个Field
        Field f = fields[0];
        //取出这个Field它的名字
        String fieldName = f.getName();
        System.out.println(fieldName);


        //获取所有的Field
        Field[] fs = studentClass.getDeclaredFields();
        System.out.println(fs.length);//4

        System.out.println("===============");

        //遍历
        for(Field f1 : fs){
            //获取属性的修饰符列表 有可能有多个
            int i = f1.getModifiers();//返回的修饰符是一个数字,每个数字是修饰符的代号!!!
            System.out.println(i);
            //可以将这个“代号”数字转换成“字符串”吗?
            String modifierString = Modifier.toString(i);
            System.out.println(modifierString);

            //获取属性的类型
            Class fieldType = f1.getType();
            String fName =fieldType.getName();//getSimpleName()获取简单类型
            System.out.println(fName);

            //获取属性的名字
            System.out.println(f1.getName());
        }

    }
}

输出结果

完整类名com.bjpowernode.java.bean.Student
Student
1
no

===============
4
2
private
java.lang.String
name
4
protected
int
age
0

boolean
sex
1
public
int
no

反编译(非重点)

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

//通过反射机制,反编译一个类的属性Field
public class ReflectTest06 {
    public static void main(String[] args) throws ClassNotFoundException {

        //创建这个是为了拼接字符串
        StringBuffer s = new StringBuffer();
        //反编译拿到的Class文件
        Class studentClass = Class.forName("java.lang.String");

        s.append( Modifier.toString(studentClass.getModifiers()) + " class "
                + studentClass.getSimpleName() + " {\n");

        Field[] fields = studentClass.getDeclaredFields();

        //反编译代码内容
        for(Field field : fields){

            s.append("\t");
            s.append(Modifier.toString(field.getModifiers()));
            s.append(" ");
            s.append(field.getType().getSimpleName());
            s.append(" ");
            s.append(field.getName());
            s.append(";\n");

        }

        s.append("}");

        System.out.println(s);

    }

}

通过反射机制访问对象属性(重点)

注意:反射机制代码复杂了,但是为了灵活。
不会自己去写反射机制代码,但有助于理解框架

import java.lang.reflect.Field;

public class ReflectTest07 {

    public static void main(String[] args) throws Exception {
        /*
        * 传统做法:访问对象的属性
        * Student s = new Student();
        * s.no = 1111;
        * */


        //使用反射机制,怎么去访问一个对象的属性。(set get)
        Class studentClass = Class.forName("com.bjpowernode.java.bean.Student");
        Object obj = studentClass.newInstance();//obj就是Student对象。(底层调用无参数构造方法)

        //获取no属性(根据属性的名称来获取Field)
        Field noFiled = studentClass.getDeclaredField("no");

        //给obj对象(Student对象)的no属性赋值

        /*
        * 虽然使用了反射机制,但是三要素还是缺一不可;
        * 1、对象obj
        * 2、no属性
        * 3、22222值
        * */

        noFiled.set(obj, 22222);//给obj对象的no属性赋值2222

        //读取属性的值
        //两个要素:获取obj对象的no属性的值。
        System.out.println(noFiled.get(obj));
    }

}

可变长度参数

可变长度参数

  • int...args这就是可变长度
    
  • 1、参数:0~N个
    
  • 2、有且只能在最后位置,一个
    
  • 3、可以当作一个数组来看待
    
  • 语法是:类型...(注意:一定是3个点。)
    

public class ArgsTest {

public static void main(String[] args) {
    m();
    m(10);
    m(10,20);

    m3("ab","cd","ef");
}

public static  void m(int...args){
    System.out.println("m方法执行了!");
}

public static  void m2(String a,int...args){
    System.out.println("m2方法执行了!");
}

public static  void m3(String...args){

    for(int i=0;i<args.length;i++) {
        System.out.println(args[i]);
    }

}

}

反射机制调用方法

/*

  • 用户业务类
  • */
public class UserService {
    public  boolean login(String name,String password){
        if("admin".equals(name) && "123".equals(password))
        return true;

        return false;

    }

    //区分一个方法,依靠方法名和参数列表。

    //退出系统的方法
    public  void logout(){
        System.out.println("系统已经安全退出!");
    }

}

反射机制调用UserService方法

import com.bjpowernode.java.service.UserService;

import java.lang.reflect.Method;

public class ReflectTest10 {
    public static void main(String[] args) throws Exception {
        //不是用反射机制,如何调用方法
        UserService user = new UserService();
       boolean loginSuccess = user.login("admin","123");
        System.out.println(loginSuccess);

        //使用反射机制来调用一个对象的方法该怎么做?
        Class userServiceClass = Class.forName("com.bjpowernode.java.service.UserService");
        //创建对象
        Object obj = userServiceClass.newInstance();
        //获取Method 限制方法名和参数
        Method loginMethod = userServiceClass.getDeclaredMethod("login", String.class, String.class);

        //调用方法
        //四要素:obj对象、loginMethod方法、"admin","123"参数、retValue返回值
        Object retValue = loginMethod.invoke(obj,"admin","123");

        System.out.println(retValue);

    }
}

给你一个类,怎么获取这个类的父类,已经实现了哪些接口

public class ReflectTest13 {

    public static void main(String[] args) throws Exception  {

        //String举例
        Class stringClass = Class.forName("java.lang.String");

        //获取String的父类
        Class superClass = stringClass.getSuperclass();
        System.out.println(superClass.getName());//Object

        //获取其实现的多个接口
        Class[] interfaces = stringClass.getInterfaces();
        for(Class in : interfaces){
            System.out.println(in.getName());
        }

    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值