Java object-oriented (xiv)

reflection

Reflecting frame design is the soul

First, the timing of load class

When you want to use a class if the class has not yet been loaded into memory, the system by loading, connect, initialize three steps to achieve this class is initialized.

  • Load: refers to the class to read the file into memory, and whom to create a Class object. Any class that is used when the system will build a Class object.
  • Connections: Verify that you have the correct internal structure and coherence, and other classes. Responsible for preparing allocate memory for static member of a class, and set the default initialization values.
  • Initialization: initialize member variables and so on.

Load opportunity

  1. Create an instance of the class
  2. Access class static variable or static variable assignment
  3. Static method call class
  4. Initiating a subclass of class
  5. Reflection mode using force the creation of a class or interface objects corresponding java.lang.Class

Second, the class loader

What is the class loader classLoader

Responsible for loading .class file into memory for, and generate the corresponding Class object.

Although we do not care about class loading mechanism, but understand this mechanism we can run a better understanding of the program.

Classification class loader

(1) Root class loader

  • Also known as the bootstrap class loader is responsible for loading Java core class
  • Such as System, String and so on. In rt.jar file under the JDK JRE's lib directory

(2) extended class loader

  • Responsible for loading the JRE extensions directory in a jar.
  • JRE in the JDK ext directory under the lib directory

(3) the system class loader

  • Responsible for loading class files from the java command in the JVM starts
  • And the jar package and class path specified environment variable classpath

Third, what is reflected

Creating an object in three stages

  1. Stage .java source file file
  2. .Class bytecode stage
  3. Create a new object name an object stage

introspection

At runtime can get JavaBean among the attribute name and get and set methods

reflection

  • JAVA reflection mechanism is in the operating state, for any class, are made known to all the properties and methods of this class ;
  • For any object, we are able to call any of its methods and properties;
  • This method of function and dynamic information call the object's reflection mechanism known as dynamic acquisition of java language.
  • Want to use reflection, we have got to get bytecode files.

Fourth, the reflection of the Class

The class name use
Class class On behalf of the class of entities in a Java application running represent classes and interfaces
Field class On behalf of the class member variables (also known as member variables of the class attribute)
Method class Method represents a class of
Constructor class Representative class constructor

(1) Class Class

  • Obtain the relevant class method
method use
forName(String className) Depending on the object class name return to class
getName() Get the full path name of the class
newInstance() Create an instance of the class
getPackage() Get the class package
getSimpleName() The name is derived class
getSuperclass() The current name is derived class inherits the parent class
getInterfaces () Obtain the current class or class implements an interface
asSubclass(Class clazz) The object passed into a class object whose subclasses representatives
Cast Representative of the object into the object class or interface
getClassLoader() Get the class loader
getClasses() It returns an array, all objects of the class and the interface class public classes contained in the array
getDeclaredClasses() It returns an array of objects of the class and the interface class for all classes in the array comprises
  • Obtained class attributes associated methods
method use
getField(String name) Obtain a public property objects
getFields() Access to all public property objects
getDeclaredField(String name) Of a property objects
getDeclaredFields() Get all the properties of the object
  • The method of obtaining the relevant class notes
method use
getAnnotation(Class annotationClass) Public Returns annotation matching the objects of the class parameter type
getAnnotations() Return all objects of that class public comment
getDeclaredAnnotation(Class annotationClass) Return all objects of the class notes match the parameter type
getDeclaredAnnotations() The return of such objects All comments
  • The method of obtaining the relevant class constructor
method use
getConstructor(Class...<?> parameterTypes) Obtaining public class constructor parameter type matching
getConstructors() Get all the public constructors of the class
getDeclaredConstructor(Class...<?> parameterTypes) Obtaining a matching class constructor parameter type
getDeclaredConstructors() Get all the class constructor
  • Obtaining a class method RELATED
method use
getMethod(String name, Class...<?> parameterTypes) Obtaining of such a public way
getMethods() All such methods to obtain public
getDeclaredMethod(String name, Class...<?> parameterTypes) Obtaining of such a method
getDeclaredMethods() All methods to get the class
  • Other important methods in class
method use
isAnnotation() If annotation type Returns true
isAnnotationPresent(Class<? extends Annotation> annotationClass) If the specified type annotation type Returns true
isAnonymousClass() If anonymous class return true
isArray() If the class is an array return true
Isen () If the enumeration class return true
isInstance(Object obj) If obj is an instance of the class return true
isInterface() If the interface class return true
isLocalClass() If the class is a partial return true
isMemberClass() If inner class return true

(2) Field-based

method use
equals(Object obj) Attributes are equal Returns true obj
get(Object obj) Obtaining the corresponding property value obj
set(Object obj, Object value) 设置obj中对应属性值

(3)Method类

方法 用途
invoke(Object obj, Object... args) 传递object对象及参数调用该对象对应的方法

(4)Constructor类

方法 用途
newInstance(Object... initargs) 根据传递的参数创建类的对象

五、获取字节码文件

1. Object类的getClass()方法

对象.getClass()

多用于对象的获取字节码的方式

2. 静态属性class

类名.class

多用于参数的传递

3. Class类中静态方法forName()

class.forName("全类名")

多用于配置文件,将类名定义在配置文件中。读取文件,加载类。

注意:在运行期间,一个类,只有一个Class对象产生。

e.g.

class Person {
    String name;
    Integer age;

    Person() {
    }

    Person(String name, Integer age) {
        this.name = name;
        this.age = age;
    }
}

public class Test {
    public static void main(String[] args) throws ClassNotFoundException {
        Class clazz1 = Class.forName("Test.Person");
        Class clazz2 = Person.class;
        Class clazz3 = new Person().getClass();
        System.out.println(clazz1 == clazz2); // true
        System.out.println(clazz2 == clazz3); // true
        System.out.println(clazz1 == clazz3); // true

    }
}

六、获取构造器

方法 用途
getConstructor(Class...<?> parameterTypes) 获得该类中与参数类型匹配的公有构造方法
getConstructors() 获得该类的所有公有构造方法
getDeclaredConstructor(Class...<?> parameterTypes) 获得该类中与参数类型匹配的构造方法
getDeclaredConstructors() 获得该类所有构造方法

获取构造器后可执行操作

方法 用途
newInstance(Object... initargs) 根据传递的参数创建类的对象

1. 获取公有的构造器

class Person {
    String name;
    Integer age;

    Person() {
    }

    private Person(String name) {
        super();
        this.name = name;
    }

    public Person(String name, Integer age) {
        super();
        this.name = name;
        this.age = age;
    }

    @Override
    public String toString() {
        return "Person [name=" + name + ", age=" + age + "]";
    }

}

public class Test {
    public static void main(String[] args) throws Exception {
        // 1. 获取字节码
        Class<?> clazz = Class.forName("Test.Person");
        Constructor<?>[] cons = clazz.getConstructors();
        for (Constructor<?> con : cons) {
            System.out.println(con);
        }

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

        Constructor<?> con = clazz.getConstructor(String.class, Integer.class);
        System.out.println(con);
        Person p = (Person) con.newInstance("zsy", 20);
        System.out.println(p);
    }
}

运行结果

public Test.Person(java.lang.String,java.lang.Integer)
--------
public Test.Person(java.lang.String,java.lang.Integer)
Person [name=zs, age=20]

2. 获取私有的构造器

class Person {
    String name;
    Integer age;

    Person() {
    }

    private Person(String name) {
        super();
        this.name = name;
    }

    public Person(String name, Integer age) {
        super();
        this.name = name;
        this.age = age;
    }

    @Override
    public String toString() {
        return "Person [name=" + name + ", age=" + age + "]";
    }

}

public class Test {
    public static void main(String[] args) throws Exception {
        // 1. 获取字节码
        Class<?> clazz = Class.forName("Test.Person");
        Constructor<?>[] cons = clazz.getDeclaredConstructors();
        for (Constructor<?> con : cons) {
            System.out.println(con);
        }

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

        Constructor<?> con = clazz.getDeclaredConstructor(String.class);
        System.out.println(con);
        Person p = (Person) con.newInstance("zs");
        System.out.println(p);
    }
}

运行结果

public Test.Person(java.lang.String,java.lang.Integer)
private Test.Person(java.lang.String)
Test.Person()
--------
private Test.Person(java.lang.String)
抛出java.lang.IllegalAccessException

会发现在使用私有构造器时会报异常

  • 使用暴力反射,忽略访问权限修饰符的安全检查
class Person {
    String name;
    Integer age;

    Person() {
    }

    private Person(String name) {
        super();
        this.name = name;
    }

    public Person(String name, Integer age) {
        super();
        this.name = name;
        this.age = age;
    }

    @Override
    public String toString() {
        return "Person [name=" + name + ", age=" + age + "]";
    }

}

public class Test {
    public static void main(String[] args) throws Exception {
        // 1. 获取字节码
        Class<?> clazz = Class.forName("Test.Person");
        Constructor<?>[] cons = clazz.getDeclaredConstructors();
        for (Constructor<?> con : cons) {
            System.out.println(con);
        }

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

        Constructor<?> con = clazz.getDeclaredConstructor(String.class);
        System.out.println(con);
        con.setAccessible(true);
        Person p = (Person) con.newInstance("zs");
        System.out.println(p);
    }
}

运行结果

public Test.Person(java.lang.String,java.lang.Integer)
private Test.Person(java.lang.String)
Test.Person()
--------
private Test.Person(java.lang.String)
Person [name=zs, age=null]

3. 获取无参构造器

class Person {
    String name;
    Integer age;

    public Person() {
    }

    public Person(String name, Integer age) {
        super();
        this.name = name;
        this.age = age;
    }

    @Override
    public String toString() {
        return "Person [name=" + name + ", age=" + age + "]";
    }

}

public class Test {
    public static void main(String[] args) throws Exception {
        // 1. 获取字节码
        Class<?> clazz = Class.forName("Test.Person");
        Constructor<?> con = clazz.getConstructor();
        System.out.println(con);    // public Test.Person()
        Person p = (Person) con.newInstance();
        System.out.println(p);      // Person [name=null, age=null]
    }
}

获取无参构造器时,可以简化为

public class Test {
    public static void main(String[] args) throws Exception {
        // 1. 获取字节码
        Class<?> clazz = Class.forName("Test.Person");
        Person p = (Person) clazz.newInstance();
        System.out.println(p);      // Person [name=null, age=null]
    }
}

七、获取字段

方法 用途
getField(String name) 获得某个公有的属性对象
getFields() 获得所有公有的属性对象
getDeclaredField(String name) 获得某个属性对象
getDeclaredFields() 获得所有属性对象

获取字段后可执行操作

方法 用途
get(Object obj) 获得obj中对应的属性值
set(Object obj, Object value) 设置obj中对应属性值

1. 获取公有的字段

直接调用 clazz.getField("字段名称")

class Person {
    public String name;
    public Integer age;
    
    protected String a;
    String b;
    private String c;

    Person() {
    }

    Person(String name, Integer age) {
        this.name = name;
        this.age = age;
    }

    @Override
    public String toString() {
        return "Person [name=" + name + ", age=" + age + "]";
    }
    
}

public class Test {
    public static void main(String[] args) throws Exception {
        // 1. 获取字节码
        Class<?> clazz = Class.forName("Test.Person");
        Field[] fields = clazz.getFields();
        for (Field field : fields) {
            System.out.println(field);
        }
        
        System.out.println("--------");
        Field name = clazz.getField("name");
        Field age = clazz.getField("age");
        System.out.println(name);
        System.out.println(age);
        System.out.println("--------");
        Person p = (Person) clazz.newInstance();
        name.set(p, "zs");
        System.out.println(name.get(p));
    }
}

运行结果:

public java.lang.String Test.Person.name
public java.lang.Integer Test.Person.age
--------
public java.lang.String Test.Person.name
public java.lang.Integer Test.Person.age
--------
zs

2. 获取私有的字段

class Person {
    public String name;
    public Integer age;
    
    protected String a;
    String b;
    private String c;

    Person() {
    }

    Person(String name, Integer age) {
        this.name = name;
        this.age = age;
    }

    @Override
    public String toString() {
        return "Person [name=" + name + ", age=" + age + "]";
    }
    
}

public class Test {
    public static void main(String[] args) throws Exception {
        // 1. 获取字节码
        Class<?> clazz = Class.forName("Test.Person");
        Field[] fields = clazz.getDeclaredFields();
        for (Field field : fields) {
            System.out.println(field);
        }
        
        System.out.println("--------");
        Field name = clazz.getDeclaredField("name");
        Field age = clazz.getDeclaredField("age");
        Field a = clazz.getDeclaredField("a");
        Field b = clazz.getDeclaredField("b");
        Field c = clazz.getDeclaredField("c");
        System.out.println(name);
        System.out.println(age);
        System.out.println(a);
        System.out.println(b);
        System.out.println(c);
        System.out.println("--------");
        Person p = (Person) clazz.newInstance();
        name.set(p, "zs");
        System.out.println(name.get(p));
        a.set(p, "a");
        System.out.println(a.get(p));
        b.set(p, "b");
        System.out.println(b.get(p));
        c.set(p, "c");
        System.out.println(c.get(p));
    }
}

运行结果:

public java.lang.String Test.Person.name
public java.lang.Integer Test.Person.age
protected java.lang.String Test.Person.a
java.lang.String Test.Person.b
private java.lang.String Test.Person.c
--------
public java.lang.String Test.Person.name
public java.lang.Integer Test.Person.age
protected java.lang.String Test.Person.a
java.lang.String Test.Person.b
private java.lang.String Test.Person.c
--------
zs
a
b
抛出java.lang.IllegalAccessException

会发现在调用和设置私有字段时会报异常

  • 使用暴力反射,忽略访问权限修饰符的安全检查
class Person {
    private String c;

    Person() {
    }
    
}

public class Test {
    public static void main(String[] args) throws Exception {
        // 1. 获取字节码
        Class<?> clazz = Class.forName("Test.Person");
        Field c = clazz.getDeclaredField("c");
        System.out.println(c);  // private java.lang.String Test.Person.c

        Person p = (Person) clazz.newInstance();
        c.setAccessible(true);
        c.set(p, "c");
        System.out.println(c.get(p));   // C
    }
}

八、获取方法

方法 用途
getMethod(String name, Class...<?> parameterTypes) 获得该类某个公有的方法
getMethods() 获得该类所有公有的方法
getDeclaredMethod(String name, Class...<?> parameterTypes) 获得该类某个方法
getDeclaredMethods() 获得该类所有方法

获取方法后可执行操作

方法 用途
invoke(Object obj, Object... args) 传递object对象及参数调用该对象对应的方法

1. 获取公有的方法

class Person {
    String name;

    public void eat(String str) {
        System.out.println(name + "在吃" + str);
    }
    
    public void work() {
        System.out.println(name + "在工作");
    }
    
    private void walk() {
        System.out.println(name + "在行走");
    }   
}

public class Test {
    public static void main(String[] args) throws Exception {
        // 1. 获取字节码
        Class<?> clazz = Class.forName("Test.Person");
        Person p = (Person) clazz.newInstance();
        p.name = "张三";
        Method method = clazz.getMethod("work");
        method.invoke(p);   // 张三在工作
    }
}

2. 获取私有的方法

类似的,使用暴力反射,忽略访问权限修饰符的安全检查

class Person {
    String name;

    public void eat(String str) {
        System.out.println(name + "在吃" + str);
    }
    
    public void work() {
        System.out.println(name + "在工作");
    }
    
    private void walk() {
        System.out.println(name + "在行走");
    }   
}

public class Test {
    public static void main(String[] args) throws Exception {
        // 1. 获取字节码
        Class<?> clazz = Class.forName("Test.Person");
        Person p = (Person) clazz.newInstance();
        p.name = "张三";
        Method method = clazz.getDeclaredMethod("walk");
        method.setAccessible(true);
        method.invoke(p); // 张三在行走
    }
}

3. 获取有参的方法

class Person {
    String name;

    public void eat(String str) {
        System.out.println(name + "在吃" + str);
    }
}

public class Test {
    public static void main(String[] args) throws Exception {
        // 1. 获取字节码
        Class<?> clazz = Class.forName("Test.Person");
        Person p = (Person) clazz.newInstance();
        p.name = "张三";
        Method method = clazz.getMethod("eat", String.class);
        method.invoke(p, "苹果"); // 张三在吃苹果
    }
}

九、反射实例

设计一个可以创建任意类的对象,执行此对象中任意方法的框架。

public class Student {
    public String name = "zs";

    public void study() {
        System.out.println(name + "在学习");
    }

    public void eat(String str) {
        System.out.println(name + "在吃" + str);
    }

    public void parents(String father, String mother) {
        System.out.println(name + "的父亲是:" + father);
        System.out.println(name + "的母亲是:" + mother);
    }
}
public class Test {
    public static void main(String[] args) throws Exception {
        Properties props = new Properties();
        InputStream ins = Test.class.getClassLoader().getResourceAsStream("Test/pro.properties");
        props.load(ins);
        ins.close();
        String className = props.getProperty("className");
        String methodName = props.getProperty("methodName");
        String methodArgs = props.getProperty("methodArgs");
        String[] argList = methodArgs.isEmpty() ? null : methodArgs.split(",");

        Class<?> clazz = Class.forName(className);
        Object object = clazz.newInstance();
        Method[] methods = clazz.getMethods();
        for (Method method : methods) {
            if(method.getName().equals(methodName)) {
                method.invoke(object, argList);
            }
        }
    }
}
className=Test.Student
methodName=parents
methodArgs=ls,ww

加载配置文件

配置文件放置位置

一、通过文件路径加载

该方式必须知道文件的真实路径。

public class Test {
    public static void main(String[] args) throws Exception {
        String aName = getProperties("a.properties");
        String bName = getProperties("src/b.properties");
        String cName = getProperties("src/Test/file/c.properties");
        System.out.print(aName + "\n" + bName + "\n" + cName);
    }

    public static String getProperties(String path) throws Exception {
        InputStream ins = new FileInputStream(path);
        Properties props = new Properties();
        props.load(ins);
        ins.close();
        String className = props.getProperty("className");
        return className;
    }
}

二、通过getResourceAsStream加载

(1)获取src下的指定资源

Class.getResourceAsStream(String path)

  • path 不以’/'开头时默认是从此类所在的包下取资源

  • path 以’/'开头则是从ClassPath根下(即'/'代表src)获取

其只是通过path构造一个绝对路径,最终还是由ClassLoader获取资源。

a.在同级目录下

有类me.class,同时在同级目录下有资源文件 myfile.properties,则应使用:me.class.getResource("myfile.properties");

b.在子目录下

com.x.y 下有类me.class,同时在子目录 com.x.y.file 下有资源文件 myfile.properties,则应使用:me.class.getResource("file/myfile.properties");

c.不在同级目录和子目录下

com.x.y 下有类me.class,同时在 com.x.file 目录下有资源文件 myfile.properties ,则应使用:me.class.getResource("/com/x/file/myfile.properties");

public class Test {
    public static void main(String[] args) throws Exception {
        //String aName = getProperties("a.properties"); // 这里a不在src下,不能获取
        String bName = getProperties("../b.properties"); // 相对路径
        String cName = getProperties("/Test/file/c.properties"); // 绝对路径
        System.out.print(bName + "\n" + cName);
    }

    public static String getProperties(String path) throws Exception {
        // getResource 获取资源的绝对路径
        URL url = Test.class.getResource(path);
        System.out.println(url);
        // getResourceAsStream  获取资源的字节流
        InputStream ins = Test.class.getResourceAsStream(path);

        Properties props = new Properties();
        props.load(ins);
        ins.close();
        String className = props.getProperty("className");
        return className;
    }
}

(2)获取web项目下的指定资源

ServletContext.getResourceAsStream(String path)

默认从WebAPP根目录(即:要发布在服务器下的项目的根目录(与src同级的web文件夹下))下取资源

path是否以’/'开头无所谓。 例:

在web项目的根目录下有myfile.xml文件,则应该使用

getServleContext().getResourceAsStream("myfile.xml");

三、通过类加载的方式进行加载

Class.getClassLoader().getResourceAsStream(String path)

  • 默认则是从ClassPath根下获取,path不能以’/'开头,最终是由ClassLoader获取资源。

a.不在同级目录和子目录下

com.x.y 下有类me.class,同时在 com.x.file 目录下有资源文件 myfile.properties ,则应使用:me.class.getClassLoader().getResourceAsStream("com/x/file/myfile.properties");

public class Test {
    public static void main(String[] args) throws Exception {
        //String aName = getProperties("a.properties"); // 这里a不在src下,不能获取
        String bName = getProperties("b.properties");
        String cName = getProperties("Test/file/c.properties");
        System.out.print(bName + "\n" + cName);
    }

    public static String getProperties(String path) throws Exception {
        // getResource 获取资源的绝对路径
        URL url = Test.class.getClassLoader().getResource(path);
        System.out.println(url);
        // getResourceAsStream  获取资源的字节流
        InputStream ins = Test.class.getClassLoader().getResourceAsStream(path);

        Properties props = new Properties();
        props.load(ins);
        ins.close();
        String className = props.getProperty("className");
        return className;
    }
}

四、通过基名

文件必须是 key=value 的properties文件

public class Test {
    public static void main(String[] args) throws Exception {
        //String aName = getProperties("a"); // 这里a不在src下,不能获取
        String bName = getProperties("b");
        String cName = getProperties("Test/file/c");
        System.out.print(bName + "\n" + cName);
    }

    public static String getProperties(String path) throws Exception {
        ResourceBundle bundle = ResourceBundle.getBundle(path);
        String className = bundle.getString("className");
        return className;
    }
}

Guess you like

Origin www.cnblogs.com/xzh0717/p/11332003.html