java反射

反射:在类运行时,能够动态获取并访问其构造结构的机制称为反射机制。

反射是Java中框架设计的核心,通过对类的构造、属性、方法等数据的获取提供抽象的底层构建

反射机制:
反射需要先获得类的class字节码,由JVM类加载器(ClassLoader)负责加载,并在内存中缓存class的内部结构。借助于Java的反射机制允许快速对类的结构数据进行访问和调用

获得class字节码文件的三种方式:
  1.Object对象的getClass()方法

获得方式一
Class<User> userClass = (Class<User>) user.getClass();

  2.类的class静态属性(一个类只有一个class,实例化多次对应的都是同一个class,所以class可以用==进行比较,a.getClass==b.getClass)

获得方式二
Class<User> userClass = User.class;

  3.Class.forName(String classPath)的加载(反射应用的核心

获得方式三  通过类路径访问类的Class信息,在不知道类型的情况下,可以不写泛型
Class<User> userClass = (Class<User>) Class.forName("com.igeek.pojo.User");

反射允许获取到类的各种构造结构,包括构造方法、方法、属性、接口、注解等
反射能够对类完成实例构建,并能对方法进行实例调用

第三种方法的使用:

Class objClass = Class.forName(“com.igeek.pojo.User”)
Object obj = objClass.newInstance()

获取类的构造方法

getConstructor(Class<T>… parameterTypes)
getConstructors()

获取类的属性

getField(String fieldName)
getFields()

获取类的方法

getMethod(String methodName,Class<T>…parameterType)
getMethods()

获取类的接口

getInterfaces()

获取类的超类

getSuperclass()

XML
Extensible Markup Language 可扩展标记语言

作用:
  1.用于对工程进行外部配置
  2.用于描述复杂格式的数据,对数据进行存储管理
  3.用于跨应用进行数据的交互

每一个XML文件都以标签的方式进行编写,并且必须要有一个跟标签
XML中每个标签都称为节点或元素,标签体的文本也是节点元素,称为文本节点

定义根标签:使用DTD或者是Schema文件来定义XML的内容

解析XML文档主要有两种方式:
1.DOM解析
特点: 将整个XML文档全部加载至内存中,在内存中对文档进行解析
2.SAX解析
特点:使用事件驱动方式进行解析,一边读取一边解析

DOM解析的使用步骤:

1.DOM4j架包导入:

File——Project Structures——Libraries——选择DOM所在的位置——OK

2.创建XML

Setting——Editor——File and Code templates——"+"——名称为“new.xml”——输入“<?xml version="1.0" encoding="UTF-8"?>”——单击"OK"

3.新建文件

新建文件进行new一个new.xml文件

MyInterfaceA

package com.igeek;

public interface MyInterfaceA {
}

MyInterfaceB

package com.igeek;

public interface MyInterfaceB {
}

Human类

package com.igeek.pojo;

public class Human {
    public int age;

    public int getAge() {
        return age;
    }

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

User类

package com.igeek.pojo;

import com.igeek.MyInterfaceA;
import com.igeek.MyInterfaceB;

//实现了A、B两个接口,继承了Human类
public class User extends Human implements MyInterfaceA, MyInterfaceB {
    //三个属性
    private int userId;
    private String userName;
    public String password;

    //空参构造方法
    public User() {
    }

    //带参构造方法
    public User(int userId, String userName, String password) {
        this.userId = userId;
        this.userName = userName;
        this.password = password;
    }

    //属性的get 、set方法
    public int getUserId() {
        return userId;
    }
    public void setUserId(int userId) {
        this.userId = userId;
    }

    public String getUserName() {
        return userName;
    }
    public void setUserName(String userName) {
        this.userName = userName;
    }

    public String getPassword() {
        return password;
    }
    public void setPassword(String password) {
        this.password = password;
    }
}

获取类的方法

package com.igeek;

import com.igeek.pojo.Human;
import com.igeek.pojo.User;

/**
 * 获得class文件
 * 反射是基于Class类型访问的
 * Class对象封装缓存了类的数据结构
 */
public class Demo1 {

    public static void main(String[] args) {
        User user = new User();
        Human human = new User();

        //获得方式一:返回值为Class,不写泛型默认为object类型,写了泛型就要进行强转
     //将类实例化以后对对象进行获取class处理

        Class<User> userClass = (Class<User>) user.getClass();

        //获得方式二:不用对类进行实例化,直接对类进行获取class处理
        Class<User> userClass = User.class;

     //获得方式三 通过类路径访问类的Class信息
        try {
            //这个方法是因为:这个方法是可以在不明确类型和其结构时使用,
       (第一个方法都已经将对象实例化出来了,不需要使用反射。)
            Class<User> userClass = (Class<User>) Class.forName("com.igeek.pojo.User");
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }

     //上面两种(第一种和第二种获取class方法)方式主要用于多态场景下的类型匹配

        if(human instanceof User){
            System.out.println("human的实例类型是user类型");
        }
        if(human.getClass() == User.class){
            System.out.println("human的实例类型是user类型");
        }
    }
}

第三种获取class方法的应用:

package com.igeek;

import com.igeek.pojo.User;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;

/**
 * 使用反射解析类的数据结构
 */
public class Demo2 {

    public static void main(String[] args) {

        try {
            //获得类的Class对象(第三种方法),该对象封装了类的数据结构信息
            Class objClass =  Class.forName("com.igeek.pojo.User");
            //使用反射将类进行实例化
            //该方式只能通过类的无参构造进行实例化
            Object obj = objClass.newInstance();
            System.out.println("实例化后的结果:"+obj);


            //获得类的所有的构造方法:返回值是构造方法的数组  构造方法的类型是Constructor类型
            Constructor[] constructors = objClass.getConstructors();
       //可获得私有的构造方法
       Constructor[] constructors=ObjClass.getDeclareConstructors();
            System.out.println("该类有"+constructors.length+"个构造方法");
            //遍历所有的构造
            for (Constructor constructor : constructors) {
                System.out.println("\n-----------------------------");
                //获得构造方法的参数数量
                int paramCount = constructor.getParameterCount();
                System.out.println("构造方法的参数数量:"+paramCount);
                //获得构造方法的参数类型
                Class[] paramClassArray = constructor.getParameterTypes();
                for (Class paramClass : paramClassArray) {
                    System.out.print(paramClass.getName()+"\t");
                }
            }
            System.out.println();

            //根据参数获得指定的构造方法
            //获得无参的构造
            Constructor constructor1 = objClass.getConstructor();
            System.out.println(constructor1);
            //根据构造方法来实例化对象
            Object obj1 = constructor1.newInstance();
            System.out.println(obj1);

            //获得带参构造方法:通过参数的class获取指定的构造方法
       //基本类型和包装类的class是不同的
            Constructor constructor2 = objClass.getConstructor(int.class,String.class,String.class);
            //使用带参的构造方法实例化对象
       //可以用Object接收
       Object obj2=constructor2.newInstance(100,"tom","123");
       //也可强转后用User接收
            User user = (User) constructor2.newInstance(100,"tom","123");
            System.out.println(user.getUserId()+"\t"+user.getUserName()+"\t"+user.getPassword());

            //获得当前类的父类
            Class superClass = objClass.getSuperclass();
            System.out.println("\n------------父类------------");
       //若结果是Object则这个类没有手动进行extends继承
            System.out.println(superClass);

            //获取实现的接口
            Class[] interfaceClasses = objClass.getInterfaces();
            System.out.println("\n---------实现的接口---------");
            for (Class interfaceClass : interfaceClasses) {
                System.out.println(interfaceClass.getName());
            }


            //获得属性
            //访问当前允许访问(具有访问权限)到的属性,包括父类继承得到的属性
       (不同包中加了protect或者加了private或者没加访问修饰符的都没有访问权限)
            Field[] fields = objClass.getFields();
            //访问当前类(本类)直接声明的属性,不论访问修饰符是什么
            Field[] fields = objClass.getDeclaredFields();
            System.out.println("\n------------属性-------------");
            for (Field field : fields) {
                //获得属性的名称
                System.out.println("属性名称:"+field.getName());
                //获得属性的类型
                System.out.println("属性类型:"+field.getType());
            }

            System.out.println("\n-------------------------------");
            //获得指定名称属性
            Field nameField = objClass.getDeclaredField("userName");
            //为属性提供访问权限,即使是私有属性也将允许访问处理
            nameField.setAccessible(true);
            //对属性进行赋值,第一个参数表示该属性从属的对象,第二个参数表示要设置的值
            //set方法允许对属性进行赋值,但是会根据属性本身的访问修饰符来决定是否可以成功操作
       (paivate时不能被set,但是setAccessible后即可不管是什么访问修饰符都可以进行设置可以访问处理)
            nameField.set(user,"jack");
            //System.out.println(user.getUserName());
            //获取属性的值
            Object value = nameField.get(user);
            System.out.println(value);

            System.out.println("--------------方法的访问-------------");
       //获取所有方法,包括私有的
       Method[] methods=objClass.getDeclareMethods();
       //获取所有方法
            Method[] methods = objClass.getMethods();
            for (Method method : methods) {
                //获取方法的名称
                System.out.println("方法名:"+method.getName());
                //获取返回值类型
                Class returnClass = method.getReturnType();
                System.out.println("返回值类型:"+returnClass);
                //获取方法的参数类型
                Class[] paramClassArray = method.getParameterTypes();
                System.out.println("方法的参数:");
                for (Class paramClass : paramClassArray) {
                    System.out.println(paramClass);
                }
                System.out.println("------------------");
            }

            //获取一个指定名称的方法:第一个参数是方法的名称,第二个参数是参数的类型,可以多个可以一个可以没有
            Method setMethod = objClass.getMethod("setUserName", String.class);
            //使用反射调用方法(修改原内容):第一个参数是代表修改的哪个对象中的内容,第二个参数是要修改成的值
            setMethod.invoke(user, "admin");
            Method getMethod = objClass.getMethod("getUserName");
            //调用方法(获得返回值):返回值是Object类型,如果调用的方法没有返回值,则返回的值null
            Object returnValue = getMethod.invoke(user);
            System.out.println("返回值是:"+returnValue);

        } catch (Exception e) {
            e.printStackTrace();
        }

    }
}

例子:

使用反射动态解析javax.swing.JFrame类

使用反射实例化该对象

使用反射动态解析该类的构造方法,获得构造方法的参数数量和类型

并使用无参构造以及带有String的构造
方法来实例化JFrame对象

package Demo1;
import java.lang.reflect.Constructor;

public class Demo {
    public static void main(String[] args) {
        //获取类方法
        try {
            Object objClass=Class.forName("javax.swing.JFrame");
            //使用反射实例化该对象
            Object obj=((Class) objClass).newInstance();
            System.out.println("实例化后的对象是:"+obj);

            //使用反射动态解析该类的构造方法,获得构造方法的参数数量和类型
            Constructor[] constructors=((Class) objClass).getConstructors();
            System.out.println("共有"+constructors.length+"个构造方法");
            //获得构造方法的参数数量和类型
            for (Constructor constructor : constructors) {
                //获取参数数量
                int count=constructor.getParameterCount();
                System.out.println("该构造方法有"+count+"个参数");
                //获取参数类型
                Class[] classes=constructor.getParameterTypes();
                for (Class aClass : classes) {
                    System.out.println(aClass.getName()+"\t");
                }
            }
            //使用无参构造
            Constructor con1=((Class) objClass).getConstructor();
            Object Obj1=con1.newInstance();
            System.out.println("无参构造实例化对象是:"+Obj1);

            //带有String的构造
            Constructor con2=((Class) objClass).getConstructor(String.class);
            Object Obj2=con2.newInstance("我是一个标题");
            System.out.println("带参构造实例化对象是:"+Obj2);

若是想要获得父类和子类所以的属性,则需要进行递归,先对子类进行属性的获得,然后获得子类的父类,如果不是Object类型,则使用递归将父类传入继续进行属性的获得

 XML的使用:

根标签:(user是标签名称,    id是标签属性,   1001是标签值,     userName和password是标签体的内容)

XML中每个标签都称为节点或元素,标签体的文本也是节点元素,称为文本节点

(紫色标记的都是节点,红色标记的都是文本加点,所以一共11个节点)

框架中灵活处理的内容都要写成配置文件,通过底层代码对配置文件进行解析,解析完成后通过反射进行处理调用

<?xml version="1.0" encoding="UTF-8"?>
//相当于集合存储用户信息
<users>
  //用户一
  <user id="1001" age=20>
    //标签体(标签内容)
    <userName>tom</userName>
    <password>tom123</password>
  </user>
  //标签二:用户二
  <user id="1002" age=21>
    //标签体(标签内容)
    <userName>jack</userName>
    <password>jack123</password>
  </user>
</users>
package com.igeek;

import org.dom4j.Attribute;
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;
import java.util.List;

/**
 * 使用DOM4J对XML文档进行解析
 */
public class Demo3 {

    public static void main(String[] args) {

        //使用ClassLoader获取资源是在类路径(src文件夹内)中访问的
        //System.out.println(Demo3.class.getClassLoader().getResource("").getPath());
        //使用类自身Class对象获取资源是在类所在的包路径中访问的
        //System.out.println(Demo3.class.getResource("").getPath());
        //从类路径中访问文件并将文件转换为输入流
        //System.out.println(Demo3.class.getClassLoader().getResourceAsStream("test.xml"));
        try {
            //创建SAX解析器对象
            SAXReader reader = new SAXReader();
            //通过获取xml文件的输入流,加载文档
       //通过类的class获取classLoader ,在通过classLoader读取文件的输入流
       //这个方式是从当前类所在的包中查询test.xml文件
       //Document document=reader.read(Dome3.class.getClass().getResourceAsStream("test.xml"));
       //这个方式是从整个项目的路径中查询test.xml文件
            Document document = reader.read(Demo3.class.getClassLoader().getResourceAsStream("test.xml"));
            //获取文档的根节点对象
            Element root = document.getRootElement();
            //访问节点的名字
            System.out.println(root.getName());
            //访问节点中的所有下一层子节点
            List<Element> list = root.elements();//获取的是数组:所有的节点
            //遍历user标签
            for (Element element : list) {

                System.out.println(element.getName());

                //访问标签的所有属性,也可以通过attribute通过名称获取单独的一个属性
                List<Attribute> attributeList = element.attributes();//得到的是集合所有的属性
                //遍历属性(属性的类型是:Attibute标签的类型是:Element)
                for (Attribute attribute : attributeList) {
                    //获取属性的名称
                    System.out.print("\t"+attribute.getName()+":");
                    //获取属性的值
                    System.out.println(" "+attribute.getValue());
                }

                //获得指定的userName子标签
                Element nameNode = element.element("userName");
                System.out.println("\t"+nameNode.getName());
                //获取userName的标签体文本:trim是去除空格(如果只是需要查找不需要操作,也可以不用接收直接打印)
                String name = nameNode.getTextTrim();
                System.out.println("\t\t"+name);
                //获取password子标签
                Element pwdNode = element.element("password");
                System.out.println("\t"+pwdNode.getName());
                System.out.println("\t\t"+pwdNode.getTextTrim());
            }

        } catch (DocumentException e) {
            e.printStackTrace();
        }
    }
}
  • 21
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值