反射

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

反射是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(); } } }

 

posted @ 2020-08-08 14:45  小小野生程序员sunny  阅读(127)  评论(0编辑  收藏  举报